@devlusoft/devix 0.1.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/bin/devix.js +2 -0
  4. package/dist/cli/build.d.ts +1 -0
  5. package/dist/cli/build.js +286 -0
  6. package/dist/cli/build.js.map +7 -0
  7. package/dist/cli/dev.d.ts +1 -0
  8. package/dist/cli/dev.js +361 -0
  9. package/dist/cli/dev.js.map +7 -0
  10. package/dist/cli/generate.d.ts +1 -0
  11. package/dist/cli/generate.js +389 -0
  12. package/dist/cli/generate.js.map +7 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +649 -0
  15. package/dist/cli/index.js.map +7 -0
  16. package/dist/cli/start.d.ts +1 -0
  17. package/dist/cli/start.js +78 -0
  18. package/dist/cli/start.js.map +7 -0
  19. package/dist/config.d.ts +21 -0
  20. package/dist/config.js +17 -0
  21. package/dist/config.js.map +7 -0
  22. package/dist/runtime/api-context.d.ts +20 -0
  23. package/dist/runtime/api-context.js +18 -0
  24. package/dist/runtime/api-context.js.map +7 -0
  25. package/dist/runtime/client-router.d.ts +13 -0
  26. package/dist/runtime/client-router.js +59 -0
  27. package/dist/runtime/client-router.js.map +7 -0
  28. package/dist/runtime/context.d.ts +27 -0
  29. package/dist/runtime/context.js +15 -0
  30. package/dist/runtime/context.js.map +7 -0
  31. package/dist/runtime/error-boundary.d.ts +19 -0
  32. package/dist/runtime/error-boundary.js +37 -0
  33. package/dist/runtime/error-boundary.js.map +7 -0
  34. package/dist/runtime/head.d.ts +3 -0
  35. package/dist/runtime/head.js +69 -0
  36. package/dist/runtime/head.js.map +7 -0
  37. package/dist/runtime/index.d.ts +5 -0
  38. package/dist/runtime/index.js +300 -0
  39. package/dist/runtime/index.js.map +7 -0
  40. package/dist/runtime/link.d.ts +8 -0
  41. package/dist/runtime/link.js +43 -0
  42. package/dist/runtime/link.js.map +7 -0
  43. package/dist/runtime/metadata.d.ts +10 -0
  44. package/dist/runtime/metadata.js +22 -0
  45. package/dist/runtime/metadata.js.map +7 -0
  46. package/dist/runtime/router-provider.d.ts +22 -0
  47. package/dist/runtime/router-provider.js +259 -0
  48. package/dist/runtime/router-provider.js.map +7 -0
  49. package/dist/server/api-router.d.ts +21 -0
  50. package/dist/server/api-router.js +64 -0
  51. package/dist/server/api-router.js.map +7 -0
  52. package/dist/server/api.d.ts +2 -0
  53. package/dist/server/api.js +123 -0
  54. package/dist/server/api.js.map +7 -0
  55. package/dist/server/collect-css.d.ts +2 -0
  56. package/dist/server/collect-css.js +15 -0
  57. package/dist/server/collect-css.js.map +7 -0
  58. package/dist/server/index.d.ts +6 -0
  59. package/dist/server/index.js +133 -0
  60. package/dist/server/index.js.map +7 -0
  61. package/dist/server/pages-router.d.ts +21 -0
  62. package/dist/server/pages-router.js +64 -0
  63. package/dist/server/pages-router.js.map +7 -0
  64. package/dist/server/render.d.ts +34 -0
  65. package/dist/server/render.js +306 -0
  66. package/dist/server/render.js.map +7 -0
  67. package/dist/server/routes.d.ts +11 -0
  68. package/dist/server/routes.js +42 -0
  69. package/dist/server/routes.js.map +7 -0
  70. package/dist/server/types.d.ts +49 -0
  71. package/dist/server/types.js +1 -0
  72. package/dist/server/types.js.map +7 -0
  73. package/dist/types.d.ts +35 -0
  74. package/dist/types.js +1 -0
  75. package/dist/types.js.map +7 -0
  76. package/dist/utils/async.d.ts +1 -0
  77. package/dist/utils/async.js +14 -0
  78. package/dist/utils/async.js.map +7 -0
  79. package/dist/utils/banner.d.ts +1 -0
  80. package/dist/utils/banner.js +34 -0
  81. package/dist/utils/banner.js.map +7 -0
  82. package/dist/utils/duration.d.ts +1 -0
  83. package/dist/utils/duration.js +22 -0
  84. package/dist/utils/duration.js.map +7 -0
  85. package/dist/utils/html.d.ts +2 -0
  86. package/dist/utils/html.js +12 -0
  87. package/dist/utils/html.js.map +7 -0
  88. package/dist/utils/patterns.d.ts +1 -0
  89. package/dist/utils/patterns.js +8 -0
  90. package/dist/utils/patterns.js.map +7 -0
  91. package/dist/vite/codegen/api.d.ts +6 -0
  92. package/dist/vite/codegen/api.js +23 -0
  93. package/dist/vite/codegen/api.js.map +7 -0
  94. package/dist/vite/codegen/client-routes.d.ts +6 -0
  95. package/dist/vite/codegen/client-routes.js +36 -0
  96. package/dist/vite/codegen/client-routes.js.map +7 -0
  97. package/dist/vite/codegen/context.d.ts +1 -0
  98. package/dist/vite/codegen/context.js +10 -0
  99. package/dist/vite/codegen/context.js.map +7 -0
  100. package/dist/vite/codegen/entry-client.d.ts +5 -0
  101. package/dist/vite/codegen/entry-client.js +64 -0
  102. package/dist/vite/codegen/entry-client.js.map +7 -0
  103. package/dist/vite/codegen/render.d.ts +6 -0
  104. package/dist/vite/codegen/render.js +31 -0
  105. package/dist/vite/codegen/render.js.map +7 -0
  106. package/dist/vite/index.d.ts +3 -0
  107. package/dist/vite/index.js +225 -0
  108. package/dist/vite/index.js.map +7 -0
  109. package/package.json +77 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/vite/codegen/entry-client.ts", "../../src/vite/codegen/client-routes.ts", "../../src/vite/codegen/render.ts", "../../src/vite/codegen/api.ts", "../../src/utils/patterns.ts", "../../src/server/pages-router.ts", "../../src/server/api-router.ts", "../../src/vite/codegen/context.ts", "../../src/vite/index.ts", "../../src/server/routes.ts", "../../src/utils/banner.ts", "../../src/server/collect-css.ts", "../../src/utils/duration.ts", "../../src/cli/dev.ts", "../../src/cli/build.ts", "../../src/cli/generate.ts", "../../src/cli/start.ts", "../../src/cli/index.ts"],
