@cloudwerk/vite-plugin 0.6.7 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +95 -87
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -228,6 +228,6 @@ declare function generateServerEntry(manifest: RouteManifest, scanResult: ScanRe
|
|
|
228
228
|
* @param options - Resolved plugin options
|
|
229
229
|
* @returns Generated JavaScript code
|
|
230
230
|
*/
|
|
231
|
-
declare function generateClientEntry(clientComponents: Map<string, ClientComponentInfo>, cssImports: Map<string, CssImportInfo[]>, options: ResolvedCloudwerkOptions): string;
|
|
231
|
+
declare function generateClientEntry(clientComponents: Map<string, ClientComponentInfo>, cssImports: Map<string, CssImportInfo[]>, options: ResolvedCloudwerkOptions, manifest?: RouteManifest): string;
|
|
232
232
|
|
|
233
233
|
export { type AssetManifest, type AssetManifestEntry, type ClientComponentInfo, type CloudwerkVitePluginOptions, type CssImportInfo, type GenerateServerEntryOptions, RESOLVED_VIRTUAL_IDS, type ResolvedCloudwerkOptions, VIRTUAL_MODULE_IDS, cloudwerkPlugin, cloudwerkPlugin as default, generateClientEntry, generateServerEntry };
|
package/dist/index.js
CHANGED
|
@@ -140,11 +140,11 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
140
140
|
if (hasOptionalCatchAll) {
|
|
141
141
|
const basePath = route.urlPattern.replace(/\/:[^/]+\{\.\*\}$/, "") || "/";
|
|
142
142
|
pageRegistrations.push(
|
|
143
|
-
` registerPage(app, '${basePath}', ${varName}, [${layoutChain}], [${middlewareChain}], ${errorModule || "null"}, ${notFoundModule || "null"})`
|
|
143
|
+
` registerPage(app, '${basePath}', ${varName}, [${layoutChain}], [${middlewareChain}], ${errorModule || "null"}, ${notFoundModule || "null"}, '${route.urlPattern}')`
|
|
144
144
|
);
|
|
145
145
|
}
|
|
146
146
|
pageRegistrations.push(
|
|
147
|
-
` registerPage(app, '${route.urlPattern}', ${varName}, [${layoutChain}], [${middlewareChain}], ${errorModule || "null"}, ${notFoundModule || "null"})`
|
|
147
|
+
` registerPage(app, '${route.urlPattern}', ${varName}, [${layoutChain}], [${middlewareChain}], ${errorModule || "null"}, ${notFoundModule || "null"}, '${route.urlPattern}')`
|
|
148
148
|
);
|
|
149
149
|
} else if (route.fileType === "route") {
|
|
150
150
|
const varName = `route_${routeIndex++}`;
|
|
@@ -363,7 +363,7 @@ async function renderNotFoundPage(notFoundModule, layoutModules, layoutLoaderDat
|
|
|
363
363
|
|
|
364
364
|
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']
|
|
365
365
|
|
|
366
|
-
function registerPage(app, pattern, pageModule, layoutModules, middlewareModules, errorModule, notFoundModule) {
|
|
366
|
+
function registerPage(app, pattern, pageModule, layoutModules, middlewareModules, errorModule, notFoundModule, routeId) {
|
|
367
367
|
// Apply middleware (wrap with adapter to convert Cloudwerk middleware to Hono middleware)
|
|
368
368
|
for (const mw of middlewareModules) {
|
|
369
369
|
app.use(pattern, createMiddlewareAdapter(mw))
|
|
@@ -428,7 +428,7 @@ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules
|
|
|
428
428
|
}
|
|
429
429
|
|
|
430
430
|
// Render the page with hydration script injection
|
|
431
|
-
return await renderWithHydration(element)
|
|
431
|
+
return await renderWithHydration(element, 200, routeId, pageProps, layoutLoaderData)
|
|
432
432
|
} catch (error) {
|
|
433
433
|
// Handle NotFoundError (check both instanceof and name for module duplication)
|
|
434
434
|
if (error instanceof NotFoundError || error?.name === 'NotFoundError') {
|
|
@@ -460,7 +460,7 @@ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules
|
|
|
460
460
|
* - CSS links are injected before </head>
|
|
461
461
|
* - Vite client (dev) and hydration script are injected before </body>
|
|
462
462
|
*/
|
|
463
|
-
async function renderWithHydration(element, status = 200) {
|
|
463
|
+
async function renderWithHydration(element, status = 200, routeId, pageProps, layoutData) {
|
|
464
464
|
// Render element to HTML string using the active renderer
|
|
465
465
|
${rendererName === "react" ? `// React: use renderToString from react-dom/server
|
|
466
466
|
const { renderToString } = await import('react-dom/server')
|
|
@@ -478,7 +478,13 @@ async function renderWithHydration(element, status = 200) {
|
|
|
478
478
|
// Inject scripts before </body>
|
|
479
479
|
// - Vite client for HMR (dev only)
|
|
480
480
|
// - Hydration script for client components
|
|
481
|
-
|
|
481
|
+
let scripts = VITE_CLIENT
|
|
482
|
+
${rendererName === "react" ? `// React: embed serialized page data for full-tree hydration
|
|
483
|
+
if (routeId) {
|
|
484
|
+
const pageData = JSON.stringify({ routeId, pageProps: pageProps || {}, layoutData: layoutData || [] }).replace(/</g, '\\\\u003c')
|
|
485
|
+
scripts += '<script id="__CLOUDWERK_DATA__" type="application/json">' + pageData + '</script>'
|
|
486
|
+
}` : ""}
|
|
487
|
+
scripts += '<script type="module" src="${clientEntryPath}"></script>'
|
|
482
488
|
const bodyCloseRegex = /<\\/body>/i
|
|
483
489
|
if (bodyCloseRegex.test(html)) {
|
|
484
490
|
html = html.replace(bodyCloseRegex, scripts + '</body>')
|
|
@@ -1085,11 +1091,11 @@ function generateCssImportStatements(cssPaths) {
|
|
|
1085
1091
|
}
|
|
1086
1092
|
return cssPaths.map((cssPath) => `import '${cssPath}'`).join("\n") + "\n\n";
|
|
1087
1093
|
}
|
|
1088
|
-
function generateClientEntry(clientComponents, cssImports, options) {
|
|
1094
|
+
function generateClientEntry(clientComponents, cssImports, options, manifest) {
|
|
1089
1095
|
const { renderer, hydrationEndpoint, isProduction } = options;
|
|
1090
1096
|
const cssPaths = collectCssImports(cssImports);
|
|
1091
1097
|
if (renderer === "react") {
|
|
1092
|
-
return generateReactClientEntry(clientComponents, cssPaths, hydrationEndpoint, isProduction);
|
|
1098
|
+
return generateReactClientEntry(clientComponents, cssPaths, hydrationEndpoint, isProduction, manifest);
|
|
1093
1099
|
}
|
|
1094
1100
|
return generateHonoClientEntry(clientComponents, cssPaths, hydrationEndpoint, isProduction);
|
|
1095
1101
|
}
|
|
@@ -1280,105 +1286,102 @@ if (document.readyState === 'loading') {
|
|
|
1280
1286
|
export { hydrate }
|
|
1281
1287
|
`;
|
|
1282
1288
|
}
|
|
1283
|
-
function generateReactClientEntry(
|
|
1289
|
+
function generateReactClientEntry(_clientComponents, cssPaths, _hydrationEndpoint, isProduction = false, manifest) {
|
|
1284
1290
|
const cssImportStatements = generateCssImportStatements(cssPaths);
|
|
1285
|
-
if (
|
|
1286
|
-
return
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1291
|
+
if (!manifest) {
|
|
1292
|
+
return `/**
|
|
1293
|
+
* Generated Cloudwerk Client Entry (React - No Routes)
|
|
1294
|
+
* This file is auto-generated by @cloudwerk/vite-plugin - do not edit
|
|
1295
|
+
*/
|
|
1296
|
+
|
|
1297
|
+
${cssImportStatements}console.debug('[Cloudwerk] No route manifest available for React hydration')
|
|
1298
|
+
`;
|
|
1299
|
+
}
|
|
1300
|
+
const pageImports = [];
|
|
1301
|
+
const layoutImports = [];
|
|
1302
|
+
const routeMapEntries = [];
|
|
1303
|
+
const importedLayouts = /* @__PURE__ */ new Map();
|
|
1304
|
+
let pageIndex = 0;
|
|
1305
|
+
let layoutIndex = 0;
|
|
1306
|
+
for (const route of manifest.routes) {
|
|
1307
|
+
if (route.fileType !== "page") continue;
|
|
1308
|
+
const pageVar = `page_${pageIndex++}`;
|
|
1309
|
+
const importPath = isProduction ? route.absolutePath : `/@fs${route.absolutePath}`;
|
|
1310
|
+
pageImports.push(`import * as ${pageVar} from '${importPath}'`);
|
|
1311
|
+
const layoutVars = [];
|
|
1312
|
+
for (const layoutPath of route.layouts) {
|
|
1313
|
+
if (!importedLayouts.has(layoutPath)) {
|
|
1314
|
+
const layoutVar = `layout_${layoutIndex++}`;
|
|
1315
|
+
const layoutImportPath = isProduction ? layoutPath : `/@fs${layoutPath}`;
|
|
1316
|
+
layoutImports.push(`import * as ${layoutVar} from '${layoutImportPath}'`);
|
|
1317
|
+
importedLayouts.set(layoutPath, layoutVar);
|
|
1318
|
+
}
|
|
1319
|
+
layoutVars.push(importedLayouts.get(layoutPath));
|
|
1320
|
+
}
|
|
1321
|
+
routeMapEntries.push(
|
|
1322
|
+
` ${JSON.stringify(route.urlPattern)}: { page: ${pageVar}.default, layouts: [${layoutVars.map((v) => `${v}.default`).join(", ")}] }`
|
|
1323
|
+
);
|
|
1299
1324
|
}
|
|
1300
|
-
const bundleMap = Object.fromEntries(
|
|
1301
|
-
Array.from(clientComponents.values()).map((info) => [info.componentId, `/@fs${info.absolutePath}`])
|
|
1302
|
-
);
|
|
1303
1325
|
return `/**
|
|
1304
|
-
* Generated Cloudwerk Client Entry (React)
|
|
1326
|
+
* Generated Cloudwerk Client Entry (React - Full Tree Hydration)
|
|
1305
1327
|
* This file is auto-generated by @cloudwerk/vite-plugin - do not edit
|
|
1306
1328
|
*/
|
|
1307
1329
|
|
|
1308
|
-
${cssImportStatements}import { hydrateRoot
|
|
1330
|
+
${cssImportStatements}import { hydrateRoot } from 'react-dom/client'
|
|
1309
1331
|
import { createElement } from 'react'
|
|
1310
1332
|
|
|
1311
|
-
//
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
// Module cache to avoid re-importing
|
|
1315
|
-
const moduleCache = new Map()
|
|
1333
|
+
// Page imports
|
|
1334
|
+
${pageImports.join("\n")}
|
|
1316
1335
|
|
|
1317
|
-
//
|
|
1318
|
-
|
|
1336
|
+
// Layout imports
|
|
1337
|
+
${layoutImports.join("\n")}
|
|
1319
1338
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
async function loadComponent(bundlePath) {
|
|
1324
|
-
if (moduleCache.has(bundlePath)) {
|
|
1325
|
-
return moduleCache.get(bundlePath)
|
|
1326
|
-
}
|
|
1327
|
-
const module = await import(/* @vite-ignore */ bundlePath)
|
|
1328
|
-
moduleCache.set(bundlePath, module)
|
|
1329
|
-
return module
|
|
1339
|
+
// Route map: routeId -> { page, layouts }
|
|
1340
|
+
const routes = {
|
|
1341
|
+
${routeMapEntries.join(",\n")}
|
|
1330
1342
|
}
|
|
1331
1343
|
|
|
1332
1344
|
/**
|
|
1333
|
-
* Hydrate
|
|
1345
|
+
* Hydrate the full React tree from serialized page data.
|
|
1334
1346
|
*/
|
|
1335
|
-
|
|
1336
|
-
const
|
|
1337
|
-
if (
|
|
1338
|
-
console.debug('[Cloudwerk] No
|
|
1347
|
+
function hydrate() {
|
|
1348
|
+
const dataEl = document.getElementById('__CLOUDWERK_DATA__')
|
|
1349
|
+
if (!dataEl) {
|
|
1350
|
+
console.debug('[Cloudwerk] No page data found for React hydration')
|
|
1339
1351
|
return
|
|
1340
1352
|
}
|
|
1341
1353
|
|
|
1342
|
-
|
|
1354
|
+
let pageData
|
|
1355
|
+
try {
|
|
1356
|
+
pageData = JSON.parse(dataEl.textContent || '{}')
|
|
1357
|
+
} catch (e) {
|
|
1358
|
+
console.error('[Cloudwerk] Failed to parse page data:', e)
|
|
1359
|
+
return
|
|
1360
|
+
}
|
|
1343
1361
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1362
|
+
const route = routes[pageData.routeId]
|
|
1363
|
+
if (!route) {
|
|
1364
|
+
console.error('[Cloudwerk] Unknown route:', pageData.routeId)
|
|
1365
|
+
return
|
|
1366
|
+
}
|
|
1347
1367
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
continue
|
|
1351
|
-
}
|
|
1368
|
+
// Reconstruct the component tree (same as server)
|
|
1369
|
+
let element = createElement(route.page, pageData.pageProps || {})
|
|
1352
1370
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1371
|
+
// Wrap with layouts (inside-out, same order as server)
|
|
1372
|
+
for (let i = route.layouts.length - 1; i >= 0; i--) {
|
|
1373
|
+
const layoutProps = {
|
|
1374
|
+
children: element,
|
|
1375
|
+
params: pageData.pageProps?.params,
|
|
1376
|
+
...(pageData.layoutData?.[i] || {}),
|
|
1357
1377
|
}
|
|
1378
|
+
element = createElement(route.layouts[i], layoutProps)
|
|
1379
|
+
}
|
|
1358
1380
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
const module = await loadComponent(bundlePath)
|
|
1362
|
-
const Component = module.default
|
|
1363
|
-
|
|
1364
|
-
if (!Component) {
|
|
1365
|
-
console.error('[Cloudwerk] No default export in component:', componentId)
|
|
1366
|
-
continue
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
// Hydrate the component using React 18 hydrateRoot
|
|
1370
|
-
const root = hydrateRoot(el, createElement(Component, props))
|
|
1371
|
-
rootCache.set(el, root)
|
|
1372
|
-
|
|
1373
|
-
// Clean up hydration attributes
|
|
1374
|
-
el.removeAttribute('data-hydrate-id')
|
|
1375
|
-
el.removeAttribute('data-hydrate-props')
|
|
1381
|
+
// Hydrate the full tree
|
|
1382
|
+
hydrateRoot(document, element)
|
|
1376
1383
|
|
|
1377
|
-
|
|
1378
|
-
} catch (error) {
|
|
1379
|
-
console.error('[Cloudwerk] Failed to hydrate component:', componentId, error)
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1384
|
+
console.debug('[Cloudwerk] Full-tree hydration complete for route:', pageData.routeId)
|
|
1382
1385
|
}
|
|
1383
1386
|
|
|
1384
1387
|
// Run hydration when DOM is ready
|
|
@@ -2500,7 +2503,8 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2500
2503
|
state.clientEntryCache = generateClientEntry(
|
|
2501
2504
|
state.clientComponents,
|
|
2502
2505
|
state.cssImports,
|
|
2503
|
-
state.options
|
|
2506
|
+
state.options,
|
|
2507
|
+
state.manifest
|
|
2504
2508
|
);
|
|
2505
2509
|
}
|
|
2506
2510
|
return state.clientEntryCache;
|
|
@@ -2627,8 +2631,12 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2627
2631
|
bundlePath: id
|
|
2628
2632
|
// Use file path for Vite to resolve in dev mode
|
|
2629
2633
|
});
|
|
2630
|
-
if (!result.success
|
|
2631
|
-
console.warn(`[cloudwerk] ${result.error}`);
|
|
2634
|
+
if (!result.success) {
|
|
2635
|
+
console.warn(`[cloudwerk] Warning: Failed to wrap client component ${componentId}: ${result.error}`);
|
|
2636
|
+
return {
|
|
2637
|
+
code: transformedCode.replace(/['"]use client['"]\s*;?\s*\n?/g, ""),
|
|
2638
|
+
map: null
|
|
2639
|
+
};
|
|
2632
2640
|
}
|
|
2633
2641
|
return {
|
|
2634
2642
|
code: result.code,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudwerk/vite-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
4
4
|
"description": "Vite plugin for Cloudwerk file-based routing with virtual entry generation",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@swc/core": "^1.3.100",
|
|
22
22
|
"@cloudwerk/core": "^0.15.3",
|
|
23
|
-
"@cloudwerk/ui": "^0.15.
|
|
23
|
+
"@cloudwerk/ui": "^0.15.6"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"vite": "^5.0.0 || ^6.0.0",
|