@devlusoft/devix 0.4.0-beta.2 → 0.4.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/README.md CHANGED
@@ -4,8 +4,6 @@ Un meta-framework ligero de React 19 con SSR, impulsado por Vite 8 + Hono.
4
4
 
5
5
  Construye aplicaciones React full-stack con enrutamiento basado en archivos, renderizado del lado del servidor, generación estática de sitios y rutas API — configuración mínima, control máximo.
6
6
 
7
- > **⚠️ En desarrollo activo** — devix está en evolución constante. Las API's pueden cambiar entre versiones sin período de deprecación. No se recomienda para uso en producción todavía.
8
-
9
7
  ## Características
10
8
 
11
9
  - **Vite 8** — HMR instantáneo y builds rápidos con Rolldown
@@ -14,8 +12,10 @@ Construye aplicaciones React full-stack con enrutamiento basado en archivos, ren
14
12
  - **SSR por defecto** — cada página se renderiza en el servidor
15
13
  - **SSG** — genera HTML estático con `generateStaticParams`
16
14
  - **Rutas API** — basadas en archivos, con `createHandler` para tipado de extremo a extremo
17
- - **$fetch** — cliente HTTP con body y respuesta tipados a partir del handler
15
+ - **$fetch** — cliente HTTP con body y respuesta tipados, con autocompletado de rutas
18
16
  - **Carga de datos** — funciones `loader` con hidratación automática en el cliente
17
+ - **Navegación programática** — `useNavigate()` con soporte de `replace` y View Transitions API
18
+ - **Revalidación de datos** — `useRevalidate()` para refrescar datos sin recargar la página
19
19
  - **Guards de ruta** — redirecciones del lado del servidor antes del renderizado
20
20
  - **SEO** — `metadata` y `generateMetadata` por página, con soporte de Open Graph y Twitter
21
21
  - **Hooks de contexto** — `useRequest()`, `useCtx()`, `useParams()` accesibles desde cualquier función del handler
@@ -136,6 +136,50 @@ export default function RootLayout({ children }: LayoutProps) {
136
136
  }
