@devlusoft/devix 0.4.1-beta.2 → 0.4.1-beta.3
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/cli/build.js +11 -4
- package/dist/cli/build.js.map +2 -2
- package/dist/cli/dev-server.js +11 -4
- package/dist/cli/dev-server.js.map +2 -2
- package/dist/cli/generate.js +10 -3
- package/dist/cli/generate.js.map +2 -2
- package/dist/cli/index.js +10 -3
- package/dist/cli/index.js.map +2 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +3 -3
- package/dist/runtime/router-provider.js +1 -1
- package/dist/runtime/router-provider.js.map +3 -3
- package/dist/utils/banner.js +1 -1
- package/dist/vite/codegen/entry-client.js +10 -3
- package/dist/vite/codegen/entry-client.js.map +2 -2
- package/dist/vite/index.js +10 -3
- package/dist/vite/index.js.map +2 -2
- package/package.json +1 -1
package/dist/cli/generate.js
CHANGED
|
@@ -37,6 +37,13 @@ if (!window.__DEVIX__) {
|
|
|
37
37
|
initialViewport: viewport,
|
|
38
38
|
})
|
|
39
39
|
)
|
|
40
|
+
|
|
41
|
+
if (window.location.hash) {
|
|
42
|
+
const id = window.location.hash.slice(1)
|
|
43
|
+
requestAnimationFrame(() => {
|
|
44
|
+
document.getElementById(id)?.scrollIntoView()
|
|
45
|
+
})
|
|
46
|
+
}
|
|
40
47
|
} else {
|
|
41
48
|
const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()
|
|
42
49
|
createRoot(root).render(
|
|
@@ -120,9 +127,9 @@ const _glob = {
|
|
|
120
127
|
export function handleApiRequest(url, request) {
|
|
121
128
|
return _handleApiRequest(url, request, _glob)
|
|
122
129
|
}
|
|
123
|
-
`}var J=s(()=>{"use strict"});function v(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var w=s(()=>{"use strict"});function E(){ht=null}var ht,
|
|
130
|
+
`}var J=s(()=>{"use strict"});function v(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var w=s(()=>{"use strict"});function E(){ht=null}var ht,B=s(()=>{"use strict";w();ht=null});function X(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),i=v(r);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}function P(){xt=null}var xt,_=s(()=>{"use strict";w();xt=null});function G(){return`
|
|
124
131
|
export {RouterContext} from '@devlusoft/devix/runtime/context'
|
|
125
|
-
`}var Y=s(()=>{"use strict"});function Rt(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function z(t){let e=new Set;for(let r of Rt(t).matchAll(yt))e.add(r[1]);return[...e]}var yt,Z=s(()=>{"use strict";yt=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g});function vt(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function K(t,e,r){return{filePath:t,urlPattern:
|
|
132
|
+
`}var Y=s(()=>{"use strict"});function Rt(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function z(t){let e=new Set;for(let r of Rt(t).matchAll(yt))e.add(r[1]);return[...e]}var yt,Z=s(()=>{"use strict";yt=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g});function vt(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function K(t,e,r){return{filePath:t,urlPattern:X(t,e),identifier:vt(t,e),methods:r}}function $(t,e){if(t.length===0)return`// auto-generado por devix \u2014 no editar
|
|
126
133
|
declare module '@devlusoft/devix' {
|
|
127
134
|
interface ApiRoutes {}
|
|
128
135
|
}
|
|
@@ -149,5 +156,5 @@ declare module '@devlusoft/devix' {
|
|
|
149
156
|
${i}
|
|
150
157
|
}
|
|
151
158
|
}
|
|
152
|
-
`}var T=s(()=>{"use strict";_()});import{readFileSync as wt,readdirSync as Et,statSync as Pt}from"node:fs";import{join as D,relative as _t}from"node:path";function Q(t,e){let r=[];for(let i of Et(t)){let o=D(t,i);Pt(o).isDirectory()?r.push(...Q(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(_t(e,o).replace(/\\/g,"/"))}return r}function A(t,e){let r=D(e,t,"api"),i;try{i=Q(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let d=wt(D(e,o),"utf-8"),h=z(d);return h.length===0?[]:[K(o,`${t}/api`,h)]}catch{return[]}})}var tt=s(()=>{"use strict";Z();T()});import{mkdirSync as $t,readFileSync as Tt,writeFileSync as Dt,existsSync as At}from"node:fs";import{join as et}from"node:path";function C(t,e){let r=et(e,".devix"),i=et(r,"routes.d.ts");return $t(r,{recursive:!0}),At(i)&&Tt(i,"utf-8")===t?!1:(Dt(i,t,"utf-8"),!0)}var rt=s(()=>{"use strict"});import{mergeConfig as Ct}from"vite";import St from"@vitejs/plugin-react";import{fileURLToPath as bt}from"node:url";import{dirname as
|
|
159
|
+
`}var T=s(()=>{"use strict";_()});import{readFileSync as wt,readdirSync as Et,statSync as Pt}from"node:fs";import{join as D,relative as _t}from"node:path";function Q(t,e){let r=[];for(let i of Et(t)){let o=D(t,i);Pt(o).isDirectory()?r.push(...Q(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(_t(e,o).replace(/\\/g,"/"))}return r}function A(t,e){let r=D(e,t,"api"),i;try{i=Q(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let d=wt(D(e,o),"utf-8"),h=z(d);return h.length===0?[]:[K(o,`${t}/api`,h)]}catch{return[]}})}var tt=s(()=>{"use strict";Z();T()});import{mkdirSync as $t,readFileSync as Tt,writeFileSync as Dt,existsSync as At}from"node:fs";import{join as et}from"node:path";function C(t,e){let r=et(e,".devix"),i=et(r,"routes.d.ts");return $t(r,{recursive:!0}),At(i)&&Tt(i,"utf-8")===t?!1:(Dt(i,t,"utf-8"),!0)}var rt=s(()=>{"use strict"});import{mergeConfig as Ct}from"vite";import St from"@vitejs/plugin-react";import{fileURLToPath as bt}from"node:url";import{dirname as It,resolve as p}from"node:path";import{parseSync as Mt}from"oxc-parser";function ot(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=p(S,"../server/render.js").replace(/\\/g,"/"),d=p(S,"../server/api.js").replace(/\\/g,"/"),h=p(S,"../runtime/client-router.js").replace(/\\/g,"/"),mt={name:"devix",enforce:"pre",resolveId(n){if(n===b)return`\0${b}`;if(n===I)return`\0${I}`;if(n===M)return`\0${M}`;if(n===O)return`\0${O}`;if(n===L)return`\0${L}`},load(n){if(n===`\0${b}`)return k({cssUrls:i});if(n===`\0${I}`)return j({pagesDir:r,matcherPath:h});if(n===`\0${M}`)return N({pagesDir:r,renderPath:o});if(n===`\0${O}`)return W({apiPath:d,appDir:e});if(n===`\0${L}`)return G()},transform(n,l,m){if(m?.ssr)return;let a=p(process.cwd(),r);if(!l.startsWith(a))return;let gt=Mt(l,n,{sourceType:"module"}),f=[];for(let c of gt.program.body){if(c.type!=="ExportNamedDeclaration"||!c.declaration)continue;let u=c.declaration;if(u.type==="FunctionDeclaration"&&u.id&&nt.has(u.id.name)&&f.push({start:c.start,end:c.end,name:u.id.name}),u.type==="VariableDeclaration"){let y=new Set;for(let R of u.declarations)R.id.type==="Identifier"&&nt.has(R.id.name)&&(y.has(c.start)||(y.add(c.start),f.push({start:c.start,end:c.end,name:R.id.name})))}}if(f.length===0)return;f.sort((c,u)=>u.start-c.start);let x=n;for(let{start:c,end:u,name:y}of f)x=x.slice(0,c)+`export const ${y} = undefined`+x.slice(u);return{code:x,map:null}},buildStart(){let n=process.cwd(),l=A(e,n);C($(l,`${e}/api`),n)},configureServer(n){let l=process.cwd(),m=()=>{let a=A(e,l);C($(a,`${e}/api`),l)};n.watcher.add(p(l,"devix.config.ts")),n.watcher.on("change",a=>{a===p(l,"devix.config.ts")&&(console.log("[devix] Config changed, restarting..."),process.exit(75))}),n.watcher.on("add",a=>{a.startsWith(p(l,r))&&E(),a.includes(`${e}/api`)&&(P(),m())}),n.watcher.on("unlink",a=>{a.startsWith(p(l,r))&&E(),a.includes(`${e}/api`)&&(P(),m())}),n.watcher.on("change",a=>{a.includes(`${e}/api`)&&!a.endsWith("middleware.ts")&&m()})}},ft={plugins:[St(),mt],publicDir:p(process.cwd(),t.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return Ct(ft,t.vite??{})}var S,b,I,M,O,L,nt,it=s(()=>{"use strict";H();q();V();J();B();_();Y();tt();T();rt();S=It(bt(import.meta.url)),b="virtual:devix/entry-client",I="virtual:devix/client-routes",M="virtual:devix/render",O="virtual:devix/api",L="virtual:devix/context",nt=new Set(["loader","guard","generateStaticParams","headers"])});function st(t){if(typeof t=="number")return t;let e=t.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)throw new Error(`[devix] Invalid duration: "${t}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(e[1]);switch(e[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}var at=s(()=>{"use strict"});var Ut={};import{writeFileSync as Ot}from"node:fs";import{resolve as Lt}from"node:path";import{build as ct}from"vite";var g,lt,Ft,ut=s(async()=>{"use strict";it();at();g=(await import(`${process.cwd()}/devix.config.ts`)).default,lt=ot(g);await ct({...lt,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await ct({...lt,configFile:!1,build:{ssr:!0,outDir:"dist/server",copyPublicDir:!1,rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});Ft={port:g.port??3e3,host:g.host??!1,loaderTimeout:st(g.loaderTimeout??1e4),output:g.output??"server"};Ot(Lt(process.cwd(),"dist/devix.config.json"),JSON.stringify(Ft,null,2),"utf-8")});import{readFileSync as kt,mkdirSync as Ht,writeFileSync as jt}from"node:fs";import{resolve as pt,join as F}from"node:path";var qt=(await import(`${process.cwd()}/devix.config.ts`)).default;qt.output!=="static"&&console.warn('[devix] Tip: set output: "static" in devix.config.ts to skip the SSR server at runtime.');await ut().then(()=>Ut);var Nt=Date.now(),dt=await import(pt(process.cwd(),"dist/server/render.js")+`?t=${Nt}`),Vt=JSON.parse(kt(pt(process.cwd(),"dist/client/.vite/manifest.json"),"utf-8")),U=await dt.getStaticRoutes();console.log(`[devix] Generating ${U.length} static page${U.length===1?"":"s"}...`);for(let t of U){let e=`http://localhost${t}`,{html:r,statusCode:i}=await dt.render(e,new Request(e),{manifest:Vt});if(i!==200){console.warn(`[devix] Skipping ${t} \u2014 status ${i}`);continue}let o=t==="/"?F(process.cwd(),"dist/client/index.html"):F(process.cwd(),"dist/client",t,"index.html");Ht(F(o,".."),{recursive:!0}),jt(o,`<!DOCTYPE html>${r}`,"utf-8"),console.log(` \u2713 ${t}`)}console.log("[devix] Generation complete.");
|
|
153
160
|
//# sourceMappingURL=generate.js.map
|
package/dist/cli/generate.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/vite/codegen/entry-client.ts", "../../src/vite/codegen/client-routes.ts", "../../src/vite/codegen/render.ts", "../../src/vite/codegen/api.ts", "../../src/utils/patterns.ts", "../../src/server/pages-router.ts", "../../src/server/api-router.ts", "../../src/vite/codegen/context.ts", "../../src/vite/codegen/extract-methods.ts", "../../src/vite/codegen/routes-dts.ts", "../../src/vite/codegen/scan-api.ts", "../../src/vite/codegen/write-routes-dts.ts", "../../src/vite/index.ts", "../../src/utils/duration.ts", "../../src/cli/build.ts", "../../src/cli/generate.ts"],
|
|
4
|
-
"sourcesContent": ["interface EntryClientOptions {\n cssUrls: string[]\n}\n\nexport function generateEntryClient({cssUrls}: EntryClientOptions): string {\n const cssImports = cssUrls.map(u => `import '${u}'`).join('\\n')\n\n return `\n${cssImports}\nimport \"@vitejs/plugin-react/preamble\"\nimport React from \"react\"\nimport {hydrateRoot, createRoot} from 'react-dom/client'\nimport {matchClientRoute, loadErrorPage, getDefaultErrorPage} from 'virtual:devix/client-routes'\nimport {RouterProvider} from '@devlusoft/devix'\n\nconst root = document.getElementById('devix-root')\n\nif (!window.__DEVIX__) {\n const ErrorPage = getDefaultErrorPage()\n createRoot(root).render(React.createElement(ErrorPage, {statusCode: 500, message: 'Server error'}))\n} else {\n const {metadata, viewport, clientEntry} = window.__DEVIX__\n const loaderData = window.__LOADER_DATA__\n const layoutsData = window.__LAYOUTS_DATA__ ?? []\n\n const matched = matchClientRoute(window.location.pathname)\n\n if (matched) {\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n hydrateRoot(\n root,\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: loaderData,\n initialParams: matched.params,\n initialPage: pageMod.default,\n initialLayouts: layoutMods.map(m => m.default),\n initialLayoutsData: layoutsData,\n initialMeta: metadata,\n initialViewport: viewport,\n })\n )\n } else {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n createRoot(root).render(\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: null,\n initialParams: {},\n initialPage: () => null,\n initialLayouts: [],\n initialLayoutsData: [],\n initialMeta: null,\n initialError: {statusCode: 404, message: 'Not found'},\n initialErrorPage: ErrorPage,\n })\n )\n }\n}\n`\n}", "interface ClientRoutesOptions {\n pagesDir: string\n matcherPath: string\n}\n\nexport function generateClientRoutes({pagesDir, matcherPath}: ClientRoutesOptions) {\n return `\nimport React from 'react'\nimport { createMatcher } from '${matcherPath}'\nconst pageFiles = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst layoutFiles = import.meta.glob('/${pagesDir}/**/layout.tsx')\nconst errorFiles = import.meta.glob('/${pagesDir}/**/error.tsx')\n\nexport const matchClientRoute = createMatcher(pageFiles, layoutFiles)\n\nexport async function loadErrorPage() {\n const key = Object.keys(errorFiles)[0]\n if (!key) return null\n const mod = await errorFiles[key]()\n return mod?.default ?? null\n}\n\nexport function getDefaultErrorPage() {\n return function DefaultError({ statusCode, message }) {\n return React.createElement('main', {\n style: { minHeight: '100dvh', display: 'flex', flexDirection: 'column', \n alignItems: 'center', justifyContent: 'center', gap: '8px',\n fontFamily: 'system-ui, sans-serif' }\n },\n React.createElement('h1', {style: {fontSize: '4rem', fontWeight: 700}}, statusCode),\n React.createElement('p', {style: {color: '#666'}}, message ?? 'An unexpected error occurred'),\n )\n }\n}\n`\n}", "interface RenderOptions {\n pagesDir: string\n renderPath: string\n}\n\nexport function generateRender({pagesDir, renderPath}: RenderOptions): string {\n return `\nimport { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${renderPath}'\n\nconst _pages = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst _layouts = import.meta.glob('/${pagesDir}/**/layout.tsx')\n\nconst _glob = {\n pages: _pages,\n layouts: _layouts,\n pagesDir: '/${pagesDir}',\n}\n\nexport function render(url, request, options) {\n return _render(url, request, _glob, options)\n}\n\nexport function runLoader(url, request, options) {\n return _runLoader(url, request, _glob, options)\n}\n\nexport function getStaticRoutes() {\n return _getStaticRoutes(_glob)\n}\n`\n}\n", "interface ApiOptions {\n apiPath: string\n appDir: string\n}\n\nexport function generateApi({apiPath, appDir}: ApiOptions): string {\n return `\nimport { handleApiRequest as _handleApiRequest } from '${apiPath}'\n\nconst _routes = import.meta.glob(['/${appDir}/api/**/*.ts', '!**/middleware.ts'])\nconst _middlewares = import.meta.glob('/${appDir}/api/**/middleware.ts')\n\nconst _glob = {\n routes: _routes,\n middlewares: _middlewares,\n apiDir: '/${appDir}/api',\n}\n\nexport function handleApiRequest(url, request) {\n return _handleApiRequest(url, request, _glob)\n}\n`\n}\n", "export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface Page {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface Layout {\n dir: string\n key: string\n}\n\nexport interface PagesResult {\n pages: Page[]\n layouts: Layout[]\n}\n\nfunction keyToRoutePattern(key: string, pagesDir: string): string {\n const rel = key.slice(pagesDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === \"/\" ? \"/\" : `/${pattern}`\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: PagesResult | null = null\n\nexport function invalidatePagesCache() {\n cache = null\n}\n\nexport function buildPages(pageKeys: string[], layoutKeys: string[], pagesDir: string): PagesResult {\n if (cache) return cache\n\n const pages: Page[] = []\n const layouts: Layout[] = []\n\n for (const key of layoutKeys) {\n layouts.push({dir: keyToDir(key), key})\n }\n\n for (const key of pageKeys) {\n const pattern = keyToRoutePattern(key, pagesDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n pages.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n\n pages.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {pages, layouts}\n return cache\n}\n\nexport function collectLayoutChain(pageKey: string, layouts: Layout[]): Layout[] {\n const pageDir = keyToDir(pageKey)\n\n return layouts\n .filter(layout => pageDir.startsWith(layout.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchPage(pathname: string, pages: Page[]): {\n page: Page\n params: Record<string, string>\n} | null {\n for (const page of pages) {\n const match = pathname.match(page.regex)\n if (match) {\n const params: Record<string, string> = {}\n page.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {page, params}\n }\n }\n return null\n}\n", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "export function generateContext(): string {\n return `\nexport {RouterContext} from '@devlusoft/devix/runtime/context'\n`\n}", "const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\nconst METHOD_EXPORT_RE = /export\\s+(?:const|async\\s+function|function)\\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\b/g\n\nfunction stripComments(content: string): string {\n return content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n}\n\nexport function extractHttpMethods(content: string): HttpMethod[] {\n const found = new Set<HttpMethod>()\n for (const match of stripComments(content).matchAll(METHOD_EXPORT_RE)) {\n found.add(match[1] as HttpMethod)\n }\n return [...found]\n}\n", "import { keyToRoutePattern } from '../../server/api-router'\nimport type { HttpMethod } from './extract-methods'\n\nexport interface RouteEntry {\n filePath: string\n urlPattern: string\n identifier: string\n methods: HttpMethod[]\n}\n\nexport function filePathToIdentifier(filePath: string, apiDir: string): string {\n return '_api_' + filePath\n .slice(`${apiDir}/`.length)\n .replace(/\\.(ts|tsx)$/, '')\n .replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nexport function buildRouteEntry(filePath: string, apiDir: string, methods: HttpMethod[]): RouteEntry {\n return {\n filePath,\n urlPattern: keyToRoutePattern(filePath, apiDir),\n identifier: filePathToIdentifier(filePath, apiDir),\n methods,\n }\n}\n\nexport function generateRoutesDts(entries: RouteEntry[], apiDir: string): string {\n if (entries.length === 0) {\n return `// auto-generado por devix \u2014 no editar\\ndeclare module '@devlusoft/devix' {\\n interface ApiRoutes {}\\n}\\n`\n }\n\n const imports = entries\n .map(e => {\n const importPath = '../' + e.filePath.replace(/\\.(ts|tsx)$/, '')\n return `import type * as ${e.identifier} from '${importPath}'`\n })\n .join('\\n')\n\n const routeLines = entries.flatMap(e =>\n e.methods.map(m =>\n ` '${m} ${e.urlPattern}': InferRoute<(typeof ${e.identifier})['${m}']>`\n )\n ).join('\\n')\n\n return `// auto-generado por devix \u2014 no editar\n${imports}\n\ntype JsonResponse<T> = Response & { readonly __body: T }\ntype UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never\ntype InferFnReturn<T> = T extends (...args: any[]) => any\n ? UnwrapJson<Awaited<ReturnType<T>>> | Exclude<Awaited<ReturnType<T>>, JsonResponse<any> | null | void | undefined>\n : never\ntype InferRoute<T> =\n T extends { readonly __return?: infer TReturn; readonly __body?: infer TBody }\n ? {\n __body: [TBody] extends [undefined] ? never : Exclude<TBody, undefined>\n __response: InferFnReturn<() => TReturn>\n }\n : InferFnReturn<T>\n\ndeclare module '@devlusoft/devix' {\n interface ApiRoutes {\n${routeLines}\n }\n}\n`\n}\n", "import {readFileSync, readdirSync, statSync} from 'node:fs'\nimport {join, relative} from 'node:path'\nimport {extractHttpMethods} from './extract-methods'\nimport {buildRouteEntry} from './routes-dts'\nimport type {RouteEntry} from './routes-dts'\n\nfunction walkDir(dir: string, root: string): string[] {\n const entries: string[] = []\n for (const name of readdirSync(dir)) {\n const full = join(dir, name)\n if (statSync(full).isDirectory()) {\n entries.push(...walkDir(full, root))\n } else if (/\\.(ts|tsx)$/.test(name)) {\n entries.push(relative(root, full).replace(/\\\\/g, '/'))\n }\n }\n return entries\n}\n\nexport function scanApiFiles(appDir: string, projectRoot: string): RouteEntry[] {\n const apiDir = join(projectRoot, appDir, 'api')\n\n let files: string[]\n try {\n files = walkDir(apiDir, projectRoot)\n } catch {\n return []\n }\n\n return files\n .filter(f => !f.endsWith('middleware.ts') && !f.endsWith('middleware.tsx'))\n .flatMap(filePath => {\n try {\n const content = readFileSync(join(projectRoot, filePath), 'utf-8')\n const methods = extractHttpMethods(content)\n if (methods.length === 0) return []\n return [buildRouteEntry(filePath, `${appDir}/api`, methods)]\n } catch {\n return []\n }\n })\n}\n", "import {mkdirSync, readFileSync, writeFileSync, existsSync} from 'node:fs'\nimport {join} from 'node:path'\n\nexport function writeRoutesDts(content: string, projectRoot: string): boolean {\n const devixDir = join(projectRoot, '.devix')\n const outPath = join(devixDir, 'routes.d.ts')\n\n mkdirSync(devixDir, {recursive: true})\n\n if (existsSync(outPath) && readFileSync(outPath, 'utf-8') === content) {\n return false\n }\n\n writeFileSync(outPath, content, 'utf-8')\n return true\n}\n", "import { UserConfig, Plugin, mergeConfig } from 'vite'\nimport type { DevixConfig } from '../config'\nimport react from '@vitejs/plugin-react'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, resolve } from 'node:path'\nimport { generateEntryClient } from './codegen/entry-client'\nimport { generateClientRoutes } from './codegen/client-routes'\nimport { generateRender } from './codegen/render'\nimport { generateApi } from './codegen/api'\nimport { invalidatePagesCache } from \"../server/pages-router\";\nimport { invalidateApiCache } from \"../server/api-router\";\nimport { generateContext } from \"./codegen/context\";\nimport { scanApiFiles } from \"./codegen/scan-api\";\nimport { generateRoutesDts } from \"./codegen/routes-dts\";\nimport { writeRoutesDts } from \"./codegen/write-routes-dts\";\nimport { parseSync } from 'oxc-parser'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nconst VIRTUAL_ENTRY_CLIENT = 'virtual:devix/entry-client'\nconst VIRTUAL_CLIENT_ROUTES = 'virtual:devix/client-routes'\nconst VIRTUAL_RENDER = 'virtual:devix/render'\nconst VIRTUAL_API = 'virtual:devix/api'\nconst VIRTUAL_CONTEXT = 'virtual:devix/context'\n\nconst SERVER_EXPORTS = new Set(['loader', 'guard', 'generateStaticParams', 'headers'])\n\nexport function devix(config: DevixConfig): UserConfig {\n const appDir = config.appDir ?? 'app'\n const pagesDir = `${appDir}/pages`\n const cssUrls = (config.css ?? []).map(u => u.startsWith('/') ? u : `/${u.replace(/^\\.\\//, '')}`)\n\n const renderPath = resolve(__dirname, '../server/render.js').replace(/\\\\/g, '/')\n const apiPath = resolve(__dirname, '../server/api.js').replace(/\\\\/g, '/')\n const matcherPath = resolve(__dirname, '../runtime/client-router.js').replace(/\\\\/g, '/')\n\n const virtualPlugin: Plugin = {\n name: 'devix',\n enforce: 'pre',\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_CLIENT) return `\\0${VIRTUAL_ENTRY_CLIENT}`\n if (id === VIRTUAL_CLIENT_ROUTES) return `\\0${VIRTUAL_CLIENT_ROUTES}`\n if (id === VIRTUAL_RENDER) return `\\0${VIRTUAL_RENDER}`\n if (id === VIRTUAL_API) return `\\0${VIRTUAL_API}`\n if (id === VIRTUAL_CONTEXT) return `\\0${VIRTUAL_CONTEXT}`\n },\n\n load(id) {\n if (id === `\\0${VIRTUAL_ENTRY_CLIENT}`)\n return generateEntryClient({ cssUrls })\n if (id === `\\0${VIRTUAL_CLIENT_ROUTES}`)\n return generateClientRoutes({ pagesDir, matcherPath })\n if (id === `\\0${VIRTUAL_RENDER}`)\n return generateRender({ pagesDir, renderPath })\n if (id === `\\0${VIRTUAL_API}`)\n return generateApi({ apiPath, appDir })\n if (id === `\\0${VIRTUAL_CONTEXT}`)\n return generateContext()\n },\n\n\n transform(code, id, options) {\n if (options?.ssr) return\n\n const resolvedPagesDir = resolve(process.cwd(), pagesDir)\n if (!id.startsWith(resolvedPagesDir)) return\n\n const ast = parseSync(id, code, { sourceType: 'module' })\n\n const replacements: { start: number; end: number; name: string }[] = []\n\n for (const node of ast.program.body) {\n if (node.type !== 'ExportNamedDeclaration' || !node.declaration) continue\n\n const decl = node.declaration\n\n if (decl.type === 'FunctionDeclaration' && decl.id && SERVER_EXPORTS.has(decl.id.name)) {\n replacements.push({ start: node.start, end: node.end, name: decl.id.name })\n }\n\n if (decl.type === 'VariableDeclaration') {\n const seen = new Set<number>()\n for (const declarator of decl.declarations) {\n if (declarator.id.type === 'Identifier' && SERVER_EXPORTS.has(declarator.id.name)) {\n if (!seen.has(node.start)) {\n seen.add(node.start)\n replacements.push({ start: node.start, end: node.end, name: declarator.id.name })\n }\n }\n }\n }\n }\n\n if (replacements.length === 0) return\n\n replacements.sort((a, b) => b.start - a.start)\n\n let result = code\n for (const { start, end, name } of replacements) {\n result = result.slice(0, start) + `export const ${name} = undefined` + result.slice(end)\n }\n\n return { code: result, map: null }\n },\n\n buildStart() {\n const root = process.cwd()\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n },\n\n configureServer(server) {\n const root = process.cwd()\n\n const regenerateDts = () => {\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n }\n\n server.watcher.add(resolve(root, 'devix.config.ts'))\n server.watcher.on('change', (file) => {\n if (file === resolve(root, 'devix.config.ts')) {\n console.log('[devix] Config changed, restarting...')\n process.exit(75)\n }\n })\n\n server.watcher.on('add', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('unlink', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('change', (file) => {\n if (file.includes(`${appDir}/api`) && !file.endsWith('middleware.ts')) {\n regenerateDts()\n }\n })\n },\n }\n\n const base: UserConfig = {\n plugins: [react(), virtualPlugin],\n publicDir: resolve(process.cwd(), config.publicDir ?? 'public'),\n ssr: { noExternal: ['@devlusoft/devix'] },\n ...(config.envPrefix ? { envPrefix: config.envPrefix } : {}),\n }\n\n return mergeConfig(base, config.vite ?? {})\n}", "export function parseDuration(value: number | string): number {\n if (typeof value === 'number') return value\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h)?$/)\n if (!match) throw new Error(`[devix] Invalid duration: \"${value}\". Use a number (ms) or a string like \"5s\", \"2m\", \"500ms\".`)\n const n = parseFloat(match[1])\n switch (match[2]) {\n case 'h': return n * 3_600_000\n case 'm': return n * 60_000\n case 's': return n * 1_000\n case 'ms':\n default: return n\n }\n}\n", "import {writeFileSync} from 'node:fs'\nimport {resolve} from 'node:path'\nimport {build} from 'vite'\nimport type {DevixConfig} from '../config'\nimport {devix} from '../vite'\nimport {parseDuration} from '../utils/duration'\n\nconst config: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nconst baseConfig = devix(config)\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n outDir: 'dist/client',\n manifest: true,\n rolldownOptions: {\n input: 'virtual:devix/entry-client',\n },\n },\n})\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n ssr: true,\n outDir: 'dist/server',\n copyPublicDir: false,\n rolldownOptions: {\n input: {\n render: 'virtual:devix/render',\n api: 'virtual:devix/api',\n },\n },\n },\n})\n\nconst runtimeConfig = {\n port: config.port ?? 3000,\n host: config.host ?? false,\n loaderTimeout: parseDuration(config.loaderTimeout ?? 10_000),\n output: config.output ?? 'server',\n}\n\nwriteFileSync(\n resolve(process.cwd(), 'dist/devix.config.json'),\n JSON.stringify(runtimeConfig, null, 2),\n 'utf-8'\n)\n\n\nexport {}", "import {readFileSync, mkdirSync, writeFileSync} from 'node:fs'\nimport {resolve, join} from 'node:path'\nimport type {Manifest} from 'vite'\nimport type {DevixConfig} from '../config'\n\nconst userConfig: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nif (userConfig.output !== 'static') {\n console.warn('[devix] Tip: set output: \"static\" in devix.config.ts to skip the SSR server at runtime.')\n}\n\nawait import('./build.js')\n\nconst t = Date.now()\nconst renderModule = await import(resolve(process.cwd(), 'dist/server/render.js') + `?t=${t}`)\n\nconst manifest: Manifest = JSON.parse(\n readFileSync(resolve(process.cwd(), 'dist/client/.vite/manifest.json'), 'utf-8')\n)\n\nconst urls: string[] = await renderModule.getStaticRoutes()\n\nconsole.log(`[devix] Generating ${urls.length} static page${urls.length === 1 ? '' : 's'}...`)\n\nfor (const url of urls) {\n const fullUrl = `http://localhost${url}`\n const {html, statusCode} = await renderModule.render(fullUrl, new Request(fullUrl), {manifest})\n\n if (statusCode !== 200) {\n console.warn(`[devix] Skipping ${url} \u2014 status ${statusCode}`)\n continue\n }\n\n const outPath = url === '/'\n ? join(process.cwd(), 'dist/client/index.html')\n : join(process.cwd(), 'dist/client', url, 'index.html')\n\n mkdirSync(join(outPath, '..'), {recursive: true})\n writeFileSync(outPath, `<!DOCTYPE html>${html}`, 'utf-8')\n console.log(` \u2713 ${url}`)\n}\n\nconsole.log('[devix] Generation complete.')\n\nexport {}\n"],
|
|
5
|
-
"mappings": "mCAIO,SAASA,EAAoB,
|
|
4
|
+
"sourcesContent": ["interface EntryClientOptions {\n cssUrls: string[]\n}\n\nexport function generateEntryClient({ cssUrls }: EntryClientOptions): string {\n const cssImports = cssUrls.map(u => `import '${u}'`).join('\\n')\n\n return `\n${cssImports}\nimport \"@vitejs/plugin-react/preamble\"\nimport React from \"react\"\nimport {hydrateRoot, createRoot} from 'react-dom/client'\nimport {matchClientRoute, loadErrorPage, getDefaultErrorPage} from 'virtual:devix/client-routes'\nimport {RouterProvider} from '@devlusoft/devix'\n\nconst root = document.getElementById('devix-root')\n\nif (!window.__DEVIX__) {\n const ErrorPage = getDefaultErrorPage()\n createRoot(root).render(React.createElement(ErrorPage, {statusCode: 500, message: 'Server error'}))\n} else {\n const {metadata, viewport, clientEntry} = window.__DEVIX__\n const loaderData = window.__LOADER_DATA__\n const layoutsData = window.__LAYOUTS_DATA__ ?? []\n\n const matched = matchClientRoute(window.location.pathname)\n\n if (matched) {\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n hydrateRoot(\n root,\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: loaderData,\n initialParams: matched.params,\n initialPage: pageMod.default,\n initialLayouts: layoutMods.map(m => m.default),\n initialLayoutsData: layoutsData,\n initialMeta: metadata,\n initialViewport: viewport,\n })\n )\n\n if (window.location.hash) {\n const id = window.location.hash.slice(1)\n requestAnimationFrame(() => {\n document.getElementById(id)?.scrollIntoView()\n })\n }\n } else {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n createRoot(root).render(\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: null,\n initialParams: {},\n initialPage: () => null,\n initialLayouts: [],\n initialLayoutsData: [],\n initialMeta: null,\n initialError: {statusCode: 404, message: 'Not found'},\n initialErrorPage: ErrorPage,\n })\n )\n }\n}\n`\n}", "interface ClientRoutesOptions {\n pagesDir: string\n matcherPath: string\n}\n\nexport function generateClientRoutes({pagesDir, matcherPath}: ClientRoutesOptions) {\n return `\nimport React from 'react'\nimport { createMatcher } from '${matcherPath}'\nconst pageFiles = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst layoutFiles = import.meta.glob('/${pagesDir}/**/layout.tsx')\nconst errorFiles = import.meta.glob('/${pagesDir}/**/error.tsx')\n\nexport const matchClientRoute = createMatcher(pageFiles, layoutFiles)\n\nexport async function loadErrorPage() {\n const key = Object.keys(errorFiles)[0]\n if (!key) return null\n const mod = await errorFiles[key]()\n return mod?.default ?? null\n}\n\nexport function getDefaultErrorPage() {\n return function DefaultError({ statusCode, message }) {\n return React.createElement('main', {\n style: { minHeight: '100dvh', display: 'flex', flexDirection: 'column', \n alignItems: 'center', justifyContent: 'center', gap: '8px',\n fontFamily: 'system-ui, sans-serif' }\n },\n React.createElement('h1', {style: {fontSize: '4rem', fontWeight: 700}}, statusCode),\n React.createElement('p', {style: {color: '#666'}}, message ?? 'An unexpected error occurred'),\n )\n }\n}\n`\n}", "interface RenderOptions {\n pagesDir: string\n renderPath: string\n}\n\nexport function generateRender({pagesDir, renderPath}: RenderOptions): string {\n return `\nimport { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${renderPath}'\n\nconst _pages = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst _layouts = import.meta.glob('/${pagesDir}/**/layout.tsx')\n\nconst _glob = {\n pages: _pages,\n layouts: _layouts,\n pagesDir: '/${pagesDir}',\n}\n\nexport function render(url, request, options) {\n return _render(url, request, _glob, options)\n}\n\nexport function runLoader(url, request, options) {\n return _runLoader(url, request, _glob, options)\n}\n\nexport function getStaticRoutes() {\n return _getStaticRoutes(_glob)\n}\n`\n}\n", "interface ApiOptions {\n apiPath: string\n appDir: string\n}\n\nexport function generateApi({apiPath, appDir}: ApiOptions): string {\n return `\nimport { handleApiRequest as _handleApiRequest } from '${apiPath}'\n\nconst _routes = import.meta.glob(['/${appDir}/api/**/*.ts', '!**/middleware.ts'])\nconst _middlewares = import.meta.glob('/${appDir}/api/**/middleware.ts')\n\nconst _glob = {\n routes: _routes,\n middlewares: _middlewares,\n apiDir: '/${appDir}/api',\n}\n\nexport function handleApiRequest(url, request) {\n return _handleApiRequest(url, request, _glob)\n}\n`\n}\n", "export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface Page {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface Layout {\n dir: string\n key: string\n}\n\nexport interface PagesResult {\n pages: Page[]\n layouts: Layout[]\n}\n\nfunction keyToRoutePattern(key: string, pagesDir: string): string {\n const rel = key.slice(pagesDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === \"/\" ? \"/\" : `/${pattern}`\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: PagesResult | null = null\n\nexport function invalidatePagesCache() {\n cache = null\n}\n\nexport function buildPages(pageKeys: string[], layoutKeys: string[], pagesDir: string): PagesResult {\n if (cache) return cache\n\n const pages: Page[] = []\n const layouts: Layout[] = []\n\n for (const key of layoutKeys) {\n layouts.push({dir: keyToDir(key), key})\n }\n\n for (const key of pageKeys) {\n const pattern = keyToRoutePattern(key, pagesDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n pages.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n\n pages.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {pages, layouts}\n return cache\n}\n\nexport function collectLayoutChain(pageKey: string, layouts: Layout[]): Layout[] {\n const pageDir = keyToDir(pageKey)\n\n return layouts\n .filter(layout => pageDir.startsWith(layout.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchPage(pathname: string, pages: Page[]): {\n page: Page\n params: Record<string, string>\n} | null {\n for (const page of pages) {\n const match = pathname.match(page.regex)\n if (match) {\n const params: Record<string, string> = {}\n page.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {page, params}\n }\n }\n return null\n}\n", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "export function generateContext(): string {\n return `\nexport {RouterContext} from '@devlusoft/devix/runtime/context'\n`\n}", "const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\nconst METHOD_EXPORT_RE = /export\\s+(?:const|async\\s+function|function)\\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\b/g\n\nfunction stripComments(content: string): string {\n return content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n}\n\nexport function extractHttpMethods(content: string): HttpMethod[] {\n const found = new Set<HttpMethod>()\n for (const match of stripComments(content).matchAll(METHOD_EXPORT_RE)) {\n found.add(match[1] as HttpMethod)\n }\n return [...found]\n}\n", "import { keyToRoutePattern } from '../../server/api-router'\nimport type { HttpMethod } from './extract-methods'\n\nexport interface RouteEntry {\n filePath: string\n urlPattern: string\n identifier: string\n methods: HttpMethod[]\n}\n\nexport function filePathToIdentifier(filePath: string, apiDir: string): string {\n return '_api_' + filePath\n .slice(`${apiDir}/`.length)\n .replace(/\\.(ts|tsx)$/, '')\n .replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nexport function buildRouteEntry(filePath: string, apiDir: string, methods: HttpMethod[]): RouteEntry {\n return {\n filePath,\n urlPattern: keyToRoutePattern(filePath, apiDir),\n identifier: filePathToIdentifier(filePath, apiDir),\n methods,\n }\n}\n\nexport function generateRoutesDts(entries: RouteEntry[], apiDir: string): string {\n if (entries.length === 0) {\n return `// auto-generado por devix \u2014 no editar\\ndeclare module '@devlusoft/devix' {\\n interface ApiRoutes {}\\n}\\n`\n }\n\n const imports = entries\n .map(e => {\n const importPath = '../' + e.filePath.replace(/\\.(ts|tsx)$/, '')\n return `import type * as ${e.identifier} from '${importPath}'`\n })\n .join('\\n')\n\n const routeLines = entries.flatMap(e =>\n e.methods.map(m =>\n ` '${m} ${e.urlPattern}': InferRoute<(typeof ${e.identifier})['${m}']>`\n )\n ).join('\\n')\n\n return `// auto-generado por devix \u2014 no editar\n${imports}\n\ntype JsonResponse<T> = Response & { readonly __body: T }\ntype UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never\ntype InferFnReturn<T> = T extends (...args: any[]) => any\n ? UnwrapJson<Awaited<ReturnType<T>>> | Exclude<Awaited<ReturnType<T>>, JsonResponse<any> | null | void | undefined>\n : never\ntype InferRoute<T> =\n T extends { readonly __return?: infer TReturn; readonly __body?: infer TBody }\n ? {\n __body: [TBody] extends [undefined] ? never : Exclude<TBody, undefined>\n __response: InferFnReturn<() => TReturn>\n }\n : InferFnReturn<T>\n\ndeclare module '@devlusoft/devix' {\n interface ApiRoutes {\n${routeLines}\n }\n}\n`\n}\n", "import {readFileSync, readdirSync, statSync} from 'node:fs'\nimport {join, relative} from 'node:path'\nimport {extractHttpMethods} from './extract-methods'\nimport {buildRouteEntry} from './routes-dts'\nimport type {RouteEntry} from './routes-dts'\n\nfunction walkDir(dir: string, root: string): string[] {\n const entries: string[] = []\n for (const name of readdirSync(dir)) {\n const full = join(dir, name)\n if (statSync(full).isDirectory()) {\n entries.push(...walkDir(full, root))\n } else if (/\\.(ts|tsx)$/.test(name)) {\n entries.push(relative(root, full).replace(/\\\\/g, '/'))\n }\n }\n return entries\n}\n\nexport function scanApiFiles(appDir: string, projectRoot: string): RouteEntry[] {\n const apiDir = join(projectRoot, appDir, 'api')\n\n let files: string[]\n try {\n files = walkDir(apiDir, projectRoot)\n } catch {\n return []\n }\n\n return files\n .filter(f => !f.endsWith('middleware.ts') && !f.endsWith('middleware.tsx'))\n .flatMap(filePath => {\n try {\n const content = readFileSync(join(projectRoot, filePath), 'utf-8')\n const methods = extractHttpMethods(content)\n if (methods.length === 0) return []\n return [buildRouteEntry(filePath, `${appDir}/api`, methods)]\n } catch {\n return []\n }\n })\n}\n", "import {mkdirSync, readFileSync, writeFileSync, existsSync} from 'node:fs'\nimport {join} from 'node:path'\n\nexport function writeRoutesDts(content: string, projectRoot: string): boolean {\n const devixDir = join(projectRoot, '.devix')\n const outPath = join(devixDir, 'routes.d.ts')\n\n mkdirSync(devixDir, {recursive: true})\n\n if (existsSync(outPath) && readFileSync(outPath, 'utf-8') === content) {\n return false\n }\n\n writeFileSync(outPath, content, 'utf-8')\n return true\n}\n", "import { UserConfig, Plugin, mergeConfig } from 'vite'\nimport type { DevixConfig } from '../config'\nimport react from '@vitejs/plugin-react'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, resolve } from 'node:path'\nimport { generateEntryClient } from './codegen/entry-client'\nimport { generateClientRoutes } from './codegen/client-routes'\nimport { generateRender } from './codegen/render'\nimport { generateApi } from './codegen/api'\nimport { invalidatePagesCache } from \"../server/pages-router\";\nimport { invalidateApiCache } from \"../server/api-router\";\nimport { generateContext } from \"./codegen/context\";\nimport { scanApiFiles } from \"./codegen/scan-api\";\nimport { generateRoutesDts } from \"./codegen/routes-dts\";\nimport { writeRoutesDts } from \"./codegen/write-routes-dts\";\nimport { parseSync } from 'oxc-parser'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nconst VIRTUAL_ENTRY_CLIENT = 'virtual:devix/entry-client'\nconst VIRTUAL_CLIENT_ROUTES = 'virtual:devix/client-routes'\nconst VIRTUAL_RENDER = 'virtual:devix/render'\nconst VIRTUAL_API = 'virtual:devix/api'\nconst VIRTUAL_CONTEXT = 'virtual:devix/context'\n\nconst SERVER_EXPORTS = new Set(['loader', 'guard', 'generateStaticParams', 'headers'])\n\nexport function devix(config: DevixConfig): UserConfig {\n const appDir = config.appDir ?? 'app'\n const pagesDir = `${appDir}/pages`\n const cssUrls = (config.css ?? []).map(u => u.startsWith('/') ? u : `/${u.replace(/^\\.\\//, '')}`)\n\n const renderPath = resolve(__dirname, '../server/render.js').replace(/\\\\/g, '/')\n const apiPath = resolve(__dirname, '../server/api.js').replace(/\\\\/g, '/')\n const matcherPath = resolve(__dirname, '../runtime/client-router.js').replace(/\\\\/g, '/')\n\n const virtualPlugin: Plugin = {\n name: 'devix',\n enforce: 'pre',\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_CLIENT) return `\\0${VIRTUAL_ENTRY_CLIENT}`\n if (id === VIRTUAL_CLIENT_ROUTES) return `\\0${VIRTUAL_CLIENT_ROUTES}`\n if (id === VIRTUAL_RENDER) return `\\0${VIRTUAL_RENDER}`\n if (id === VIRTUAL_API) return `\\0${VIRTUAL_API}`\n if (id === VIRTUAL_CONTEXT) return `\\0${VIRTUAL_CONTEXT}`\n },\n\n load(id) {\n if (id === `\\0${VIRTUAL_ENTRY_CLIENT}`)\n return generateEntryClient({ cssUrls })\n if (id === `\\0${VIRTUAL_CLIENT_ROUTES}`)\n return generateClientRoutes({ pagesDir, matcherPath })\n if (id === `\\0${VIRTUAL_RENDER}`)\n return generateRender({ pagesDir, renderPath })\n if (id === `\\0${VIRTUAL_API}`)\n return generateApi({ apiPath, appDir })\n if (id === `\\0${VIRTUAL_CONTEXT}`)\n return generateContext()\n },\n\n\n transform(code, id, options) {\n if (options?.ssr) return\n\n const resolvedPagesDir = resolve(process.cwd(), pagesDir)\n if (!id.startsWith(resolvedPagesDir)) return\n\n const ast = parseSync(id, code, { sourceType: 'module' })\n\n const replacements: { start: number; end: number; name: string }[] = []\n\n for (const node of ast.program.body) {\n if (node.type !== 'ExportNamedDeclaration' || !node.declaration) continue\n\n const decl = node.declaration\n\n if (decl.type === 'FunctionDeclaration' && decl.id && SERVER_EXPORTS.has(decl.id.name)) {\n replacements.push({ start: node.start, end: node.end, name: decl.id.name })\n }\n\n if (decl.type === 'VariableDeclaration') {\n const seen = new Set<number>()\n for (const declarator of decl.declarations) {\n if (declarator.id.type === 'Identifier' && SERVER_EXPORTS.has(declarator.id.name)) {\n if (!seen.has(node.start)) {\n seen.add(node.start)\n replacements.push({ start: node.start, end: node.end, name: declarator.id.name })\n }\n }\n }\n }\n }\n\n if (replacements.length === 0) return\n\n replacements.sort((a, b) => b.start - a.start)\n\n let result = code\n for (const { start, end, name } of replacements) {\n result = result.slice(0, start) + `export const ${name} = undefined` + result.slice(end)\n }\n\n return { code: result, map: null }\n },\n\n buildStart() {\n const root = process.cwd()\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n },\n\n configureServer(server) {\n const root = process.cwd()\n\n const regenerateDts = () => {\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n }\n\n server.watcher.add(resolve(root, 'devix.config.ts'))\n server.watcher.on('change', (file) => {\n if (file === resolve(root, 'devix.config.ts')) {\n console.log('[devix] Config changed, restarting...')\n process.exit(75)\n }\n })\n\n server.watcher.on('add', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('unlink', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('change', (file) => {\n if (file.includes(`${appDir}/api`) && !file.endsWith('middleware.ts')) {\n regenerateDts()\n }\n })\n },\n }\n\n const base: UserConfig = {\n plugins: [react(), virtualPlugin],\n publicDir: resolve(process.cwd(), config.publicDir ?? 'public'),\n ssr: { noExternal: ['@devlusoft/devix'] },\n ...(config.envPrefix ? { envPrefix: config.envPrefix } : {}),\n }\n\n return mergeConfig(base, config.vite ?? {})\n}", "export function parseDuration(value: number | string): number {\n if (typeof value === 'number') return value\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h)?$/)\n if (!match) throw new Error(`[devix] Invalid duration: \"${value}\". Use a number (ms) or a string like \"5s\", \"2m\", \"500ms\".`)\n const n = parseFloat(match[1])\n switch (match[2]) {\n case 'h': return n * 3_600_000\n case 'm': return n * 60_000\n case 's': return n * 1_000\n case 'ms':\n default: return n\n }\n}\n", "import {writeFileSync} from 'node:fs'\nimport {resolve} from 'node:path'\nimport {build} from 'vite'\nimport type {DevixConfig} from '../config'\nimport {devix} from '../vite'\nimport {parseDuration} from '../utils/duration'\n\nconst config: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nconst baseConfig = devix(config)\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n outDir: 'dist/client',\n manifest: true,\n rolldownOptions: {\n input: 'virtual:devix/entry-client',\n },\n },\n})\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n ssr: true,\n outDir: 'dist/server',\n copyPublicDir: false,\n rolldownOptions: {\n input: {\n render: 'virtual:devix/render',\n api: 'virtual:devix/api',\n },\n },\n },\n})\n\nconst runtimeConfig = {\n port: config.port ?? 3000,\n host: config.host ?? false,\n loaderTimeout: parseDuration(config.loaderTimeout ?? 10_000),\n output: config.output ?? 'server',\n}\n\nwriteFileSync(\n resolve(process.cwd(), 'dist/devix.config.json'),\n JSON.stringify(runtimeConfig, null, 2),\n 'utf-8'\n)\n\n\nexport {}", "import {readFileSync, mkdirSync, writeFileSync} from 'node:fs'\nimport {resolve, join} from 'node:path'\nimport type {Manifest} from 'vite'\nimport type {DevixConfig} from '../config'\n\nconst userConfig: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nif (userConfig.output !== 'static') {\n console.warn('[devix] Tip: set output: \"static\" in devix.config.ts to skip the SSR server at runtime.')\n}\n\nawait import('./build.js')\n\nconst t = Date.now()\nconst renderModule = await import(resolve(process.cwd(), 'dist/server/render.js') + `?t=${t}`)\n\nconst manifest: Manifest = JSON.parse(\n readFileSync(resolve(process.cwd(), 'dist/client/.vite/manifest.json'), 'utf-8')\n)\n\nconst urls: string[] = await renderModule.getStaticRoutes()\n\nconsole.log(`[devix] Generating ${urls.length} static page${urls.length === 1 ? '' : 's'}...`)\n\nfor (const url of urls) {\n const fullUrl = `http://localhost${url}`\n const {html, statusCode} = await renderModule.render(fullUrl, new Request(fullUrl), {manifest})\n\n if (statusCode !== 200) {\n console.warn(`[devix] Skipping ${url} \u2014 status ${statusCode}`)\n continue\n }\n\n const outPath = url === '/'\n ? join(process.cwd(), 'dist/client/index.html')\n : join(process.cwd(), 'dist/client', url, 'index.html')\n\n mkdirSync(join(outPath, '..'), {recursive: true})\n writeFileSync(outPath, `<!DOCTYPE html>${html}`, 'utf-8')\n console.log(` \u2713 ${url}`)\n}\n\nconsole.log('[devix] Generation complete.')\n\nexport {}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["generateEntryClient", "cssUrls", "u", "init_entry_client", "__esmMin", "generateClientRoutes", "pagesDir", "matcherPath", "init_client_routes", "__esmMin", "generateRender", "pagesDir", "renderPath", "init_render", "__esmMin", "generateApi", "apiPath", "appDir", "init_api", "__esmMin", "routePattern", "rel", "init_patterns", "__esmMin", "invalidatePagesCache", "cache", "init_pages_router", "__esmMin", "init_patterns", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "invalidateApiCache", "cache", "init_api_router", "__esmMin", "init_patterns", "generateContext", "init_context", "__esmMin", "stripComments", "content", "extractHttpMethods", "found", "match", "METHOD_EXPORT_RE", "init_extract_methods", "__esmMin", "filePathToIdentifier", "filePath", "apiDir", "buildRouteEntry", "methods", "keyToRoutePattern", "generateRoutesDts", "entries", "imports", "e", "importPath", "routeLines", "m", "init_routes_dts", "__esmMin", "init_api_router", "readFileSync", "readdirSync", "statSync", "join", "relative", "walkDir", "dir", "root", "entries", "name", "full", "scanApiFiles", "appDir", "projectRoot", "apiDir", "files", "f", "filePath", "content", "methods", "extractHttpMethods", "buildRouteEntry", "init_scan_api", "__esmMin", "init_extract_methods", "init_routes_dts", "mkdirSync", "readFileSync", "writeFileSync", "existsSync", "join", "writeRoutesDts", "content", "projectRoot", "devixDir", "outPath", "init_write_routes_dts", "__esmMin", "mergeConfig", "react", "fileURLToPath", "dirname", "resolve", "parseSync", "devix", "config", "appDir", "pagesDir", "cssUrls", "u", "renderPath", "__dirname", "apiPath", "matcherPath", "virtualPlugin", "id", "VIRTUAL_ENTRY_CLIENT", "VIRTUAL_CLIENT_ROUTES", "VIRTUAL_RENDER", "VIRTUAL_API", "VIRTUAL_CONTEXT", "generateEntryClient", "generateClientRoutes", "generateRender", "generateApi", "generateContext", "code", "options", "resolvedPagesDir", "ast", "replacements", "node", "decl", "SERVER_EXPORTS", "seen", "declarator", "a", "b", "result", "start", "end", "name", "root", "entries", "scanApiFiles", "writeRoutesDts", "generateRoutesDts", "server", "regenerateDts", "file", "invalidatePagesCache", "invalidateApiCache", "base", "init_vite", "__esmMin", "init_entry_client", "init_client_routes", "init_render", "init_api", "init_pages_router", "init_api_router", "init_context", "init_scan_api", "init_routes_dts", "init_write_routes_dts", "parseDuration", "value", "match", "n", "init_duration", "__esmMin", "build_exports", "writeFileSync", "resolve", "build", "config", "baseConfig", "runtimeConfig", "init_build", "__esmMin", "init_vite", "init_duration", "devix", "parseDuration", "readFileSync", "mkdirSync", "writeFileSync", "resolve", "join", "userConfig", "t", "renderModule", "manifest", "urls", "url", "fullUrl", "html", "statusCode", "outPath"]
|
|
7
7
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -38,6 +38,13 @@ if (!window.__DEVIX__) {
|
|
|
38
38
|
initialViewport: viewport,
|
|
39
39
|
})
|
|
40
40
|
)
|
|
41
|
+
|
|
42
|
+
if (window.location.hash) {
|
|
43
|
+
const id = window.location.hash.slice(1)
|
|
44
|
+
requestAnimationFrame(() => {
|
|
45
|
+
document.getElementById(id)?.scrollIntoView()
|
|
46
|
+
})
|
|
47
|
+
}
|
|
41
48
|
} else {
|
|
42
49
|
const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()
|
|
43
50
|
createRoot(root).render(
|
|
@@ -123,7 +130,7 @@ export function handleApiRequest(url, request) {
|
|
|
123
130
|
}
|
|
124
131
|
`}var et=s(()=>{"use strict"});function S(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var T=s(()=>{"use strict"});function $(){Ft=null}var Ft,rt=s(()=>{"use strict";T();Ft=null});function ot(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),i=S(r);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}function b(){Ht=null}var Ht,D=s(()=>{"use strict";T();Ht=null});function nt(){return`
|
|
125
132
|
export {RouterContext} from '@devlusoft/devix/runtime/context'
|
|
126
|
-
`}var it=s(()=>{"use strict"});function Nt(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function st(t){let e=new Set;for(let r of Nt(t).matchAll(qt))e.add(r[1]);return[...e]}var qt,at=s(()=>{"use strict";qt=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g});function Vt(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function ct(t,e,r){return{filePath:t,urlPattern:ot(t,e),identifier:Vt(t,e),methods:r}}function
|
|
133
|
+
`}var it=s(()=>{"use strict"});function Nt(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function st(t){let e=new Set;for(let r of Nt(t).matchAll(qt))e.add(r[1]);return[...e]}var qt,at=s(()=>{"use strict";qt=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g});function Vt(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function ct(t,e,r){return{filePath:t,urlPattern:ot(t,e),identifier:Vt(t,e),methods:r}}function A(t,e){if(t.length===0)return`// auto-generado por devix \u2014 no editar
|
|
127
134
|
declare module '@devlusoft/devix' {
|
|
128
135
|
interface ApiRoutes {}
|
|
129
136
|
}
|
|
@@ -150,7 +157,7 @@ declare module '@devlusoft/devix' {
|
|
|
150
157
|
${i}
|
|
151
158
|
}
|
|
152
159
|
}
|
|
153
|
-
`}var
|
|
160
|
+
`}var C=s(()=>{"use strict";D()});import{readFileSync as Wt,readdirSync as Jt,statSync as Bt}from"node:fs";import{join as O,relative as Xt}from"node:path";function lt(t,e){let r=[];for(let i of Jt(t)){let o=O(t,i);Bt(o).isDirectory()?r.push(...lt(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(Xt(e,o).replace(/\\/g,"/"))}return r}function M(t,e){let r=O(e,t,"api"),i;try{i=lt(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=Wt(O(e,o),"utf-8"),m=st(a);return m.length===0?[]:[ct(o,`${t}/api`,m)]}catch{return[]}})}var ut=s(()=>{"use strict";at();C()});import{mkdirSync as Gt,readFileSync as Yt,writeFileSync as zt,existsSync as Zt}from"node:fs";import{join as pt}from"node:path";function I(t,e){let r=pt(e,".devix"),i=pt(r,"routes.d.ts");return Gt(r,{recursive:!0}),Zt(i)&&Yt(i,"utf-8")===t?!1:(zt(i,t,"utf-8"),!0)}var dt=s(()=>{"use strict"});import{mergeConfig as Kt}from"vite";import Qt from"@vitejs/plugin-react";import{fileURLToPath as te}from"node:url";import{dirname as ee,resolve as d}from"node:path";import{parseSync as re}from"oxc-parser";function ft(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=d(k,"../server/render.js").replace(/\\/g,"/"),a=d(k,"../server/api.js").replace(/\\/g,"/"),m=d(k,"../runtime/client-router.js").replace(/\\/g,"/"),h={name:"devix",enforce:"pre",resolveId(n){if(n===L)return`\0${L}`;if(n===U)return`\0${U}`;if(n===j)return`\0${j}`;if(n===F)return`\0${F}`;if(n===H)return`\0${H}`},load(n){if(n===`\0${L}`)return G({cssUrls:i});if(n===`\0${U}`)return z({pagesDir:r,matcherPath:m});if(n===`\0${j}`)return K({pagesDir:r,renderPath:o});if(n===`\0${F}`)return tt({apiPath:a,appDir:e});if(n===`\0${H}`)return nt()},transform(n,l,x){if(x?.ssr)return;let c=d(process.cwd(),r);if(!l.startsWith(c))return;let Ot=re(l,n,{sourceType:"module"}),v=[];for(let u of Ot.program.body){if(u.type!=="ExportNamedDeclaration"||!u.declaration)continue;let p=u.declaration;if(p.type==="FunctionDeclaration"&&p.id&&mt.has(p.id.name)&&v.push({start:u.start,end:u.end,name:p.id.name}),p.type==="VariableDeclaration"){let w=new Set;for(let P of p.declarations)P.id.type==="Identifier"&&mt.has(P.id.name)&&(w.has(u.start)||(w.add(u.start),v.push({start:u.start,end:u.end,name:P.id.name})))}}if(v.length===0)return;v.sort((u,p)=>p.start-u.start);let R=n;for(let{start:u,end:p,name:w}of v)R=R.slice(0,u)+`export const ${w} = undefined`+R.slice(p);return{code:R,map:null}},buildStart(){let n=process.cwd(),l=M(e,n);I(A(l,`${e}/api`),n)},configureServer(n){let l=process.cwd(),x=()=>{let c=M(e,l);I(A(c,`${e}/api`),l)};n.watcher.add(d(l,"devix.config.ts")),n.watcher.on("change",c=>{c===d(l,"devix.config.ts")&&(console.log("[devix] Config changed, restarting..."),process.exit(75))}),n.watcher.on("add",c=>{c.startsWith(d(l,r))&&$(),c.includes(`${e}/api`)&&(b(),x())}),n.watcher.on("unlink",c=>{c.startsWith(d(l,r))&&$(),c.includes(`${e}/api`)&&(b(),x())}),n.watcher.on("change",c=>{c.includes(`${e}/api`)&&!c.endsWith("middleware.ts")&&x()})}},g={plugins:[Qt(),h],publicDir:d(process.cwd(),t.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return Kt(g,t.vite??{})}var k,L,U,j,F,H,mt,gt=s(()=>{"use strict";Y();Z();Q();et();rt();D();it();ut();C();dt();k=ee(te(import.meta.url)),L="virtual:devix/entry-client",U="virtual:devix/client-routes",j="virtual:devix/render",F="virtual:devix/api",H="virtual:devix/context",mt=new Set(["loader","guard","generateStaticParams","headers"])});function ht(t){if(typeof t=="number")return t;let e=t.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)throw new Error(`[devix] Invalid duration: "${t}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(e[1]);switch(e[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}var xt=s(()=>{"use strict"});var Rt={};import{writeFileSync as oe}from"node:fs";import{resolve as ne}from"node:path";import{build as vt}from"vite";var y,yt,ie,q=s(async()=>{"use strict";gt();xt();y=(await import(`${process.cwd()}/devix.config.ts`)).default,yt=ft(y);await vt({...yt,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await vt({...yt,configFile:!1,build:{ssr:!0,outDir:"dist/server",copyPublicDir:!1,rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});ie={port:y.port??3e3,host:y.host??!1,loaderTimeout:ht(y.loaderTimeout??1e4),output:y.output??"server"};oe(ne(process.cwd(),"dist/devix.config.json"),JSON.stringify(ie,null,2),"utf-8")});var de={};import{readFileSync as se,mkdirSync as ae,writeFileSync as ce}from"node:fs";import{resolve as wt,join as N}from"node:path";var le,ue,_t,pe,V,Et=s(async()=>{"use strict";le=(await import(`${process.cwd()}/devix.config.ts`)).default;le.output!=="static"&&console.warn('[devix] Tip: set output: "static" in devix.config.ts to skip the SSR server at runtime.');await q().then(()=>Rt);ue=Date.now(),_t=await import(wt(process.cwd(),"dist/server/render.js")+`?t=${ue}`),pe=JSON.parse(se(wt(process.cwd(),"dist/client/.vite/manifest.json"),"utf-8")),V=await _t.getStaticRoutes();console.log(`[devix] Generating ${V.length} static page${V.length===1?"":"s"}...`);for(let t of V){let e=`http://localhost${t}`,{html:r,statusCode:i}=await _t.render(e,new Request(e),{manifest:pe});if(i!==200){console.warn(`[devix] Skipping ${t} \u2014 status ${i}`);continue}let o=t==="/"?N(process.cwd(),"dist/client/index.html"):N(process.cwd(),"dist/client",t,"index.html");ae(N(o,".."),{recursive:!0}),ce(o,`<!DOCTYPE html>${r}`,"utf-8"),console.log(` \u2713 ${t}`)}console.log("[devix] Generation complete.")});function Pt(t,{apiModule:e,renderModule:r,loaderTimeout:i}){t.all("/api/*",async o=>{try{return await e.handleApiRequest(o.req.url,o.req.raw)}catch(a){return console.error(a),o.json({error:"internal error"},500)}}),t.get("/_data/*",async o=>{try{let{pathname:a,search:m}=new URL(o.req.url,"http://localhost"),h=a.replace(/^\/_data/,"")+m,g=await r.runLoader(h,o.req.raw,{loaderTimeout:i});return g.error?o.json({error:"internal error"},500):o.json(g)}catch(a){return console.error(a),o.json({error:"internal error"},500)}})}function St(t,{renderModule:e,manifest:r,loaderTimeout:i}){t.get("*",async o=>{try{let{html:a,statusCode:m,headers:h}=await e.render(o.req.url,o.req.raw,{manifest:r,loaderTimeout:i}),g=o.html(`<!DOCTYPE html>${a}`,m);for(let[n,l]of Object.entries(h))g.headers.set(n,l);return g}catch(a){return console.error(a),o.text("Internal Server Error",500)}})}var Tt=s(()=>{"use strict"});import{loadEnv as me}from"vite";function $t(t){let e=me(t,process.cwd(),"");for(let[r,i]of Object.entries(e))process.env[r]===void 0&&(process.env[r]=i)}var bt=s(()=>{"use strict"});var we={};import{readFileSync as Dt}from"node:fs";import{serve as fe}from"@hono/node-server";import{serveStatic as ge}from"@hono/node-server/serve-static";import{Hono as he}from"hono";import{resolve as _,join as xe}from"node:path";var W,J,B,f,ve,ye,E,Re,At=s(async()=>{"use strict";Tt();bt();$t("production");try{f=JSON.parse(Dt(_(process.cwd(),"dist/devix.config.json"),"utf-8")),f.output!=="static"&&(W=await import(_(process.cwd(),"dist/server/render.js")),J=await import(_(process.cwd(),"dist/server/api.js"))),B=JSON.parse(Dt(_(process.cwd(),"dist/client/.vite/manifest.json"),"utf-8"))}catch{console.error('[devix] Build not found. Run "devix build" first.'),process.exit(1)}ve=Number(process.env.PORT)||f.port||3e3,ye=typeof f.host=="string"?f.host:f.host?"0.0.0.0":process.env.HOST||"0.0.0.0",E=new he,Re=xe(process.cwd(),"dist/client");E.use("/*",ge({root:Re,onFound:(t,e)=>{e.header("Cache-Control",t.includes("/assets/")?"public, immutable, max-age=31536000":"no-cache")}}));f.output==="static"?console.log("[devix] Static mode \u2014 serving pre-generated files from dist/client"):(Pt(E,{renderModule:W,apiModule:J,manifest:B}),St(E,{renderModule:W,apiModule:J,manifest:B,loaderTimeout:f.loaderTimeout}));fe({fetch:E.fetch,port:ve,hostname:ye},t=>console.log(`http://${t.address}:${t.port}`))});var Ct=process.argv[2];switch(Ct){case"dev":await Promise.resolve().then(()=>(X(),jt));break;case"build":await q().then(()=>Rt);break;case"generate":await Et().then(()=>de);break;case"start":await At().then(()=>we);break;case"--version":case"-v":{console.log("0.4.1-beta.3");break}case"--help":case"-h":console.log(`
|
|
154
161
|
devix \u2014 a lightweight SSR framework
|
|
155
162
|
|
|
156
163
|
Usage:
|
|
@@ -166,5 +173,5 @@ Options:
|
|
|
166
173
|
Output modes (set in devix.config.ts):
|
|
167
174
|
output: "server" SSR mode \u2014 devix start handles requests dynamically (default)
|
|
168
175
|
output: "static" SSG mode \u2014 devix generate pre-renders all pages; devix start serves static files only
|
|
169
|
-
`.trim());break;default:console.error(`Unknown command: ${
|
|
176
|
+
`.trim());break;default:console.error(`Unknown command: ${Ct}`),console.error("Usage: devix <dev|build|generate|start>"),process.exit(1)}
|
|
170
177
|
//# sourceMappingURL=index.js.map
|