@cfdez11/vex 0.9.0 → 0.10.0
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/bin/vex.js +1 -1
- package/dist/client/services/navigation/create-layouts.js +1 -1
- package/dist/client/services/navigation/navigate.js +1 -1
- package/dist/server/build-static.js +1 -1
- package/dist/server/utils/component-processor.js +2 -2
- package/dist/server/utils/files.js +1 -1
- package/package.json +1 -1
package/dist/bin/vex.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn}from"child_process";import{fileURLToPath}from"url";import path from"path";const __dirname=path.dirname(fileURLToPath(import.meta.url)),serverDir=path.resolve(__dirname,"..","server"),[command]=process.argv.slice(2),commands={dev:()=>spawn("node",["--watch",path.join(serverDir,"index.js")],{stdio:"inherit"}),build:()=>spawn("node",[path.join(serverDir,"prebuild.js")],{stdio:"inherit"}),start:()=>spawn("node",[path.join(serverDir,"index.js")],{stdio:"inherit",env:{...process.env,NODE_ENV:"production"}}),"build:static":()=>spawn("node",[path.join(serverDir,"build-static.js")],{stdio:"inherit"})};commands[command]||(console.error(`Unknown command: "${command}"
|
|
2
|
+
import{spawn}from"child_process";import{fileURLToPath}from"url";import path from"path";const __dirname=path.dirname(fileURLToPath(import.meta.url)),serverDir=path.resolve(__dirname,"..","server"),[command]=process.argv.slice(2),commands={dev:()=>spawn("node",["--watch",path.join(serverDir,"index.js")],{stdio:"inherit"}),build:()=>spawn("node",[path.join(serverDir,"prebuild.js")],{stdio:"inherit"}),start:()=>spawn("node",[path.join(serverDir,"index.js")],{stdio:"inherit",env:{...process.env,NODE_ENV:"production"}}),"build:static":()=>spawn("node",[path.join(serverDir,"build-static.js")],{stdio:"inherit",env:{...process.env,NODE_ENV:"production"}})};commands[command]||(console.error(`Unknown command: "${command}"
|
|
3
3
|
Available: dev, build, build:static, start`),process.exit(1));const child=commands[command]();child.on("exit",code=>process.exit(code??0));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function createLayoutRenderer(){const renderedLayouts=new Map;function cleanNotNeeded(routeLayouts){for(const name of renderedLayouts.keys())routeLayouts.some(l=>l.name===name)||renderedLayouts.delete(name)}function getNearestRendered(routeLayouts){const reversed=routeLayouts.toReversed();for(const layout of reversed)if(renderedLayouts.has(layout.name))return renderedLayouts.get(layout.name);return null}function getLayoutsToRender(routeLayouts,nearestRendered){if(!nearestRendered)return routeLayouts;const reversed=routeLayouts.toReversed(),idx=reversed.findIndex(l=>l.name===nearestRendered.name);return idx===-1?routeLayouts:reversed.slice(0,idx)}async function loadLayoutModules(layouts){return Promise.all(layouts.map(layout=>import(layout.importPath)))}async function generate({routeLayouts=[],pageNode,metadata}){if(!pageNode||routeLayouts.length===0)return{layoutId:null,node:pageNode,metadata};cleanNotNeeded(routeLayouts);const nearestRendered=getNearestRendered(routeLayouts),layoutsToRender=getLayoutsToRender(routeLayouts,nearestRendered),modules=await loadLayoutModules(layoutsToRender);let htmlContainerNode=pageNode,deepestMetadata=metadata;for(let i=modules.length-1;i>=0;i--){const layout=layoutsToRender[i],mod=modules[i],
|
|
1
|
+
function createLayoutRenderer(){const renderedLayouts=new Map;function cleanNotNeeded(routeLayouts){for(const name of renderedLayouts.keys())routeLayouts.some(l=>l.name===name)||renderedLayouts.delete(name)}function getNearestRendered(routeLayouts){const reversed=routeLayouts.toReversed();for(const layout of reversed)if(renderedLayouts.has(layout.name))return renderedLayouts.get(layout.name);return null}function getLayoutsToRender(routeLayouts,nearestRendered){if(!nearestRendered)return routeLayouts;const reversed=routeLayouts.toReversed(),idx=reversed.findIndex(l=>l.name===nearestRendered.name);return idx===-1?routeLayouts:reversed.slice(0,idx)}async function loadLayoutModules(layouts){return Promise.all(layouts.map(layout=>import(layout.importPath)))}async function generate({routeLayouts=[],pageNode,metadata}){if(!pageNode||routeLayouts.length===0)return{layoutId:null,node:pageNode,metadata};cleanNotNeeded(routeLayouts);const nearestRendered=getNearestRendered(routeLayouts),layoutsToRender=getLayoutsToRender(routeLayouts,nearestRendered),modules=await loadLayoutModules(layoutsToRender);let htmlContainerNode=pageNode,deepestMetadata=metadata;for(let i=modules.length-1;i>=0;i--){const layout=layoutsToRender[i],mod=modules[i],childrenWrapper=document.createElement("vex-root");childrenWrapper.appendChild(htmlContainerNode);const marker=document.createElement("template");htmlContainerNode=mod.hydrateClientComponent(marker,{children:childrenWrapper}),!deepestMetadata&&mod.metadata&&(deepestMetadata=mod.metadata),renderedLayouts.set(layout.name,{name:layout.name,children:childrenWrapper,node:htmlContainerNode})}return{layoutId:nearestRendered?.name??null,node:htmlContainerNode,metadata:deepestMetadata}}function patch(layoutId,node){const record=renderedLayouts.get(layoutId);record&&record.children.replaceChildren(node)}function reset(){renderedLayouts.clear()}return{generate,patch,reset}}export{createLayoutRenderer};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{findRouteWithParams}from"./router.js";import{updateRouteParams}from"./use-route-params.js";import{renderPage}from"./render-page.js";import{renderSSRPage}from"./render-ssr.js";async function navigateInternal({path,addToHistory,controller,layoutRenderer,onFinish}){updateRouteParams(path);const routePath=path.split("?")[0],{route}=findRouteWithParams(routePath);addToHistory&&history.pushState({},"",path);try{if(route?.meta?.ssr){layoutRenderer.reset(),await renderSSRPage(path,controller.signal);return}if(route?.meta?.requiresAuth&&!app.Store?.loggedIn){location.href="/account/login";return}if(route?.meta?.guestOnly&&app.Store?.loggedIn){location.href="/account";return}await renderPage({route,layoutRenderer})}finally{onFinish()}}export{navigateInternal};
|
|
1
|
+
import{findRouteWithParams}from"./router.js";import{routes}from"../_routes.js";import{updateRouteParams}from"./use-route-params.js";import{renderPage}from"./render-page.js";import{renderSSRPage}from"./render-ssr.js";async function navigateInternal({path,addToHistory,controller,layoutRenderer,onFinish}){updateRouteParams(path);const routePath=path.split("?")[0],{route:matchedRoute}=findRouteWithParams(routePath),route=matchedRoute??routes.find(r=>r.isNotFound)??null;addToHistory&&history.pushState({},"",path);try{if(route?.meta?.ssr){layoutRenderer.reset(),await renderSSRPage(path,controller.signal);return}if(route?.meta?.requiresAuth&&!app.Store?.loggedIn){location.href="/account/login";return}if(route?.meta?.guestOnly&&app.Store?.loggedIn){location.href="/account";return}await renderPage({route,layoutRenderer})}finally{onFinish()}}export{navigateInternal};
|
|
@@ -3,4 +3,4 @@ import"dotenv/config";import fs from"fs/promises";import path from"path";import{
|
|
|
3
3
|
</head>`),await fs.writeFile(path.join(DIST_DIR,"index.html"),shell,"utf-8"),console.log("\u{1F4E6} Copying framework assets...");for(const asset of["favicon.ico","app.webmanifest"])try{await fs.copyFile(path.join(CLIENT_DIR,asset),path.join(DIST_DIR,"_vexjs",asset))}catch{}console.log("\u{1F4E6} Copying services..."),await fs.cp(path.join(GENERATED_DIR,"services"),path.join(DIST_DIR,"_vexjs","services"),{recursive:!0}),console.log("\u{1F4E6} Copying component bundles..."),await fs.cp(path.join(GENERATED_DIR,"_components"),path.join(DIST_DIR,"_vexjs","_components"),{recursive:!0}),console.log("\u{1F4E6} Copying pre-bundled user JS files...");try{await fs.cp(USER_GENERATED_DIR,path.join(DIST_DIR,"_vexjs","user"),{recursive:!0})}catch{}console.log("\u{1F4E6} Copying public assets...");const publicDir=path.join(PROJECT_ROOT,"public");try{await fs.cp(publicDir,DIST_DIR,{recursive:!0})}catch{}const CACHE_DIR=path.join(GENERATED_DIR,"_cache"),ssgRoutes=serverRoutes.filter(r=>r.meta.revalidate==="never"||r.meta.revalidate===!1);if(ssgRoutes.length>0){console.log("\u{1F4C4} Copying pre-rendered SSG pages...");for(const route of ssgRoutes){const cacheFile=path.join(CACHE_DIR,`${generateComponentId(route.serverPath)}.html`);try{const html=await fs.readFile(cacheFile,"utf-8"),routeSegment=route.serverPath==="/"?"":route.serverPath,destPath=path.join(DIST_DIR,routeSegment,"index.html");await fs.mkdir(path.dirname(destPath),{recursive:!0}),await fs.writeFile(destPath,html,"utf-8"),console.log(` \u2713 ${route.serverPath}`)}catch{console.warn(` \u2717 ${route.serverPath} (no cached HTML found)`)}}}const ssrOnlyRoutes=serverRoutes.filter(r=>r.meta.ssr);if(ssrOnlyRoutes.length>0){console.warn(`
|
|
4
4
|
\u26A0\uFE0F The following routes require a server and were NOT included in the static build:`);for(const r of ssrOnlyRoutes)console.warn(` ${r.path} (SSR)`);console.warn(` These routes will show a 404 in the static build.
|
|
5
5
|
`)}console.log("\u2705 Static build complete! Output: dist/"),console.log(`
|
|
6
|
-
To serve locally: npx serve dist`),console.log("Static host note: configure your host to serve dist/index.html for all 404s (SPA fallback).");
|
|
6
|
+
To serve locally: npx serve dist`),console.log("Static host note: configure your host to serve dist/index.html for all 404s (SPA fallback)."),process.exit(0);
|
|
@@ -35,7 +35,7 @@ export function hydrateClientComponent(marker, incomingProps = {}) {
|
|
|
35
35
|
effect(() => render());
|
|
36
36
|
return wrapper;
|
|
37
37
|
}
|
|
38
|
-
`.trim(),outfile=path.join(CLIENT_COMPONENTS_DIR,`${componentName}.js`);return await esbuild.build({stdin:{contents:entrySource,resolveDir:componentFilePath?path.dirname(componentFilePath):CLIENT_COMPONENTS_DIR},bundle:!0,outfile,format:"esm",platform:"browser",plugins:[createVexAliasPlugin()],logLevel:"silent"}),null}function getIfPageCanCSR(revalidate,hasServerComponents,hasGetData){const neverRevalidate=getRevalidateSeconds(revalidate??0)===-1;return!hasServerComponents&&(neverRevalidate||!hasGetData)}async function generateServerComponentHTML(componentPath){const{getStaticPaths,getData,getMetadata,serverComponents,...restProcessHtmlFile}=await processHtmlFile(componentPath),metadata=getMetadata?await getMetadata({req:{params:{}},props:{}}):null,canCSR=getIfPageCanCSR(metadata?.revalidate,serverComponents.size>0,typeof getData=="function"),paths=getStaticPaths?await getStaticPaths():[],result={htmls:[],canCSR,metadata,getStaticPaths,getData,getMetadata,serverComponents,...restProcessHtmlFile};if(!componentPath.includes(PAGES_DIR))return result;if(paths.length===0&&getData){const{html,pageHtml,metadata:pageMetadata}=await renderPageWithLayout(componentPath,{},!0);return result.htmls.push({params:{},html,pageHtml,metadata:pageMetadata}),result}for(const path2 of paths){const{html,pageHtml,metadata:metadata2}=await renderPageWithLayout(componentPath,{req:path2},!0);result.htmls.push({params:path2.params,html,pageHtml,metadata:metadata2})}return result}async function processClientComponent(componentName,componentAbsPath,props={}){const targetId=`client-${componentName}-${Date.now()}`,componentImport=generateComponentId(componentAbsPath),propsJson=serializeClientComponentProps(props);return`<template id="${targetId}" data-client:component="${componentImport}" data-client:props='${propsJson}'></template>`}function isTemplateExpression(value){return typeof value=="string"&&/^\$\{[\s\S]+\}$/.test(value.trim())}function serializeRuntimePropValue(value){return isTemplateExpression(value)?value.trim().slice(2,-1).trim():JSON.stringify(value)}function serializeClientComponentProps(props={}){return Object.values(props).some(isTemplateExpression)?`\${JSON.stringify({ ${Object.entries(props).map(([key,value])=>`${JSON.stringify(key)}: ${serializeRuntimePropValue(value)}`).join(", ")} })}`:JSON.stringify(props)}function extractVPropsObject(clientCode){const match=clientCode.match(/xprops\s*\(\s*(\{[\s\S]*?\})\s*\)/);return match?match[1]:null}function extractVPropsDefaults(clientCode){const xpropsLiteral=extractVPropsObject(clientCode);if(!xpropsLiteral)return{};const xpropsDef=safeObjectEval(xpropsLiteral),defaults={};for(const key in xpropsDef){const def=xpropsDef[key];def&&typeof def=="object"&&"default"in def&&(defaults[key]=def.default)}return defaults}function safeObjectEval(objectLiteral){return Function(`"use strict"; return (${objectLiteral})`)()}function applyDefaultProps(xpropsDefined,componentProps){const finalProps={};for(const key in xpropsDefined){const def=xpropsDefined[key];key in componentProps?finalProps[key]=componentProps[key]:"default"in def?finalProps[key]=def.default:finalProps[key]=void 0}return finalProps}function computeProps(clientCode,componentProps){const xpropsLiteral=extractVPropsObject(clientCode);if(!xpropsLiteral)return componentProps;const xpropsDefined=safeObjectEval(xpropsLiteral);return applyDefaultProps(xpropsDefined,componentProps)}function addComputedProps(clientCode,componentProps){const xpropsRegex=/const\s+props\s*=\s*xprops\s*\([\s\S]*?\)\s*;?/;if(!xpropsRegex.test(clientCode))return clientCode;const computedProps=computeProps(clientCode,componentProps);return clientCode.replace(xpropsRegex,`const props = { ...${JSON.stringify(computedProps)}, ...incomingProps };`)}async function getMetadataAndStaticPaths(getMetadata,getStaticPaths){const promises=[];getMetadata&&promises.push(getMetadata({req:{params:{}},props:{}})),getStaticPaths&&promises.push(getStaticPaths());const[metadata,paths]=await Promise.all(promises);return{metadata:metadata||DEFAULT_METADATA,paths:paths||[]}}function fillRoute(route,params){return route.replace(/:([a-zA-Z0-9_]+)/g,(_,key)=>{if(params[key]===void 0)throw new Error(`Missing parameter "${key}"`);return params[key]})}async function saveClientComponent({metadata,clientCode,template,clientImports,clientComponents,componentName,componentFilePath}){await generateClientComponentModule({metadata,clientCode,template,clientImports,clientComponents,componentFilePath,componentName})}const processedComponentsInBuild=new Set;async function generateComponentAndFillCache(filePath){if(processedComponentsInBuild.has(filePath))return"Already processed";processedComponentsInBuild.add(filePath);const urlPath=getRoutePath(filePath),{template,htmls:serverHtmls,canCSR,clientImports,metadata,clientCode,clientComponents,serverComponents}=await generateServerComponentHTML(filePath),saveServerHtmlsPromises=[],saveClientHtmlPromises=[],saveComponentsPromises=[];if(serverHtmls.length)for(const{params,html,pageHtml,metadata:pageMetadata}of serverHtmls){const cacheKey=fillRoute(urlPath,params);saveServerHtmlsPromises.push(saveComponentHtmlDisk({componentPath:cacheKey,html})),canCSR&&saveServerHtmlsPromises.push(saveClientComponent({metadata:pageMetadata,clientCode,template:pageHtml,clientImports,clientComponents,componentName:generateComponentId(cacheKey),componentFilePath:filePath}))}if(canCSR&&serverHtmls.length===0&&saveClientHtmlPromises.push(saveClientComponent({metadata,clientCode,template,clientImports,clientComponents,componentName:generateComponentId(urlPath),componentFilePath:filePath})),serverComponents.size>0){const serverComponentPaths=Array.from(serverComponents.values()).map(({path:path2})=>path2);saveComponentsPromises.push(...serverComponentPaths.map(generateComponentAndFillCache))}if(clientComponents.size>0){const clientComponentPaths=Array.from(clientComponents.values()).map(({path:path2})=>path2);saveComponentsPromises.push(...clientComponentPaths.map(generateComponentAndFillCache))}return await Promise.all([...saveServerHtmlsPromises,...saveClientHtmlPromises,...saveComponentsPromises]),"Component generated"}async function generateComponentsAndFillCache(){processedComponentsInBuild.clear();const generateComponentsPromises=(await getPageFiles({layouts:!0})).map(file=>generateComponentAndFillCache(file.fullpath));return await Promise.all(generateComponentsPromises),"Components generation completed"}async function getRouteFileData(file){const data={serverRoutes:[],clientRoutes:[]},[processedFileData,layoutPaths]=await Promise.all([processHtmlFile(file.fullpath),getLayoutPaths(file.fullpath)]),{getData,getMetadata,getStaticPaths,serverComponents}=processedFileData,filePath=getOriginalRoutePath(file.fullpath),urlPath=getRoutePath(file.fullpath),{metadata,paths}=await getMetadataAndStaticPaths(getMetadata,getStaticPaths),canCSR=getIfPageCanCSR(metadata?.revalidate,serverComponents.size>0,typeof getData=="function");if(data.serverRoutes.push({path:filePath,serverPath:urlPath,isNotFound:file.path.includes("/not-found/"),meta:{ssr:!canCSR,requiresAuth:!1,revalidate:metadata?.revalidate??0}}),!canCSR)return data.clientRoutes.push(`{
|
|
38
|
+
`.trim(),outfile=path.join(CLIENT_COMPONENTS_DIR,`${componentName}.js`),isProd=process.env.NODE_ENV==="production";return await esbuild.build({stdin:{contents:entrySource,resolveDir:componentFilePath?path.dirname(componentFilePath):CLIENT_COMPONENTS_DIR},bundle:!0,outfile,format:"esm",platform:"browser",plugins:[createVexAliasPlugin()],minify:isProd,logLevel:"silent"}),null}function getIfPageCanCSR(revalidate,hasServerComponents,hasGetData){const neverRevalidate=getRevalidateSeconds(revalidate??0)===-1;return!hasServerComponents&&(neverRevalidate||!hasGetData)}async function generateServerComponentHTML(componentPath){const{getStaticPaths,getData,getMetadata,serverComponents,...restProcessHtmlFile}=await processHtmlFile(componentPath),metadata=getMetadata?await getMetadata({req:{params:{}},props:{}}):null,canCSR=getIfPageCanCSR(metadata?.revalidate,serverComponents.size>0,typeof getData=="function"),paths=getStaticPaths?await getStaticPaths():[],result={htmls:[],canCSR,metadata,getStaticPaths,getData,getMetadata,serverComponents,...restProcessHtmlFile};if(!componentPath.includes(PAGES_DIR))return result;if(paths.length===0&&getData){const{html,pageHtml,metadata:pageMetadata}=await renderPageWithLayout(componentPath,{},!0);return result.htmls.push({params:{},html,pageHtml,metadata:pageMetadata}),result}for(const path2 of paths){const{html,pageHtml,metadata:metadata2}=await renderPageWithLayout(componentPath,{req:path2},!0);result.htmls.push({params:path2.params,html,pageHtml,metadata:metadata2})}return result}async function processClientComponent(componentName,componentAbsPath,props={}){const targetId=`client-${componentName}-${Date.now()}`,componentImport=generateComponentId(componentAbsPath),propsJson=serializeClientComponentProps(props);return`<template id="${targetId}" data-client:component="${componentImport}" data-client:props='${propsJson}'></template>`}function isTemplateExpression(value){return typeof value=="string"&&/^\$\{[\s\S]+\}$/.test(value.trim())}function serializeRuntimePropValue(value){return isTemplateExpression(value)?value.trim().slice(2,-1).trim():JSON.stringify(value)}function serializeClientComponentProps(props={}){return Object.values(props).some(isTemplateExpression)?`\${JSON.stringify({ ${Object.entries(props).map(([key,value])=>`${JSON.stringify(key)}: ${serializeRuntimePropValue(value)}`).join(", ")} })}`:JSON.stringify(props)}function extractVPropsObject(clientCode){const match=clientCode.match(/xprops\s*\(\s*(\{[\s\S]*?\})\s*\)/);return match?match[1]:null}function extractVPropsDefaults(clientCode){const xpropsLiteral=extractVPropsObject(clientCode);if(!xpropsLiteral)return{};const xpropsDef=safeObjectEval(xpropsLiteral),defaults={};for(const key in xpropsDef){const def=xpropsDef[key];def&&typeof def=="object"&&"default"in def&&(defaults[key]=def.default)}return defaults}function safeObjectEval(objectLiteral){return Function(`"use strict"; return (${objectLiteral})`)()}function applyDefaultProps(xpropsDefined,componentProps){const finalProps={};for(const key in xpropsDefined){const def=xpropsDefined[key];key in componentProps?finalProps[key]=componentProps[key]:"default"in def?finalProps[key]=def.default:finalProps[key]=void 0}return finalProps}function computeProps(clientCode,componentProps){const xpropsLiteral=extractVPropsObject(clientCode);if(!xpropsLiteral)return componentProps;const xpropsDefined=safeObjectEval(xpropsLiteral);return applyDefaultProps(xpropsDefined,componentProps)}function addComputedProps(clientCode,componentProps){const xpropsRegex=/const\s+props\s*=\s*xprops\s*\([\s\S]*?\)\s*;?/;if(!xpropsRegex.test(clientCode))return clientCode;const computedProps=computeProps(clientCode,componentProps);return clientCode.replace(xpropsRegex,`const props = { ...${JSON.stringify(computedProps)}, ...incomingProps };`)}async function getMetadataAndStaticPaths(getMetadata,getStaticPaths){const promises=[];getMetadata&&promises.push(getMetadata({req:{params:{}},props:{}})),getStaticPaths&&promises.push(getStaticPaths());const[metadata,paths]=await Promise.all(promises);return{metadata:metadata||DEFAULT_METADATA,paths:paths||[]}}function fillRoute(route,params){return route.replace(/:([a-zA-Z0-9_]+)/g,(_,key)=>{if(params[key]===void 0)throw new Error(`Missing parameter "${key}"`);return params[key]})}async function saveClientComponent({metadata,clientCode,template,clientImports,clientComponents,componentName,componentFilePath}){await generateClientComponentModule({metadata,clientCode,template,clientImports,clientComponents,componentFilePath,componentName})}const processedComponentsInBuild=new Set;async function generateComponentAndFillCache(filePath){if(processedComponentsInBuild.has(filePath))return"Already processed";processedComponentsInBuild.add(filePath);const urlPath=getRoutePath(filePath),{template,htmls:serverHtmls,canCSR,clientImports,metadata,clientCode,clientComponents,serverComponents}=await generateServerComponentHTML(filePath),saveServerHtmlsPromises=[],saveClientHtmlPromises=[],saveComponentsPromises=[];if(serverHtmls.length)for(const{params,html,pageHtml,metadata:pageMetadata}of serverHtmls){const cacheKey=fillRoute(urlPath,params);saveServerHtmlsPromises.push(saveComponentHtmlDisk({componentPath:cacheKey,html})),canCSR&&saveServerHtmlsPromises.push(saveClientComponent({metadata:pageMetadata,clientCode,template:pageHtml,clientImports,clientComponents,componentName:generateComponentId(cacheKey),componentFilePath:filePath}))}if(canCSR&&serverHtmls.length===0&&saveClientHtmlPromises.push(saveClientComponent({metadata,clientCode,template,clientImports,clientComponents,componentName:generateComponentId(urlPath),componentFilePath:filePath})),serverComponents.size>0){const serverComponentPaths=Array.from(serverComponents.values()).map(({path:path2})=>path2);saveComponentsPromises.push(...serverComponentPaths.map(generateComponentAndFillCache))}if(clientComponents.size>0){const clientComponentPaths=Array.from(clientComponents.values()).map(({path:path2})=>path2);saveComponentsPromises.push(...clientComponentPaths.map(generateComponentAndFillCache))}return await Promise.all([...saveServerHtmlsPromises,...saveClientHtmlPromises,...saveComponentsPromises]),"Component generated"}async function generateComponentsAndFillCache(){processedComponentsInBuild.clear();const generateComponentsPromises=(await getPageFiles({layouts:!0})).map(file=>generateComponentAndFillCache(file.fullpath));return await Promise.all(generateComponentsPromises),"Components generation completed"}async function getRouteFileData(file){const data={serverRoutes:[],clientRoutes:[]},[processedFileData,layoutPaths]=await Promise.all([processHtmlFile(file.fullpath),getLayoutPaths(file.fullpath)]),{getData,getMetadata,getStaticPaths,serverComponents}=processedFileData,filePath=getOriginalRoutePath(file.fullpath),urlPath=getRoutePath(file.fullpath),{metadata,paths}=await getMetadataAndStaticPaths(getMetadata,getStaticPaths),canCSR=getIfPageCanCSR(metadata?.revalidate,serverComponents.size>0,typeof getData=="function");if(data.serverRoutes.push({path:filePath,serverPath:urlPath,isNotFound:file.path.includes("/not-found/"),meta:{ssr:!canCSR,requiresAuth:!1,revalidate:metadata?.revalidate??0}}),!canCSR)return data.clientRoutes.push(`{
|
|
39
39
|
path: "${urlPath}",
|
|
40
40
|
meta: {
|
|
41
41
|
ssr: true,
|
|
@@ -65,4 +65,4 @@ export function hydrateClientComponent(marker, incomingProps = {}) {
|
|
|
65
65
|
ssr: false,
|
|
66
66
|
requiresAuth: false,
|
|
67
67
|
},
|
|
68
|
-
}`)}return data}async function generateRoutes(){const pageFiles=await getPageFiles(),serverRoutes=[],clientRoutes=[],routeFilesPromises=pageFiles.map(pageFile=>getRouteFileData(pageFile)),routeFiles=await Promise.all(routeFilesPromises);for(const routeFile of routeFiles){const{serverRoutes:serverRoutesFile,clientRoutes:clientRoutesFile}=routeFile;serverRoutesFile?.length&&serverRoutes.push(...serverRoutesFile),clientRoutesFile?.length&&clientRoutes.push(...clientRoutesFile)}return await Promise.all([saveClientRoutesFile(clientRoutes),saveServerRoutesFile(serverRoutes)]),{serverRoutes}}async function buildUserFile(filePath){const rel=path.relative(SRC_DIR,filePath).replace(/\\/g,"/"),outfile=path.join(USER_GENERATED_DIR,rel);await esbuild.build({entryPoints:[filePath],bundle:!0,format:"esm",outfile,plugins:[createVexAliasPlugin()]})}async function buildUserFiles(){const collect=async dir=>{let entries;try{entries=await fs.readdir(dir,{withFileTypes:!0})}catch{return}await Promise.all(entries.map(async entry=>{if(WATCH_IGNORE.has(entry.name))return;const full=path.join(dir,entry.name);if(entry.isDirectory())await collect(full);else if(entry.name.endsWith(".js"))try{await buildUserFile(full)}catch(e){console.error(`[build] Failed to bundle user file ${full}:`,e.message)}}))};await collect(SRC_DIR)}async function build(){return await generateComponentsAndFillCache(),await buildUserFiles(),generateRoutes()}export{build,generateClientComponentModule,generateComponentsAndFillCache,generateRoutes,processClientComponent,processHtmlFile,renderHtmlFile,renderPageWithLayout};
|
|
68
|
+
}`)}return data}async function generateRoutes(){const pageFiles=await getPageFiles(),serverRoutes=[],clientRoutes=[],routeFilesPromises=pageFiles.map(pageFile=>getRouteFileData(pageFile)),routeFiles=await Promise.all(routeFilesPromises);for(const routeFile of routeFiles){const{serverRoutes:serverRoutesFile,clientRoutes:clientRoutesFile}=routeFile;serverRoutesFile?.length&&serverRoutes.push(...serverRoutesFile),clientRoutesFile?.length&&clientRoutes.push(...clientRoutesFile)}return await Promise.all([saveClientRoutesFile(clientRoutes),saveServerRoutesFile(serverRoutes)]),{serverRoutes}}async function buildUserFile(filePath){const rel=path.relative(SRC_DIR,filePath).replace(/\\/g,"/"),outfile=path.join(USER_GENERATED_DIR,rel);await esbuild.build({entryPoints:[filePath],bundle:!0,format:"esm",outfile,plugins:[createVexAliasPlugin()],minify:process.env.NODE_ENV==="production"})}async function buildUserFiles(){const collect=async dir=>{let entries;try{entries=await fs.readdir(dir,{withFileTypes:!0})}catch{return}await Promise.all(entries.map(async entry=>{if(WATCH_IGNORE.has(entry.name))return;const full=path.join(dir,entry.name);if(entry.isDirectory())await collect(full);else if(entry.name.endsWith(".js"))try{await buildUserFile(full)}catch(e){console.error(`[build] Failed to bundle user file ${full}:`,e.message)}}))};await collect(SRC_DIR)}async function build(){return await generateComponentsAndFillCache(),await buildUserFiles(),generateRoutes()}export{build,generateClientComponentModule,generateComponentsAndFillCache,generateRoutes,processClientComponent,processHtmlFile,renderHtmlFile,renderPageWithLayout};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs from"fs/promises";import{watch,existsSync,statSync,readFileSync}from"fs";import path from"path";import crypto from"crypto";import{fileURLToPath,pathToFileURL}from"url";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),FRAMEWORK_DIR=path.resolve(__dirname,"..",".."),PROJECT_ROOT=process.cwd(),ROOT_DIR=PROJECT_ROOT;let _vexConfig={};try{_vexConfig=JSON.parse(readFileSync(path.join(PROJECT_ROOT,"vex.config.json"),"utf-8"))}catch{}const SRC_DIR=path.resolve(PROJECT_ROOT,_vexConfig.srcDir||"."),WATCH_IGNORE=new Set(["dist","build","out",".output",".vexjs","public","node_modules",".git",".svn","coverage",".nyc_output",".next",".nuxt",".svelte-kit",".astro","tmp","temp",".cache",".claude",...(_vexConfig.watchIgnore||[]).filter(p=>!/[\/\*\.]/.test(p))]),WATCH_IGNORE_FILES=(_vexConfig.watchIgnore||[]).filter(p=>/[\/\*\.]/.test(p)),PAGES_DIR=path.resolve(SRC_DIR,"pages"),SERVER_APP_DIR=path.join(FRAMEWORK_DIR,"server"),CLIENT_DIR=path.join(FRAMEWORK_DIR,"client"),CLIENT_SERVICES_DIR=path.join(CLIENT_DIR,"services"),GENERATED_DIR=path.join(PROJECT_ROOT,".vexjs"),CACHE_DIR=path.join(GENERATED_DIR,"_cache"),CLIENT_COMPONENTS_DIR=path.join(GENERATED_DIR,"_components"),USER_GENERATED_DIR=path.join(GENERATED_DIR,"user"),ROOT_HTML_USER=path.join(PROJECT_ROOT,"root.html"),ROOT_HTML_DEFAULT=path.join(FRAMEWORK_DIR,"server","root.html"),ROOT_HTML_DIR=ROOT_HTML_USER;async function initializeDirectories(){try{const servicesDir=path.join(GENERATED_DIR,"services");return await Promise.all([fs.mkdir(GENERATED_DIR,{recursive:!0}),fs.mkdir(CACHE_DIR,{recursive:!0}),fs.mkdir(CLIENT_COMPONENTS_DIR,{recursive:!0}),fs.mkdir(USER_GENERATED_DIR,{recursive:!0}),fs.mkdir(servicesDir,{recursive:!0})]),await fs.cp(CLIENT_SERVICES_DIR,servicesDir,{recursive:!0}),!0}catch(err){console.error("Failed to create cache directory:",err)}}function adjustClientModulePath(modulePath,importStatement,componentFilePath=null){if(modulePath.startsWith("/_vexjs/"))return{path:modulePath,importStatement};const isRelative=(modulePath.startsWith("./")||modulePath.startsWith("../"))&&componentFilePath,isAtAlias=modulePath.startsWith("@/")||modulePath==="@";if(isRelative||isAtAlias){let resolvedPath;if(isAtAlias)resolvedPath=path.resolve(SRC_DIR,modulePath.replace(/^@\//,"").replace(/^@$/,""));else{const componentDir=path.dirname(componentFilePath);resolvedPath=path.resolve(componentDir,modulePath)}path.extname(resolvedPath)||(existsSync(resolvedPath+".js")?resolvedPath+=".js":existsSync(path.join(resolvedPath,"index.js"))?resolvedPath=path.join(resolvedPath,"index.js"):resolvedPath+=".js");const adjustedPath2=`/_vexjs/user/${path.relative(SRC_DIR,resolvedPath).replace(/\\/g,"/")}`,adjustedImportStatement2=importStatement.replace(modulePath,adjustedPath2);return{path:adjustedPath2,importStatement:adjustedImportStatement2}}let relative=modulePath.replace(/^vex\//,""),adjustedPath=`/_vexjs/services/${relative}`;const fsPath=path.join(CLIENT_SERVICES_DIR,relative);existsSync(fsPath)&&statSync(fsPath).isDirectory()?adjustedPath+="/index.js":path.extname(adjustedPath)||(adjustedPath+=".js");const adjustedImportStatement=importStatement.replace(modulePath,adjustedPath);return{path:adjustedPath,importStatement:adjustedImportStatement}}function getRelativePath(from,to){return path.relative(from,to)}function getDirectoryName(filePath){return path.dirname(filePath)}const layoutPathsCache=new Map;process.env.NODE_ENV!=="production"&&watch(PAGES_DIR,{recursive:!0},(_,filename)=>{(filename==="layout.vex"||filename?.endsWith(`${path.sep}layout.vex`))&&layoutPathsCache.clear()});async function _getLayoutPaths(pagePath){const layouts=[],relativePath=getRelativePath(PAGES_DIR,pagePath),pathSegments=getDirectoryName(relativePath).split(path.sep),baseLayout=path.join(PAGES_DIR,"layout.vex");await fileExists(baseLayout)&&layouts.push(baseLayout);let currentPath=PAGES_DIR;for(const segment of pathSegments){if(segment==="."||segment==="..")continue;currentPath=path.join(currentPath,segment);const layoutPath=path.join(currentPath,"layout.vex");await fileExists(layoutPath)&&layouts.push(layoutPath)}return layouts}async function getLayoutPaths(pagePath){if(layoutPathsCache.has(pagePath))return layoutPathsCache.get(pagePath);const result=await _getLayoutPaths(pagePath);return layoutPathsCache.set(pagePath,result),result}function formatFileContent(content){return content.trim()}async function writeFile(filePath,content){const formattedContent=formatFileContent(content);return fs.writeFile(filePath,formattedContent,"utf-8")}function readFile(filePath){return fs.readFile(filePath,"utf-8")}async function fileExists(filePath){try{return await fs.access(filePath),!0}catch{return!1}}function getAutogeneratedComponentName(componentPath){return`_${componentPath.replace(ROOT_DIR+path.sep,"").split(path.sep).filter(Boolean).join("_").replaceAll(".vex","").replaceAll(path.sep,"_").replaceAll("-","_").replaceAll(":","")}`}function generateComponentId(componentPath,options={}){const{length=8,prefix=!0}=options,relativePath=componentPath.replace(ROOT_DIR+path.sep,""),hash=crypto.createHash("sha256").update(relativePath).digest("hex").slice(0,length),baseName=getAutogeneratedComponentName(componentPath).replace(/^_/,"");return prefix?`_${baseName}_${hash}`:hash}const getPagePath=pageName=>path.resolve(PAGES_DIR,pageName,"page.vex"),getRootTemplate=async()=>{try{return await fs.access(ROOT_HTML_USER),await fs.readFile(ROOT_HTML_USER,"utf-8")}catch{return await fs.readFile(ROOT_HTML_DEFAULT,"utf-8")}};async function readDirectoryRecursive(dir){const entries=await fs.readdir(dir,{withFileTypes:!0}),files=[];for(const entry of entries){const fullpath=path.join(dir,entry.name);entry.isDirectory()?files.push(...await readDirectoryRecursive(fullpath)):files.push({path:fullpath.replace(ROOT_DIR,""),fullpath,name:entry.name})}return files}const getComponentNameFromPath=(fullFilepath,fileName)=>{const filePath=fullFilepath.replace(ROOT_DIR+path.sep,"");if(filePath.startsWith(path.join("pages",path.sep))){const segments=filePath.split(path.sep);return segments.length===2?segments[0].replace(".vex",""):segments[segments.length-2].replace(".vex","")}return fileName.replace(".vex","")};async function getComponentHtmlDisk({componentPath}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",[existsHtml,existsMeta]=await Promise.all([fileExists(filePath),fileExists(metaPath)]);if(!existsMeta||!existsHtml)return{html:null,meta:null};const[html,meta]=await Promise.all([fs.readFile(filePath,"utf-8"),fs.readFile(metaPath,"utf-8")]).then(([htmlContent,metaContent])=>[htmlContent,JSON.parse(metaContent)]);return{html,meta}}async function saveComponentHtmlDisk({componentPath,html}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",meta={generatedAt:Date.now(),isStale:!1,path:componentPath};await Promise.all([writeFile(filePath,html,"utf-8"),writeFile(metaPath,JSON.stringify(meta),"utf-8")])}async function markComponentHtmlStale({componentPath}){const metaPath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html")+".meta.json";if(!await fileExists(metaPath))return;const meta=JSON.parse(await fs.readFile(metaPath,"utf-8"));meta.isStale=!0,await writeFile(metaPath,JSON.stringify(meta),"utf-8")}async function saveServerRoutesFile(serverRoutes){await writeFile(path.join(GENERATED_DIR,"_routes.js"),`// Auto-generated by prebuild \u2014 do not edit manually.
|
|
1
|
+
import fs from"fs/promises";import{watch,existsSync,statSync,readFileSync}from"fs";import path from"path";import crypto from"crypto";import{fileURLToPath,pathToFileURL}from"url";import esbuild from"esbuild";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),FRAMEWORK_DIR=path.resolve(__dirname,"..",".."),PROJECT_ROOT=process.cwd(),ROOT_DIR=PROJECT_ROOT;let _vexConfig={};try{_vexConfig=JSON.parse(readFileSync(path.join(PROJECT_ROOT,"vex.config.json"),"utf-8"))}catch{}const SRC_DIR=path.resolve(PROJECT_ROOT,_vexConfig.srcDir||"."),WATCH_IGNORE=new Set(["dist","build","out",".output",".vexjs","public","node_modules",".git",".svn","coverage",".nyc_output",".next",".nuxt",".svelte-kit",".astro","tmp","temp",".cache",".claude",...(_vexConfig.watchIgnore||[]).filter(p=>!/[\/\*\.]/.test(p))]),WATCH_IGNORE_FILES=(_vexConfig.watchIgnore||[]).filter(p=>/[\/\*\.]/.test(p)),PAGES_DIR=path.resolve(SRC_DIR,"pages"),SERVER_APP_DIR=path.join(FRAMEWORK_DIR,"server"),CLIENT_DIR=path.join(FRAMEWORK_DIR,"client"),CLIENT_SERVICES_DIR=path.join(CLIENT_DIR,"services"),GENERATED_DIR=path.join(PROJECT_ROOT,".vexjs"),CACHE_DIR=path.join(GENERATED_DIR,"_cache"),CLIENT_COMPONENTS_DIR=path.join(GENERATED_DIR,"_components"),USER_GENERATED_DIR=path.join(GENERATED_DIR,"user"),ROOT_HTML_USER=path.join(PROJECT_ROOT,"root.html"),ROOT_HTML_DEFAULT=path.join(FRAMEWORK_DIR,"server","root.html"),ROOT_HTML_DIR=ROOT_HTML_USER;async function minifyServicesDir(src,dest){const jsFiles=[],otherFiles=[],collect=async(srcDir,destDir)=>{const entries=await fs.readdir(srcDir,{withFileTypes:!0});await Promise.all(entries.map(async entry=>{const srcPath=path.join(srcDir,entry.name),destPath=path.join(destDir,entry.name);entry.isDirectory()?(await fs.mkdir(destPath,{recursive:!0}),await collect(srcPath,destPath)):entry.name.endsWith(".js")?jsFiles.push({in:srcPath,out:destPath}):otherFiles.push({src:srcPath,dest:destPath})}))};await collect(src,dest),await Promise.all([esbuild.build({entryPoints:jsFiles.map(f=>f.in),bundle:!1,format:"esm",platform:"browser",minify:!0,legalComments:"none",outdir:dest,outbase:src,logLevel:"silent"}),...otherFiles.map(f=>fs.copyFile(f.src,f.dest))])}async function initializeDirectories(){try{const servicesDir=path.join(GENERATED_DIR,"services");return await Promise.all([fs.mkdir(GENERATED_DIR,{recursive:!0}),fs.mkdir(CACHE_DIR,{recursive:!0}),fs.mkdir(CLIENT_COMPONENTS_DIR,{recursive:!0}),fs.mkdir(USER_GENERATED_DIR,{recursive:!0}),fs.mkdir(servicesDir,{recursive:!0})]),process.env.NODE_ENV==="production"?await minifyServicesDir(CLIENT_SERVICES_DIR,servicesDir):await fs.cp(CLIENT_SERVICES_DIR,servicesDir,{recursive:!0}),!0}catch(err){console.error("Failed to create cache directory:",err)}}function adjustClientModulePath(modulePath,importStatement,componentFilePath=null){if(modulePath.startsWith("/_vexjs/"))return{path:modulePath,importStatement};const isRelative=(modulePath.startsWith("./")||modulePath.startsWith("../"))&&componentFilePath,isAtAlias=modulePath.startsWith("@/")||modulePath==="@";if(isRelative||isAtAlias){let resolvedPath;if(isAtAlias)resolvedPath=path.resolve(SRC_DIR,modulePath.replace(/^@\//,"").replace(/^@$/,""));else{const componentDir=path.dirname(componentFilePath);resolvedPath=path.resolve(componentDir,modulePath)}path.extname(resolvedPath)||(existsSync(resolvedPath+".js")?resolvedPath+=".js":existsSync(path.join(resolvedPath,"index.js"))?resolvedPath=path.join(resolvedPath,"index.js"):resolvedPath+=".js");const adjustedPath2=`/_vexjs/user/${path.relative(SRC_DIR,resolvedPath).replace(/\\/g,"/")}`,adjustedImportStatement2=importStatement.replace(modulePath,adjustedPath2);return{path:adjustedPath2,importStatement:adjustedImportStatement2}}let relative=modulePath.replace(/^vex\//,""),adjustedPath=`/_vexjs/services/${relative}`;const fsPath=path.join(CLIENT_SERVICES_DIR,relative);existsSync(fsPath)&&statSync(fsPath).isDirectory()?adjustedPath+="/index.js":path.extname(adjustedPath)||(adjustedPath+=".js");const adjustedImportStatement=importStatement.replace(modulePath,adjustedPath);return{path:adjustedPath,importStatement:adjustedImportStatement}}function getRelativePath(from,to){return path.relative(from,to)}function getDirectoryName(filePath){return path.dirname(filePath)}const layoutPathsCache=new Map;process.env.NODE_ENV!=="production"&&watch(PAGES_DIR,{recursive:!0},(_,filename)=>{(filename==="layout.vex"||filename?.endsWith(`${path.sep}layout.vex`))&&layoutPathsCache.clear()});async function _getLayoutPaths(pagePath){const layouts=[],relativePath=getRelativePath(PAGES_DIR,pagePath),pathSegments=getDirectoryName(relativePath).split(path.sep),baseLayout=path.join(PAGES_DIR,"layout.vex");await fileExists(baseLayout)&&layouts.push(baseLayout);let currentPath=PAGES_DIR;for(const segment of pathSegments){if(segment==="."||segment==="..")continue;currentPath=path.join(currentPath,segment);const layoutPath=path.join(currentPath,"layout.vex");await fileExists(layoutPath)&&layouts.push(layoutPath)}return layouts}async function getLayoutPaths(pagePath){if(layoutPathsCache.has(pagePath))return layoutPathsCache.get(pagePath);const result=await _getLayoutPaths(pagePath);return layoutPathsCache.set(pagePath,result),result}function formatFileContent(content){return content.trim()}async function writeFile(filePath,content){const formattedContent=formatFileContent(content);return fs.writeFile(filePath,formattedContent,"utf-8")}function readFile(filePath){return fs.readFile(filePath,"utf-8")}async function fileExists(filePath){try{return await fs.access(filePath),!0}catch{return!1}}function getAutogeneratedComponentName(componentPath){return`_${componentPath.replace(ROOT_DIR+path.sep,"").split(path.sep).filter(Boolean).join("_").replaceAll(".vex","").replaceAll(path.sep,"_").replaceAll("-","_").replaceAll(":","")}`}function generateComponentId(componentPath,options={}){const{length=8,prefix=!0}=options,relativePath=componentPath.replace(ROOT_DIR+path.sep,""),hash=crypto.createHash("sha256").update(relativePath).digest("hex").slice(0,length),baseName=getAutogeneratedComponentName(componentPath).replace(/^_/,"");return prefix?`_${baseName}_${hash}`:hash}const getPagePath=pageName=>path.resolve(PAGES_DIR,pageName,"page.vex"),getRootTemplate=async()=>{try{return await fs.access(ROOT_HTML_USER),await fs.readFile(ROOT_HTML_USER,"utf-8")}catch{return await fs.readFile(ROOT_HTML_DEFAULT,"utf-8")}};async function readDirectoryRecursive(dir){const entries=await fs.readdir(dir,{withFileTypes:!0}),files=[];for(const entry of entries){const fullpath=path.join(dir,entry.name);entry.isDirectory()?files.push(...await readDirectoryRecursive(fullpath)):files.push({path:fullpath.replace(ROOT_DIR,""),fullpath,name:entry.name})}return files}const getComponentNameFromPath=(fullFilepath,fileName)=>{const filePath=fullFilepath.replace(ROOT_DIR+path.sep,"");if(filePath.startsWith(path.join("pages",path.sep))){const segments=filePath.split(path.sep);return segments.length===2?segments[0].replace(".vex",""):segments[segments.length-2].replace(".vex","")}return fileName.replace(".vex","")};async function getComponentHtmlDisk({componentPath}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",[existsHtml,existsMeta]=await Promise.all([fileExists(filePath),fileExists(metaPath)]);if(!existsMeta||!existsHtml)return{html:null,meta:null};const[html,meta]=await Promise.all([fs.readFile(filePath,"utf-8"),fs.readFile(metaPath,"utf-8")]).then(([htmlContent,metaContent])=>[htmlContent,JSON.parse(metaContent)]);return{html,meta}}async function saveComponentHtmlDisk({componentPath,html}){const filePath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html"),metaPath=filePath+".meta.json",meta={generatedAt:Date.now(),isStale:!1,path:componentPath};await Promise.all([writeFile(filePath,html,"utf-8"),writeFile(metaPath,JSON.stringify(meta),"utf-8")])}async function markComponentHtmlStale({componentPath}){const metaPath=path.join(CACHE_DIR,generateComponentId(componentPath)+".html")+".meta.json";if(!await fileExists(metaPath))return;const meta=JSON.parse(await fs.readFile(metaPath,"utf-8"));meta.isStale=!0,await writeFile(metaPath,JSON.stringify(meta),"utf-8")}async function saveServerRoutesFile(serverRoutes){await writeFile(path.join(GENERATED_DIR,"_routes.js"),`// Auto-generated by prebuild \u2014 do not edit manually.
|
|
2
2
|
export const routes = ${JSON.stringify(serverRoutes,null,2)};
|
|
3
3
|
`)}async function saveClientRoutesFile(clientRoutes){const clientFileCode=`
|
|
4
4
|
import { loadRouteComponent } from './cache.js';
|
package/package.json
CHANGED