Skip to content

Commit cbc2a99

Browse files
authored
fix(win): use windows-safe paths (#362)
1 parent 93e0cbe commit cbc2a99

File tree

4 files changed

+37
-27
lines changed

4 files changed

+37
-27
lines changed

src/generators/web/constants.mjs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
import { parse, relative, sep, dirname } from 'node:path';
2+
import { resolve } from 'node:path/posix';
3+
import { fileURLToPath } from 'node:url';
4+
5+
// Convert the current module's URL to a filesystem path,
6+
// then calculate the relative path from the system root directory
7+
// to this file. This relative path uses platform-specific separators,
8+
// so replace them with forward slashes ("/") for consistency and web compatibility.
9+
// Finally, prepend a leading slash to form an absolute root-relative path string.
10+
//
11+
// This produces a POSIX-style absolute path, even on Windows systems.
12+
const dir = dirname(fileURLToPath(import.meta.url));
13+
export const ROOT = '/' + relative(parse(dir).root, dir).replaceAll(sep, '/');
14+
115
/**
216
* @typedef {Object} JSXImportConfig
317
* @property {string} name - The name of the component to be imported.
@@ -12,19 +26,19 @@
1226
export const JSX_IMPORTS = {
1327
NavBar: {
1428
name: 'NavBar',
15-
source: new URL('./ui/components/NavBar', import.meta.url).pathname,
29+
source: resolve(ROOT, './ui/components/NavBar'),
1630
},
1731
SideBar: {
1832
name: 'SideBar',
19-
source: new URL('./ui/components/SideBar', import.meta.url).pathname,
33+
source: resolve(ROOT, './ui/components/SideBar'),
2034
},
2135
MetaBar: {
2236
name: 'MetaBar',
23-
source: new URL('./ui/components/MetaBar', import.meta.url).pathname,
37+
source: resolve(ROOT, './ui/components/MetaBar'),
2438
},
2539
CodeBox: {
2640
name: 'CodeBox',
27-
source: new URL('./ui/components/CodeBox', import.meta.url).pathname,
41+
source: resolve(ROOT, './ui/components/CodeBox'),
2842
},
2943
CodeTabs: {
3044
name: 'CodeTabs',

src/generators/web/utils/bundle.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ export default async function bundleCode(code, { server = false } = {}) {
3737
// External dependencies to exclude from bundling.
3838
// These are expected to be available at runtime in the server environment.
3939
// This reduces bundle size and avoids bundling shared server libs.
40-
external: server ? ['preact', '@node-core/ui-components'] : [],
40+
external: server
41+
? ['preact', 'preact-render-to-string', '@node-core/ui-components']
42+
: [],
4143

4244
// Inject global compile-time constants that will be replaced in code.
4345
// These are useful for tree-shaking and conditional branching.

src/generators/web/utils/generate.mjs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { JSX_IMPORTS } from '../constants.mjs';
1+
import { resolve } from 'node:path/posix';
2+
3+
import { JSX_IMPORTS, ROOT } from '../constants.mjs';
24

35
/**
46
* Creates an ES Module `import` statement as a string, based on parameters.
@@ -54,43 +56,35 @@ export default () => {
5456
// Import client-side CSS styles.
5557
// This ensures that styles used in the rendered app are loaded on the client.
5658
// The use of `new URL(...).pathname` resolves the absolute path for `entrypoint.jsx`.
57-
createImportDeclaration(
58-
null,
59-
new URL('../ui/index.css', import.meta.url).pathname
60-
),
59+
createImportDeclaration(null, resolve(ROOT, './ui/index.css')),
6160

6261
// Import `hydrate()` from Preact — needed to attach to server-rendered HTML.
6362
// This is a named import (not default), hence `false` as the third argument.
6463
createImportDeclaration('hydrate', 'preact', false),
6564

66-
'',
67-
6865
// Hydration call: binds the component to an element with ID "root"
6966
// This assumes SSR has placed matching HTML there, which, it has.
7067
`hydrate(${componentCode}, document.getElementById("root"));`,
71-
].join('\n');
68+
].join('');
7269
};
7370

7471
/**
7572
* Builds a server-side rendering (SSR) program.
7673
*
7774
* @param {string} componentCode - Code expression representing a JSX component
75+
* @param {string} variable - The variable to output it to
7876
*/
79-
const buildServerProgram = componentCode => {
77+
const buildServerProgram = (componentCode, variable) => {
8078
return [
8179
// JSX component imports
8280
...baseImports,
8381

84-
// Import `renderToStringAsync()` from Preact's SSR module
85-
createImportDeclaration(
86-
'renderToStringAsync',
87-
'preact-render-to-string',
88-
false
89-
),
82+
// Import Preact's SSR module
83+
createImportDeclaration('render', 'preact-render-to-string', false),
9084

9185
// Render the component to an HTML string
9286
// The output can be embedded directly into the server's HTML template
93-
`const code = renderToStringAsync(${componentCode});`,
87+
`const ${variable} = render(${componentCode});`,
9488
].join('\n');
9589
};
9690

src/generators/web/utils/processing.mjs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { toJs, jsx } from 'estree-util-to-js';
33

44
import bundleCode from './bundle.mjs';
55

6+
// Generate a unique variable name to capture the result of the server-side code.
7+
// This prevents naming conflicts.
8+
const SSRvariable = `_${Math.random().toString(36).slice(2)}`;
9+
610
/**
711
* Executes server-side JavaScript code in a safe, isolated context.
812
* This function takes a string of JavaScript code, bundles it, and then runs it
@@ -19,18 +23,14 @@ export async function executeServerCode(serverCode, requireFn) {
1923
// for execution, ensuring all necessary dependencies are self-contained.
2024
const { js: bundledServer } = await bundleCode(serverCode, { server: true });
2125

22-
// Generate a unique variable name to capture the result of the server-side code.
23-
// This prevents naming conflicts.
24-
const variable = `_${Math.random().toString(36).slice(2)}`;
25-
2626
// Create a new Function from the bundled server code.
2727
// The `require` argument is passed into the function's scope, allowing the
2828
// `bundledServer` code to use it for dynamic imports.
2929
// The `return ${variable};` statement ensures that the value assigned to
3030
// the dynamic variable within the `bundledServer` code is returned by this function.
3131
const executedFunction = new Function(
3232
'require',
33-
`let ${variable};${bundledServer}return ${variable};`
33+
`${bundledServer}\nreturn ${SSRvariable};`
3434
);
3535

3636
// Execute the dynamically created function with the provided `requireFn`.
@@ -62,7 +62,7 @@ export async function processJSXEntry(
6262
// `buildServerProgram` takes the JSX-derived code and prepares it for server execution.
6363
// `executeServerCode` then runs this code in a Node.js environment to produce
6464
// the initial HTML content (dehydrated state) that will be sent to the client.
65-
const serverCode = buildServerProgram(code);
65+
const serverCode = buildServerProgram(code, SSRvariable);
6666
const dehydrated = await executeServerCode(serverCode, requireFn);
6767

6868
// `buildClientProgram` prepares the JSX-derived code for client-side execution.

0 commit comments

Comments
 (0)