@@ -5,65 +5,79 @@ import bundleCode from './bundle.mjs';
55
66/**
77 * Executes server-side JavaScript code in a safe, isolated context.
8- * This function takes a string of JavaScript code, bundles it , and then runs it
8+ * This function takes a Map of JavaScript code strings , bundles them together , and then runs each
99 * within a new Function constructor to prevent scope pollution and allow for
1010 * dynamic module loading via a provided `require` function.
1111 * The result of the server-side execution is expected to be assigned to a
1212 * dynamically generated variable name, which is then returned.
1313 *
14- * @param {string } serverCode - The server-side JavaScript code to execute as a string .
14+ * @param {Map< string, string> } serverCodeMap - Map of fileName to server-side JavaScript code.
1515 * @param {ReturnType<import('node:module').createRequire> } requireFn - A Node.js `require` function
16+ * @returns {Promise<Map<string, string>> } Map of fileName to dehydrated HTML content
1617 */
17- export async function executeServerCode ( serverCode , requireFn ) {
18- // Bundle the server-side code. This step resolves imports and prepares the code
19- // for execution, ensuring all necessary dependencies are self-contained.
20- const { js : bundledServer } = await bundleCode ( serverCode , { server : true } ) ;
21-
22- // Create a new Function from the bundled server code.
23- // The `require` argument is passed into the function's scope, allowing the
24- // `bundledServer` code to use it for dynamic imports.
25- const executedFunction = new Function ( 'require' , bundledServer ) ;
26-
27- // Execute the dynamically created function with the provided `requireFn`.
28- // The result of this execution is the dehydrated content from the server-side rendering.
29- return executedFunction ( requireFn ) ;
18+ export async function executeServerCode ( serverCodeMap , requireFn ) {
19+ // Execute each bundled server code and collect results
20+ const dehydratedMap = new Map ( ) ;
21+
22+ for ( const [ fileName , serverCode ] of serverCodeMap . entries ( ) ) {
23+ // Bundle all server-side code together. This step resolves imports and prepares the code
24+ // for execution, ensuring all necessary dependencies are self-contained.
25+ const { jsMap } = await bundleCode ( serverCode , { server : true } ) ;
26+
27+ // Create a new Function from the bundled server code.
28+ // The `require` argument is passed into the function's scope, allowing the
29+ // `bundledServer` code to use it for dynamic imports.
30+ const executedFunction = new Function ( 'require' , jsMap [ 'entrypoint.jsx' ] ) ;
31+
32+ // Execute the dynamically created function with the provided `requireFn`.
33+ // The result of this execution is the dehydrated content from the server-side rendering.
34+ dehydratedMap . set ( fileName , executedFunction ( requireFn ) ) ;
35+ }
36+
37+ return dehydratedMap ;
3038}
3139
3240/**
33- * Processes a single JSX AST (Abstract Syntax Tree) entry to generate a complete
34- * HTML page , including server-side rendered content, client-side JavaScript, and CSS.
41+ * Processes multiple JSX AST (Abstract Syntax Tree) entries to generate complete
42+ * HTML pages , including server-side rendered content, client-side JavaScript, and CSS.
3543 *
36- * @param {import('../jsx-ast/utils/buildContent.mjs').JSXContent } entry - The JSX AST entry to process.
44+ * @param {import('../jsx-ast/utils/buildContent.mjs').JSXContent[] } entries - The JSX AST entries to process.
3745 * @param {string } template - The HTML template string that serves as the base for the output page.
3846 * @param {ReturnType<import('./generate.mjs')> } astBuilders - The AST generators
3947 * @param {Object } options - Processing options
4048 * @param {string } options.version - The version to generate the documentation for
4149 * @param {ReturnType<import('node:module').createRequire> } requireFn - A Node.js `require` function.
4250 */
43- export async function processJSXEntry (
44- entry ,
51+ export async function processJSXEntries (
52+ entries ,
4553 template ,
4654 { buildServerProgram, buildClientProgram } ,
4755 requireFn ,
4856 { version }
4957) {
50- // `estree-util-to-js` with the `jsx` handler converts the AST nodes into a string
51- // that represents the equivalent JavaScript code, including JSX syntax.
52- const { value : code } = toJs ( entry , { handlers : jsx } ) ;
58+ // Convert all entries to JavaScript code
59+ const serverCodeMap = new Map ( ) ;
60+ const clientCodeMap = new Map ( ) ;
5361
54- // `buildServerProgram` takes the JSX-derived code and prepares it for server execution.
55- // `executeServerCode` then runs this code in a Node.js environment to produce
56- // the initial HTML content (dehydrated state) that will be sent to the client.
57- const serverCode = buildServerProgram ( code ) ;
62+ for ( const entry of entries ) {
63+ const fileName = `${ entry . data . api } .jsx` ;
5864
59- const dehydrated = await executeServerCode ( serverCode , requireFn ) ;
65+ // `estree-util-to-js` with the `jsx` handler converts the AST nodes into a string
66+ // that represents the equivalent JavaScript code, including JSX syntax.
67+ const { value : code } = toJs ( entry , { handlers : jsx } ) ;
6068
61- // `buildClientProgram` prepares the JSX-derived code for client-side execution.
62- // `bundleCode` then bundles this client-side code, resolving imports and
63- // potentially generating associated CSS. This bundle will hydrate the SSR content.
64- const clientCode = buildClientProgram ( code ) ;
69+ // `buildServerProgram` takes the JSX-derived code and prepares it for server execution.
70+ serverCodeMap . set ( fileName , buildServerProgram ( code ) ) ;
6571
66- const clientBundle = await bundleCode ( clientCode ) ;
72+ // `buildClientProgram` prepares the JSX-derived code for client-side execution.
73+ clientCodeMap . set ( fileName , buildClientProgram ( code ) ) ;
74+ }
75+
76+ // Execute all server code at once
77+ const dehydratedMap = await executeServerCode ( serverCodeMap , requireFn ) ;
78+
79+ // Bundle all client code at once
80+ const clientBundle = await bundleCode ( clientCodeMap ) ;
6781
6882 // Rolldown's experimental.chunkImportMap generates the import map automatically
6983 // The import map is extracted by our plugin and returned as HTML
@@ -77,22 +91,31 @@ export async function processJSXEntry(
7791 hash : '' , // No need for manual hashing, Rolldown handles cache busting via importMap
7892 } ) ) ;
7993
80- const title = `${ entry . data . heading . data . name } | Node.js v${ version } Documentation` ;
94+ // Process each entry to create HTML
95+ const results = entries . map ( entry => {
96+ const fileName = `${ entry . data . api } .jsx` ;
97+ const dehydrated = dehydratedMap . get ( fileName ) ;
98+ const mainJsCode = clientBundle . jsMap [ fileName ] ;
99+
100+ const title = `${ entry . data . heading . data . name } | Node.js v${ version } Documentation` ;
101+
102+ // Replace template placeholders with actual content
103+ const renderedHtml = template
104+ . replace ( '{{title}}' , title )
105+ . replace ( '{{dehydrated}}' , dehydrated ?? '' )
106+ . replace ( '{{importMap}}' , importMapScript )
107+ . replace ( '{{mainJsCode}}' , ( ) => mainJsCode ) ;
81108
82- // Replace template placeholders with actual content
83- const renderedHtml = template
84- . replace ( '{{title}}' , title )
85- . replace ( '{{dehydrated}}' , dehydrated ?? '' )
86- . replace ( '{{importMap}}' , importMapScript )
87- . replace ( '{{mainJsCode}}' , ( ) => clientBundle . js ) ;
109+ // The input to `minify` must be a Buffer.
110+ const finalHTMLBuffer = HTMLMinifier . minify ( Buffer . from ( renderedHtml ) , { } ) ;
88111
89- // The input to `minify` must be a Buffer.
90- const finalHTMLBuffer = HTMLMinifier . minify ( Buffer . from ( renderedHtml ) , { } ) ;
112+ return { html : finalHTMLBuffer , api : entry . data . api } ;
113+ } ) ;
91114
92115 // Return the generated HTML, CSS, and any JS chunks from code splitting
93116 // Note: main JS is inlined in HTML, so we don't return it separately
94117 return {
95- html : finalHTMLBuffer ,
118+ results ,
96119 css : clientBundle . css ,
97120 jsChunks : chunksWithHashes ,
98121 } ;
0 commit comments