-
Notifications
You must be signed in to change notification settings - Fork 260
Description
Is your feature request or improvement related to a problem?
Yes. This improvement resolves a critical cross-platform compatibility bug that caused Markdown content to fail rendering on Windows systems.
The core problem was inconsistent handling of file path separators: Windows uses backslashes (\) while Unix-like systems use forward slashes (/). The framework was mixing these formats in internal logic, leading to:
- Broken file dependency resolution.
- Failure to load assets (CSS/JavaScript) and partials on Markdown pages.
- Incorrect hierarchical path comparisons for shared assets.
This resulted in Markdown pages appearing blank or unstyled when served on a Windows platform.
Solution you'd like
Implement a comprehensive, centralized path normalization strategy to ensure all internal file system operations and path comparisons consistently use the POSIX forward slash (/) separator.
This is achieved by introducing a toPosix() utility and applying it strategically at the entry points of file system operations, and updating all path manipulation logic to rely exclusively on /.
Proposed Code Changes
| File | Changes | Rationale |
|---|---|---|
packages/nuekit/src/tools/fswalk.js |
Add toPosix and apply to relativePath. |
Normalizes paths returned by fs at the earliest point. |
packages/nuekit/src/deps.js |
Add toPosix, normalize AUTO_INCLUDED, fix all comparisons and constructions. |
Ensures all dependency logic operates on consistent POSIX paths. |
packages/nuekit/src/file.js |
Fix separator use in getFileInfo() and getURL(). |
Corrects file metadata and URL generation. |
1. packages/nuekit/src/tools/fswalk.js
// Add toPosix normalization utility
function toPosix(path) {
return path.split(sep).join('/')
}
async function walkDirectory(root) {
// ... (existing code)
for (const name of names) {
// ... (existing code)
if (stat.isDirectory()) {
await walkDirectory(fullPath, fn, root)
} else {
let relativePath = relative(root, fullPath)
// Normalize to POSIX path separators
relativePath = toPosix(relativePath) // <--- CHANGE
fn(relativePath)
}
}
}2. packages/nuekit/src/deps.js
// Add toPosix utility
function toPosix(path) {
return path.split(sep).join('/')
}
// Normalize AUTO_INCLUDED paths
const AUTO_INCLUDED = ['data', 'design', 'ui'].map(dir => toPosix(join('@shared', dir))) // <--- CHANGE
export function isDep(dep, dir) {
// ... (existing code)
// Shared directory check:
if (dep.startsWith('@shared')) return true
// Check if file is inside a SPA directory
// Changed dir + sep to dir + '/'
if (dir && dep.startsWith(dir + '/')) return true // <--- CHANGE
// Changed dir + sep to dir + '/'
const shared = join('@shared', dir) + '/' // <--- CHANGE
// ... (existing code)
}
// Fixed hierarchical UI directory construction in getDeps()
const ui_dir = pageDir ? toPosix(join(pageDir, 'ui')) : 'ui' // <--- CHANGE
// Simplified parseDirs() function
export function parseDirs(dir) {
// Removed normalize() and sep. Use consistent '/'
const els = dir.split('/') // <--- CHANGE
return els.map((el, i) => els.slice(0, i + 1).join('/'))
}3. packages/nuekit/src/file.js
export function getFileInfo(file) {
// ... (existing code)
if (dir) {
// Changed from sep to '/' for consistent split
if (dir.includes('/')) info.basedir = dir.split('/')[0] // <--- CHANGE
info.dir = dir
}
return info
}
export function getURL(dir, name) {
// ... (existing code)
// Changed from sep to '/', added null check
const els = dir ? dir.split('/') : [] // <--- CHANGE
// ... (existing code)
}Alternatives you've considered
-
Platform-Specific Conditional Logic: Using
path.sepand checkingprocess.platform === 'win32'throughout the code.- Reason for rejection: This is far more complex, difficult to maintain, and would clutter the codebase. Normalizing to POSIX is cleaner and aligns with web standards for URLs.
-
Relying on built-in Node functions (e.g.,
path.normalize): These functions often revert to the native platform separator when returning a path, which would reintroduce the issue in components that rely on specific string comparisons.- Reason for rejection: A controlled, explicit normalization to the POSIX format (
/) across the entire internal pipeline is necessary to guarantee consistency, especially for string-based comparisons and URL construction.
- Reason for rejection: A controlled, explicit normalization to the POSIX format (
Additional context
The technical reasoning for this approach is cross-platform consistency. Using the POSIX forward slash (/) works on all operating systems and is the standard for web URLs. Normalizing paths early in the pipeline (at file system entry points) ensures that all downstream consumers (dependency resolution, URL generation) operate on clean, consistent data, eliminating the need for platform-specific branching.
This fix is crucial for Windows development and deployment environments and introduces no breaking changes to public APIs.
Verification: Test on both platforms:
- Windows: Navigate to
/docs/. Markdown must render correctly with all CSS/JS assets loaded. - Linux/macOS: Confirm no regression.
- Console: No path-related warnings or errors.
Final note
This entire work, including the root cause analysis, the path normalization strategy, and the specific code changes, was designed and implemented with the assistance of an AI. While the solution addresses the identified cross-platform compatibility issues, it is crucial that these changes be thoroughly tested across all target platforms (Windows, Linux, macOS). Deep verification is required to ensure the robustness of the new path handling logic and to confirm that no regressions have been introduced into existing functionality.