137
137
  ```
138
138
 
139
+ ### Tipado de params
140
+
141
+ Pasa el tipo de params directamente sin necesidad de un loader:
142
+
143
+ ```tsx
144
+ import type { PageProps } from '@devlusoft/devix'
145
+
146
+ export default function ProviderPage({ params }: PageProps<{ providerId: string }>) {
147
+ return <h1>{params.providerId}</h1>
148
+ }
149
+ ```
150
+
151
+ ### Navegación programática
152
+
153
+ ```tsx
154
+ import { useNavigate, useRevalidate } from '@devlusoft/devix'
155
+
156
+ function MyComponent() {
157
+ const navigate = useNavigate()
158
+ const revalidate = useRevalidate()
159
+
160
+ return (
161
+ <>
162
+ <button onClick={() => navigate('/dashboard')}>Ir al dashboard</button>
163
+ <button onClick={() => navigate('/login', { replace: true })}>Login (sin historial)</button>
164
+ <button onClick={() => navigate('/shop', { viewTransition: true })}>Con animación</button>
165
+ <button onClick={() => revalidate()}>Refrescar datos</button>
166
+ </>
167
+ )
168
+ }
169
+ ```
170
+
171
+ ### redirect con replace
172
+
173
+ ```ts
174
+ import { redirect } from '@devlusoft/devix'
175
+
176
+ export async function loader({ request }: LoaderContext) {
177
+ const user = await getSession(request)
178
+ if (!user) return redirect('/login', { replace: true })
179
+ return user
180
+ }
181
+ ```
182
+
139
183
  ### Rutas API
140
184
 
141
185
  `createHandler` da tipado de extremo a extremo — el body y el retorno se infieren automáticamente para `$fetch`:
@@ -203,6 +247,7 @@ export default defineConfig({
203
247
  port: 3000, // puerto del servidor dev y producción (default: 3000)
204
248
  host: false, // bind a 0.0.0.0 (default: false)
205
249
  appDir: 'app', // directorio de la app (default: 'app')
250
+ publicDir: 'public', // directorio de archivos estáticos (default: 'public')
206
251
  output: 'server', // 'server' | 'static' (default: 'server')
207
252
  loaderTimeout: 10_000, // timeout de los loaders en ms (default: 10000)
208
253
  css: ['./app/styles/global.css'], // archivos CSS globales
package/dist/cli/build.js CHANGED
@@ -54,7 +54,7 @@ if (!window.__DEVIX__) {
54
54
  )
55
55
  }
56
56
  }
57
- `}function A({pagesDir:t,matcherPath:e}){return`
57
+ `}function D({pagesDir:t,matcherPath:e}){return`
58
58
  import React from 'react'
59
59
  import { createMatcher } from '${e}'
60
60
  const pageFiles = import.meta.glob(['/${t}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
@@ -82,7 +82,7 @@ export function getDefaultErrorPage() {
82
82
  )
83
83
  }
84
84
  }
85
- `}function D({pagesDir:t,renderPath:e}){return`
85
+ `}function A({pagesDir:t,renderPath:e}){return`
86
86
  import { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${e}'
87
87
 
88
88
  const _pages = import.meta.glob(['/${t}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
@@ -149,5 +149,5 @@ declare module '@devlusoft/devix' {
149
149
  ${i}
150
150
  }
151
151
  }
152
- `}function M(t,e){let r=[];for(let i of z(t)){let o=x(t,i);G(o).isDirectory()?r.push(...M(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(Y(e,o).replace(/\\/g,"/"))}return r}function y(t,e){let r=x(e,t,"api"),i;try{i=M(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=X(x(e,o),"utf-8"),p=I(a);return p.length===0?[]:[L(o,`${t}/api`,p)]}catch{return[]}})}import{mkdirSync as Z,readFileSync as K,writeFileSync as Q,existsSync as tt}from"node:fs";import{join as O}from"node:path";function R(t,e){let r=O(e,".devix"),i=O(r,"routes.d.ts");return Z(r,{recursive:!0}),tt(i)&&K(i,"utf-8")===t?!1:(Q(i,t,"utf-8"),!0)}var _=ot(nt(import.meta.url)),v="virtual:devix/entry-client",E="virtual:devix/client-routes",P="virtual:devix/render",T="virtual:devix/api",w="virtual:devix/context";function U(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=l(_,"../server/render.js").replace(/\\/g,"/"),a=l(_,"../server/api.js").replace(/\\/g,"/"),p=l(_,"../runtime/client-router.js").replace(/\\/g,"/"),j={name:"devix",enforce:"pre",resolveId(n){if(n===v)return`\0${v}`;if(n===E)return`\0${E}`;if(n===P)return`\0${P}`;if(n===T)return`\0${T}`;if(n===w)return`\0${w}`},load(n){if(n===`\0${v}`)return $({cssUrls:i});if(n===`\0${E}`)return A({pagesDir:r,matcherPath:p});if(n===`\0${P}`)return D({pagesDir:r,renderPath:o});if(n===`\0${T}`)return C({apiPath:a,appDir:e});if(n===`\0${w}`)return S()},buildStart(){let n=process.cwd(),u=y(e,n);R(h(u,`${e}/api`),n)},configureServer(n){let u=process.cwd(),d=()=>{let s=y(e,u);R(h(s,`${e}/api`),u)};n.watcher.on("add",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),d())}),n.watcher.on("unlink",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),d())}),n.watcher.on("change",s=>{s.includes(`${e}/api`)&&!s.endsWith("middleware.ts")&&d()})}},q={plugins:[rt(),j],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return et(q,t.vite??{})}function F(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 c=(await import(`${process.cwd()}/devix.config.ts`)).default,H=U(c);await k({...H,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await k({...H,configFile:!1,build:{ssr:!0,outDir:"dist/server",rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});var at={port:c.port??3e3,host:c.host??!1,loaderTimeout:F(c.loaderTimeout??1e4),output:c.output??"server"};it(st(process.cwd(),"dist/devix.config.json"),JSON.stringify(at,null,2),"utf-8");
152
+ `}function M(t,e){let r=[];for(let i of z(t)){let o=x(t,i);G(o).isDirectory()?r.push(...M(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(Y(e,o).replace(/\\/g,"/"))}return r}function y(t,e){let r=x(e,t,"api"),i;try{i=M(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=X(x(e,o),"utf-8"),p=I(a);return p.length===0?[]:[L(o,`${t}/api`,p)]}catch{return[]}})}import{mkdirSync as Z,readFileSync as K,writeFileSync as Q,existsSync as tt}from"node:fs";import{join as O}from"node:path";function R(t,e){let r=O(e,".devix"),i=O(r,"routes.d.ts");return Z(r,{recursive:!0}),tt(i)&&K(i,"utf-8")===t?!1:(Q(i,t,"utf-8"),!0)}var _=ot(nt(import.meta.url)),v="virtual:devix/entry-client",E="virtual:devix/client-routes",P="virtual:devix/render",T="virtual:devix/api",w="virtual:devix/context";function U(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=l(_,"../server/render.js").replace(/\\/g,"/"),a=l(_,"../server/api.js").replace(/\\/g,"/"),p=l(_,"../runtime/client-router.js").replace(/\\/g,"/"),j={name:"devix",enforce:"pre",resolveId(n){if(n===v)return`\0${v}`;if(n===E)return`\0${E}`;if(n===P)return`\0${P}`;if(n===T)return`\0${T}`;if(n===w)return`\0${w}`},load(n){if(n===`\0${v}`)return $({cssUrls:i});if(n===`\0${E}`)return D({pagesDir:r,matcherPath:p});if(n===`\0${P}`)return A({pagesDir:r,renderPath:o});if(n===`\0${T}`)return C({apiPath:a,appDir:e});if(n===`\0${w}`)return S()},buildStart(){let n=process.cwd(),u=y(e,n);R(h(u,`${e}/api`),n)},configureServer(n){let u=process.cwd(),d=()=>{let s=y(e,u);R(h(s,`${e}/api`),u)};n.watcher.on("add",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),d())}),n.watcher.on("unlink",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),d())}),n.watcher.on("change",s=>{s.includes(`${e}/api`)&&!s.endsWith("middleware.ts")&&d()})}},q={plugins:[rt(),j],publicDir:l(process.cwd(),t.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return et(q,t.vite??{})}function F(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 c=(await import(`${process.cwd()}/devix.config.ts`)).default,H=U(c);await k({...H,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await k({...H,configFile:!1,build:{ssr:!0,outDir:"dist/server",copyPublicDir:!1,rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});var at={port:c.port??3e3,host:c.host??!1,loaderTimeout:F(c.loaderTimeout??1e4),output:c.output??"server"};it(st(process.cwd(),"dist/devix.config.json"),JSON.stringify(at,null,2),"utf-8");
153
153
  //# sourceMappingURL=build.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cli/build.ts", "../../src/vite/index.ts", "../../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/scan-api.ts", "../../src/vite/codegen/extract-methods.ts", "../../src/vite/codegen/routes-dts.ts", "../../src/vite/codegen/write-routes-dts.ts", "../../src/utils/duration.ts"],
4
- "sourcesContent": ["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 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 {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\";\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\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 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.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 ssr: {noExternal: ['@devlusoft/devix']},\n ...(config.envPrefix ? {envPrefix: config.envPrefix} : {}),\n }\n\n return mergeConfig(base, config.vite ?? {})\n}", "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}", "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", "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 {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", "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"],
5
- "mappings": "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",
4
+ "sourcesContent": ["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 {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\";\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\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 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.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}", "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}", "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", "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 {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", "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"],
5
+ "mappings": "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",
6
6
  "names": ["writeFileSync", "resolve", "build", "mergeConfig", "react", "fileURLToPath", "dirname", "resolve", "generateEntryClient", "cssUrls", "u", "generateClientRoutes", "pagesDir", "matcherPath", "generateRender", "pagesDir", "renderPath", "generateApi", "apiPath", "appDir", "routePattern", "rel", "cache", "invalidatePagesCache", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "cache", "invalidateApiCache", "generateContext", "readFileSync", "readdirSync", "statSync", "join", "relative", "METHOD_EXPORT_RE", "stripComments", "content", "extractHttpMethods", "found", "match", "filePathToIdentifier", "filePath", "apiDir", "buildRouteEntry", "methods", "keyToRoutePattern", "generateRoutesDts", "entries", "imports", "e", "importPath", "routeLines", "m", "walkDir", "dir", "root", "entries", "name", "readdirSync", "full", "join", "statSync", "relative", "scanApiFiles", "appDir", "projectRoot", "apiDir", "files", "f", "filePath", "content", "readFileSync", "methods", "extractHttpMethods", "buildRouteEntry", "mkdirSync", "readFileSync", "writeFileSync", "existsSync", "join", "writeRoutesDts", "content", "projectRoot", "devixDir", "outPath", "__dirname", "dirname", "fileURLToPath", "VIRTUAL_ENTRY_CLIENT", "VIRTUAL_CLIENT_ROUTES", "VIRTUAL_RENDER", "VIRTUAL_API", "VIRTUAL_CONTEXT", "devix", "config", "appDir", "pagesDir", "cssUrls", "u", "renderPath", "resolve", "apiPath", "matcherPath", "virtualPlugin", "id", "generateEntryClient", "generateClientRoutes", "generateRender", "generateApi", "generateContext", "root", "entries", "scanApiFiles", "writeRoutesDts", "generateRoutesDts", "server", "regenerateDts", "file", "invalidatePagesCache", "invalidateApiCache", "base", "react", "mergeConfig", "parseDuration", "value", "match", "n", "config", "baseConfig", "devix", "build", "runtimeConfig", "parseDuration", "writeFileSync", "resolve"]
7
7
  }
package/dist/cli/dev.js CHANGED
@@ -1,4 +1,4 @@
1
- import{createServer as he}from"node:http";import{createServer as ye}from"vite";import{getRequestListener as xe}from"@hono/node-server";import{Hono as Re}from"hono";import{mergeConfig as le}from"vite";import ue from"@vitejs/plugin-react";import{fileURLToPath as pe}from"node:url";import{dirname as de,resolve as g}from"node:path";function b({cssUrls:e}){return`
1
+ import{createServer as he}from"node:http";import{createServer as ye}from"vite";import{getRequestListener as xe}from"@hono/node-server";import{Hono as Re}from"hono";import{mergeConfig as le}from"vite";import ue from"@vitejs/plugin-react";import{fileURLToPath as pe}from"node:url";import{dirname as de,resolve as m}from"node:path";function S({cssUrls:e}){return`
2
2
  ${e.map(r=>`import '${r}'`).join(`
3
3
  `)}
4
4
  import "@vitejs/plugin-react/preamble"
@@ -149,7 +149,7 @@ declare module '@devlusoft/devix' {
149
149
  ${o}
150
150
  }
151
151
  }
152
- `}function q(e,t){let r=[];for(let o of re(e)){let n=_(e,o);ne(n).isDirectory()?r.push(...q(n,t)):/\.(ts|tsx)$/.test(o)&&r.push(oe(t,n).replace(/\\/g,"/"))}return r}function w(e,t){let r=_(t,e,"api"),o;try{o=q(r,t)}catch{return[]}return o.filter(n=>!n.endsWith("middleware.ts")&&!n.endsWith("middleware.tsx")).flatMap(n=>{try{let s=te(_(t,n),"utf-8"),l=U(s);return l.length===0?[]:[H(n,`${e}/api`,l)]}catch{return[]}})}import{mkdirSync as ie,readFileSync as se,writeFileSync as ae,existsSync as ce}from"node:fs";import{join as j}from"node:path";function E(e,t){let r=j(t,".devix"),o=j(r,"routes.d.ts");return ie(r,{recursive:!0}),ce(o)&&se(o,"utf-8")===e?!1:(ae(o,e,"utf-8"),!0)}var $=de(pe(import.meta.url)),P="virtual:devix/entry-client",T="virtual:devix/client-routes",A="virtual:devix/render",D="virtual:devix/api",C="virtual:devix/context";function F(e){let t=e.appDir??"app",r=`${t}/pages`,o=(e.css??[]).map(i=>i.startsWith("/")?i:`/${i.replace(/^\.\//,"")}`),n=g($,"../server/render.js").replace(/\\/g,"/"),s=g($,"../server/api.js").replace(/\\/g,"/"),l=g($,"../runtime/client-router.js").replace(/\\/g,"/"),f={name:"devix",enforce:"pre",resolveId(i){if(i===P)return`\0${P}`;if(i===T)return`\0${T}`;if(i===A)return`\0${A}`;if(i===D)return`\0${D}`;if(i===C)return`\0${C}`},load(i){if(i===`\0${P}`)return b({cssUrls:o});if(i===`\0${T}`)return I({pagesDir:r,matcherPath:l});if(i===`\0${A}`)return L({pagesDir:r,renderPath:n});if(i===`\0${D}`)return M({apiPath:s,appDir:t});if(i===`\0${C}`)return k()},buildStart(){let i=process.cwd(),u=w(t,i);E(v(u,`${t}/api`),i)},configureServer(i){let u=process.cwd(),h=()=>{let c=w(t,u);E(v(c,`${t}/api`),u)};i.watcher.on("add",c=>{c.startsWith(g(u,r))&&x(),c.includes(`${t}/api`)&&(R(),h())}),i.watcher.on("unlink",c=>{c.startsWith(g(u,r))&&x(),c.includes(`${t}/api`)&&(R(),h())}),i.watcher.on("change",c=>{c.includes(`${t}/api`)&&!c.endsWith("middleware.ts")&&h()})}},p={plugins:[ue(),f],ssr:{noExternal:["@devlusoft/devix"]},...e.envPrefix?{envPrefix:e.envPrefix}:{}};return le(p,e.vite??{})}function V(e,{apiModule:t,renderModule:r,loaderTimeout:o}){e.all("/api/*",async n=>{try{return await t.handleApiRequest(n.req.url,n.req.raw)}catch(s){return console.error(s),n.json({error:"internal error"},500)}}),e.get("/_data/*",async n=>{try{let{pathname:s,search:l}=new URL(n.req.url,"http://localhost"),f=s.replace(/^\/_data/,"")+l,p=await r.runLoader(f,n.req.raw,{loaderTimeout:o});return p.error?n.json({error:"internal error"},500):n.json(p)}catch(s){return console.error(s),n.json({error:"internal error"},500)}})}import a from"picocolors";import{networkInterfaces as me}from"node:os";function fe(e){let t=me();for(let r of Object.values(t))for(let o of r??[])if(o.family==="IPv4"&&!o.internal)return`http://${o.address}:${e}/`;return null}function N(e){let t="0.4.0-beta.2",r=fe(e);console.log(),console.log(` ${a.bold(a.yellow("devix"))} ${a.dim(`v${t}`)}`),console.log(),console.log(` ${a.green("\u279C")} ${a.bold("Local:")} ${a.cyan(`http://localhost:${e}/`)}`),console.log(r?` ${a.green("\u279C")} ${a.bold("Network:")} ${a.cyan(r)}`:` ${a.green("\u279C")} ${a.bold("Network:")} ${a.dim("use --host to expose")}`),console.log()}async function W(e){let t=new Set;for(let[,r]of e.moduleGraph.idToModuleMap)r.id&&(r.id.endsWith(".css")||r.id.includes(".css?"))&&t.add(r.url);return[...t]}function B(e){if(typeof e=="number")return e;let t=e.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!t)throw new Error(`[devix] Invalid duration: "${e}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(t[1]);switch(t[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}import{loadEnv as ge}from"vite";function X(e){let t=ge(e,process.cwd(),"");for(let[r,o]of Object.entries(t))process.env[r]===void 0&&(process.env[r]=o)}X("development");var J="virtual:devix/render",ve="virtual:devix/api",m=(await import(`${process.cwd()}/devix.config.ts`)).default,Y=Number(process.env.PORT)||m.port||3e3,_e=typeof m.host=="string"?m.host:m.host?"0.0.0.0":"localhost",d=await ye({...F(m),configFile:!1,appType:"custom",server:{middlewareMode:!0}}),G={render:async(...e)=>(await d.ssrLoadModule(J)).render(...e),runLoader:async(...e)=>(await d.ssrLoadModule(J)).runLoader(...e)},we={handleApiRequest:async(...e)=>(await d.ssrLoadModule(ve)).handleApiRequest(...e)},S=new Re;V(S,{renderModule:G,apiModule:we});S.get("*",async e=>{try{let{html:t,statusCode:r,headers:o}=await G.render(e.req.url,e.req.raw,{loaderTimeout:B(m.loaderTimeout??1e4)}),s=(await W(d)).map(i=>`<link rel="stylesheet" href="${i}">`).join(`
152
+ `}function q(e,t){let r=[];for(let o of re(e)){let n=_(e,o);ne(n).isDirectory()?r.push(...q(n,t)):/\.(ts|tsx)$/.test(o)&&r.push(oe(t,n).replace(/\\/g,"/"))}return r}function w(e,t){let r=_(t,e,"api"),o;try{o=q(r,t)}catch{return[]}return o.filter(n=>!n.endsWith("middleware.ts")&&!n.endsWith("middleware.tsx")).flatMap(n=>{try{let s=te(_(t,n),"utf-8"),l=U(s);return l.length===0?[]:[H(n,`${e}/api`,l)]}catch{return[]}})}import{mkdirSync as ie,readFileSync as se,writeFileSync as ae,existsSync as ce}from"node:fs";import{join as j}from"node:path";function E(e,t){let r=j(t,".devix"),o=j(r,"routes.d.ts");return ie(r,{recursive:!0}),ce(o)&&se(o,"utf-8")===e?!1:(ae(o,e,"utf-8"),!0)}var $=de(pe(import.meta.url)),P="virtual:devix/entry-client",T="virtual:devix/client-routes",D="virtual:devix/render",A="virtual:devix/api",C="virtual:devix/context";function F(e){let t=e.appDir??"app",r=`${t}/pages`,o=(e.css??[]).map(i=>i.startsWith("/")?i:`/${i.replace(/^\.\//,"")}`),n=m($,"../server/render.js").replace(/\\/g,"/"),s=m($,"../server/api.js").replace(/\\/g,"/"),l=m($,"../runtime/client-router.js").replace(/\\/g,"/"),g={name:"devix",enforce:"pre",resolveId(i){if(i===P)return`\0${P}`;if(i===T)return`\0${T}`;if(i===D)return`\0${D}`;if(i===A)return`\0${A}`;if(i===C)return`\0${C}`},load(i){if(i===`\0${P}`)return S({cssUrls:o});if(i===`\0${T}`)return I({pagesDir:r,matcherPath:l});if(i===`\0${D}`)return L({pagesDir:r,renderPath:n});if(i===`\0${A}`)return M({apiPath:s,appDir:t});if(i===`\0${C}`)return k()},buildStart(){let i=process.cwd(),u=w(t,i);E(v(u,`${t}/api`),i)},configureServer(i){let u=process.cwd(),h=()=>{let c=w(t,u);E(v(c,`${t}/api`),u)};i.watcher.on("add",c=>{c.startsWith(m(u,r))&&x(),c.includes(`${t}/api`)&&(R(),h())}),i.watcher.on("unlink",c=>{c.startsWith(m(u,r))&&x(),c.includes(`${t}/api`)&&(R(),h())}),i.watcher.on("change",c=>{c.includes(`${t}/api`)&&!c.endsWith("middleware.ts")&&h()})}},p={plugins:[ue(),g],publicDir:m(process.cwd(),e.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...e.envPrefix?{envPrefix:e.envPrefix}:{}};return le(p,e.vite??{})}function V(e,{apiModule:t,renderModule:r,loaderTimeout:o}){e.all("/api/*",async n=>{try{return await t.handleApiRequest(n.req.url,n.req.raw)}catch(s){return console.error(s),n.json({error:"internal error"},500)}}),e.get("/_data/*",async n=>{try{let{pathname:s,search:l}=new URL(n.req.url,"http://localhost"),g=s.replace(/^\/_data/,"")+l,p=await r.runLoader(g,n.req.raw,{loaderTimeout:o});return p.error?n.json({error:"internal error"},500):n.json(p)}catch(s){return console.error(s),n.json({error:"internal error"},500)}})}import a from"picocolors";import{networkInterfaces as me}from"node:os";function fe(e){let t=me();for(let r of Object.values(t))for(let o of r??[])if(o.family==="IPv4"&&!o.internal)return`http://${o.address}:${e}/`;return null}function N(e){let t="0.4.0",r=fe(e);console.log(),console.log(` ${a.bold(a.yellow("devix"))} ${a.dim(`v${t}`)}`),console.log(),console.log(` ${a.green("\u279C")} ${a.bold("Local:")} ${a.cyan(`http://localhost:${e}/`)}`),console.log(r?` ${a.green("\u279C")} ${a.bold("Network:")} ${a.cyan(r)}`:` ${a.green("\u279C")} ${a.bold("Network:")} ${a.dim("use --host to expose")}`),console.log()}async function W(e){let t=new Set;for(let[,r]of e.moduleGraph.idToModuleMap)r.id&&(r.id.endsWith(".css")||r.id.includes(".css?"))&&t.add(r.url);return[...t]}function B(e){if(typeof e=="number")return e;let t=e.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!t)throw new Error(`[devix] Invalid duration: "${e}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(t[1]);switch(t[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}import{loadEnv as ge}from"vite";function X(e){let t=ge(e,process.cwd(),"");for(let[r,o]of Object.entries(t))process.env[r]===void 0&&(process.env[r]=o)}X("development");var J="virtual:devix/render",ve="virtual:devix/api",f=(await import(`${process.cwd()}/devix.config.ts`)).default,Y=Number(process.env.PORT)||f.port||3e3,_e=typeof f.host=="string"?f.host:f.host?"0.0.0.0":"localhost",d=await ye({...F(f),configFile:!1,appType:"custom",server:{middlewareMode:!0}}),G={render:async(...e)=>(await d.ssrLoadModule(J)).render(...e),runLoader:async(...e)=>(await d.ssrLoadModule(J)).runLoader(...e)},we={handleApiRequest:async(...e)=>(await d.ssrLoadModule(ve)).handleApiRequest(...e)},b=new Re;V(b,{renderModule:G,apiModule:we});b.get("*",async e=>{try{let{html:t,statusCode:r,headers:o}=await G.render(e.req.url,e.req.raw,{loaderTimeout:B(f.loaderTimeout??1e4)}),s=(await W(d)).map(i=>`<link rel="stylesheet" href="${i}">`).join(`
153
153
  `),l=s?t.replace("</head>",`${s}
154
- </head>`):t,f=await d.transformIndexHtml(e.req.url,`<!DOCTYPE html>${l}`),p=e.html(f,r);for(let[i,u]of Object.entries(o))p.headers.set(i,u);return p}catch(t){return d.ssrFixStacktrace(t),console.error(t),e.text("Internal Server Error",500)}});var Ee=xe(S.fetch);he(async(e,t)=>{await new Promise(r=>d.middlewares(e,t,r)),t.writableEnded||await Ee(e,t)}).listen(Y,_e,()=>{N(Y)});
154
+ </head>`):t,g=await d.transformIndexHtml(e.req.url,`<!DOCTYPE html>${l}`),p=e.html(g,r);for(let[i,u]of Object.entries(o))p.headers.set(i,u);return p}catch(t){return d.ssrFixStacktrace(t),console.error(t),e.text("Internal Server Error",500)}});var Ee=xe(b.fetch);he(async(e,t)=>{await new Promise(r=>d.middlewares(e,t,r)),t.writableEnded||await Ee(e,t)}).listen(Y,_e,()=>{N(Y)});
155
155
  //# sourceMappingURL=dev.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cli/dev.ts", "../../src/vite/index.ts", "../../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/scan-api.ts", "../../src/vite/codegen/extract-methods.ts", "../../src/vite/codegen/routes-dts.ts", "../../src/vite/codegen/write-routes-dts.ts", "../../src/server/routes.ts", "../../src/utils/banner.ts", "../../src/server/collect-css.ts", "../../src/utils/duration.ts", "../../src/utils/env.ts"],
4
- "sourcesContent": ["import {createServer} from 'node:http'\nimport {createServer as createViteServer} from 'vite'\nimport {getRequestListener} from '@hono/node-server'\nimport {Hono} from 'hono'\nimport type {DevixConfig} from '../config'\nimport {devix} from '../vite'\nimport {registerApiRoutes} from '../server/routes'\nimport {printDevBanner} from \"../utils/banner\";\nimport {collectCss} from \"../server/collect-css\";\nimport {parseDuration} from \"../utils/duration\";\nimport {loadDotenv} from \"../utils/env\";\n\nloadDotenv('development')\n\nconst VIRTUAL_RENDER = 'virtual:devix/render'\nconst VIRTUAL_API = 'virtual:devix/api'\n\nconst config: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nconst port = Number(process.env.PORT) || config.port || 3000\nconst host = typeof config.host === 'string' ? config.host : config.host ? '0.0.0.0' : 'localhost'\n\nconst vite = await createViteServer({\n ...devix(config),\n configFile: false,\n appType: 'custom',\n server: {middlewareMode: true},\n})\n\nconst renderModule = {\n render: async (...args: any[]) => (await vite.ssrLoadModule(VIRTUAL_RENDER)).render(...args),\n runLoader: async (...args: any[]) => (await vite.ssrLoadModule(VIRTUAL_RENDER)).runLoader(...args),\n}\n\nconst apiModule = {\n handleApiRequest: async (...args: any[]) => (await vite.ssrLoadModule(VIRTUAL_API)).handleApiRequest(...args),\n}\n\nconst app = new Hono()\n\nregisterApiRoutes(app, {renderModule, apiModule})\n\napp.get('*', async (c) => {\n try {\n const {html, statusCode, headers} = await renderModule.render(c.req.url, c.req.raw, {loaderTimeout: parseDuration(config.loaderTimeout ?? 10_000)})\n\n const cssUrls = await collectCss(vite)\n const cssLinks = cssUrls.map(url => `<link rel=\"stylesheet\" href=\"${url}\">`).join('\\n')\n\n const htmlWithCss = cssLinks ? html.replace('</head>', `${cssLinks}\\n</head>`) : html\n const transformed = await vite.transformIndexHtml(c.req.url, `<!DOCTYPE html>${htmlWithCss}`)\n\n const res = c.html(transformed, statusCode)\n for (const [key, value] of Object.entries(headers as Record<string, string>)) {\n res.headers.set(key, value)\n }\n return res\n } catch (e) {\n vite.ssrFixStacktrace(e as Error)\n console.error(e)\n return c.text('Internal Server Error', 500)\n }\n})\n\nconst honoHandler = getRequestListener(app.fetch)\n\ncreateServer(async (req, res) => {\n await new Promise<void>(resolve => vite.middlewares(req, res, resolve))\n if (!res.writableEnded) await honoHandler(req, res)\n}).listen(port, host, () => {\n printDevBanner(port)\n})\n\nexport {}", "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\";\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\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 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.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 ssr: {noExternal: ['@devlusoft/devix']},\n ...(config.envPrefix ? {envPrefix: config.envPrefix} : {}),\n }\n\n return mergeConfig(base, config.vite ?? {})\n}", "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}", "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", "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 {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 type {Hono} from 'hono'\nimport type {Manifest} from 'vite'\n\ninterface ServerOptions {\n renderModule: any\n apiModule: any\n manifest?: Manifest\n loaderTimeout?: number\n}\n\nexport function registerApiRoutes(app: Hono, {apiModule, renderModule, loaderTimeout}: ServerOptions) {\n app.all('/api/*', async (c) => {\n try {\n return await apiModule.handleApiRequest(c.req.url, c.req.raw)\n } catch (e) {\n console.error(e)\n return c.json({error: 'internal error'}, 500)\n }\n })\n\n app.get('/_data/*', async (c) => {\n try {\n const {pathname, search} = new URL(c.req.url, 'http://localhost')\n const url = pathname.replace(/^\\/_data/, '') + search\n\n const data = await renderModule.runLoader(url, c.req.raw, {loaderTimeout})\n if (data.error) return c.json({error: 'internal error'}, 500)\n return c.json(data)\n } catch (e) {\n console.error(e)\n return c.json({error: 'internal error'}, 500)\n }\n })\n}\n\nexport function registerSsrRoute(app: Hono, {renderModule, manifest, loaderTimeout}: ServerOptions) {\n app.get('*', async (c) => {\n try {\n const {html, statusCode, headers} = await renderModule.render(c.req.url, c.req.raw, {manifest, loaderTimeout})\n const res = c.html(`<!DOCTYPE html>${html}`, statusCode)\n for (const [key, value] of Object.entries(headers as Record<string, string>)) {\n res.headers.set(key, value)\n }\n return res\n } catch (e) {\n console.error(e)\n return c.text('Internal Server Error', 500)\n }\n })\n}", "import pc from 'picocolors'\nimport {networkInterfaces} from 'node:os'\n\ndeclare const __DEVIX_VERSION__: string\n\nfunction getNetworkUrl(port: number): string | null {\n const nets = networkInterfaces()\n for (const interfaces of Object.values(nets)) {\n for (const net of interfaces ?? []) {\n if (net.family === 'IPv4' && !net.internal) {\n return `http://${net.address}:${port}/`\n }\n }\n }\n return null\n}\n\nexport function printDevBanner(port: number) {\n const version = __DEVIX_VERSION__\n const networkUrl = getNetworkUrl(port)\n\n console.log()\n console.log(` ${pc.bold(pc.yellow('devix'))} ${pc.dim(`v${version}`)}`)\n console.log()\n console.log(` ${pc.green('\u279C')} ${pc.bold('Local:')} ${pc.cyan(`http://localhost:${port}/`)}`)\n if (networkUrl) {\n console.log(` ${pc.green('\u279C')} ${pc.bold('Network:')} ${pc.cyan(networkUrl)}`)\n } else {\n console.log(` ${pc.green('\u279C')} ${pc.bold('Network:')} ${pc.dim('use --host to expose')}`)\n }\n console.log()\n}", "import type {ViteDevServer} from 'vite'\n\nexport async function collectCss(vite: ViteDevServer): Promise<string[]> {\n const cssUrls = new Set<string>()\n\n for (const [, mod] of vite.moduleGraph.idToModuleMap) {\n if (!mod.id) continue\n if (mod.id.endsWith('.css') || mod.id.includes('.css?')) {\n cssUrls.add(mod.url)\n }\n }\n\n return [...cssUrls]\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 {loadEnv} from 'vite'\n\nexport function loadDotenv(mode: string) {\n const env = loadEnv(mode, process.cwd(), '')\n for (const [key, value] of Object.entries(env)) {\n if (process.env[key] === undefined) {\n process.env[key] = value\n }\n }\n}\n"],
5
- "mappings": "AAAA,OAAQ,gBAAAA,OAAmB,YAC3B,OAAQ,gBAAgBC,OAAuB,OAC/C,OAAQ,sBAAAC,OAAyB,oBACjC,OAAQ,QAAAC,OAAW,OCHnB,OAA4B,eAAAC,OAAkB,OAE9C,OAAOC,OAAW,uBAClB,OAAQ,iBAAAC,OAAoB,WAC5B,OAAQ,WAAAC,GAAS,WAAAC,MAAc,YCAxB,SAASC,EAAoB,CAAC,QAAAC,CAAO,EAA+B,CAGvE,MAAO;AAAA,EAFYA,EAAQ,IAAIC,GAAK,WAAWA,CAAC,GAAG,EAAE,KAAK;AAAA,CAAI,CAGtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAuDZ,CC1DO,SAASC,EAAqB,CAAC,SAAAC,EAAU,YAAAC,CAAW,EAAwB,CAC/E,MAAO;AAAA;AAAA,iCAEsBA,CAAW;AAAA,wCACJD,CAAQ;AAAA,yCACPA,CAAQ;AAAA,wCACTA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAwBhD,CC9BO,SAASE,EAAe,CAAC,SAAAC,EAAU,WAAAC,CAAU,EAA0B,CAC1E,MAAO;AAAA,mGACwFA,CAAU;AAAA;AAAA,qCAExED,CAAQ;AAAA,sCACPA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,kBAK5BA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAe1B,CCzBO,SAASE,EAAY,CAAC,QAAAC,EAAS,OAAAC,CAAM,EAAuB,CAC/D,MAAO;AAAA,yDAC8CD,CAAO;AAAA;AAAA,sCAE1BC,CAAM;AAAA,0CACFA,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA,gBAKhCA,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAOtB,CCtBO,SAASC,EAAaC,EAAqB,CAC9C,OAAOA,EACE,QAAQ,qBAAsB,EAAE,EAChC,QAAQ,aAAc,EAAE,EACxB,QAAQ,mBAAoB,EAAE,EAC9B,QAAQ,eAAgB,KAAK,GAC/B,GACX,CCsBA,IAAIC,EAA4B,KAEzB,SAASC,GAAuB,CACnCD,EAAQ,IACZ,CCdO,SAASE,EAAkBC,EAAaC,EAAwB,CACnE,IAAMC,EAAMF,EAAI,MAAMC,EAAO,OAAS,CAAC,EAAE,QAAQ,MAAO,GAAG,EACrDE,EAAUC,EAAaF,CAAG,EAChC,OAAOC,IAAY,IAAM,OAAS,QAAQA,CAAO,GAAG,QAAQ,SAAU,OAAO,CACjF,CAMA,IAAIE,EAA0B,KAEvB,SAASC,GAAqB,CACjCD,EAAQ,IACZ,CCjCO,SAASE,GAA0B,CACtC,MAAO;AAAA;AAAA,CAGX,CCJA,OAAQ,gBAAAC,GAAc,eAAAC,GAAa,YAAAC,OAAe,UAClD,OAAQ,QAAAC,EAAM,YAAAC,OAAe,YCE7B,IAAMC,EAAmB,6FAEzB,SAASC,EAAcC,EAAyB,CAC5C,OAAOA,EACF,QAAQ,oBAAqB,EAAE,EAC/B,QAAQ,YAAa,EAAE,CAChC,CAEO,SAASC,EAAmBD,EAA+B,CAC9D,IAAME,EAAQ,IAAI,IAClB,QAAWC,KAASJ,EAAcC,CAAO,EAAE,SAASF,CAAgB,EAChEI,EAAM,IAAIC,EAAM,CAAC,CAAe,EAEpC,MAAO,CAAC,GAAGD,CAAK,CACpB,CCPO,SAASE,GAAqBC,EAAkBC,EAAwB,CAC3E,MAAO,QAAUD,EACZ,MAAM,GAAGC,CAAM,IAAI,MAAM,EACzB,QAAQ,cAAe,EAAE,EACzB,QAAQ,gBAAiB,GAAG,CACrC,CAEO,SAASC,EAAgBF,EAAkBC,EAAgBE,EAAmC,CACjG,MAAO,CACH,SAAAH,EACA,WAAYI,EAAkBJ,EAAUC,CAAM,EAC9C,WAAYF,GAAqBC,EAAUC,CAAM,EACjD,QAAAE,CACJ,CACJ,CAEO,SAASE,EAAkBC,EAAuBL,EAAwB,CAC7E,GAAIK,EAAQ,SAAW,EACnB,MAAO;AAAA;AAAA;AAAA;AAAA,EAGX,IAAMC,EAAUD,EACX,IAAIE,GAAK,CACN,IAAMC,EAAa,MAAQD,EAAE,SAAS,QAAQ,cAAe,EAAE,EAC/D,MAAO,oBAAoBA,EAAE,UAAU,UAAUC,CAAU,GAC/D,CAAC,EACA,KAAK;AAAA,CAAI,EAERC,EAAaJ,EAAQ,QAAQE,GAC/BA,EAAE,QAAQ,IAAIG,GACV,QAAQA,CAAC,IAAIH,EAAE,UAAU,yBAAyBA,EAAE,UAAU,MAAMG,CAAC,KACzE,CACJ,EAAE,KAAK;AAAA,CAAI,EAEX,MAAO;AAAA,EACTJ,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBPG,CAAU;AAAA;AAAA;AAAA,CAIZ,CF5DA,SAASE,EAAQC,EAAaC,EAAwB,CAClD,IAAMC,EAAoB,CAAC,EAC3B,QAAWC,KAAQC,GAAYJ,CAAG,EAAG,CACjC,IAAMK,EAAOC,EAAKN,EAAKG,CAAI,EACvBI,GAASF,CAAI,EAAE,YAAY,EAC3BH,EAAQ,KAAK,GAAGH,EAAQM,EAAMJ,CAAI,CAAC,EAC5B,cAAc,KAAKE,CAAI,GAC9BD,EAAQ,KAAKM,GAASP,EAAMI,CAAI,EAAE,QAAQ,MAAO,GAAG,CAAC,CAE7D,CACA,OAAOH,CACX,CAEO,SAASO,EAAaC,EAAgBC,EAAmC,CAC5E,IAAMC,EAASN,EAAKK,EAAaD,EAAQ,KAAK,EAE1CG,EACJ,GAAI,CACAA,EAAQd,EAAQa,EAAQD,CAAW,CACvC,MAAQ,CACJ,MAAO,CAAC,CACZ,CAEA,OAAOE,EACF,OAAOC,GAAK,CAACA,EAAE,SAAS,eAAe,GAAK,CAACA,EAAE,SAAS,gBAAgB,CAAC,EACzE,QAAQC,GAAY,CACjB,GAAI,CACA,IAAMC,EAAUC,GAAaX,EAAKK,EAAaI,CAAQ,EAAG,OAAO,EAC3DG,EAAUC,EAAmBH,CAAO,EAC1C,OAAIE,EAAQ,SAAW,EAAU,CAAC,EAC3B,CAACE,EAAgBL,EAAU,GAAGL,CAAM,OAAQQ,CAAO,CAAC,CAC/D,MAAQ,CACJ,MAAO,CAAC,CACZ,CACJ,CAAC,CACT,CGzCA,OAAQ,aAAAG,GAAW,gBAAAC,GAAc,iBAAAC,GAAe,cAAAC,OAAiB,UACjE,OAAQ,QAAAC,MAAW,YAEZ,SAASC,EAAeC,EAAiBC,EAA8B,CAC1E,IAAMC,EAAWJ,EAAKG,EAAa,QAAQ,EACrCE,EAAUL,EAAKI,EAAU,aAAa,EAI5C,OAFAR,GAAUQ,EAAU,CAAC,UAAW,EAAI,CAAC,EAEjCL,GAAWM,CAAO,GAAKR,GAAaQ,EAAS,OAAO,IAAMH,EACnD,IAGXJ,GAAcO,EAASH,EAAS,OAAO,EAChC,GACX,CZCA,IAAMI,EAAYC,GAAQC,GAAc,YAAY,GAAG,CAAC,EAElDC,EAAuB,6BACvBC,EAAwB,8BACxBC,EAAiB,uBACjBC,EAAc,oBACdC,EAAkB,wBAEjB,SAASC,EAAMC,EAAiC,CACnD,IAAMC,EAASD,EAAO,QAAU,MAC1BE,EAAW,GAAGD,CAAM,SACpBE,GAAWH,EAAO,KAAO,CAAC,GAAG,IAAII,GAAKA,EAAE,WAAW,GAAG,EAAIA,EAAI,IAAIA,EAAE,QAAQ,QAAS,EAAE,CAAC,EAAE,EAE1FC,EAAaC,EAAQf,EAAW,qBAAqB,EAAE,QAAQ,MAAO,GAAG,EACzEgB,EAAUD,EAAQf,EAAW,kBAAkB,EAAE,QAAQ,MAAO,GAAG,EACnEiB,EAAcF,EAAQf,EAAW,6BAA6B,EAAE,QAAQ,MAAO,GAAG,EAElFkB,EAAwB,CAC1B,KAAM,QACN,QAAS,MAET,UAAUC,EAAI,CACV,GAAIA,IAAOhB,EAAsB,MAAO,KAAKA,CAAoB,GACjE,GAAIgB,IAAOf,EAAuB,MAAO,KAAKA,CAAqB,GACnE,GAAIe,IAAOd,EAAgB,MAAO,KAAKA,CAAc,GACrD,GAAIc,IAAOb,EAAa,MAAO,KAAKA,CAAW,GAC/C,GAAIa,IAAOZ,EAAiB,MAAO,KAAKA,CAAe,EAC3D,EAEA,KAAKY,EAAI,CACL,GAAIA,IAAO,KAAKhB,CAAoB,GAChC,OAAOiB,EAAoB,CAAC,QAAAR,CAAO,CAAC,EACxC,GAAIO,IAAO,KAAKf,CAAqB,GACjC,OAAOiB,EAAqB,CAAC,SAAAV,EAAU,YAAAM,CAAW,CAAC,EACvD,GAAIE,IAAO,KAAKd,CAAc,GAC1B,OAAOiB,EAAe,CAAC,SAAAX,EAAU,WAAAG,CAAU,CAAC,EAChD,GAAIK,IAAO,KAAKb,CAAW,GACvB,OAAOiB,EAAY,CAAC,QAAAP,EAAS,OAAAN,CAAM,CAAC,EACxC,GAAIS,IAAO,KAAKZ,CAAe,GAC3B,OAAOiB,EAAgB,CAC/B,EAEA,YAAa,CACT,IAAMC,EAAO,QAAQ,IAAI,EACnBC,EAAUC,EAAajB,EAAQe,CAAI,EACzCG,EAAeC,EAAkBH,EAAS,GAAGhB,CAAM,MAAM,EAAGe,CAAI,CACpE,EAEA,gBAAgBK,EAAQ,CACpB,IAAML,EAAO,QAAQ,IAAI,EAEnBM,EAAgB,IAAM,CACxB,IAAML,EAAUC,EAAajB,EAAQe,CAAI,EACzCG,EAAeC,EAAkBH,EAAS,GAAGhB,CAAM,MAAM,EAAGe,CAAI,CACpE,EAEAK,EAAO,QAAQ,GAAG,MAAQE,GAAS,CAC3BA,EAAK,WAAWjB,EAAQU,EAAMd,CAAQ,CAAC,GAAGsB,EAAqB,EAC/DD,EAAK,SAAS,GAAGtB,CAAM,MAAM,IAAKwB,EAAmB,EAAGH,EAAc,EAC9E,CAAC,EACDD,EAAO,QAAQ,GAAG,SAAWE,GAAS,CAC9BA,EAAK,WAAWjB,EAAQU,EAAMd,CAAQ,CAAC,GAAGsB,EAAqB,EAC/DD,EAAK,SAAS,GAAGtB,CAAM,MAAM,IAAKwB,EAAmB,EAAGH,EAAc,EAC9E,CAAC,EACDD,EAAO,QAAQ,GAAG,SAAWE,GAAS,CAC9BA,EAAK,SAAS,GAAGtB,CAAM,MAAM,GAAK,CAACsB,EAAK,SAAS,eAAe,GAChED,EAAc,CAEtB,CAAC,CACL,CACJ,EAEMI,EAAmB,CACrB,QAAS,CAACC,GAAM,EAAGlB,CAAa,EAChC,IAAK,CAAC,WAAY,CAAC,kBAAkB,CAAC,EACtC,GAAIT,EAAO,UAAY,CAAC,UAAWA,EAAO,SAAS,EAAI,CAAC,CAC5D,EAEA,OAAO4B,GAAYF,EAAM1B,EAAO,MAAQ,CAAC,CAAC,CAC9C,CarFO,SAAS6B,EAAkBC,EAAW,CAAC,UAAAC,EAAW,aAAAC,EAAc,cAAAC,CAAa,EAAkB,CAClGH,EAAI,IAAI,SAAU,MAAOI,GAAM,CAC3B,GAAI,CACA,OAAO,MAAMH,EAAU,iBAAiBG,EAAE,IAAI,IAAKA,EAAE,IAAI,GAAG,CAChE,OAASC,EAAG,CACR,eAAQ,MAAMA,CAAC,EACRD,EAAE,KAAK,CAAC,MAAO,gBAAgB,EAAG,GAAG,CAChD,CACJ,CAAC,EAEDJ,EAAI,IAAI,WAAY,MAAOI,GAAM,CAC7B,GAAI,CACA,GAAM,CAAC,SAAAE,EAAU,OAAAC,CAAM,EAAI,IAAI,IAAIH,EAAE,IAAI,IAAK,kBAAkB,EAC1DI,EAAMF,EAAS,QAAQ,WAAY,EAAE,EAAIC,EAEzCE,EAAO,MAAMP,EAAa,UAAUM,EAAKJ,EAAE,IAAI,IAAK,CAAC,cAAAD,CAAa,CAAC,EACzE,OAAIM,EAAK,MAAcL,EAAE,KAAK,CAAC,MAAO,gBAAgB,EAAG,GAAG,EACrDA,EAAE,KAAKK,CAAI,CACtB,OAASJ,EAAG,CACR,eAAQ,MAAMA,CAAC,EACRD,EAAE,KAAK,CAAC,MAAO,gBAAgB,EAAG,GAAG,CAChD,CACJ,CAAC,CACL,CCjCA,OAAOM,MAAQ,aACf,OAAQ,qBAAAC,OAAwB,UAIhC,SAASC,GAAcC,EAA6B,CAChD,IAAMC,EAAOH,GAAkB,EAC/B,QAAWI,KAAc,OAAO,OAAOD,CAAI,EACvC,QAAWE,KAAOD,GAAc,CAAC,EAC7B,GAAIC,EAAI,SAAW,QAAU,CAACA,EAAI,SAC9B,MAAO,UAAUA,EAAI,OAAO,IAAIH,CAAI,IAIhD,OAAO,IACX,CAEO,SAASI,EAAeJ,EAAc,CACzC,IAAMK,EAAU,eACVC,EAAaP,GAAcC,CAAI,EAErC,QAAQ,IAAI,EACZ,QAAQ,IAAI,KAAKH,EAAG,KAAKA,EAAG,OAAO,OAAO,CAAC,CAAC,IAAIA,EAAG,IAAI,IAAIQ,CAAO,EAAE,CAAC,EAAE,EACvE,QAAQ,IAAI,EACZ,QAAQ,IAAI,KAAKR,EAAG,MAAM,QAAG,CAAC,KAAKA,EAAG,KAAK,QAAQ,CAAC,MAAMA,EAAG,KAAK,oBAAoBG,CAAI,GAAG,CAAC,EAAE,EAE5F,QAAQ,IADRM,EACY,KAAKT,EAAG,MAAM,QAAG,CAAC,KAAKA,EAAG,KAAK,UAAU,CAAC,IAAIA,EAAG,KAAKS,CAAU,CAAC,GAEjE,KAAKT,EAAG,MAAM,QAAG,CAAC,KAAKA,EAAG,KAAK,UAAU,CAAC,IAAIA,EAAG,IAAI,sBAAsB,CAAC,EAFT,EAInF,QAAQ,IAAI,CAChB,CC7BA,eAAsBU,EAAWC,EAAwC,CACrE,IAAMC,EAAU,IAAI,IAEpB,OAAW,CAAC,CAAEC,CAAG,IAAKF,EAAK,YAAY,cAC9BE,EAAI,KACLA,EAAI,GAAG,SAAS,MAAM,GAAKA,EAAI,GAAG,SAAS,OAAO,IAClDD,EAAQ,IAAIC,EAAI,GAAG,EAI3B,MAAO,CAAC,GAAGD,CAAO,CACtB,CCbO,SAASE,EAAcC,EAAgC,CAC1D,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,IAAMC,EAAQD,EAAM,KAAK,EAAE,MAAM,iCAAiC,EAClE,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,8BAA8BD,CAAK,4DAA4D,EAC3H,IAAME,EAAI,WAAWD,EAAM,CAAC,CAAC,EAC7B,OAAQA,EAAM,CAAC,EAAG,CACd,IAAK,IAAM,OAAOC,EAAI,KACtB,IAAK,IAAM,OAAOA,EAAI,IACtB,IAAK,IAAM,OAAOA,EAAI,IAEtB,QAAW,OAAOA,CACtB,CACJ,CCZA,OAAQ,WAAAC,OAAc,OAEf,SAASC,EAAWC,EAAc,CACrC,IAAMC,EAAMH,GAAQE,EAAM,QAAQ,IAAI,EAAG,EAAE,EAC3C,OAAW,CAACE,EAAKC,CAAK,IAAK,OAAO,QAAQF,CAAG,EACrC,QAAQ,IAAIC,CAAG,IAAM,SACrB,QAAQ,IAAIA,CAAG,EAAIC,EAG/B,ClBGAC,EAAW,aAAa,EAExB,IAAMC,EAAiB,uBACjBC,GAAc,oBAEdC,GAAuB,MAAM,OAAO,GAAG,QAAQ,IAAI,CAAC,qBAAqB,QACzEC,EAAO,OAAO,QAAQ,IAAI,IAAI,GAAKD,EAAO,MAAQ,IAClDE,GAAO,OAAOF,EAAO,MAAS,SAAWA,EAAO,KAAOA,EAAO,KAAO,UAAY,YAEjFG,EAAO,MAAMC,GAAiB,CAChC,GAAGC,EAAML,CAAM,EACf,WAAY,GACZ,QAAS,SACT,OAAQ,CAAC,eAAgB,EAAI,CACjC,CAAC,EAEKM,EAAe,CACjB,OAAQ,SAAUC,KAAiB,MAAMJ,EAAK,cAAcL,CAAc,GAAG,OAAO,GAAGS,CAAI,EAC3F,UAAW,SAAUA,KAAiB,MAAMJ,EAAK,cAAcL,CAAc,GAAG,UAAU,GAAGS,CAAI,CACrG,EAEMC,GAAY,CACd,iBAAkB,SAAUD,KAAiB,MAAMJ,EAAK,cAAcJ,EAAW,GAAG,iBAAiB,GAAGQ,CAAI,CAChH,EAEME,EAAM,IAAIC,GAEhBC,EAAkBF,EAAK,CAAC,aAAAH,EAAc,UAAAE,EAAS,CAAC,EAEhDC,EAAI,IAAI,IAAK,MAAOG,GAAM,CACtB,GAAI,CACA,GAAM,CAAC,KAAAC,EAAM,WAAAC,EAAY,QAAAC,CAAO,EAAI,MAAMT,EAAa,OAAOM,EAAE,IAAI,IAAKA,EAAE,IAAI,IAAK,CAAC,cAAeI,EAAchB,EAAO,eAAiB,GAAM,CAAC,CAAC,EAG5IiB,GADU,MAAMC,EAAWf,CAAI,GACZ,IAAIgB,GAAO,gCAAgCA,CAAG,IAAI,EAAE,KAAK;AAAA,CAAI,EAEhFC,EAAcH,EAAWJ,EAAK,QAAQ,UAAW,GAAGI,CAAQ;AAAA,QAAW,EAAIJ,EAC3EQ,EAAc,MAAMlB,EAAK,mBAAmBS,EAAE,IAAI,IAAK,kBAAkBQ,CAAW,EAAE,EAEtFE,EAAMV,EAAE,KAAKS,EAAaP,CAAU,EAC1C,OAAW,CAACS,EAAKC,CAAK,IAAK,OAAO,QAAQT,CAAiC,EACvEO,EAAI,QAAQ,IAAIC,EAAKC,CAAK,EAE9B,OAAOF,CACX,OAASG,EAAG,CACR,OAAAtB,EAAK,iBAAiBsB,CAAU,EAChC,QAAQ,MAAMA,CAAC,EACRb,EAAE,KAAK,wBAAyB,GAAG,CAC9C,CACJ,CAAC,EAED,IAAMc,GAAcC,GAAmBlB,EAAI,KAAK,EAEhDmB,GAAa,MAAOC,EAAKP,IAAQ,CAC7B,MAAM,IAAI,QAAcQ,GAAW3B,EAAK,YAAY0B,EAAKP,EAAKQ,CAAO,CAAC,EACjER,EAAI,eAAe,MAAMI,GAAYG,EAAKP,CAAG,CACtD,CAAC,EAAE,OAAOrB,EAAMC,GAAM,IAAM,CACxB6B,EAAe9B,CAAI,CACvB,CAAC",
4
+ "sourcesContent": ["import {createServer} from 'node:http'\nimport {createServer as createViteServer} from 'vite'\nimport {getRequestListener} from '@hono/node-server'\nimport {Hono} from 'hono'\nimport type {DevixConfig} from '../config'\nimport {devix} from '../vite'\nimport {registerApiRoutes} from '../server/routes'\nimport {printDevBanner} from \"../utils/banner\";\nimport {collectCss} from \"../server/collect-css\";\nimport {parseDuration} from \"../utils/duration\";\nimport {loadDotenv} from \"../utils/env\";\n\nloadDotenv('development')\n\nconst VIRTUAL_RENDER = 'virtual:devix/render'\nconst VIRTUAL_API = 'virtual:devix/api'\n\nconst config: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nconst port = Number(process.env.PORT) || config.port || 3000\nconst host = typeof config.host === 'string' ? config.host : config.host ? '0.0.0.0' : 'localhost'\n\nconst vite = await createViteServer({\n ...devix(config),\n configFile: false,\n appType: 'custom',\n server: {middlewareMode: true},\n})\n\nconst renderModule = {\n render: async (...args: any[]) => (await vite.ssrLoadModule(VIRTUAL_RENDER)).render(...args),\n runLoader: async (...args: any[]) => (await vite.ssrLoadModule(VIRTUAL_RENDER)).runLoader(...args),\n}\n\nconst apiModule = {\n handleApiRequest: async (...args: any[]) => (await vite.ssrLoadModule(VIRTUAL_API)).handleApiRequest(...args),\n}\n\nconst app = new Hono()\n\nregisterApiRoutes(app, {renderModule, apiModule})\n\napp.get('*', async (c) => {\n try {\n const {html, statusCode, headers} = await renderModule.render(c.req.url, c.req.raw, {loaderTimeout: parseDuration(config.loaderTimeout ?? 10_000)})\n\n const cssUrls = await collectCss(vite)\n const cssLinks = cssUrls.map(url => `<link rel=\"stylesheet\" href=\"${url}\">`).join('\\n')\n\n const htmlWithCss = cssLinks ? html.replace('</head>', `${cssLinks}\\n</head>`) : html\n const transformed = await vite.transformIndexHtml(c.req.url, `<!DOCTYPE html>${htmlWithCss}`)\n\n const res = c.html(transformed, statusCode)\n for (const [key, value] of Object.entries(headers as Record<string, string>)) {\n res.headers.set(key, value)\n }\n return res\n } catch (e) {\n vite.ssrFixStacktrace(e as Error)\n console.error(e)\n return c.text('Internal Server Error', 500)\n }\n})\n\nconst honoHandler = getRequestListener(app.fetch)\n\ncreateServer(async (req, res) => {\n await new Promise<void>(resolve => vite.middlewares(req, res, resolve))\n if (!res.writableEnded) await honoHandler(req, res)\n}).listen(port, host, () => {\n printDevBanner(port)\n})\n\nexport {}", "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\";\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\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 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.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}", "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}", "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", "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 {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 type {Hono} from 'hono'\nimport type {Manifest} from 'vite'\n\ninterface ServerOptions {\n renderModule: any\n apiModule: any\n manifest?: Manifest\n loaderTimeout?: number\n}\n\nexport function registerApiRoutes(app: Hono, {apiModule, renderModule, loaderTimeout}: ServerOptions) {\n app.all('/api/*', async (c) => {\n try {\n return await apiModule.handleApiRequest(c.req.url, c.req.raw)\n } catch (e) {\n console.error(e)\n return c.json({error: 'internal error'}, 500)\n }\n })\n\n app.get('/_data/*', async (c) => {\n try {\n const {pathname, search} = new URL(c.req.url, 'http://localhost')\n const url = pathname.replace(/^\\/_data/, '') + search\n\n const data = await renderModule.runLoader(url, c.req.raw, {loaderTimeout})\n if (data.error) return c.json({error: 'internal error'}, 500)\n return c.json(data)\n } catch (e) {\n console.error(e)\n return c.json({error: 'internal error'}, 500)\n }\n })\n}\n\nexport function registerSsrRoute(app: Hono, {renderModule, manifest, loaderTimeout}: ServerOptions) {\n app.get('*', async (c) => {\n try {\n const {html, statusCode, headers} = await renderModule.render(c.req.url, c.req.raw, {manifest, loaderTimeout})\n const res = c.html(`<!DOCTYPE html>${html}`, statusCode)\n for (const [key, value] of Object.entries(headers as Record<string, string>)) {\n res.headers.set(key, value)\n }\n return res\n } catch (e) {\n console.error(e)\n return c.text('Internal Server Error', 500)\n }\n })\n}", "import pc from 'picocolors'\nimport {networkInterfaces} from 'node:os'\n\ndeclare const __DEVIX_VERSION__: string\n\nfunction getNetworkUrl(port: number): string | null {\n const nets = networkInterfaces()\n for (const interfaces of Object.values(nets)) {\n for (const net of interfaces ?? []) {\n if (net.family === 'IPv4' && !net.internal) {\n return `http://${net.address}:${port}/`\n }\n }\n }\n return null\n}\n\nexport function printDevBanner(port: number) {\n const version = __DEVIX_VERSION__\n const networkUrl = getNetworkUrl(port)\n\n console.log()\n console.log(` ${pc.bold(pc.yellow('devix'))} ${pc.dim(`v${version}`)}`)\n console.log()\n console.log(` ${pc.green('\u279C')} ${pc.bold('Local:')} ${pc.cyan(`http://localhost:${port}/`)}`)\n if (networkUrl) {\n console.log(` ${pc.green('\u279C')} ${pc.bold('Network:')} ${pc.cyan(networkUrl)}`)\n } else {\n console.log(` ${pc.green('\u279C')} ${pc.bold('Network:')} ${pc.dim('use --host to expose')}`)\n }\n console.log()\n}", "import type {ViteDevServer} from 'vite'\n\nexport async function collectCss(vite: ViteDevServer): Promise<string[]> {\n const cssUrls = new Set<string>()\n\n for (const [, mod] of vite.moduleGraph.idToModuleMap) {\n if (!mod.id) continue\n if (mod.id.endsWith('.css') || mod.id.includes('.css?')) {\n cssUrls.add(mod.url)\n }\n }\n\n return [...cssUrls]\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 {loadEnv} from 'vite'\n\nexport function loadDotenv(mode: string) {\n const env = loadEnv(mode, process.cwd(), '')\n for (const [key, value] of Object.entries(env)) {\n if (process.env[key] === undefined) {\n process.env[key] = value\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["createServer", "createViteServer", "getRequestListener", "Hono", "mergeConfig", "react", "fileURLToPath", "dirname", "resolve", "generateEntryClient", "cssUrls", "u", "generateClientRoutes", "pagesDir", "matcherPath", "generateRender", "pagesDir", "renderPath", "generateApi", "apiPath", "appDir", "routePattern", "rel", "cache", "invalidatePagesCache", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "cache", "invalidateApiCache", "generateContext", "readFileSync", "readdirSync", "statSync", "join", "relative", "METHOD_EXPORT_RE", "stripComments", "content", "extractHttpMethods", "found", "match", "filePathToIdentifier", "filePath", "apiDir", "buildRouteEntry", "methods", "keyToRoutePattern", "generateRoutesDts", "entries", "imports", "e", "importPath", "routeLines", "m", "walkDir", "dir", "root", "entries", "name", "readdirSync", "full", "join", "statSync", "relative", "scanApiFiles", "appDir", "projectRoot", "apiDir", "files", "f", "filePath", "content", "readFileSync", "methods", "extractHttpMethods", "buildRouteEntry", "mkdirSync", "readFileSync", "writeFileSync", "existsSync", "join", "writeRoutesDts", "content", "projectRoot", "devixDir", "outPath", "__dirname", "dirname", "fileURLToPath", "VIRTUAL_ENTRY_CLIENT", "VIRTUAL_CLIENT_ROUTES", "VIRTUAL_RENDER", "VIRTUAL_API", "VIRTUAL_CONTEXT", "devix", "config", "appDir", "pagesDir", "cssUrls", "u", "renderPath", "resolve", "apiPath", "matcherPath", "virtualPlugin", "id", "generateEntryClient", "generateClientRoutes", "generateRender", "generateApi", "generateContext", "root", "entries", "scanApiFiles", "writeRoutesDts", "generateRoutesDts", "server", "regenerateDts", "file", "invalidatePagesCache", "invalidateApiCache", "base", "react", "mergeConfig", "registerApiRoutes", "app", "apiModule", "renderModule", "loaderTimeout", "c", "e", "pathname", "search", "url", "data", "pc", "networkInterfaces", "getNetworkUrl", "port", "nets", "interfaces", "net", "printDevBanner", "version", "networkUrl", "collectCss", "vite", "cssUrls", "mod", "parseDuration", "value", "match", "n", "loadEnv", "loadDotenv", "mode", "env", "key", "value", "loadDotenv", "VIRTUAL_RENDER", "VIRTUAL_API", "config", "port", "host", "vite", "createViteServer", "devix", "renderModule", "args", "apiModule", "app", "Hono", "registerApiRoutes", "c", "html", "statusCode", "headers", "parseDuration", "cssLinks", "collectCss", "url", "htmlWithCss", "transformed", "res", "key", "value", "e", "honoHandler", "getRequestListener", "createServer", "req", "resolve", "printDevBanner"]
7
7
  }
@@ -149,5 +149,5 @@ declare module '@devlusoft/devix' {
149
149
  ${i}
150
150
  }
151
151
  }
152
- `}var v=s(()=>{"use strict";y()});import{readFileSync as gt,readdirSync as mt,statSync as ft}from"node:fs";import{join as w,relative as ht}from"node:path";function G(t,e){let r=[];for(let i of mt(t)){let o=w(t,i);ft(o).isDirectory()?r.push(...G(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(ht(e,o).replace(/\\/g,"/"))}return r}function E(t,e){let r=w(e,t,"api"),i;try{i=G(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let c=gt(w(e,o),"utf-8"),d=V(c);return d.length===0?[]:[B(o,`${t}/api`,d)]}catch{return[]}})}var X=s(()=>{"use strict";J();v()});import{mkdirSync as xt,readFileSync as yt,writeFileSync as Rt,existsSync as vt}from"node:fs";import{join as Y}from"node:path";function _(t,e){let r=Y(e,".devix"),i=Y(r,"routes.d.ts");return xt(r,{recursive:!0}),vt(i)&&yt(i,"utf-8")===t?!1:(Rt(i,t,"utf-8"),!0)}var z=s(()=>{"use strict"});import{mergeConfig as wt}from"vite";import Et from"@vitejs/plugin-react";import{fileURLToPath as _t}from"node:url";import{dirname as Pt,resolve as u}from"node:path";function Z(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=u(P,"../server/render.js").replace(/\\/g,"/"),c=u(P,"../server/api.js").replace(/\\/g,"/"),d=u(P,"../runtime/client-router.js").replace(/\\/g,"/"),st={name:"devix",enforce:"pre",resolveId(n){if(n===$)return`\0${$}`;if(n===T)return`\0${T}`;if(n===D)return`\0${D}`;if(n===A)return`\0${A}`;if(n===C)return`\0${C}`},load(n){if(n===`\0${$}`)return M({cssUrls:i});if(n===`\0${T}`)return I({pagesDir:r,matcherPath:d});if(n===`\0${D}`)return U({pagesDir:r,renderPath:o});if(n===`\0${A}`)return F({apiPath:c,appDir:e});if(n===`\0${C}`)return N()},buildStart(){let n=process.cwd(),l=E(e,n);_(R(l,`${e}/api`),n)},configureServer(n){let l=process.cwd(),g=()=>{let a=E(e,l);_(R(a,`${e}/api`),l)};n.watcher.on("add",a=>{a.startsWith(u(l,r))&&h(),a.includes(`${e}/api`)&&(x(),g())}),n.watcher.on("unlink",a=>{a.startsWith(u(l,r))&&h(),a.includes(`${e}/api`)&&(x(),g())}),n.watcher.on("change",a=>{a.includes(`${e}/api`)&&!a.endsWith("middleware.ts")&&g()})}},at={plugins:[Et(),st],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return wt(at,t.vite??{})}var P,$,T,D,A,C,K=s(()=>{"use strict";O();L();k();H();j();y();W();X();v();z();P=Pt(_t(import.meta.url)),$="virtual:devix/entry-client",T="virtual:devix/client-routes",D="virtual:devix/render",A="virtual:devix/api",C="virtual:devix/context"});function Q(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 tt=s(()=>{"use strict"});var At={};import{writeFileSync as $t}from"node:fs";import{resolve as Tt}from"node:path";import{build as et}from"vite";var p,rt,Dt,nt=s(async()=>{"use strict";K();tt();p=(await import(`${process.cwd()}/devix.config.ts`)).default,rt=Z(p);await et({...rt,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await et({...rt,configFile:!1,build:{ssr:!0,outDir:"dist/server",rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});Dt={port:p.port??3e3,host:p.host??!1,loaderTimeout:Q(p.loaderTimeout??1e4),output:p.output??"server"};$t(Tt(process.cwd(),"dist/devix.config.json"),JSON.stringify(Dt,null,2),"utf-8")});import{readFileSync as Ct,mkdirSync as St,writeFileSync as bt}from"node:fs";import{resolve as ot,join as S}from"node:path";var Mt=(await import(`${process.cwd()}/devix.config.ts`)).default;Mt.output!=="static"&&console.warn('[devix] Tip: set output: "static" in devix.config.ts to skip the SSR server at runtime.');await nt().then(()=>At);var Ot=Date.now(),it=await import(ot(process.cwd(),"dist/server/render.js")+`?t=${Ot}`),It=JSON.parse(Ct(ot(process.cwd(),"dist/client/.vite/manifest.json"),"utf-8")),b=await it.getStaticRoutes();console.log(`[devix] Generating ${b.length} static page${b.length===1?"":"s"}...`);for(let t of b){let e=`http://localhost${t}`,{html:r,statusCode:i}=await it.render(e,new Request(e),{manifest:It});if(i!==200){console.warn(`[devix] Skipping ${t} \u2014 status ${i}`);continue}let o=t==="/"?S(process.cwd(),"dist/client/index.html"):S(process.cwd(),"dist/client",t,"index.html");St(S(o,".."),{recursive:!0}),bt(o,`<!DOCTYPE html>${r}`,"utf-8"),console.log(` \u2713 ${t}`)}console.log("[devix] Generation complete.");
152
+ `}var v=s(()=>{"use strict";y()});import{readFileSync as gt,readdirSync as mt,statSync as ft}from"node:fs";import{join as w,relative as ht}from"node:path";function G(t,e){let r=[];for(let i of mt(t)){let o=w(t,i);ft(o).isDirectory()?r.push(...G(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(ht(e,o).replace(/\\/g,"/"))}return r}function E(t,e){let r=w(e,t,"api"),i;try{i=G(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let c=gt(w(e,o),"utf-8"),d=V(c);return d.length===0?[]:[B(o,`${t}/api`,d)]}catch{return[]}})}var X=s(()=>{"use strict";J();v()});import{mkdirSync as xt,readFileSync as yt,writeFileSync as Rt,existsSync as vt}from"node:fs";import{join as Y}from"node:path";function P(t,e){let r=Y(e,".devix"),i=Y(r,"routes.d.ts");return xt(r,{recursive:!0}),vt(i)&&yt(i,"utf-8")===t?!1:(Rt(i,t,"utf-8"),!0)}var z=s(()=>{"use strict"});import{mergeConfig as wt}from"vite";import Et from"@vitejs/plugin-react";import{fileURLToPath as Pt}from"node:url";import{dirname as _t,resolve as u}from"node:path";function Z(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=u(_,"../server/render.js").replace(/\\/g,"/"),c=u(_,"../server/api.js").replace(/\\/g,"/"),d=u(_,"../runtime/client-router.js").replace(/\\/g,"/"),st={name:"devix",enforce:"pre",resolveId(n){if(n===$)return`\0${$}`;if(n===T)return`\0${T}`;if(n===D)return`\0${D}`;if(n===A)return`\0${A}`;if(n===C)return`\0${C}`},load(n){if(n===`\0${$}`)return M({cssUrls:i});if(n===`\0${T}`)return I({pagesDir:r,matcherPath:d});if(n===`\0${D}`)return U({pagesDir:r,renderPath:o});if(n===`\0${A}`)return F({apiPath:c,appDir:e});if(n===`\0${C}`)return N()},buildStart(){let n=process.cwd(),l=E(e,n);P(R(l,`${e}/api`),n)},configureServer(n){let l=process.cwd(),g=()=>{let a=E(e,l);P(R(a,`${e}/api`),l)};n.watcher.on("add",a=>{a.startsWith(u(l,r))&&h(),a.includes(`${e}/api`)&&(x(),g())}),n.watcher.on("unlink",a=>{a.startsWith(u(l,r))&&h(),a.includes(`${e}/api`)&&(x(),g())}),n.watcher.on("change",a=>{a.includes(`${e}/api`)&&!a.endsWith("middleware.ts")&&g()})}},at={plugins:[Et(),st],publicDir:u(process.cwd(),t.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return wt(at,t.vite??{})}var _,$,T,D,A,C,K=s(()=>{"use strict";O();L();k();H();j();y();W();X();v();z();_=_t(Pt(import.meta.url)),$="virtual:devix/entry-client",T="virtual:devix/client-routes",D="virtual:devix/render",A="virtual:devix/api",C="virtual:devix/context"});function Q(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 tt=s(()=>{"use strict"});var At={};import{writeFileSync as $t}from"node:fs";import{resolve as Tt}from"node:path";import{build as et}from"vite";var p,rt,Dt,nt=s(async()=>{"use strict";K();tt();p=(await import(`${process.cwd()}/devix.config.ts`)).default,rt=Z(p);await et({...rt,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await et({...rt,configFile:!1,build:{ssr:!0,outDir:"dist/server",copyPublicDir:!1,rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});Dt={port:p.port??3e3,host:p.host??!1,loaderTimeout:Q(p.loaderTimeout??1e4),output:p.output??"server"};$t(Tt(process.cwd(),"dist/devix.config.json"),JSON.stringify(Dt,null,2),"utf-8")});import{readFileSync as Ct,mkdirSync as St,writeFileSync as bt}from"node:fs";import{resolve as ot,join as S}from"node:path";var Mt=(await import(`${process.cwd()}/devix.config.ts`)).default;Mt.output!=="static"&&console.warn('[devix] Tip: set output: "static" in devix.config.ts to skip the SSR server at runtime.');await nt().then(()=>At);var Ot=Date.now(),it=await import(ot(process.cwd(),"dist/server/render.js")+`?t=${Ot}`),It=JSON.parse(Ct(ot(process.cwd(),"dist/client/.vite/manifest.json"),"utf-8")),b=await it.getStaticRoutes();console.log(`[devix] Generating ${b.length} static page${b.length===1?"":"s"}...`);for(let t of b){let e=`http://localhost${t}`,{html:r,statusCode:i}=await it.render(e,new Request(e),{manifest:It});if(i!==200){console.warn(`[devix] Skipping ${t} \u2014 status ${i}`);continue}let o=t==="/"?S(process.cwd(),"dist/client/index.html"):S(process.cwd(),"dist/client",t,"index.html");St(S(o,".."),{recursive:!0}),bt(o,`<!DOCTYPE html>${r}`,"utf-8"),console.log(` \u2713 ${t}`)}console.log("[devix] Generation complete.");
153
153
  //# sourceMappingURL=generate.js.map