4
+ "sourcesContent": ["interface EntryClientOptions {\n cssUrls: string[]\n}\n\nexport function generateEntryClient({cssUrls}: EntryClientOptions): string {\n const cssImports = cssUrls.map(u => `import '${u}'`).join('\\n')\n\n return `\n${cssImports}\nimport \"@vitejs/plugin-react/preamble\"\nimport React from \"react\"\nimport {hydrateRoot, createRoot} from 'react-dom/client'\nimport {matchClientRoute, loadErrorPage, getDefaultErrorPage} from 'virtual:devix/client-routes'\nimport {RouterProvider} from '@devlusoft/devix'\n\nconst root = document.getElementById('devix-root')\n\nif (!window.__DEVIX__) {\n const ErrorPage = getDefaultErrorPage()\n createRoot(root).render(React.createElement(ErrorPage, {statusCode: 500, message: 'Server error'}))\n} else {\n const {metadata, viewport, clientEntry} = window.__DEVIX__\n const loaderData = window.__LOADER_DATA__\n const layoutsData = window.__LAYOUTS_DATA__ ?? []\n\n const matched = matchClientRoute(window.location.pathname)\n\n if (matched) {\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n hydrateRoot(\n root,\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: loaderData,\n initialParams: matched.params,\n initialPage: pageMod.default,\n initialLayouts: layoutMods.map(m => m.default),\n initialLayoutsData: layoutsData,\n initialMeta: metadata,\n initialViewport: viewport,\n })\n )\n } else {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n createRoot(root).render(\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: null,\n initialParams: {},\n initialPage: () => null,\n initialLayouts: [],\n initialLayoutsData: [],\n initialMeta: null,\n initialError: {statusCode: 404, message: 'Not found'},\n initialErrorPage: ErrorPage,\n })\n )\n }\n}\n`\n}", "interface ClientRoutesOptions {\n pagesDir: string\n matcherPath: string\n}\n\nexport function generateClientRoutes({pagesDir, matcherPath}: ClientRoutesOptions) {\n return `\nimport React from 'react'\nimport { createMatcher } from '${matcherPath}'\nconst pageFiles = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst layoutFiles = import.meta.glob('/${pagesDir}/**/layout.tsx')\nconst errorFiles = import.meta.glob('/${pagesDir}/**/error.tsx')\n\nexport const matchClientRoute = createMatcher(pageFiles, layoutFiles)\n\nexport async function loadErrorPage() {\n const key = Object.keys(errorFiles)[0]\n if (!key) return null\n const mod = await errorFiles[key]()\n return mod?.default ?? null\n}\n\nexport function getDefaultErrorPage() {\n return function DefaultError({ statusCode, message }) {\n return React.createElement('main', {\n style: { minHeight: '100dvh', display: 'flex', flexDirection: 'column', \n alignItems: 'center', justifyContent: 'center', gap: '8px',\n fontFamily: 'system-ui, sans-serif' }\n },\n React.createElement('h1', {style: {fontSize: '4rem', fontWeight: 700}}, statusCode),\n React.createElement('p', {style: {color: '#666'}}, message ?? 'An unexpected error occurred'),\n )\n }\n}\n`\n}", "interface RenderOptions {\n pagesDir: string\n renderPath: string\n}\n\nexport function generateRender({pagesDir, renderPath}: RenderOptions): string {\n return `\nimport { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${renderPath}'\n\nconst _pages = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst _layouts = import.meta.glob('/${pagesDir}/**/layout.tsx')\n\nconst _glob = {\n pages: _pages,\n layouts: _layouts,\n pagesDir: '/${pagesDir}',\n}\n\nexport function render(url, request, options) {\n return _render(url, request, _glob, options)\n}\n\nexport function runLoader(url, request, options) {\n return _runLoader(url, request, _glob, options)\n}\n\nexport function getStaticRoutes() {\n return _getStaticRoutes(_glob)\n}\n`\n}\n", "interface ApiOptions {\n apiPath: string\n appDir: string\n}\n\nexport function generateApi({apiPath, appDir}: ApiOptions): string {\n return `\nimport { handleApiRequest as _handleApiRequest } from '${apiPath}'\n\nconst _routes = import.meta.glob(['/${appDir}/api/**/*.ts', '!**/middleware.ts'])\nconst _middlewares = import.meta.glob('/${appDir}/api/**/middleware.ts')\n\nconst _glob = {\n routes: _routes,\n middlewares: _middlewares,\n apiDir: '/${appDir}/api',\n}\n\nexport function handleApiRequest(url, request) {\n return _handleApiRequest(url, request, _glob)\n}\n`\n}\n", "export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface Page {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface Layout {\n dir: string\n key: string\n}\n\nexport interface PagesResult {\n pages: Page[]\n layouts: Layout[]\n}\n\nfunction keyToRoutePattern(key: string, pagesDir: string): string {\n const rel = key.slice(pagesDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === \"/\" ? \"/\" : `/${pattern}`\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: PagesResult | null = null\n\nexport function invalidatePagesCache() {\n cache = null\n}\n\nexport function buildPages(pageKeys: string[], layoutKeys: string[], pagesDir: string): PagesResult {\n if (cache) return cache\n\n const pages: Page[] = []\n const layouts: Layout[] = []\n\n for (const key of layoutKeys) {\n layouts.push({dir: keyToDir(key), key})\n }\n\n for (const key of pageKeys) {\n const pattern = keyToRoutePattern(key, pagesDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n pages.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n\n pages.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {pages, layouts}\n return cache\n}\n\nexport function collectLayoutChain(pageKey: string, layouts: Layout[]): Layout[] {\n const pageDir = keyToDir(pageKey)\n\n return layouts\n .filter(layout => pageDir.startsWith(layout.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchPage(pathname: string, pages: Page[]): {\n page: Page\n params: Record<string, string>\n} | null {\n for (const page of pages) {\n const match = pathname.match(page.regex)\n if (match) {\n const params: Record<string, string> = {}\n page.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {page, params}\n }\n }\n return null\n}\n", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nfunction 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 {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\";\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 configureServer(server) {\n server.watcher.on('add', (file) => {\n if (file.startsWith(resolve(process.cwd(), pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) invalidateApiCache()\n })\n server.watcher.on('unlink', (file) => {\n if (file.startsWith(resolve(process.cwd(), pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) invalidateApiCache()\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}", "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 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'\nimport {createRequire} from 'node:module'\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 req = createRequire(import.meta.url)\n const version = req('../../package.json').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 {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\";\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 {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 {readFileSync, mkdirSync, writeFileSync} from 'node:fs'\nimport {resolve, join} from 'node:path'\nimport type {Manifest} from 'vite'\nimport type {DevixConfig} from '../config'\n\nconst userConfig: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nif (userConfig.output !== 'static') {\n console.warn('[devix] Tip: set output: \"static\" in devix.config.ts to skip the SSR server at runtime.')\n}\n\nawait import('./build.js')\n\nconst t = Date.now()\nconst renderModule = await import(resolve(process.cwd(), 'dist/server/render.js') + `?t=${t}`)\n\nconst manifest: Manifest = JSON.parse(\n readFileSync(resolve(process.cwd(), 'dist/client/.vite/manifest.json'), 'utf-8')\n)\n\nconst urls: string[] = await renderModule.getStaticRoutes()\n\nconsole.log(`[devix] Generating ${urls.length} static page${urls.length === 1 ? '' : 's'}...`)\n\nfor (const url of urls) {\n const fullUrl = `http://localhost${url}`\n const {html, statusCode} = await renderModule.render(fullUrl, new Request(fullUrl), {manifest})\n\n if (statusCode !== 200) {\n console.warn(`[devix] Skipping ${url} \u2014 status ${statusCode}`)\n continue\n }\n\n const outPath = url === '/'\n ? join(process.cwd(), 'dist/client/index.html')\n : join(process.cwd(), 'dist/client', url, 'index.html')\n\n mkdirSync(join(outPath, '..'), {recursive: true})\n writeFileSync(outPath, `<!DOCTYPE html>${html}`, 'utf-8')\n console.log(` \u2713 ${url}`)\n}\n\nconsole.log('[devix] Generation complete.')\n\nexport {}\n", "import {readFileSync} from 'node:fs'\nimport {serve} from '@hono/node-server'\nimport {serveStatic} from '@hono/node-server/serve-static'\nimport {Hono} from 'hono'\nimport {resolve} from 'node:path'\nimport type {Manifest} from 'vite'\nimport {registerApiRoutes, registerSsrRoute} from '../server/routes'\n\nlet renderModule: any\nlet apiModule: any\nlet manifest: Manifest\nlet runtimeConfig: { port: number, host: string | boolean, loaderTimeout: number, output: 'server' | 'static' }\n\ntry {\n runtimeConfig = JSON.parse(readFileSync(resolve(process.cwd(), 'dist/devix.config.json'), 'utf-8'))\n if (runtimeConfig.output !== 'static') {\n renderModule = await import(resolve(process.cwd(), 'dist/server/render.js'))\n apiModule = await import(resolve(process.cwd(), 'dist/server/api.js'))\n }\n manifest = JSON.parse(readFileSync(resolve(process.cwd(), 'dist/client/.vite/manifest.json'), 'utf-8'))\n} catch {\n console.error('[devix] Build not found. Run \"devix build\" first.')\n process.exit(1)\n}\n\nconst port = Number(process.env.PORT) || runtimeConfig!.port || 3000\nconst host = typeof runtimeConfig!.host === 'string'\n ? runtimeConfig!.host\n : runtimeConfig!.host ? '0.0.0.0' : (process.env.HOST || '0.0.0.0')\n\nconst app = new Hono()\n\napp.use('/*', serveStatic({\n root: './dist/client',\n onFound: (_path, c) => {\n c.header('Cache-Control', _path.includes('/assets/')\n ? 'public, immutable, max-age=31536000'\n : 'no-cache')\n }\n}))\n\nif (runtimeConfig!.output === 'static') {\n console.log('[devix] Static mode \u2014 serving pre-generated files from dist/client')\n} else {\n registerApiRoutes(app, {renderModule, apiModule, manifest})\n registerSsrRoute(app, {renderModule, apiModule, manifest, loaderTimeout: runtimeConfig!.loaderTimeout})\n}\n\nserve({fetch: app.fetch, port, hostname: host}, (info) => console.log(`http://${info.address}:${info.port}`))\n\nexport {}", "#!/usr/bin/env node\nimport {readFileSync} from 'node:fs'\nimport {join, dirname} from 'node:path'\nimport {fileURLToPath} from 'node:url'\n\nconst command = process.argv[2]\n\nswitch (command) {\n case 'dev':\n await import(\"./dev.js\")\n break\n case \"build\":\n await import(\"./build.js\")\n break\n case \"generate\":\n await import(\"./generate.js\")\n break\n case \"start\":\n await import(\"./start.js\")\n break\n case '--version':\n case '-v': {\n const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../package.json'), 'utf-8'))\n console.log(pkg.version)\n break\n }\n case '--help':\n case '-h':\n console.log(`\ndevix \u2014 a lightweight SSR framework\n\nUsage:\n devix dev Start development server\n devix build Build for production\n devix generate Build and generate static HTML (SSG)\n devix start Start production server\n\nOptions:\n -v, --version Show version\n -h, --help Show this help\n\nOutput modes (set in devix.config.ts):\n output: \"server\" SSR mode \u2014 devix start handles requests dynamically (default)\n output: \"static\" SSG mode \u2014 devix generate pre-renders all pages; devix start serves static files only\n `.trim())\n break\n default:\n console.error(`Unknown command: ${command}`)\n console.error('Usage: devix <dev|build|generate|start>')\n process.exit(1)\n}\n\nexport {}"],
5
+ "mappings": "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",
6
+ "names": ["cache", "config", "app", "apiModule", "renderModule", "manifest", "port", "vite", "VIRTUAL_RENDER", "VIRTUAL_API", "resolve", "resolve", "config", "writeFileSync", "resolve", "renderModule", "readFileSync", "Hono", "resolve", "renderModule", "apiModule", "manifest", "runtimeConfig", "port", "host", "app", "readFileSync", "join", "dirname", "fileURLToPath"]
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
1
+ // src/cli/start.ts
2
+ import { readFileSync } from "node:fs";
3
+ import { serve } from "@hono/node-server";
4
+ import { serveStatic } from "@hono/node-server/serve-static";
5
+ import { Hono } from "hono";
6
+ import { resolve } from "node:path";
7
+
8
+ // src/server/routes.ts
9
+ function registerApiRoutes(app2, { apiModule: apiModule2, renderModule: renderModule2, loaderTimeout }) {
10
+ app2.all("/api/*", async (c) => {
11
+ try {
12
+ return await apiModule2.handleApiRequest(c.req.url, c.req.raw);
13
+ } catch (e) {
14
+ console.error(e);
15
+ return c.json({ error: "internal error" }, 500);
16
+ }
17
+ });
18
+ app2.get("/_data/*", async (c) => {
19
+ try {
20
+ const { pathname, search } = new URL(c.req.url, "http://localhost");
21
+ const url = pathname.replace(/^\/_data/, "") + search;
22
+ const data = await renderModule2.runLoader(url, c.req.raw, { loaderTimeout });
23
+ return c.json(data);
24
+ } catch (e) {
25
+ console.error(e);
26
+ return c.json({ error: "internal error" }, 500);
27
+ }
28
+ });
29
+ }
30
+ function registerSsrRoute(app2, { renderModule: renderModule2, manifest: manifest2, loaderTimeout }) {
31
+ app2.get("*", async (c) => {
32
+ try {
33
+ const { html, statusCode, headers } = await renderModule2.render(c.req.url, c.req.raw, { manifest: manifest2, loaderTimeout });
34
+ const res = c.html(`<!DOCTYPE html>${html}`, statusCode);
35
+ for (const [key, value] of Object.entries(headers)) {
36
+ res.headers.set(key, value);
37
+ }
38
+ return res;
39
+ } catch (e) {
40
+ console.error(e);
41
+ return c.text("Internal Server Error", 500);
42
+ }
43
+ });
44
+ }
45
+
46
+ // src/cli/start.ts
47
+ var renderModule;
48
+ var apiModule;
49
+ var manifest;
50
+ var runtimeConfig;
51
+ try {
52
+ runtimeConfig = JSON.parse(readFileSync(resolve(process.cwd(), "dist/devix.config.json"), "utf-8"));
53
+ if (runtimeConfig.output !== "static") {
54
+ renderModule = await import(resolve(process.cwd(), "dist/server/render.js"));
55
+ apiModule = await import(resolve(process.cwd(), "dist/server/api.js"));
56
+ }
57
+ manifest = JSON.parse(readFileSync(resolve(process.cwd(), "dist/client/.vite/manifest.json"), "utf-8"));
58
+ } catch {
59
+ console.error('[devix] Build not found. Run "devix build" first.');
60
+ process.exit(1);
61
+ }
62
+ var port = Number(process.env.PORT) || runtimeConfig.port || 3e3;
63
+ var host = typeof runtimeConfig.host === "string" ? runtimeConfig.host : runtimeConfig.host ? "0.0.0.0" : process.env.HOST || "0.0.0.0";
64
+ var app = new Hono();
65
+ app.use("/*", serveStatic({
66
+ root: "./dist/client",
67
+ onFound: (_path, c) => {
68
+ c.header("Cache-Control", _path.includes("/assets/") ? "public, immutable, max-age=31536000" : "no-cache");
69
+ }
70
+ }));
71
+ if (runtimeConfig.output === "static") {
72
+ console.log("[devix] Static mode \u2014 serving pre-generated files from dist/client");
73
+ } else {
74
+ registerApiRoutes(app, { renderModule, apiModule, manifest });
75
+ registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout });
76
+ }
77
+ serve({ fetch: app.fetch, port, hostname: host }, (info) => console.log(`http://${info.address}:${info.port}`));
78
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cli/start.ts", "../../src/server/routes.ts"],
4
+ "sourcesContent": ["import {readFileSync} from 'node:fs'\nimport {serve} from '@hono/node-server'\nimport {serveStatic} from '@hono/node-server/serve-static'\nimport {Hono} from 'hono'\nimport {resolve} from 'node:path'\nimport type {Manifest} from 'vite'\nimport {registerApiRoutes, registerSsrRoute} from '../server/routes'\n\nlet renderModule: any\nlet apiModule: any\nlet manifest: Manifest\nlet runtimeConfig: { port: number, host: string | boolean, loaderTimeout: number, output: 'server' | 'static' }\n\ntry {\n runtimeConfig = JSON.parse(readFileSync(resolve(process.cwd(), 'dist/devix.config.json'), 'utf-8'))\n if (runtimeConfig.output !== 'static') {\n renderModule = await import(resolve(process.cwd(), 'dist/server/render.js'))\n apiModule = await import(resolve(process.cwd(), 'dist/server/api.js'))\n }\n manifest = JSON.parse(readFileSync(resolve(process.cwd(), 'dist/client/.vite/manifest.json'), 'utf-8'))\n} catch {\n console.error('[devix] Build not found. Run \"devix build\" first.')\n process.exit(1)\n}\n\nconst port = Number(process.env.PORT) || runtimeConfig!.port || 3000\nconst host = typeof runtimeConfig!.host === 'string'\n ? runtimeConfig!.host\n : runtimeConfig!.host ? '0.0.0.0' : (process.env.HOST || '0.0.0.0')\n\nconst app = new Hono()\n\napp.use('/*', serveStatic({\n root: './dist/client',\n onFound: (_path, c) => {\n c.header('Cache-Control', _path.includes('/assets/')\n ? 'public, immutable, max-age=31536000'\n : 'no-cache')\n }\n}))\n\nif (runtimeConfig!.output === 'static') {\n console.log('[devix] Static mode \u2014 serving pre-generated files from dist/client')\n} else {\n registerApiRoutes(app, {renderModule, apiModule, manifest})\n registerSsrRoute(app, {renderModule, apiModule, manifest, loaderTimeout: runtimeConfig!.loaderTimeout})\n}\n\nserve({fetch: app.fetch, port, hostname: host}, (info) => console.log(`http://${info.address}:${info.port}`))\n\nexport {}", "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 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}"],
5
+ "mappings": "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",
6
+ "names": ["app", "apiModule", "renderModule", "manifest"]
7
+ }
@@ -0,0 +1,21 @@
1
+ import type { UserConfig } from "vite";
2
+ export interface DevixConfig {
3
+ port?: number;
4
+ host?: string | boolean;
5
+ css?: string[];
6
+ appDir?: string;
7
+ envPrefix?: string | string[];
8
+ html?: {
9
+ lang?: string;
10
+ };
11
+ vite?: UserConfig;
12
+ loaderTimeout?: number | string;
13
+ output?: 'server' | 'static';
14
+ }
15
+ export interface ResolvedDirs {
16
+ appDir: string;
17
+ pagesDir: string;
18
+ apiDir: string;
19
+ }
20
+ export declare function defineConfig(config: DevixConfig): DevixConfig;
21
+ export declare function resolveDirs(config: DevixConfig): ResolvedDirs;
package/dist/config.js ADDED
@@ -0,0 +1,17 @@
1
+ // src/config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+ function resolveDirs(config) {
6
+ const appDir = config.appDir ?? "app";
7
+ return {
8
+ appDir,
9
+ pagesDir: `${appDir}/pages`,
10
+ apiDir: `${appDir}/api`
11
+ };
12
+ }
13
+ export {
14
+ defineConfig,
15
+ resolveDirs
16
+ };
17
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/config.ts"],
4
+ "sourcesContent": ["import type { UserConfig } from \"vite\"\n\nexport interface DevixConfig {\n port?: number\n host?: string | boolean\n css?: string[]\n appDir?: string\n envPrefix?: string | string[]\n html?: { lang?: string }\n vite?: UserConfig\n loaderTimeout?: number | string\n output?: 'server' | 'static'\n}\n\nexport interface ResolvedDirs {\n appDir: string\n pagesDir: string\n apiDir: string\n}\n\nexport function defineConfig(config: DevixConfig): DevixConfig { return config }\n\nexport function resolveDirs(config: DevixConfig): ResolvedDirs {\n const appDir = config.appDir ?? \"app\"\n return {\n appDir,\n pagesDir: `${appDir}/pages`,\n apiDir: `${appDir}/api`,\n }\n}"],
5
+ "mappings": ";AAoBO,SAAS,aAAa,QAAkC;AAAE,SAAO;AAAO;AAExE,SAAS,YAAY,QAAmC;AAC3D,QAAM,SAAS,OAAO,UAAU;AAChC,SAAO;AAAA,IACH;AAAA,IACA,UAAU,GAAG,MAAM;AAAA,IACnB,QAAQ,GAAG,MAAM;AAAA,EACrB;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,20 @@
1
+ export declare class RouteContext {
2
+ readonly params: Record<string, string>;
3
+ private _state;
4
+ constructor(params?: Record<string, string>);
5
+ set<T>(key: string, value: T): void;
6
+ get<T>(key: string): T | undefined;
7
+ }
8
+ export type RouteHandler = (ctx: RouteContext, request: Request) => Promise<Response | null> | Response | null;
9
+ export interface MiddlewareModule {
10
+ middleware: (ctx: RouteContext, request: Request) => Promise<Response | null> | Response | null;
11
+ }
12
+ export interface RouteModule {
13
+ GET?: RouteHandler;
14
+ POST?: RouteHandler;
15
+ PUT?: RouteHandler;
16
+ PATCH?: RouteHandler;
17
+ DELETE?: RouteHandler;
18
+ HEAD?: RouteHandler;
19
+ OPTIONS?: RouteHandler;
20
+ }
@@ -0,0 +1,18 @@
1
+ // src/runtime/api-context.ts
2
+ var RouteContext = class {
3
+ params;
4
+ _state = /* @__PURE__ */ new Map();
5
+ constructor(params = {}) {
6
+ this.params = params;
7
+ }
8
+ set(key, value) {
9
+ this._state.set(key, value);
10
+ }
11
+ get(key) {
12
+ return this._state.get(key);
13
+ }
14
+ };
15
+ export {
16
+ RouteContext
17
+ };
18
+ //# sourceMappingURL=api-context.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/runtime/api-context.ts"],
4
+ "sourcesContent": ["export class RouteContext {\n readonly params: Record<string, string>\n private _state = new Map<string, unknown>()\n\n constructor(params: Record<string, string> = {}) {\n this.params = params\n }\n\n set<T>(key: string, value: T): void {\n this._state.set(key, value)\n }\n\n get<T>(key: string): T | undefined {\n return this._state.get(key) as T\n }\n}\n\nexport type RouteHandler = (ctx: RouteContext, request: Request) => Promise<Response | null> | Response | null\n\nexport interface MiddlewareModule {\n middleware: (ctx: RouteContext, request: Request) => Promise<Response | null> | Response | null\n}\n\nexport interface RouteModule {\n GET?: RouteHandler\n POST?: RouteHandler\n PUT?: RouteHandler\n PATCH?: RouteHandler\n DELETE?: RouteHandler\n HEAD?: RouteHandler\n OPTIONS?: RouteHandler\n}"],
5
+ "mappings": ";AAAO,IAAM,eAAN,MAAmB;AAAA,EACb;AAAA,EACD,SAAS,oBAAI,IAAqB;AAAA,EAE1C,YAAY,SAAiC,CAAC,GAAG;AAC7C,SAAK,SAAS;AAAA,EAClB;AAAA,EAEA,IAAO,KAAa,OAAgB;AAChC,SAAK,OAAO,IAAI,KAAK,KAAK;AAAA,EAC9B;AAAA,EAEA,IAAO,KAA4B;AAC/B,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC9B;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import { ComponentType } from "react";
2
+ export interface ClientMatch {
3
+ load: () => Promise<{
4
+ default: ComponentType<any>;
5
+ }>;
6
+ loadLayouts: Array<() => Promise<{
7
+ default: ComponentType<any>;
8
+ }>>;
9
+ params: Record<string, string>;
10
+ }
11
+ type GlobMap = Record<string, () => Promise<any>>;
12
+ export declare function createMatcher(pageFiles: GlobMap, layoutFiles: GlobMap): (pathname: string) => ClientMatch | null;
13
+ export {};
@@ -0,0 +1,59 @@
1
+ // src/utils/patterns.ts
2
+ function routePattern(rel) {
3
+ return rel.replace(/\.(tsx|ts|jsx|js)$/, "").replace(/\(.*?\)\//g, "").replace(/^index$|\/index$/, "").replace(/\[([^\]]+)]/g, ":$1") || "/";
4
+ }
5
+
6
+ // src/runtime/client-router.ts
7
+ function fileToRoutePattern(filePath, pagesPrefix) {
8
+ const rel = filePath.slice(pagesPrefix.length);
9
+ return routePattern(rel);
10
+ }
11
+ function collectLayoutChain(pageFile, layoutFiles) {
12
+ const parts = pageFile.split("/");
13
+ const chain = [];
14
+ for (let i = 3; i <= parts.length - 1; i++) {
15
+ const dir = parts.slice(0, i).join("/");
16
+ const lp = `${dir}/layout.tsx`;
17
+ const lpts = `${dir}/layout.ts`;
18
+ if (layoutFiles[lp]) {
19
+ chain.push(layoutFiles[lp]);
20
+ } else if (layoutFiles[lpts]) {
21
+ chain.push(layoutFiles[lpts]);
22
+ }
23
+ }
24
+ return chain;
25
+ }
26
+ function createMatcher(pageFiles, layoutFiles) {
27
+ const routes = Object.keys(pageFiles).filter((f) => !f.split("/").pop().startsWith("layout")).map((file) => {
28
+ const pagesPrefix = file.slice(0, file.indexOf("/pages/") + "/pages".length);
29
+ const pattern = fileToRoutePattern(file, pagesPrefix);
30
+ const params = [...pattern.matchAll(/:([^/]+)/g)].map((m) => m[1]);
31
+ return { file, pattern, params, load: pageFiles[file], loadLayouts: collectLayoutChain(
32
+ file,
33
+ layoutFiles
34
+ ) };
35
+ }).sort((a, b) => {
36
+ const aScore = (a.pattern.match(/:/g) || []).length;
37
+ const bScore = (b.pattern.match(/:/g) || []).length;
38
+ if (aScore !== bScore) return aScore - bScore;
39
+ return b.pattern.length - a.pattern.length;
40
+ });
41
+ return function matchClientRoute(pathname) {
42
+ for (const route of routes) {
43
+ const regexStr = route.pattern.replace(/:[^/]+/g, "([^/]+)").replace(/\//g, "\\/");
44
+ const match = pathname.match(new RegExp(`^${regexStr}$`));
45
+ if (match) {
46
+ const params = {};
47
+ route.params.forEach((name, i) => {
48
+ params[name] = decodeURIComponent(match[i + 1]);
49
+ });
50
+ return { load: route.load, loadLayouts: route.loadLayouts, params };
51
+ }
52
+ }
53
+ return null;
54
+ };
55
+ }
56
+ export {
57
+ createMatcher
58
+ };
59
+ //# sourceMappingURL=client-router.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/patterns.ts", "../../src/runtime/client-router.ts"],
4
+ "sourcesContent": ["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 {ComponentType} from \"react\";\nimport {routePattern} from \"../utils/patterns\";\n\nexport interface ClientMatch {\n load: () => Promise<{ default: ComponentType<any> }>\n loadLayouts: Array<() => Promise<{ default: ComponentType<any> }>>\n params: Record<string, string>\n}\n\ntype GlobMap = Record<string, () => Promise<any>>\n\nfunction fileToRoutePattern(filePath: string, pagesPrefix: string): string {\n const rel = filePath.slice(pagesPrefix.length)\n return routePattern(rel)\n}\n\nfunction collectLayoutChain(pageFile: string, layoutFiles: GlobMap): Array<() => Promise<any>> {\n const parts = pageFile.split('/')\n const chain: Array<() => Promise<any>> = []\n\n for (let i = 3; i <= parts.length - 1; i++) {\n const dir = parts.slice(0, i).join('/')\n const lp = `${dir}/layout.tsx`\n const lpts = `${dir}/layout.ts`\n if (layoutFiles[lp]) {\n chain.push(layoutFiles[lp])\n } else if (layoutFiles[lpts]) {\n chain.push(layoutFiles[lpts])\n }\n }\n\n return chain\n}\n\nexport function createMatcher(pageFiles: GlobMap, layoutFiles: GlobMap) {\n const routes = Object.keys(pageFiles)\n .filter(f => !f.split('/').pop()!.startsWith('layout'))\n .map(file => {\n const pagesPrefix = file.slice(0, file.indexOf('/pages/') + '/pages'.length)\n const pattern = fileToRoutePattern(file, pagesPrefix)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n return { file, pattern, params, load: pageFiles[file], loadLayouts: collectLayoutChain(file,\n layoutFiles) }\n })\n .sort((a, b) => {\n const aScore = (a.pattern.match(/:/g) || []).length\n const bScore = (b.pattern.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.pattern.length - a.pattern.length\n })\n\n return function matchClientRoute(pathname: string): ClientMatch | null {\n for (const route of routes) {\n const regexStr = route.pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n const match = pathname.match(new RegExp(`^${regexStr}$`))\n\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 { load: route.load, loadLayouts: route.loadLayouts, params }\n }\n }\n return null\n }\n}"],
5
+ "mappings": ";AAAO,SAAS,aAAa,KAAqB;AAC9C,SAAO,IACE,QAAQ,sBAAsB,EAAE,EAChC,QAAQ,cAAc,EAAE,EACxB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,gBAAgB,KAAK,KAC/B;AACX;;;ACIA,SAAS,mBAAmB,UAAkB,aAA6B;AACvE,QAAM,MAAM,SAAS,MAAM,YAAY,MAAM;AAC7C,SAAO,aAAa,GAAG;AAC3B;AAEA,SAAS,mBAAmB,UAAkB,aAAiD;AAC3F,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,QAAM,QAAmC,CAAC;AAE1C,WAAS,IAAI,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK;AACxC,UAAM,MAAM,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACtC,UAAM,KAAK,GAAG,GAAG;AACjB,UAAM,OAAO,GAAG,GAAG;AACnB,QAAI,YAAY,EAAE,GAAG;AACjB,YAAM,KAAK,YAAY,EAAE,CAAC;AAAA,IAC9B,WAAW,YAAY,IAAI,GAAG;AAC1B,YAAM,KAAK,YAAY,IAAI,CAAC;AAAA,IAChC;AAAA,EACJ;AAEA,SAAO;AACX;AAEO,SAAS,cAAc,WAAoB,aAAsB;AACpE,QAAM,SAAS,OAAO,KAAK,SAAS,EAC/B,OAAO,OAAK,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,EAAG,WAAW,QAAQ,CAAC,EACrD,IAAI,UAAQ;AACT,UAAM,cAAc,KAAK,MAAM,GAAG,KAAK,QAAQ,SAAS,IAAI,SAAS,MAAM;AAC3E,UAAM,UAAU,mBAAmB,MAAM,WAAW;AACpD,UAAM,SAAS,CAAC,GAAG,QAAQ,SAAS,WAAW,CAAC,EAAE,IAAI,OAAK,EAAE,CAAC,CAAC;AAC/D,WAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,UAAU,IAAI,GAAG,aAAa;AAAA,MAAmB;AAAA,MAC/E;AAAA,IAAW,EAAE;AAAA,EACzB,CAAC,EACA,KAAK,CAAC,GAAG,MAAM;AACZ,UAAM,UAAU,EAAE,QAAQ,MAAM,IAAI,KAAK,CAAC,GAAG;AAC7C,UAAM,UAAU,EAAE,QAAQ,MAAM,IAAI,KAAK,CAAC,GAAG;AAC7C,QAAI,WAAW,OAAQ,QAAO,SAAS;AACvC,WAAO,EAAE,QAAQ,SAAS,EAAE,QAAQ;AAAA,EACxC,CAAC;AAEL,SAAO,SAAS,iBAAiB,UAAsC;AACnE,eAAW,SAAS,QAAQ;AACxB,YAAM,WAAW,MAAM,QAClB,QAAQ,WAAW,SAAS,EAC5B,QAAQ,OAAO,KAAK;AACzB,YAAM,QAAQ,SAAS,MAAM,IAAI,OAAO,IAAI,QAAQ,GAAG,CAAC;AAExD,UAAI,OAAO;AACP,cAAM,SAAiC,CAAC;AACxC,cAAM,OAAO,QAAQ,CAAC,MAAM,MAAM;AAC9B,iBAAO,IAAI,IAAI,mBAAmB,MAAM,IAAI,CAAC,CAAC;AAAA,QAClD,CAAC;AACD,eAAO,EAAE,MAAM,MAAM,MAAM,aAAa,MAAM,aAAa,OAAO;AAAA,MACtE;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,27 @@
1
+ import { Context, ComponentType } from "react";
2
+ import { Metadata, Viewport } from "../types";
3
+ import { LayoutProps, PageProps } from "../server/types";
4
+ export interface RouterContextValue {
5
+ pathname: string;
6
+ params: Record<string, string>;
7
+ loaderData: unknown;
8
+ layoutsData: unknown[];
9
+ Page: ComponentType<PageProps>;
10
+ layouts: ComponentType<LayoutProps>[];
11
+ metadata: Metadata | null;
12
+ viewport?: Viewport;
13
+ navigate: (to: string) => void;
14
+ isNavigating: boolean;
15
+ }
16
+ export interface PageMetaContextValue {
17
+ metadata: Metadata | null;
18
+ viewport?: Viewport;
19
+ clientEntry?: string;
20
+ }
21
+ export interface RouteDataContextValue {
22
+ loaderData: unknown;
23
+ params: Record<string, string>;
24
+ }
25
+ export declare const RouterContext: Context<RouterContextValue | null>;
26
+ export declare const PageMetaContext: Context<PageMetaContextValue | null>;
27
+ export declare const RouteDataContext: Context<RouteDataContextValue | null>;
@@ -0,0 +1,15 @@
1
+ // src/runtime/context.tsx
2
+ import { createContext } from "react";
3
+ var g = globalThis;
4
+ g.__devix_RouterContext__ ??= createContext(null);
5
+ var RouterContext = g.__devix_RouterContext__;
6
+ g.__devix_PageMetaContext__ ??= createContext(null);
7
+ g.__devix_RouteDataContext__ ??= createContext(null);
8
+ var PageMetaContext = g.__devix_PageMetaContext__;
9
+ var RouteDataContext = g.__devix_RouteDataContext__;
10
+ export {
11
+ PageMetaContext,
12
+ RouteDataContext,
13
+ RouterContext
14
+ };
15
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/runtime/context.tsx"],
4
+ "sourcesContent": ["import {createContext, Context, ComponentType} from \"react\";\nimport {Metadata, Viewport} from \"../types\";\nimport {LayoutProps, PageProps} from \"../server/types\";\n\nexport interface RouterContextValue {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n navigate: (to: string) => void\n isNavigating: boolean\n}\n\nexport interface PageMetaContextValue {\n metadata: Metadata | null\n viewport?: Viewport\n clientEntry?: string\n}\n\nexport interface RouteDataContextValue {\n loaderData: unknown\n params: Record<string, string>\n}\n\nconst g = globalThis as any\n\ng.__devix_RouterContext__ ??= createContext<RouterContextValue | null>(null)\nexport const RouterContext: Context<RouterContextValue | null> = g.__devix_RouterContext__\n\ng.__devix_PageMetaContext__ ??= createContext<PageMetaContextValue | null>(null)\ng.__devix_RouteDataContext__ ??= createContext<RouteDataContextValue | null>(null)\n\nexport const PageMetaContext: Context<PageMetaContextValue | null> = g.__devix_PageMetaContext__\nexport const RouteDataContext: Context<RouteDataContextValue | null> = g.__devix_RouteDataContext__\n\n"],
5
+ "mappings": ";AAAA,SAAQ,qBAA4C;AA4BpD,IAAM,IAAI;AAEV,EAAE,4BAA4B,cAAyC,IAAI;AACpE,IAAM,gBAAoD,EAAE;AAEnE,EAAE,8BAA8B,cAA2C,IAAI;AAC/E,EAAE,+BAA+B,cAA4C,IAAI;AAE1E,IAAM,kBAAwD,EAAE;AAChE,IAAM,mBAA0D,EAAE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,19 @@
1
+ import { Component, ComponentType, ReactNode } from "react";
2
+ import { ErrorProps } from "../server/types";
3
+ interface Props {
4
+ ErrorPage?: ComponentType<ErrorProps>;
5
+ children: ReactNode;
6
+ }
7
+ interface State {
8
+ error: ErrorProps | null;
9
+ }
10
+ export declare class DevixErrorBoundary extends Component<Props, State> {
11
+ state: State;
12
+ static getDerivedStateFromError(err: unknown): State;
13
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
14
+ }
15
+ export declare class DevixError extends Error {
16
+ statusCode: number;
17
+ constructor(statusCode: number, message: string);
18
+ }
19
+ export {};
@@ -0,0 +1,37 @@
1
+ // src/runtime/error-boundary.tsx
2
+ import { Component } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var DevixErrorBoundary = class extends Component {
5
+ state = { error: null };
6
+ static getDerivedStateFromError(err) {
7
+ if (err instanceof DevixError) {
8
+ return {
9
+ error: { statusCode: err.statusCode, message: err.message }
10
+ };
11
+ }
12
+ return {
13
+ error: { statusCode: 500, message: err instanceof Error ? err.message : "Unknown error" }
14
+ };
15
+ }
16
+ render() {
17
+ if (this.state.error && this.props.ErrorPage) {
18
+ return /* @__PURE__ */ jsx(this.props.ErrorPage, { ...this.state.error });
19
+ }
20
+ if (this.state.error) {
21
+ return /* @__PURE__ */ jsx("h1", { children: this.state.error.statusCode });
22
+ }
23
+ return this.props.children;
24
+ }
25
+ };
26
+ var DevixError = class extends Error {
27
+ statusCode;
28
+ constructor(statusCode, message) {
29
+ super(message);
30
+ this.statusCode = statusCode;
31
+ }
32
+ };
33
+ export {
34
+ DevixError,
35
+ DevixErrorBoundary
36
+ };
37
+ //# sourceMappingURL=error-boundary.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/runtime/error-boundary.tsx"],
4
+ "sourcesContent": ["import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n"],
5
+ "mappings": ";AAAA,SAAQ,iBAA0C;AA4B/B;AAhBZ,IAAM,qBAAN,cAAiC,UAAwB;AAAA,EAC5D,QAAe,EAAE,OAAO,KAAK;AAAA,EAE7B,OAAO,yBAAyB,KAAqB;AACjD,QAAI,eAAe,YAAY;AAC3B,aAAO;AAAA,QACH,OAAO,EAAC,YAAY,IAAI,YAAY,SAAS,IAAI,QAAO;AAAA,MAC5D;AAAA,IACJ;AACA,WAAQ;AAAA,MACJ,OAAO,EAAC,YAAY,KAAK,SAAS,eAAe,QAAQ,IAAI,UAAU,gBAAe;AAAA,IAC1F;AAAA,EACJ;AAAA,EAEA,SAAS;AACL,QAAI,KAAK,MAAM,SAAS,KAAK,MAAM,WAAW;AAC1C,aAAO,oBAAC,KAAK,MAAM,WAAX,EAAsB,GAAG,KAAK,MAAM,OAAO;AAAA,IACvD;AACA,QAAI,KAAK,MAAM,OAAO;AAClB,aAAO,oBAAC,QAAI,eAAK,MAAM,MAAM,YAAW;AAAA,IAC5C;AACA,WAAO,KAAK,MAAM;AAAA,EACtB;AACJ;AAEO,IAAM,aAAN,cAAyB,MAAM;AAAA,EAClC;AAAA,EACA,YAAY,YAAoB,SAAiB;AAC7C,UAAM,OAAO;AACb,SAAK,aAAa;AAAA,EACtB;AACJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,3 @@
1
+ import { Metadata, Viewport } from "../types";
2
+ import { ReactNode } from "react";
3
+ export declare function buildHeadNodes(metadata: Metadata, viewport?: Viewport): ReactNode;
@@ -0,0 +1,69 @@
1
+ // src/runtime/head.tsx
2
+ import { Fragment, jsx } from "react/jsx-runtime";
3
+ function collectTags(metadata, viewport) {
4
+ const tags = [];
5
+ if (metadata.title)
6
+ tags.push({ tag: "title", children: metadata.title });
7
+ if (metadata.description)
8
+ tags.push({ tag: "meta", name: "description", content: metadata.description });
9
+ if (metadata.keywords?.length)
10
+ tags.push({ tag: "meta", name: "keywords", content: metadata.keywords.join(", ") });
11
+ const ogTitle = metadata.og?.title ?? metadata.title;
12
+ if (ogTitle) tags.push({ tag: "meta", property: "og:title", content: ogTitle });
13
+ const ogDesc = metadata.og?.description ?? metadata.description;
14
+ if (ogDesc) tags.push({ tag: "meta", property: "og:description", content: ogDesc });
15
+ if (metadata.og?.image) tags.push({ tag: "meta", property: "og:image", content: metadata.og.image });
16
+ if (metadata.og?.type) tags.push({ tag: "meta", property: "og:type", content: metadata.og.type });
17
+ if (metadata.og?.url) tags.push({ tag: "meta", property: "og:url", content: metadata.og.url });
18
+ const twTitle = metadata.twitter?.title ?? metadata.title;
19
+ if (twTitle) tags.push({ tag: "meta", name: "twitter:title", content: twTitle });
20
+ const twDesc = metadata.twitter?.description ?? metadata.description;
21
+ if (twDesc) tags.push({ tag: "meta", name: "twitter:description", content: twDesc });
22
+ if (metadata.twitter?.card) tags.push({
23
+ tag: "meta",
24
+ name: "twitter:card",
25
+ content: metadata.twitter.card
26
+ });
27
+ if (metadata.twitter?.image) tags.push({
28
+ tag: "meta",
29
+ name: "twitter:image",
30
+ content: metadata.twitter.image
31
+ });
32
+ if (metadata.twitter?.creator) tags.push({
33
+ tag: "meta",
34
+ name: "twitter:creator",
35
+ content: metadata.twitter.creator
36
+ });
37
+ if (metadata.canonical) tags.push({ tag: "link", rel: "canonical", href: metadata.canonical });
38
+ if (metadata.robots) tags.push({ tag: "meta", name: "robots", content: metadata.robots });
39
+ if (metadata.alternates) {
40
+ for (const [lang, href] of Object.entries(metadata.alternates))
41
+ tags.push({ tag: "link", rel: "alternate", href, hrefLang: lang });
42
+ }
43
+ if (viewport) {
44
+ const parts = [];
45
+ if (viewport.width !== void 0) parts.push(`width=${viewport.width}`);
46
+ if (viewport.initialScale !== void 0) parts.push(`initial-scale=${viewport.initialScale}`);
47
+ if (viewport.maximumScale !== void 0) parts.push(`maximum-scale=${viewport.maximumScale}`);
48
+ if (viewport.userScalable !== void 0) parts.push(`user-scalable=${viewport.userScalable ? "yes" : "no"}`);
49
+ if (parts.length) tags.push({ tag: "meta", name: "viewport", content: parts.join(", ") });
50
+ if (viewport.themeColor) tags.push({
51
+ tag: "meta",
52
+ name: "theme-color",
53
+ content: viewport.themeColor
54
+ });
55
+ }
56
+ return tags;
57
+ }
58
+ function buildHeadNodes(metadata, viewport) {
59
+ const tags = collectTags(metadata, viewport);
60
+ return /* @__PURE__ */ jsx(Fragment, { children: tags.map((t, i) => {
61
+ if (t.tag === "title") return /* @__PURE__ */ jsx("title", { children: t.children }, i);
62
+ if (t.tag === "link") return /* @__PURE__ */ jsx("link", { rel: t.rel, href: t.href, hrefLang: t.hrefLang }, i);
63
+ return /* @__PURE__ */ jsx("meta", { name: t.name, property: t.property, content: t.content }, i);
64
+ }) });
65
+ }
66
+ export {
67
+ buildHeadNodes
68
+ };
69
+ //# sourceMappingURL=head.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/runtime/head.tsx"],
4
+ "sourcesContent": ["import {Metadata, Viewport} from \"../types\";\nimport {ReactNode} from \"react\";\n\ntype MetaTag =\n | { tag: 'title'; children: string }\n | { tag: 'meta'; name?: string; property?: string; content: string }\n | { tag: 'link'; rel: string; href: string; hrefLang?: string }\n\nfunction collectTags(metadata: Metadata, viewport?: Viewport): MetaTag[] {\n const tags: MetaTag[] = []\n\n if (metadata.title)\n tags.push({tag: 'title', children: metadata.title})\n if (metadata.description)\n tags.push({tag: 'meta', name: 'description', content: metadata.description})\n if (metadata.keywords?.length)\n tags.push({tag: 'meta', name: 'keywords', content: metadata.keywords.join(', ')})\n\n const ogTitle = metadata.og?.title ?? metadata.title\n if (ogTitle) tags.push({tag: 'meta', property: 'og:title', content: ogTitle})\n const ogDesc = metadata.og?.description ?? metadata.description\n if (ogDesc) tags.push({tag: 'meta', property: 'og:description', content: ogDesc})\n if (metadata.og?.image) tags.push({tag: 'meta', property: 'og:image', content: metadata.og.image})\n if (metadata.og?.type) tags.push({tag: 'meta', property: 'og:type', content: metadata.og.type})\n if (metadata.og?.url) tags.push({tag: 'meta', property: 'og:url', content: metadata.og.url})\n\n const twTitle = metadata.twitter?.title ?? metadata.title\n if (twTitle) tags.push({tag: 'meta', name: 'twitter:title', content: twTitle})\n const twDesc = metadata.twitter?.description ?? metadata.description\n if (twDesc) tags.push({tag: 'meta', name: 'twitter:description', content: twDesc})\n if (metadata.twitter?.card) tags.push({\n tag: 'meta', name: 'twitter:card', content:\n metadata.twitter.card\n })\n if (metadata.twitter?.image) tags.push({\n tag: 'meta', name: 'twitter:image', content:\n metadata.twitter.image\n })\n if (metadata.twitter?.creator) tags.push({\n tag: 'meta', name: 'twitter:creator', content:\n metadata.twitter.creator\n })\n\n if (metadata.canonical) tags.push({tag: 'link', rel: 'canonical', href: metadata.canonical})\n if (metadata.robots) tags.push({tag: 'meta', name: 'robots', content: metadata.robots})\n if (metadata.alternates) {\n for (const [lang, href] of Object.entries(metadata.alternates))\n tags.push({tag: 'link', rel: 'alternate', href, hrefLang: lang})\n }\n\n if (viewport) {\n const parts: string[] = []\n if (viewport.width !== undefined) parts.push(`width=${viewport.width}`)\n if (viewport.initialScale !== undefined) parts.push(`initial-scale=${viewport.initialScale}`)\n if (viewport.maximumScale !== undefined) parts.push(`maximum-scale=${viewport.maximumScale}`)\n if (viewport.userScalable !== undefined) parts.push(`user-scalable=${viewport.userScalable ? 'yes' :\n 'no'}`)\n if (parts.length) tags.push({tag: 'meta', name: 'viewport', content: parts.join(', ')})\n if (viewport.themeColor) tags.push({\n tag: 'meta', name: 'theme-color', content: viewport.themeColor\n })\n }\n\n return tags\n}\n\nexport function buildHeadNodes(metadata: Metadata, viewport?: Viewport): ReactNode {\n const tags = collectTags(metadata, viewport)\n\n return <>\n {tags.map((t, i) => {\n if (t.tag === 'title') return <title key={i}>{t.children}</title>\n if (t.tag === 'link') return <link key={i} rel={t.rel} href={t.href} hrefLang={t.hrefLang}/>\n return <meta key={i} name={t.name} property={t.property} content={t.content}/>\n })}\n </>\n}"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }