@idlesummer/pen 0.1.0 → 0.2.1

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/LICENSE CHANGED
@@ -3,4 +3,19 @@ MIT License
3
3
  Copyright (c) 2025 idlesummer
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation...
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -52,12 +52,6 @@ A lot, honestly:
52
52
  I'll get to these eventually. Or maybe you will! PRs welcome.
53
53
 
54
54
  ## Installation
55
-
56
- ```bash
57
- npm install github:idlesummer/pen
58
- ```
59
-
60
- Once published to npm, it'll be:
61
55
  ```bash
62
56
  npm install @idlesummer/pen
63
57
  ```
package/dist/bin.mjs CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import{f as e,i as t,n,r,t as i}from"./component-map-CLP6MqF8.mjs";import{join as a,resolve as o}from"path";import{pathToFileURL as s}from"url";import{existsSync as c}from"fs";import{Command as l}from"commander";import{duration as u,fileList as d,pipe as f}from"@idlesummer/tasker";import{mkdir as p,writeFile as m}from"fs/promises";import{build as h}from"rolldown";import g from"rollup-plugin-node-externals";var _=Object.create,v=Object.defineProperty,y=Object.getOwnPropertyDescriptor,b=Object.getOwnPropertyNames,x=Object.getPrototypeOf,S=Object.prototype.hasOwnProperty,C=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),w=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=b(t),a=0,o=i.length,s;a<o;a++)s=i[a],!S.call(e,s)&&s!==n&&v(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=y(t,s))||r.enumerable});return e},T=(e,t,n)=>(n=e==null?{}:_(x(e)),w(t||!e||!e.__esModule?v(n,`default`,{value:e,enumerable:!0}):n,e));const E=`@idlesummer/pen`,D=`0.1.0`,O=`@idlesummer/pen`.split(`/`)?.[1]??`@idlesummer/pen`;var k=T(C(((e,t)=>{let n=process||{},r=n.argv||[],i=n.env||{},a=!(i.NO_COLOR||r.includes(`--no-color`))&&(!!i.FORCE_COLOR||r.includes(`--color`)||n.platform===`win32`||(n.stdout||{}).isTTY&&i.TERM!==`dumb`||!!i.CI),o=(e,t,n=e)=>r=>{let i=``+r,a=i.indexOf(t,e.length);return~a?e+s(i,t,n,a)+t:e+i+t},s=(e,t,n,r)=>{let i=``,a=0;do i+=e.substring(a,r)+n,a=r+t.length,r=e.indexOf(t,a);while(~r);return i+e.substring(a)},c=(e=a)=>{let t=e?o:()=>String;return{isColorSupported:e,reset:t(`\x1B[0m`,`\x1B[0m`),bold:t(`\x1B[1m`,`\x1B[22m`,`\x1B[22m\x1B[1m`),dim:t(`\x1B[2m`,`\x1B[22m`,`\x1B[22m\x1B[2m`),italic:t(`\x1B[3m`,`\x1B[23m`),underline:t(`\x1B[4m`,`\x1B[24m`),inverse:t(`\x1B[7m`,`\x1B[27m`),hidden:t(`\x1B[8m`,`\x1B[28m`),strikethrough:t(`\x1B[9m`,`\x1B[29m`),black:t(`\x1B[30m`,`\x1B[39m`),red:t(`\x1B[31m`,`\x1B[39m`),green:t(`\x1B[32m`,`\x1B[39m`),yellow:t(`\x1B[33m`,`\x1B[39m`),blue:t(`\x1B[34m`,`\x1B[39m`),magenta:t(`\x1B[35m`,`\x1B[39m`),cyan:t(`\x1B[36m`,`\x1B[39m`),white:t(`\x1B[37m`,`\x1B[39m`),gray:t(`\x1B[90m`,`\x1B[39m`),bgBlack:t(`\x1B[40m`,`\x1B[49m`),bgRed:t(`\x1B[41m`,`\x1B[49m`),bgGreen:t(`\x1B[42m`,`\x1B[49m`),bgYellow:t(`\x1B[43m`,`\x1B[49m`),bgBlue:t(`\x1B[44m`,`\x1B[49m`),bgMagenta:t(`\x1B[45m`,`\x1B[49m`),bgCyan:t(`\x1B[46m`,`\x1B[49m`),bgWhite:t(`\x1B[47m`,`\x1B[49m`),blackBright:t(`\x1B[90m`,`\x1B[39m`),redBright:t(`\x1B[91m`,`\x1B[39m`),greenBright:t(`\x1B[92m`,`\x1B[39m`),yellowBright:t(`\x1B[93m`,`\x1B[39m`),blueBright:t(`\x1B[94m`,`\x1B[39m`),magentaBright:t(`\x1B[95m`,`\x1B[39m`),cyanBright:t(`\x1B[96m`,`\x1B[39m`),whiteBright:t(`\x1B[97m`,`\x1B[39m`),bgBlackBright:t(`\x1B[100m`,`\x1B[49m`),bgRedBright:t(`\x1B[101m`,`\x1B[49m`),bgGreenBright:t(`\x1B[102m`,`\x1B[49m`),bgYellowBright:t(`\x1B[103m`,`\x1B[49m`),bgBlueBright:t(`\x1B[104m`,`\x1B[49m`),bgMagentaBright:t(`\x1B[105m`,`\x1B[49m`),bgCyanBright:t(`\x1B[106m`,`\x1B[49m`),bgWhiteBright:t(`\x1B[107m`,`\x1B[49m`)}};t.exports=c(),t.exports.createColors=c}))(),1);const A=[{name:`Scanning filesystem...`,onSuccess:(e,t)=>`Scanned filesystem (${u(t)})`,run:async e=>({fileTree:t(e.appDir)})},{name:`Building segment tree...`,onSuccess:(e,t)=>`Built segment tree (${u(t)})`,run:async e=>({segmentTree:r(e.fileTree)})},{name:`Generating manifest`,onSuccess:(e,t)=>`Generated manifest (${u(t)})`,run:async e=>({manifest:n(e.segmentTree)})}],j=[{name:`Writing manifest.ts`,onSuccess:(e,t)=>`Saved manifest.ts (${u(t)})`,run:async e=>{let t=a(e.outDir,`generated`),n=a(t,`manifest.ts`);await p(t,{recursive:!0}),await m(n,[`// Auto-generated by ${E}`,`// Do not manually edit this file`,``,`import { RouteManifest } from '${E}'`,``,`export const manifest: RouteManifest = ${JSON.stringify(e.manifest,null,2)} as const`,``].join(`
3
- `),`utf-8`)}},{name:`Writing components.ts`,onSuccess:(e,t)=>`Saved components.ts (${u(t)})`,run:async e=>{let t=a(e.outDir,`generated`),n=a(t,`components.ts`);await p(t,{recursive:!0});let r=i(e.manifest,e.outDir),o=Object.entries(r).sort(([e],[t])=>e.localeCompare(t)),s=o.map(([e,t],n)=>`import Component${n} from '${t}'`).join(`
4
- `),c=o.map(([e],t)=>` '${e.replace(/\\/g,`\\\\`)}': Component${t},`).join(`
5
- `);await m(n,[`// Auto-generated by ${E}`,`// Do not manually edit this file`,``,`import { ComponentMap } from '${E}'`,``,s,``,`export const components: ComponentMap = {`,c,`} as const`,``].join(`
6
- `),`utf-8`)}},{name:`Writing entry.ts`,onSuccess:(e,t)=>`Saved entry.ts (${u(t)})`,run:async e=>{let t=a(e.outDir,`generated`),n=a(t,`entry.ts`);await p(t,{recursive:!0}),await m(n,[`// Auto-generated by ${E}`,`// Do not manually edit this file`,``,`import { createElement } from 'react'`,`import { render } from 'ink'`,`import { App } from '${E}'`,``,`import { manifest } from './manifest'`,`import { components } from './components'`,``,`export async function run(initialUrl: string) {`,` const element = createElement(App, { initialUrl, manifest, components })`,` const { waitUntilExit } = render(element)`,` await waitUntilExit()`,`}`,``].join(`
7
- `),`utf-8`)}}],M={name:`Compiling application`,onSuccess:(e,t)=>`Compiled application (${u(t)})`,onError:e=>`Compilation failed: ${e.message}`,run:async e=>{await h({input:o(a(e.outDir,`generated`,`entry.ts`)),platform:`node`,resolve:{extensions:[`.ts`,`.tsx`,`.js`,`.jsx`]},plugins:[g()],output:{dir:o(a(e.outDir,`dist`)),format:`esm`,sourcemap:!0,minify:!0}})}},N={name:`build`,desc:`Build the route manifest and compile application`,action:async()=>{try{let{appDir:t,outDir:n}=await e();console.log(k.default.cyan(` Starting production build...
8
- `)),console.log(k.default.bold(` ✎ ${O} v${D}\n`)),console.log(k.default.dim(` entry: ${t}`)),console.log(k.default.dim(` target: node24`)),console.log(k.default.dim(` output: ${n}`)),console.log();let{duration:r}=await f([...A,...j,M]).run({appDir:t,outDir:n});console.log(),console.log(d(n,`**/*`)),console.log(),console.log(`${k.default.green(`✓`)} Built in ${k.default.bold(u(r))}`),console.log()}catch(e){console.error(`${k.default.red(`✗`)} Build failed`),console.log();let t=e instanceof Error?e.message:String(e);console.error(k.default.red(t)),console.log(),process.exit(1)}}},P={name:`init`,desc:`Initialize a new Pen project`,action:async()=>{if(console.log(k.default.cyan(`\n Initializing ${O} project...\n`)),c(`pen.config.ts`)){console.error(k.default.yellow(`⚠`)+` Project already initialized`),console.error(k.default.dim(` pen.config.ts already exists`));return}await m(`pen.config.ts`,[`import { defineConfig } from '${E}'`,``,`export default defineConfig({`,` appDir: './src/app',`,` outDir: './.pen',`,`})`,``].join(`
9
- `),`utf-8`),console.log(k.default.green(`✓`)+` Created pen.config.ts`);let e=`./src/app`;await p(e,{recursive:!0});let t=[`import { Box, Text } from 'ink'`,`import type { ReactNode } from 'react'`,``,`export default function Layout({ children }: { children?: ReactNode }) {`,` return (`,` <Box flexDirection="column" padding={1}>`,` <Box marginBottom={1} borderStyle="round" borderColor="cyan" paddingX={2}>`,` <Text bold color="cyan">Welcome to Pen</Text>`,` </Box>`,` {children}`,` </Box>`,` )`,`}`,``].join(`
10
- `);await m(a(e,`layout.tsx`),t,`utf-8`),console.log(k.default.green(`✓`)+` Created src/app/layout.tsx`);let n=[`import { useState } from 'react'`,`import { Box, Text, useInput } from 'ink'`,``,`export default function Screen() {`,` const [count, setCount] = useState(0)`,``,` useInput((input) => {`,` if (input === ' ') setCount(c => c + 1)`,` })`,``,` return (`,` <Box flexDirection="column" gap={1}>`,` <Box>`,` <Text>Count: <Text bold color="green">{count}</Text></Text>`,` </Box>`,` <Box>`,` <Text dimColor>Press <Text bold>SPACE</Text> to increment</Text>`,` </Box>`,` </Box>`,` )`,`}`,``].join(`
11
- `);await m(a(e,`screen.tsx`),n,`utf-8`),console.log(k.default.green(`✓`)+` Created src/app/screen.tsx`),console.log(),console.log(k.default.green(`✓`)+` Project initialized!`),console.log(),console.log(k.default.bold(` Next steps:`)),console.log(),console.log(k.default.dim(` 1. Add scripts to package.json:`)),console.log(),console.log(` {`),console.log(` "scripts": {`),console.log(` "build": "${O} build",`),console.log(` "start": "${O} start"`),console.log(` }`),console.log(` }`),console.log(),console.log(k.default.dim(` 2. Run "npm run build" to build your app`)),console.log(k.default.dim(` 3. Run "npm run start" to start your app`)),console.log()}},F={name:`start`,desc:`Start the application`,action:async()=>{let{outDir:t}=await e(),n=o(t,`dist`,`entry.js`);if(!c(n))throw console.error(k.default.red(`✗`)+` Build not found`),console.error(k.default.dim(` Run "${O} build" first`)),Error(`Build not found`);let{run:r}=await import(s(n).href);await r(`/`)}};function I(e){e.name(O).description(`File-based routing for React Ink apps (experimental)`).version(D);let t=[N,P,F];for(let{name:n,desc:r,action:i}of t)e.command(n).description(r).action(i);return e}async function L(e){let t=I(new l);try{return await t.parseAsync(e),0}catch(e){let t=e instanceof Error?e.message:`Unknown error`;return console.error(t),1}}process.exit(await L(process.argv));export{};
2
+ import{a as e,f as t,i as n,n as r,r as i,t as a}from"./element-tree-DoH7UgRL.mjs";import{join as o,resolve as s}from"path";import{pathToFileURL as c}from"url";import{existsSync as l}from"fs";import{Command as u}from"commander";import{duration as d,fileList as f,pipe as p}from"@idlesummer/tasker";import{mkdir as m,writeFile as h}from"fs/promises";import{build as g}from"rolldown";import _ from"rollup-plugin-node-externals";var v=Object.create,y=Object.defineProperty,b=Object.getOwnPropertyDescriptor,x=Object.getOwnPropertyNames,S=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty,w=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),T=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=x(t),a=0,o=i.length,s;a<o;a++)s=i[a],!C.call(e,s)&&s!==n&&y(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=b(t,s))||r.enumerable});return e},E=(e,t,n)=>(n=e==null?{}:v(S(e)),T(t||!e||!e.__esModule?y(n,`default`,{value:e,enumerable:!0}):n,e));const D=`@idlesummer/pen`,O=`0.2.1`,k=`@idlesummer/pen`.split(`/`)?.[1]??`@idlesummer/pen`;var A=E(w(((e,t)=>{let n=process||{},r=n.argv||[],i=n.env||{},a=!(i.NO_COLOR||r.includes(`--no-color`))&&(!!i.FORCE_COLOR||r.includes(`--color`)||n.platform===`win32`||(n.stdout||{}).isTTY&&i.TERM!==`dumb`||!!i.CI),o=(e,t,n=e)=>r=>{let i=``+r,a=i.indexOf(t,e.length);return~a?e+s(i,t,n,a)+t:e+i+t},s=(e,t,n,r)=>{let i=``,a=0;do i+=e.substring(a,r)+n,a=r+t.length,r=e.indexOf(t,a);while(~r);return i+e.substring(a)},c=(e=a)=>{let t=e?o:()=>String;return{isColorSupported:e,reset:t(`\x1B[0m`,`\x1B[0m`),bold:t(`\x1B[1m`,`\x1B[22m`,`\x1B[22m\x1B[1m`),dim:t(`\x1B[2m`,`\x1B[22m`,`\x1B[22m\x1B[2m`),italic:t(`\x1B[3m`,`\x1B[23m`),underline:t(`\x1B[4m`,`\x1B[24m`),inverse:t(`\x1B[7m`,`\x1B[27m`),hidden:t(`\x1B[8m`,`\x1B[28m`),strikethrough:t(`\x1B[9m`,`\x1B[29m`),black:t(`\x1B[30m`,`\x1B[39m`),red:t(`\x1B[31m`,`\x1B[39m`),green:t(`\x1B[32m`,`\x1B[39m`),yellow:t(`\x1B[33m`,`\x1B[39m`),blue:t(`\x1B[34m`,`\x1B[39m`),magenta:t(`\x1B[35m`,`\x1B[39m`),cyan:t(`\x1B[36m`,`\x1B[39m`),white:t(`\x1B[37m`,`\x1B[39m`),gray:t(`\x1B[90m`,`\x1B[39m`),bgBlack:t(`\x1B[40m`,`\x1B[49m`),bgRed:t(`\x1B[41m`,`\x1B[49m`),bgGreen:t(`\x1B[42m`,`\x1B[49m`),bgYellow:t(`\x1B[43m`,`\x1B[49m`),bgBlue:t(`\x1B[44m`,`\x1B[49m`),bgMagenta:t(`\x1B[45m`,`\x1B[49m`),bgCyan:t(`\x1B[46m`,`\x1B[49m`),bgWhite:t(`\x1B[47m`,`\x1B[49m`),blackBright:t(`\x1B[90m`,`\x1B[39m`),redBright:t(`\x1B[91m`,`\x1B[39m`),greenBright:t(`\x1B[92m`,`\x1B[39m`),yellowBright:t(`\x1B[93m`,`\x1B[39m`),blueBright:t(`\x1B[94m`,`\x1B[39m`),magentaBright:t(`\x1B[95m`,`\x1B[39m`),cyanBright:t(`\x1B[96m`,`\x1B[39m`),whiteBright:t(`\x1B[97m`,`\x1B[39m`),bgBlackBright:t(`\x1B[100m`,`\x1B[49m`),bgRedBright:t(`\x1B[101m`,`\x1B[49m`),bgGreenBright:t(`\x1B[102m`,`\x1B[49m`),bgYellowBright:t(`\x1B[103m`,`\x1B[49m`),bgBlueBright:t(`\x1B[104m`,`\x1B[49m`),bgMagentaBright:t(`\x1B[105m`,`\x1B[49m`),bgCyanBright:t(`\x1B[106m`,`\x1B[49m`),bgWhiteBright:t(`\x1B[107m`,`\x1B[49m`)}};t.exports=c(),t.exports.createColors=c}))(),1);const j={name:`Building file tree`,onSuccess:(e,t)=>`Built file tree (${d(t)})`,run:async t=>({fileTree:e(t.appDir)})},M={name:`Building segment tree...`,onSuccess:(e,t)=>`Built segment tree (${d(t)})`,run:async e=>({segmentTree:n(e.fileTree)})},N={name:`Building route manifest`,onSuccess:(e,t)=>`Built route table (${d(t)})`,run:async e=>({manifest:i(e.segmentTree,e.outDir)})},P={name:`Building component map`,onSuccess:(e,t)=>`Built component map (${d(t)})`,run:async e=>({componentMap:r(e.manifest)})},F={name:`Building element trees`,onSuccess:(e,t)=>`Built element trees (${d(t)})`,run:async e=>({elementTrees:a(e.manifest,e.componentMap)})},I={name:`Writing route-table.ts`,onSuccess:(e,t)=>`Saved route-table.ts (${d(t)})`,run:async e=>{let t=o(e.outDir,`generated`),n=o(t,`route-table.ts`),r=[`// Auto-generated by ${D}`,`// Do not manually edit this file`,`// NOTE: This file is kept for documentation/debugging.`,`// Runtime uses pre-built routes from routes.ts instead.`,``,`import type { RouteTable } from '${D}'`,``,`export const manifest: RouteTable = ${JSON.stringify(e.manifest,null,2)} as const`,``].join(`
3
+ `);await m(t,{recursive:!0}),await h(n,r,`utf-8`)}},L={name:`Writing element-tree.ts`,onSuccess:(e,t)=>`Saved element-tree.ts (${d(t)})`,run:async e=>{let t=o(e.outDir,`generated`),n=o(t,`element-tree.ts`),r=[`// Auto-generated by ${D}`,`// Do not manually edit this file`,`// NOTE: This file is kept for documentation/debugging.`,``,`import type { ElementTreeMap } from '${D}'`,``,`export const elementTrees: ElementTreeMap = ${JSON.stringify(e.elementTrees,null,2)} as const`,``].join(`
4
+ `);await m(t,{recursive:!0}),await h(n,r,`utf-8`)}},R={name:`Writing component-map.ts`,onSuccess:(e,t)=>`Saved component-map.ts (${d(t)})`,run:async e=>{let t=o(e.outDir,`generated`),n=o(t,`component-map.ts`),r=e.componentMap,i=[`// Auto-generated by ${D}`,`// Do not manually edit this file`,``,`import type { ComponentMap } from '${D}'`,``,`export const componentMap: ComponentMap = ${JSON.stringify(r,null,2)} as const`,``].join(`
5
+ `);await m(t,{recursive:!0}),await h(n,i,`utf-8`)}},z={name:`Writing compiled-routes.ts`,onSuccess:(e,t)=>`Saved compiled-routes.ts (${d(t)})`,run:async e=>{let t=o(e.outDir,`generated`),n=o(t,`compiled-routes.ts`),r=e.elementTrees,i=e.componentMap,a=Object.keys(i).map((e,t)=>`import Component${t} from '${e}'`).join(`
6
+ `),s=[];for(let[e,t]of Object.entries(r)){let n=` ${B(t).replace(/\n/g,`
7
+ `)}`;s.push(` '${e}':\n${n},`)}let c=[`// Auto-generated by ${D}`,`// Do not manually edit this file`,``,`import type { CompiledRoutes } from '${D}'`,`import { createElement } from 'react'`,``,a,``,`// Compiled route elements generated at build time`,`export const compiledRoutes: CompiledRoutes = {`,s.join(`
8
+ `),`} as const`,``].join(`
9
+ `);await m(t,{recursive:!0}),await h(n,c,`utf-8`)}};function B(e,t=0){let n=` `.repeat(t),r=Object.entries(e.props).map(([e,t])=>`${e}: ${t}`).join(`, `);if(!e.children)return`createElement(${e.component}, { ${r} })`;let i=B(e.children,t+1);return`createElement(${e.component}, { ${r} },\n${n} ${i}\n${n})`}const V={name:`Writing entry.ts`,onSuccess:(e,t)=>`Saved entry.ts (${d(t)})`,run:async e=>{let t=o(e.outDir,`generated`),n=o(t,`entry.ts`),r=[`// Auto-generated by ${D}`,`// Do not manually edit this file`,``,`import { createElement } from 'react'`,`import { render } from 'ink'`,`import { App } from '${D}'`,`import { compiledRoutes as routes } from './compiled-routes'`,``,`export async function run(initialUrl: string) {`,` const element = createElement(App, { initialUrl, routes })`,` const { waitUntilExit } = render(element)`,` await waitUntilExit()`,`}`,``].join(`
10
+ `);await m(t,{recursive:!0}),await h(n,r,`utf-8`)}},H={name:`Compiling application`,onSuccess:(e,t)=>`Compiled application (${d(t)})`,onError:e=>`Compilation failed: ${e.message}`,run:async e=>{await g({input:s(o(e.outDir,`generated`,`entry.ts`)),platform:`node`,resolve:{extensions:[`.ts`,`.tsx`,`.js`,`.jsx`]},plugins:[_()],output:{dir:s(o(e.outDir,`dist`)),format:`esm`,sourcemap:!0,minify:!0}})}},U={name:`build`,desc:`Build the route manifest and compile application`,action:async()=>{try{let{appDir:e,outDir:n,emitMetadata:r}=await t();console.log(A.default.cyan(` Starting production build...
11
+ `)),console.log(A.default.bold(` ✎ ${k} v${O}\n`)),console.log(A.default.dim(` entry: ${e}`)),console.log(A.default.dim(` target: node24`)),console.log(A.default.dim(` output: ${n}`)),console.log();let{duration:i}=await p([j,M,N,P,F,r&&I,r&&L,r&&R,z,V,H]).run({appDir:e,outDir:n});console.log(),console.log(f(n,`**/*`)),console.log(),console.log(`${A.default.green(`✓`)} Built in ${A.default.bold(d(i))}`),console.log()}catch(e){console.error(`${A.default.red(`✗`)} Build failed`),console.log();let t=e instanceof Error?e.message:String(e);console.error(A.default.red(t)),console.log(),process.exit(1)}}},W={name:`init`,desc:`Initialize a new Pen project`,action:async()=>{if(console.log(A.default.cyan(`\n Initializing ${k} project...\n`)),l(`pen.config.ts`)){console.error(A.default.yellow(`⚠`)+` Project already initialized`),console.error(A.default.dim(` pen.config.ts already exists`));return}await h(`pen.config.ts`,[`import { defineConfig } from '${D}'`,``,`export default defineConfig({`,` appDir: './src/app',`,` outDir: './.pen',`,`})`,``].join(`
12
+ `),`utf-8`),console.log(A.default.green(`✓`)+` Created pen.config.ts`);let e=`./src/app`;await m(e,{recursive:!0});let t=[`import { Box, Text } from 'ink'`,`import type { ReactNode } from 'react'`,``,`export default function Layout({ children }: { children?: ReactNode }) {`,` return (`,` <Box flexDirection="column" padding={1}>`,` <Box marginBottom={1} borderStyle="round" borderColor="cyan" paddingX={2}>`,` <Text bold color="cyan">Welcome to Pen</Text>`,` </Box>`,` {children}`,` </Box>`,` )`,`}`,``].join(`
13
+ `);await h(o(e,`layout.tsx`),t,`utf-8`),console.log(A.default.green(`✓`)+` Created src/app/layout.tsx`);let n=[`import { useState } from 'react'`,`import { Box, Text, useInput } from 'ink'`,``,`export default function Screen() {`,` const [count, setCount] = useState(0)`,``,` useInput((input) => {`,` if (input === ' ') setCount(c => c + 1)`,` })`,``,` return (`,` <Box flexDirection="column" gap={1}>`,` <Box>`,` <Text>Count: <Text bold color="green">{count}</Text></Text>`,` </Box>`,` <Box>`,` <Text dimColor>Press <Text bold>SPACE</Text> to increment</Text>`,` </Box>`,` </Box>`,` )`,`}`,``].join(`
14
+ `);await h(o(e,`screen.tsx`),n,`utf-8`),console.log(A.default.green(`✓`)+` Created src/app/screen.tsx`),console.log(),console.log(A.default.green(`✓`)+` Project initialized!`),console.log(),console.log(A.default.bold(` Next steps:`)),console.log(),console.log(A.default.dim(` 1. Add scripts to package.json:`)),console.log(),console.log(` {`),console.log(` "scripts": {`),console.log(` "build": "${k} build",`),console.log(` "start": "${k} start"`),console.log(` }`),console.log(` }`),console.log(),console.log(A.default.dim(` 2. Run "npm run build" to build your app`)),console.log(A.default.dim(` 3. Run "npm run start" to start your app`)),console.log()}},G={name:`start`,desc:`Start the application`,action:async()=>{let{outDir:e}=await t(),n=s(e,`dist`,`entry.js`);if(!l(n))throw console.error(A.default.red(`✗`)+` Build not found`),console.error(A.default.dim(` Run "${k} build" first`)),Error(`Build not found`);let{run:r}=await import(c(n).href);await r(`/`)}};function K(e){e.name(k).description(`File-based routing for React Ink apps (experimental)`).version(O);let t=[U,W,G];for(let{name:n,desc:r,action:i}of t)e.command(n).description(r).action(i);return e}async function q(e){let t=K(new u);try{return await t.parseAsync(e),0}catch(e){let t=e instanceof Error?e.message:`Unknown error`;return console.error(t),1}}process.exit(await q(process.argv));export{};
12
15
  //# sourceMappingURL=bin.mjs.map
package/dist/bin.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.mjs","names":["rolldownBuild","build","pc","pc","pc","run","build"],"sources":["../src/core/constants.ts","../node_modules/picocolors/picocolors.js","../src/cli/commands/build/tasks/scan.ts","../src/cli/commands/build/tasks/generate.ts","../src/cli/commands/build/tasks/compile.ts","../src/cli/commands/build/index.ts","../src/cli/commands/init/index.ts","../src/cli/commands/start/index.ts","../src/cli/index.ts","../src/bin.ts"],"sourcesContent":["// Build-time globals injected by tsdown via `define`.\r\n// tsdown replaces these placeholders with actual values\r\n// from package.json at compile time.\r\n\r\ndeclare const __DESCRIPTION__: string\r\ndeclare const __PACKAGE_NAME__: string\r\ndeclare const __VERSION__: string\r\n\r\n// Package metadata\r\nexport const DESCRIPTION = __DESCRIPTION__\r\nexport const PACKAGE_NAME = __PACKAGE_NAME__\r\nexport const VERSION = __VERSION__\r\n\r\n// Framework metadata\r\nexport const CLI_NAME = __PACKAGE_NAME__.split('/')?.[1] ?? __PACKAGE_NAME__\r\n","let p = process || {}, argv = p.argv || [], env = p.env || {}\nlet isColorSupported =\n\t!(!!env.NO_COLOR || argv.includes(\"--no-color\")) &&\n\t(!!env.FORCE_COLOR || argv.includes(\"--color\") || p.platform === \"win32\" || ((p.stdout || {}).isTTY && env.TERM !== \"dumb\") || !!env.CI)\n\nlet formatter = (open, close, replace = open) =>\n\tinput => {\n\t\tlet string = \"\" + input, index = string.indexOf(close, open.length)\n\t\treturn ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close\n\t}\n\nlet replaceClose = (string, close, replace, index) => {\n\tlet result = \"\", cursor = 0\n\tdo {\n\t\tresult += string.substring(cursor, index) + replace\n\t\tcursor = index + close.length\n\t\tindex = string.indexOf(close, cursor)\n\t} while (~index)\n\treturn result + string.substring(cursor)\n}\n\nlet createColors = (enabled = isColorSupported) => {\n\tlet f = enabled ? formatter : () => String\n\treturn {\n\t\tisColorSupported: enabled,\n\t\treset: f(\"\\x1b[0m\", \"\\x1b[0m\"),\n\t\tbold: f(\"\\x1b[1m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[1m\"),\n\t\tdim: f(\"\\x1b[2m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[2m\"),\n\t\titalic: f(\"\\x1b[3m\", \"\\x1b[23m\"),\n\t\tunderline: f(\"\\x1b[4m\", \"\\x1b[24m\"),\n\t\tinverse: f(\"\\x1b[7m\", \"\\x1b[27m\"),\n\t\thidden: f(\"\\x1b[8m\", \"\\x1b[28m\"),\n\t\tstrikethrough: f(\"\\x1b[9m\", \"\\x1b[29m\"),\n\n\t\tblack: f(\"\\x1b[30m\", \"\\x1b[39m\"),\n\t\tred: f(\"\\x1b[31m\", \"\\x1b[39m\"),\n\t\tgreen: f(\"\\x1b[32m\", \"\\x1b[39m\"),\n\t\tyellow: f(\"\\x1b[33m\", \"\\x1b[39m\"),\n\t\tblue: f(\"\\x1b[34m\", \"\\x1b[39m\"),\n\t\tmagenta: f(\"\\x1b[35m\", \"\\x1b[39m\"),\n\t\tcyan: f(\"\\x1b[36m\", \"\\x1b[39m\"),\n\t\twhite: f(\"\\x1b[37m\", \"\\x1b[39m\"),\n\t\tgray: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\n\t\tbgBlack: f(\"\\x1b[40m\", \"\\x1b[49m\"),\n\t\tbgRed: f(\"\\x1b[41m\", \"\\x1b[49m\"),\n\t\tbgGreen: f(\"\\x1b[42m\", \"\\x1b[49m\"),\n\t\tbgYellow: f(\"\\x1b[43m\", \"\\x1b[49m\"),\n\t\tbgBlue: f(\"\\x1b[44m\", \"\\x1b[49m\"),\n\t\tbgMagenta: f(\"\\x1b[45m\", \"\\x1b[49m\"),\n\t\tbgCyan: f(\"\\x1b[46m\", \"\\x1b[49m\"),\n\t\tbgWhite: f(\"\\x1b[47m\", \"\\x1b[49m\"),\n\n\t\tblackBright: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\t\tredBright: f(\"\\x1b[91m\", \"\\x1b[39m\"),\n\t\tgreenBright: f(\"\\x1b[92m\", \"\\x1b[39m\"),\n\t\tyellowBright: f(\"\\x1b[93m\", \"\\x1b[39m\"),\n\t\tblueBright: f(\"\\x1b[94m\", \"\\x1b[39m\"),\n\t\tmagentaBright: f(\"\\x1b[95m\", \"\\x1b[39m\"),\n\t\tcyanBright: f(\"\\x1b[96m\", \"\\x1b[39m\"),\n\t\twhiteBright: f(\"\\x1b[97m\", \"\\x1b[39m\"),\n\n\t\tbgBlackBright: f(\"\\x1b[100m\", \"\\x1b[49m\"),\n\t\tbgRedBright: f(\"\\x1b[101m\", \"\\x1b[49m\"),\n\t\tbgGreenBright: f(\"\\x1b[102m\", \"\\x1b[49m\"),\n\t\tbgYellowBright: f(\"\\x1b[103m\", \"\\x1b[49m\"),\n\t\tbgBlueBright: f(\"\\x1b[104m\", \"\\x1b[49m\"),\n\t\tbgMagentaBright: f(\"\\x1b[105m\", \"\\x1b[49m\"),\n\t\tbgCyanBright: f(\"\\x1b[106m\", \"\\x1b[49m\"),\n\t\tbgWhiteBright: f(\"\\x1b[107m\", \"\\x1b[49m\"),\n\t}\n}\n\nmodule.exports = createColors()\nmodule.exports.createColors = createColors\n","import { duration } from '@idlesummer/tasker'\r\nimport { buildFileTree } from '@/core/route-builder'\r\nimport { buildSegmentTree } from '@/core/route-builder'\r\nimport { buildRouteManifest } from '@/core/route-builder'\r\nimport type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\n\r\nexport const scanTasks: Task<BuildContext>[] = [\r\n {\r\n name: 'Scanning filesystem...',\r\n onSuccess: (_, dur) => `Scanned filesystem (${duration(dur)})`,\r\n run: async (ctx) => ({ fileTree: buildFileTree(ctx.appDir) }),\r\n },\r\n {\r\n name: 'Building segment tree...',\r\n onSuccess: (_, dur) => `Built segment tree (${duration(dur)})`,\r\n run: async (ctx) => ({ segmentTree: buildSegmentTree(ctx.fileTree!) }),\r\n },\r\n {\r\n name: 'Generating manifest',\r\n onSuccess: (_, dur) => `Generated manifest (${duration(dur)})`,\r\n run: async (ctx) => {\r\n // await delay(500) // Simulate work\r\n const manifest = buildRouteManifest(ctx.segmentTree!) // Safe: set by previous task\r\n return { manifest }\r\n },\r\n },\r\n]\r\n","import { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { PACKAGE_NAME } from '@/core/constants'\r\nimport { buildComponentMap } from '@/core/route-builder'\r\nimport type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\n\r\nexport const generateTasks: Task<BuildContext>[] = [\r\n {\r\n name: 'Writing manifest.ts',\r\n onSuccess: (_, dur) => `Saved manifest.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const manifestPath = join(genDir, 'manifest.ts')\r\n await mkdir(genDir, { recursive: true })\r\n\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '',\r\n `import { RouteManifest } from '${PACKAGE_NAME}'`,\r\n '',\r\n `export const manifest: RouteManifest = ${JSON.stringify(ctx.manifest!, null, 2)} as const`,\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(manifestPath, code, 'utf-8')\r\n },\r\n },\r\n {\r\n name: 'Writing components.ts',\r\n onSuccess: (_, dur) => `Saved components.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const componentsPath = join(genDir, 'components.ts')\r\n await mkdir(genDir, { recursive: true })\r\n\r\n const componentMap = buildComponentMap(ctx.manifest!, ctx.outDir)\r\n const entries = Object.entries(componentMap).sort(([a], [b]) => a.localeCompare(b))\r\n\r\n const imports = entries\r\n .map(([_, importPath], i) => `import Component${i} from '${importPath}'`)\r\n .join('\\n')\r\n\r\n const exports = entries\r\n .map(([absPath], i) => ` '${absPath.replace(/\\\\/g, '\\\\\\\\')}': Component${i},`)\r\n .join('\\n')\r\n\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '',\r\n `import { ComponentMap } from '${PACKAGE_NAME}'`,\r\n '',\r\n imports,\r\n '',\r\n 'export const components: ComponentMap = {',\r\n exports,\r\n '} as const',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(componentsPath, code, 'utf-8')\r\n },\r\n },\r\n {\r\n name: 'Writing entry.ts',\r\n onSuccess: (_, dur) => `Saved entry.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const entryPath = join(genDir, 'entry.ts')\r\n await mkdir(genDir, { recursive: true })\r\n\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '',\r\n 'import { createElement } from \\'react\\'',\r\n 'import { render } from \\'ink\\'',\r\n `import { App } from '${PACKAGE_NAME}'`,\r\n '',\r\n 'import { manifest } from \\'./manifest\\'',\r\n 'import { components } from \\'./components\\'',\r\n '',\r\n 'export async function run(initialUrl: string) {',\r\n ' const element = createElement(App, { initialUrl, manifest, components })',\r\n ' const { waitUntilExit } = render(element)',\r\n ' await waitUntilExit()',\r\n '}',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(entryPath, code, 'utf-8')\r\n },\r\n },\r\n]\r\n","import { join, resolve } from 'path'\r\nimport { build as rolldownBuild } from 'rolldown'\r\nimport nodeExternals from 'rollup-plugin-node-externals'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\n\r\nexport const compileTask: Task<BuildContext> = {\r\n name: 'Compiling application',\r\n onSuccess: (_, dur) => `Compiled application (${duration(dur)})`,\r\n onError: (err) => `Compilation failed: ${err.message}`,\r\n run: async (ctx) => {\r\n const entryPoint = resolve(join(ctx.outDir, 'generated', 'entry.ts'))\r\n\r\n await rolldownBuild({\r\n input: entryPoint,\r\n platform: 'node',\r\n resolve: {\r\n extensions: ['.ts', '.tsx', '.js', '.jsx'],\r\n },\r\n plugins: [nodeExternals()],\r\n output: {\r\n dir: resolve(join(ctx.outDir, 'dist')), // Also resolve output\r\n format: 'esm',\r\n sourcemap: true,\r\n minify: true,\r\n },\r\n })\r\n },\r\n}\r\n","import pc from 'picocolors'\r\nimport { pipe, duration, fileList } from '@idlesummer/tasker'\r\nimport { loadConfig } from '@/core/config'\r\nimport { CLI_NAME, VERSION } from '@/core/constants'\r\nimport { scanTasks } from './tasks/scan'\r\nimport { generateTasks } from './tasks/generate'\r\nimport { compileTask } from './tasks/compile'\r\nimport type { CLICommand } from '../../types'\r\n\r\nexport const build: CLICommand = {\r\n name: 'build',\r\n desc: 'Build the route manifest and compile application',\r\n action: async () => {\r\n try {\r\n const { appDir, outDir } = await loadConfig()\r\n console.log(pc.cyan(' Starting production build...\\n'))\r\n console.log(pc.bold(` ✎ ${CLI_NAME} v${VERSION}\\n`))\r\n console.log(pc.dim( ` entry: ${appDir}`))\r\n console.log(pc.dim( ' target: node24'))\r\n console.log(pc.dim( ` output: ${outDir}`))\r\n console.log()\r\n\r\n const tasks = [...scanTasks, ...generateTasks, compileTask]\r\n const pipeline = pipe(tasks)\r\n\r\n const { duration: dur } = await pipeline.run({ appDir, outDir })\r\n console.log()\r\n console.log(fileList(outDir, '**/*'))\r\n console.log()\r\n console.log(`${pc.green('✓')} Built in ${pc.bold(duration(dur))}`)\r\n console.log()\r\n }\r\n\r\n catch (err) {\r\n console.error(`${pc.red('✗')} Build failed`)\r\n console.log()\r\n\r\n const message = err instanceof Error ? err.message : String(err)\r\n console.error(pc.red(message))\r\n console.log()\r\n process.exit(1)\r\n }\r\n },\r\n}\r\n","import { existsSync } from 'fs'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport pc from 'picocolors'\r\n\r\nimport { CLI_NAME, PACKAGE_NAME } from '@/core/constants'\r\nimport type { CLICommand } from '../../types'\r\n\r\nexport const init: CLICommand = {\r\n name: 'init',\r\n desc: 'Initialize a new Pen project',\r\n action: async () => {\r\n console.log(pc.cyan(`\\n Initializing ${CLI_NAME} project...\\n`))\r\n\r\n // Check if already initialized\r\n if (existsSync('pen.config.ts')) {\r\n console.error(pc.yellow('⚠') + ' Project already initialized')\r\n console.error(pc.dim(' pen.config.ts already exists'))\r\n return\r\n }\r\n\r\n // Create pen.config.ts\r\n const configContent = [\r\n `import { defineConfig } from '${PACKAGE_NAME}'`,\r\n '',\r\n 'export default defineConfig({',\r\n ' appDir: \\'./src/app\\',',\r\n ' outDir: \\'./.pen\\',',\r\n '})',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile('pen.config.ts', configContent, 'utf-8')\r\n console.log(pc.green('✓') + ' Created pen.config.ts')\r\n\r\n // Create src/app directory\r\n const appDir = './src/app'\r\n await mkdir(appDir, { recursive: true })\r\n\r\n // Create layout.tsx\r\n const layoutContent = [\r\n 'import { Box, Text } from \\'ink\\'',\r\n 'import type { ReactNode } from \\'react\\'',\r\n '',\r\n 'export default function Layout({ children }: { children?: ReactNode }) {',\r\n ' return (',\r\n ' <Box flexDirection=\"column\" padding={1}>',\r\n ' <Box marginBottom={1} borderStyle=\"round\" borderColor=\"cyan\" paddingX={2}>',\r\n ' <Text bold color=\"cyan\">Welcome to Pen</Text>',\r\n ' </Box>',\r\n ' {children}',\r\n ' </Box>',\r\n ' )',\r\n '}',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(join(appDir, 'layout.tsx'), layoutContent, 'utf-8')\r\n console.log(pc.green('✓') + ' Created src/app/layout.tsx')\r\n\r\n // Create screen.tsx\r\n const screenContent = [\r\n 'import { useState } from \\'react\\'',\r\n 'import { Box, Text, useInput } from \\'ink\\'',\r\n '',\r\n 'export default function Screen() {',\r\n ' const [count, setCount] = useState(0)',\r\n '',\r\n ' useInput((input) => {',\r\n ' if (input === \\' \\') setCount(c => c + 1)',\r\n ' })',\r\n '',\r\n ' return (',\r\n ' <Box flexDirection=\"column\" gap={1}>',\r\n ' <Box>',\r\n ' <Text>Count: <Text bold color=\"green\">{count}</Text></Text>',\r\n ' </Box>',\r\n ' <Box>',\r\n ' <Text dimColor>Press <Text bold>SPACE</Text> to increment</Text>',\r\n ' </Box>',\r\n ' </Box>',\r\n ' )',\r\n '}',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(join(appDir, 'screen.tsx'), screenContent, 'utf-8')\r\n console.log(pc.green('✓') + ' Created src/app/screen.tsx')\r\n\r\n // Success message with instructions\r\n console.log()\r\n console.log(pc.green('✓') + ' Project initialized!')\r\n console.log()\r\n console.log(pc.bold(' Next steps:'))\r\n console.log()\r\n console.log(pc.dim(' 1. Add scripts to package.json:'))\r\n console.log()\r\n console.log(' {')\r\n console.log(' \"scripts\": {')\r\n console.log(` \"build\": \"${CLI_NAME} build\",`)\r\n console.log(` \"start\": \"${CLI_NAME} start\"`)\r\n console.log(' }')\r\n console.log(' }')\r\n console.log()\r\n console.log(pc.dim(' 2. Run \"npm run build\" to build your app'))\r\n console.log(pc.dim(' 3. Run \"npm run start\" to start your app'))\r\n console.log()\r\n },\r\n}\r\n","import { existsSync } from 'fs'\r\nimport { resolve } from 'path'\r\nimport { pathToFileURL } from 'url'\r\nimport pc from 'picocolors'\r\n\r\nimport { loadConfig } from '@/core/config'\r\nimport { CLI_NAME } from '@/core/constants'\r\nimport type { CLICommand } from '../../types'\r\n\r\nexport const start: CLICommand = {\r\n name: 'start',\r\n desc: 'Start the application',\r\n action: async () => {\r\n const { outDir } = await loadConfig()\r\n const initialUrl = '/'\r\n const entryPath = resolve(outDir, 'dist', 'entry.js')\r\n\r\n // Check if build exists\r\n if (!existsSync(entryPath)) {\r\n console.error(pc.red('✗') + ' Build not found')\r\n console.error(pc.dim(` Run \"${CLI_NAME} build\" first`))\r\n throw new Error('Build not found') // Let Commander handle exit\r\n }\r\n\r\n // Import and run the bundled entry\r\n const entryUrl = pathToFileURL(entryPath).href\r\n const { run } = await import(entryUrl)\r\n\r\n // Run with initial URL\r\n await run(initialUrl)\r\n },\r\n}\r\n","import { Command } from 'commander'\r\nimport { CLI_NAME, DESCRIPTION, VERSION } from '@/core/constants'\r\nimport { build } from './commands/build'\r\nimport { init } from './commands/init'\r\nimport { start } from './commands/start'\r\n\r\nfunction configureProgram(program: Command) {\r\n program\r\n .name(CLI_NAME)\r\n .description(DESCRIPTION)\r\n .version(VERSION)\r\n\r\n const commands = [build, init, start] as const\r\n for (const { name, desc, action } of commands) {\r\n program\r\n .command(name)\r\n .description(desc)\r\n .action(action)\r\n }\r\n return program\r\n}\r\n\r\nexport async function run(argv: string[]) {\r\n const command = new Command()\r\n const program = configureProgram(command)\r\n\r\n try {\r\n await program.parseAsync(argv)\r\n return 0\r\n }\r\n catch (err) {\r\n const message = err instanceof Error ? err.message : 'Unknown error'\r\n console.error(message)\r\n return 1\r\n }\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { run } from './cli'\r\n\r\nprocess.exit(await run(process.argv))\r\n"],"x_google_ignoreList":[1],"mappings":"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"}
1
+ {"version":3,"file":"bin.mjs","names":["rolldownBuild","build","pc","pc","pc","run","build"],"sources":["../src/pen/constants.ts","../node_modules/picocolors/picocolors.js","../src/cli/commands/build/tasks/build-file-tree.ts","../src/cli/commands/build/tasks/build-segment-tree.ts","../src/cli/commands/build/tasks/build-route-table.ts","../src/cli/commands/build/tasks/build-component-map.ts","../src/cli/commands/build/tasks/build-element-tree.ts","../src/cli/commands/build/tasks/write-route-table-file.ts","../src/cli/commands/build/tasks/write-element-tree-file.ts","../src/cli/commands/build/tasks/write-component-map-file.ts","../src/cli/commands/build/tasks/write-compiled-routes-file.ts","../src/cli/commands/build/tasks/write-entry-file.ts","../src/cli/commands/build/tasks/compile-application.ts","../src/cli/commands/build/index.ts","../src/cli/commands/init/index.ts","../src/cli/commands/start/index.ts","../src/cli/index.ts","../src/bin.ts"],"sourcesContent":["// Build-time globals injected by tsdown via `define`.\r\n// tsdown replaces these placeholders with actual values\r\n// from package.json at compile time.\r\n\r\ndeclare const __DESCRIPTION__: string\r\ndeclare const __PACKAGE_NAME__: string\r\ndeclare const __VERSION__: string\r\n\r\n// Package metadata\r\nexport const DESCRIPTION = __DESCRIPTION__\r\nexport const PACKAGE_NAME = __PACKAGE_NAME__\r\nexport const VERSION = __VERSION__\r\n\r\n// Framework metadata\r\nexport const CLI_NAME = __PACKAGE_NAME__.split('/')?.[1] ?? __PACKAGE_NAME__\r\n","let p = process || {}, argv = p.argv || [], env = p.env || {}\nlet isColorSupported =\n\t!(!!env.NO_COLOR || argv.includes(\"--no-color\")) &&\n\t(!!env.FORCE_COLOR || argv.includes(\"--color\") || p.platform === \"win32\" || ((p.stdout || {}).isTTY && env.TERM !== \"dumb\") || !!env.CI)\n\nlet formatter = (open, close, replace = open) =>\n\tinput => {\n\t\tlet string = \"\" + input, index = string.indexOf(close, open.length)\n\t\treturn ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close\n\t}\n\nlet replaceClose = (string, close, replace, index) => {\n\tlet result = \"\", cursor = 0\n\tdo {\n\t\tresult += string.substring(cursor, index) + replace\n\t\tcursor = index + close.length\n\t\tindex = string.indexOf(close, cursor)\n\t} while (~index)\n\treturn result + string.substring(cursor)\n}\n\nlet createColors = (enabled = isColorSupported) => {\n\tlet f = enabled ? formatter : () => String\n\treturn {\n\t\tisColorSupported: enabled,\n\t\treset: f(\"\\x1b[0m\", \"\\x1b[0m\"),\n\t\tbold: f(\"\\x1b[1m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[1m\"),\n\t\tdim: f(\"\\x1b[2m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[2m\"),\n\t\titalic: f(\"\\x1b[3m\", \"\\x1b[23m\"),\n\t\tunderline: f(\"\\x1b[4m\", \"\\x1b[24m\"),\n\t\tinverse: f(\"\\x1b[7m\", \"\\x1b[27m\"),\n\t\thidden: f(\"\\x1b[8m\", \"\\x1b[28m\"),\n\t\tstrikethrough: f(\"\\x1b[9m\", \"\\x1b[29m\"),\n\n\t\tblack: f(\"\\x1b[30m\", \"\\x1b[39m\"),\n\t\tred: f(\"\\x1b[31m\", \"\\x1b[39m\"),\n\t\tgreen: f(\"\\x1b[32m\", \"\\x1b[39m\"),\n\t\tyellow: f(\"\\x1b[33m\", \"\\x1b[39m\"),\n\t\tblue: f(\"\\x1b[34m\", \"\\x1b[39m\"),\n\t\tmagenta: f(\"\\x1b[35m\", \"\\x1b[39m\"),\n\t\tcyan: f(\"\\x1b[36m\", \"\\x1b[39m\"),\n\t\twhite: f(\"\\x1b[37m\", \"\\x1b[39m\"),\n\t\tgray: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\n\t\tbgBlack: f(\"\\x1b[40m\", \"\\x1b[49m\"),\n\t\tbgRed: f(\"\\x1b[41m\", \"\\x1b[49m\"),\n\t\tbgGreen: f(\"\\x1b[42m\", \"\\x1b[49m\"),\n\t\tbgYellow: f(\"\\x1b[43m\", \"\\x1b[49m\"),\n\t\tbgBlue: f(\"\\x1b[44m\", \"\\x1b[49m\"),\n\t\tbgMagenta: f(\"\\x1b[45m\", \"\\x1b[49m\"),\n\t\tbgCyan: f(\"\\x1b[46m\", \"\\x1b[49m\"),\n\t\tbgWhite: f(\"\\x1b[47m\", \"\\x1b[49m\"),\n\n\t\tblackBright: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\t\tredBright: f(\"\\x1b[91m\", \"\\x1b[39m\"),\n\t\tgreenBright: f(\"\\x1b[92m\", \"\\x1b[39m\"),\n\t\tyellowBright: f(\"\\x1b[93m\", \"\\x1b[39m\"),\n\t\tblueBright: f(\"\\x1b[94m\", \"\\x1b[39m\"),\n\t\tmagentaBright: f(\"\\x1b[95m\", \"\\x1b[39m\"),\n\t\tcyanBright: f(\"\\x1b[96m\", \"\\x1b[39m\"),\n\t\twhiteBright: f(\"\\x1b[97m\", \"\\x1b[39m\"),\n\n\t\tbgBlackBright: f(\"\\x1b[100m\", \"\\x1b[49m\"),\n\t\tbgRedBright: f(\"\\x1b[101m\", \"\\x1b[49m\"),\n\t\tbgGreenBright: f(\"\\x1b[102m\", \"\\x1b[49m\"),\n\t\tbgYellowBright: f(\"\\x1b[103m\", \"\\x1b[49m\"),\n\t\tbgBlueBright: f(\"\\x1b[104m\", \"\\x1b[49m\"),\n\t\tbgMagentaBright: f(\"\\x1b[105m\", \"\\x1b[49m\"),\n\t\tbgCyanBright: f(\"\\x1b[106m\", \"\\x1b[49m\"),\n\t\tbgWhiteBright: f(\"\\x1b[107m\", \"\\x1b[49m\"),\n\t}\n}\n\nmodule.exports = createColors()\nmodule.exports.createColors = createColors\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { createFileTree } from '@/pen/compiler'\r\n\r\n// ===== Main Task =====\r\n\r\nexport const buildFileTree: Task<BuildContext> = {\r\n name: 'Building file tree',\r\n onSuccess: (_, dur) => `Built file tree (${duration(dur)})`,\r\n run: async (ctx) => ({\r\n fileTree: createFileTree(ctx.appDir),\r\n }),\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { createSegmentTree } from '@/pen/compiler'\r\n\r\n// ===== Main Task =====\r\n\r\nexport const buildSegmentTree: Task<BuildContext> = {\r\n name: 'Building segment tree...',\r\n onSuccess: (_, dur) => `Built segment tree (${duration(dur)})`,\r\n run: async (ctx) => ({\r\n segmentTree: createSegmentTree(ctx.fileTree!),\r\n }),\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { createRouteTable } from '@/pen/compiler'\r\n\r\n// ===== Main Task =====\r\n\r\nexport const buildRouteTable: Task<BuildContext> = {\r\n name: 'Building route manifest',\r\n onSuccess: (_, dur) => `Built route table (${duration(dur)})`,\r\n run: async (ctx) => ({\r\n manifest: createRouteTable(ctx.segmentTree!, ctx.outDir),\r\n }),\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { createComponentMap } from '@/pen/compiler'\r\n\r\nexport const buildComponentMap: Task<BuildContext> = {\r\n name: 'Building component map',\r\n onSuccess: (_, dur) => `Built component map (${duration(dur)})`,\r\n run: async (ctx) => ({\r\n componentMap: createComponentMap(ctx.manifest!),\r\n }),\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { createElementTrees } from '@/pen/compiler'\r\n\r\nexport const buildElementTree: Task<BuildContext> = {\r\n name: 'Building element trees',\r\n onSuccess: (_, dur) => `Built element trees (${duration(dur)})`,\r\n run: async (ctx) => ({\r\n elementTrees: createElementTrees(ctx.manifest!, ctx.componentMap!),\r\n }),\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { PACKAGE_NAME } from '@/pen/constants'\r\n\r\nexport const writeRouteTableFile: Task<BuildContext> = {\r\n name: 'Writing route-table.ts',\r\n onSuccess: (_, dur) => `Saved route-table.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const outDir = join(genDir, 'route-table.ts')\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '// NOTE: This file is kept for documentation/debugging.',\r\n '// Runtime uses pre-built routes from routes.ts instead.',\r\n '',\r\n `import type { RouteTable } from '${PACKAGE_NAME}'`,\r\n '',\r\n `export const manifest: RouteTable = ${JSON.stringify(ctx.manifest!, null, 2)} as const`,\r\n '',\r\n ].join('\\n')\r\n\r\n await mkdir(genDir, { recursive: true })\r\n await writeFile(outDir, code, 'utf-8')\r\n },\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { PACKAGE_NAME } from '@/pen/constants'\r\n\r\n// ===== Main Task =====\r\n\r\nexport const writeElementTreeFile: Task<BuildContext> = {\r\n name: 'Writing element-tree.ts',\r\n onSuccess: (_, dur) => `Saved element-tree.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const outDir = join(genDir, 'element-tree.ts')\r\n\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '// NOTE: This file is kept for documentation/debugging.',\r\n '',\r\n `import type { ElementTreeMap } from '${PACKAGE_NAME}'`,\r\n '',\r\n `export const elementTrees: ElementTreeMap = ${JSON.stringify(ctx.elementTrees!, null, 2)} as const`,\r\n '',\r\n ].join('\\n')\r\n\r\n await mkdir(genDir, { recursive: true })\r\n await writeFile(outDir, code, 'utf-8')\r\n },\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { PACKAGE_NAME } from '@/pen/constants'\r\n\r\nexport const writeComponentMapFile: Task<BuildContext> = {\r\n name: 'Writing component-map.ts',\r\n onSuccess: (_, dur) => `Saved component-map.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const outDir = join(genDir, 'component-map.ts')\r\n\r\n const componentMap = ctx.componentMap!\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '',\r\n `import type { ComponentMap } from '${PACKAGE_NAME}'`,\r\n '',\r\n `export const componentMap: ComponentMap = ${JSON.stringify(componentMap, null, 2)} as const`,\r\n '',\r\n ].join('\\n')\r\n\r\n await mkdir(genDir, { recursive: true })\r\n await writeFile(outDir, code, 'utf-8')\r\n },\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { ElementTree } from '@/pen/compiler'\r\nimport type { BuildContext } from '../types'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { PACKAGE_NAME } from '@/pen/constants'\r\n\r\nexport const writeCompiledRoutesFile: Task<BuildContext> = {\r\n name: 'Writing compiled-routes.ts',\r\n onSuccess: (_, dur) => `Saved compiled-routes.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const outDir = join(genDir, 'compiled-routes.ts')\r\n const elementTrees = ctx.elementTrees!\r\n const componentMap = ctx.componentMap!\r\n\r\n // Get sorted imports from component map\r\n const sortedImports = Object.keys(componentMap)\r\n\r\n // Generate component imports\r\n const importStatements = sortedImports\r\n .map((importPath, i) => `import Component${i} from '${importPath}'`)\r\n .join('\\n')\r\n\r\n // Generate pre-built route elements from element trees\r\n const routeElements: string[] = []\r\n for (const [url, tree] of Object.entries(elementTrees)) {\r\n const elementCode = ` ${serialize(tree).replace(/\\n/g, '\\n ')}`\r\n routeElements.push(` '${url}':\\n${elementCode},`)\r\n }\r\n\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '',\r\n `import type { CompiledRoutes } from '${PACKAGE_NAME}'`,\r\n 'import { createElement } from \\'react\\'',\r\n '',\r\n importStatements,\r\n '',\r\n '// Compiled route elements generated at build time',\r\n 'export const compiledRoutes: CompiledRoutes = {',\r\n routeElements.join('\\n'),\r\n '} as const',\r\n '',\r\n ].join('\\n')\r\n\r\n await mkdir(genDir, { recursive: true })\r\n await writeFile(outDir, code, 'utf-8')\r\n },\r\n}\r\n\r\n/** Serializes an ElementTree to React.createElement() code string. */\r\nexport function serialize(tree: ElementTree, indent = 0): string {\r\n const spaces = ' '.repeat(indent)\r\n\r\n // Props are already pre-serialized (strings have quotes, identifiers don't)\r\n const props = Object.entries(tree.props)\r\n .map(([key, value]) => `${key}: ${value}`)\r\n .join(', ')\r\n\r\n if (!tree.children)\r\n return `createElement(${tree.component}, { ${props} })`\r\n\r\n const childCode = serialize(tree.children, indent + 1)\r\n return `createElement(${tree.component}, { ${props} },\\n${spaces} ${childCode}\\n${spaces})`\r\n}\r\n","import type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport { PACKAGE_NAME } from '@/pen/constants'\r\n\r\n// ===== Main Task =====\r\n\r\nexport const writeEntryFile: Task<BuildContext> = {\r\n name: 'Writing entry.ts',\r\n onSuccess: (_, dur) => `Saved entry.ts (${duration(dur)})`,\r\n run: async (ctx) => {\r\n const genDir = join(ctx.outDir, 'generated')\r\n const outDir = join(genDir, 'entry.ts')\r\n const code = [\r\n `// Auto-generated by ${PACKAGE_NAME}`,\r\n '// Do not manually edit this file',\r\n '',\r\n 'import { createElement } from \\'react\\'',\r\n 'import { render } from \\'ink\\'',\r\n `import { App } from '${PACKAGE_NAME}'`,\r\n 'import { compiledRoutes as routes } from \\'./compiled-routes\\'',\r\n '',\r\n 'export async function run(initialUrl: string) {',\r\n ' const element = createElement(App, { initialUrl, routes })',\r\n ' const { waitUntilExit } = render(element)',\r\n ' await waitUntilExit()',\r\n '}',\r\n '',\r\n ].join('\\n')\r\n\r\n await mkdir(genDir, { recursive: true })\r\n await writeFile(outDir, code, 'utf-8')\r\n },\r\n}\r\n","import { join, resolve } from 'path'\r\nimport { build as rolldownBuild } from 'rolldown'\r\nimport nodeExternals from 'rollup-plugin-node-externals'\r\nimport { duration } from '@idlesummer/tasker'\r\nimport type { Task } from '@idlesummer/tasker'\r\nimport type { BuildContext } from '../types'\r\n\r\nexport const compileApplication: Task<BuildContext> = {\r\n name: 'Compiling application',\r\n onSuccess: (_, dur) => `Compiled application (${duration(dur)})`,\r\n onError: (err) => `Compilation failed: ${err.message}`,\r\n run: async (ctx) => {\r\n await rolldownBuild({\r\n input: resolve(join(ctx.outDir, 'generated', 'entry.ts')), // entry point\r\n platform: 'node',\r\n resolve: {\r\n extensions: ['.ts', '.tsx', '.js', '.jsx'],\r\n },\r\n plugins: [nodeExternals()],\r\n output: {\r\n dir: resolve(join(ctx.outDir, 'dist')),\r\n format: 'esm',\r\n sourcemap: true,\r\n minify: true,\r\n },\r\n })\r\n },\r\n}\r\n","import type { CLICommand } from '../../types'\r\nimport pc from 'picocolors'\r\nimport { pipe, duration, fileList } from '@idlesummer/tasker'\r\nimport { loadConfig } from '@/pen/config'\r\nimport { CLI_NAME, VERSION } from '@/pen/constants'\r\n\r\n// Import individual tasks\r\nimport { buildFileTree } from './tasks/build-file-tree'\r\nimport { buildSegmentTree } from './tasks/build-segment-tree'\r\nimport { buildRouteTable } from './tasks/build-route-table'\r\nimport { buildComponentMap } from './tasks/build-component-map'\r\nimport { buildElementTree } from './tasks/build-element-tree'\r\nimport { writeRouteTableFile } from './tasks/write-route-table-file'\r\nimport { writeElementTreeFile } from './tasks/write-element-tree-file'\r\nimport { writeComponentMapFile } from './tasks/write-component-map-file'\r\nimport { writeCompiledRoutesFile } from './tasks/write-compiled-routes-file'\r\nimport { writeEntryFile } from './tasks/write-entry-file'\r\nimport { compileApplication } from './tasks/compile-application'\r\n\r\nexport const build: CLICommand = {\r\n name: 'build',\r\n desc: 'Build the route manifest and compile application',\r\n action: async () => {\r\n try {\r\n const { appDir, outDir, emitMetadata } = await loadConfig()\r\n console.log(pc.cyan(' Starting production build...\\n'))\r\n console.log(pc.bold(` ✎ ${CLI_NAME} v${VERSION}\\n`))\r\n console.log(pc.dim( ` entry: ${appDir}`))\r\n console.log(pc.dim( ' target: node24'))\r\n console.log(pc.dim( ` output: ${outDir}`))\r\n console.log()\r\n\r\n const pipeline = pipe([\r\n buildFileTree,\r\n buildSegmentTree,\r\n buildRouteTable,\r\n buildComponentMap,\r\n buildElementTree,\r\n\r\n // Conditionally add metadata file generation tasks\r\n emitMetadata && writeRouteTableFile,\r\n emitMetadata && writeElementTreeFile,\r\n emitMetadata && writeComponentMapFile,\r\n\r\n writeCompiledRoutesFile,\r\n writeEntryFile,\r\n compileApplication,\r\n ])\r\n\r\n const { duration: dur } = await pipeline.run({ appDir, outDir })\r\n console.log()\r\n console.log(fileList(outDir, '**/*'))\r\n console.log()\r\n console.log(`${pc.green('✓')} Built in ${pc.bold(duration(dur))}`)\r\n console.log()\r\n }\r\n\r\n catch (err) {\r\n console.error(`${pc.red('✗')} Build failed`)\r\n console.log()\r\n\r\n const message = err instanceof Error ? err.message : String(err)\r\n console.error(pc.red(message))\r\n console.log()\r\n process.exit(1)\r\n }\r\n },\r\n}\r\n","import { existsSync } from 'fs'\r\nimport { mkdir, writeFile } from 'fs/promises'\r\nimport { join } from 'path'\r\nimport pc from 'picocolors'\r\n\r\nimport { CLI_NAME, PACKAGE_NAME } from '@/pen/constants'\r\nimport type { CLICommand } from '../../types'\r\n\r\nexport const init: CLICommand = {\r\n name: 'init',\r\n desc: 'Initialize a new Pen project',\r\n action: async () => {\r\n console.log(pc.cyan(`\\n Initializing ${CLI_NAME} project...\\n`))\r\n\r\n // Check if already initialized\r\n if (existsSync('pen.config.ts')) {\r\n console.error(pc.yellow('⚠') + ' Project already initialized')\r\n console.error(pc.dim(' pen.config.ts already exists'))\r\n return\r\n }\r\n\r\n // Create pen.config.ts\r\n const configContent = [\r\n `import { defineConfig } from '${PACKAGE_NAME}'`,\r\n '',\r\n 'export default defineConfig({',\r\n ' appDir: \\'./src/app\\',',\r\n ' outDir: \\'./.pen\\',',\r\n '})',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile('pen.config.ts', configContent, 'utf-8')\r\n console.log(pc.green('✓') + ' Created pen.config.ts')\r\n\r\n // Create src/app directory\r\n const appDir = './src/app'\r\n await mkdir(appDir, { recursive: true })\r\n\r\n // Create layout.tsx\r\n const layoutContent = [\r\n 'import { Box, Text } from \\'ink\\'',\r\n 'import type { ReactNode } from \\'react\\'',\r\n '',\r\n 'export default function Layout({ children }: { children?: ReactNode }) {',\r\n ' return (',\r\n ' <Box flexDirection=\"column\" padding={1}>',\r\n ' <Box marginBottom={1} borderStyle=\"round\" borderColor=\"cyan\" paddingX={2}>',\r\n ' <Text bold color=\"cyan\">Welcome to Pen</Text>',\r\n ' </Box>',\r\n ' {children}',\r\n ' </Box>',\r\n ' )',\r\n '}',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(join(appDir, 'layout.tsx'), layoutContent, 'utf-8')\r\n console.log(pc.green('✓') + ' Created src/app/layout.tsx')\r\n\r\n // Create screen.tsx\r\n const screenContent = [\r\n 'import { useState } from \\'react\\'',\r\n 'import { Box, Text, useInput } from \\'ink\\'',\r\n '',\r\n 'export default function Screen() {',\r\n ' const [count, setCount] = useState(0)',\r\n '',\r\n ' useInput((input) => {',\r\n ' if (input === \\' \\') setCount(c => c + 1)',\r\n ' })',\r\n '',\r\n ' return (',\r\n ' <Box flexDirection=\"column\" gap={1}>',\r\n ' <Box>',\r\n ' <Text>Count: <Text bold color=\"green\">{count}</Text></Text>',\r\n ' </Box>',\r\n ' <Box>',\r\n ' <Text dimColor>Press <Text bold>SPACE</Text> to increment</Text>',\r\n ' </Box>',\r\n ' </Box>',\r\n ' )',\r\n '}',\r\n '',\r\n ].join('\\n')\r\n\r\n await writeFile(join(appDir, 'screen.tsx'), screenContent, 'utf-8')\r\n console.log(pc.green('✓') + ' Created src/app/screen.tsx')\r\n\r\n // Success message with instructions\r\n console.log()\r\n console.log(pc.green('✓') + ' Project initialized!')\r\n console.log()\r\n console.log(pc.bold(' Next steps:'))\r\n console.log()\r\n console.log(pc.dim(' 1. Add scripts to package.json:'))\r\n console.log()\r\n console.log(' {')\r\n console.log(' \"scripts\": {')\r\n console.log(` \"build\": \"${CLI_NAME} build\",`)\r\n console.log(` \"start\": \"${CLI_NAME} start\"`)\r\n console.log(' }')\r\n console.log(' }')\r\n console.log()\r\n console.log(pc.dim(' 2. Run \"npm run build\" to build your app'))\r\n console.log(pc.dim(' 3. Run \"npm run start\" to start your app'))\r\n console.log()\r\n },\r\n}\r\n","import { existsSync } from 'fs'\r\nimport { resolve } from 'path'\r\nimport { pathToFileURL } from 'url'\r\nimport pc from 'picocolors'\r\n\r\nimport { loadConfig } from '@/pen/config'\r\nimport { CLI_NAME } from '@/pen/constants'\r\nimport type { CLICommand } from '../../types'\r\n\r\nexport const start: CLICommand = {\r\n name: 'start',\r\n desc: 'Start the application',\r\n action: async () => {\r\n const { outDir } = await loadConfig()\r\n const initialUrl = '/'\r\n const entryPath = resolve(outDir, 'dist', 'entry.js')\r\n\r\n // Check if build exists\r\n if (!existsSync(entryPath)) {\r\n console.error(pc.red('✗') + ' Build not found')\r\n console.error(pc.dim(` Run \"${CLI_NAME} build\" first`))\r\n throw new Error('Build not found') // Let Commander handle exit\r\n }\r\n\r\n // Import and run the bundled entry\r\n const entryUrl = pathToFileURL(entryPath).href\r\n const { run } = await import(entryUrl)\r\n\r\n // Run with initial URL\r\n await run(initialUrl)\r\n },\r\n}\r\n","import { Command } from 'commander'\r\nimport { CLI_NAME, DESCRIPTION, VERSION } from '@/pen/constants'\r\nimport { build } from './commands/build'\r\nimport { init } from './commands/init'\r\nimport { start } from './commands/start'\r\n\r\nfunction configureProgram(program: Command) {\r\n program\r\n .name(CLI_NAME)\r\n .description(DESCRIPTION)\r\n .version(VERSION)\r\n\r\n const commands = [build, init, start] as const\r\n for (const { name, desc, action } of commands) {\r\n program\r\n .command(name)\r\n .description(desc)\r\n .action(action)\r\n }\r\n return program\r\n}\r\n\r\nexport async function run(argv: string[]) {\r\n const command = new Command()\r\n const program = configureProgram(command)\r\n\r\n try {\r\n await program.parseAsync(argv)\r\n return 0\r\n }\r\n catch (err) {\r\n const message = err instanceof Error ? err.message : 'Unknown error'\r\n console.error(message)\r\n return 1\r\n }\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { run } from './cli'\r\n\r\nprocess.exit(await run(process.argv))\r\n"],"x_google_ignoreList":[1],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import{join as e,parse as t,posix as n,relative as r,resolve as i}from"path";import{pathToFileURL as a}from"url";import{readdirSync as o,statSync as s}from"fs";function c(e){return e}const l={appDir:`./src/app`,outDir:`./.pen`,emitMetadata:!1};async function u(){try{let e=(await import(a(i(process.cwd(),`pen.config.ts`)).href)).default||{};return{...l,...e}}catch{return l}}function d(e,t){let{visit:n,expand:r,attach:i}=t,a=[e];for(;a.length;){let e=a.pop();n?.(e);let t=r?.(e)??[];for(let n=t.length-1;n>=0;n--){let r=t[n];i?.(r,e),a.push(r)}}}function f(e,t){let{visit:n,parent:r}=t,i=e;for(;i;)n(i),i=r(i)}var p=class extends Error{constructor(e){super(e),this.name=`FileRouterError`}},m=class extends p{constructor(e){super(`Directory not found: "${e}"`),this.path=e,this.name=`DirectoryNotFoundError`}},h=class extends p{constructor(e){super(`Path is not a directory: "${e}"`),this.path=e,this.name=`NotADirectoryError`}},g=class extends p{constructor(e){super(`Root path is a file, not a directory: "${e}"\n\nThe app directory must be a directory, not a file.`),this.path=e,this.name=`RootIsFileError`}},_=class extends p{constructor(e,t){super(`Conflicting screen routes found at "${e}":\n`+t.map(e=>` - ${e}`).join(`
2
+ `)+`
3
+
4
+ Each URL can only have one screen file.
5
+ Move one screen to a different directory or rename the route segment.`),this.url=e,this.files=t,this.name=`DuplicateScreenError`}};function v(e){let t=i(e);y(t);let n={name:`app`,relPath:`/app`,absPath:t,children:[]};return d(n,{attach:(e,t)=>t.children.push(e),expand:e=>e.children?o(e.absPath,{withFileTypes:!0}).filter(e=>e.isFile()||e.isDirectory()).map(t=>b(t,e)).sort((e,t)=>e.name.localeCompare(t.name)):[]}),n}function y(e){let t=s(e,{throwIfNoEntry:!1});if(!t)throw new m(e);if(!t.isDirectory())throw new h(e)}function b(t,r){let i=n.join(r.relPath,t.name),a=e(r.absPath,t.name);return t.isDirectory()?{name:t.name,relPath:i,absPath:a,children:[]}:{name:t.name,relPath:i,absPath:a}}const x=[`layout`,`screen`,`error`,`not-found`];function S(e){let t=C(e);return w(t),t}function C(e){if(e.children===void 0)throw new g(e.absPath);let t={segment:``,url:`/`,type:`page`,roles:{},children:[],file:e};return d(t,{attach:(e,t)=>t.children.push(e),expand:e=>(e.file.children??[]).filter(e=>e.children&&!e.name.startsWith(`_`)).map(t=>T(t,e)).sort((e,t)=>e.segment.localeCompare(t.segment))}),t}function w(e){let t={};d(e,{expand:e=>e.children??[],visit:e=>{E(e),D(e,t)}})}function T(e,t){let r=e.name.startsWith(`(`)&&e.name.endsWith(`)`);return{segment:e.name,url:r?t.url:`${n.join(t.url,e.name)}/`,type:r?`group`:`page`,roles:{},parent:t,children:[],file:e}}function E(e){for(let n of e.file.children??[]){let{name:r,ext:i}=t(n.name);i===`.tsx`&&x.includes(r)&&(e.roles[r]=n.absPath)}}function D(e,t){if(!e.roles.screen)return;let n=t[e.url];if(n)throw new _(e.url,[n,e.file.absPath]);t[e.url]=e.file.absPath}function O(n){let r=t(n);return e(r.dir,r.name)}function k(e,t){let n={},r=`${t}/generated`;return d(e,{expand:e=>e.children??[],visit:e=>{if(!e.roles.screen)return;let t=e.url;n[t]={url:t,chain:A(e,r)}}}),n}function A(e,t){let n=[];return f(e,{parent:e=>e.parent,visit:i=>{let a={},o=Object.entries(i.roles);for(let[n,s]of o)(n!==`screen`||i===e)&&(a[n]=`${r(t,O(s)).replace(/\\/g,`/`)}.js`);n.push(a)}}),n}function j(e){let t=new Set;for(let n of Object.values(e))for(let e of n.chain)for(let n of x)e[n]&&t.add(e[n]);let n=Array.from(t).sort(),r={};for(let e=0;e<n.length;e++)r[n[e]]=e;return r}function M(e,t){let n={};for(let[r,i]of Object.entries(e))n[r]=N(i,t);return n}function N(e,t){let n=Object.keys(t),r=t[e.chain[0].screen],i=JSON.stringify(n[r]),a={component:`Component${r}`,props:{key:i}};for(let r of e.chain){if(r[`not-found`]){let e=t[r[`not-found`]];a={component:`NotFoundBoundary`,props:{key:JSON.stringify(n[e]),fallback:`Component${e}`},children:a}}if(r.error){let e=t[r.error];a={component:`ErrorBoundary`,props:{key:JSON.stringify(n[e]),fallback:`Component${e}`},children:a}}if(r.layout){let e=t[r.layout],i=JSON.stringify(n[e]);a={component:`Component${e}`,props:{key:i},children:a}}}return a}export{v as a,p as c,c as d,u as f,S as i,h as l,j as n,m as o,k as r,_ as s,M as t,g as u};
6
+ //# sourceMappingURL=element-tree-DoH7UgRL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-tree-DoH7UgRL.mjs","names":[],"sources":["../src/pen/config.ts","../src/lib/tree.ts","../src/pen/compiler/errors.ts","../src/pen/compiler/builders/file-tree.ts","../src/pen/compiler/builders/segment-tree.ts","../src/lib/path-utils.ts","../src/pen/compiler/builders/route-manifest.ts","../src/pen/compiler/builders/component-map.ts","../src/pen/compiler/builders/element-tree.ts"],"sourcesContent":["import { resolve } from 'path'\r\nimport { pathToFileURL } from 'url'\r\n\r\n/**\r\n * Resolved configuration with all fields guaranteed to be present.\r\n * This is what loadConfig() returns after merging user config with defaults.\r\n */\r\nexport type ResolvedPenConfig = {\r\n /**\r\n * Directory containing your app routes.\r\n * @default './src/app'\r\n */\r\n appDir: string\r\n\r\n /**\r\n * Output directory for generated files and build artifacts.\r\n * @default './.pen'\r\n */\r\n outDir: string\r\n\r\n /**\r\n * Emit metadata files (manifest, components, element-tree) to aid debugging and tooling.\r\n * These files provide introspection into your app's structure.\r\n * @default true\r\n */\r\n emitMetadata: boolean\r\n}\r\n\r\n/**\r\n * User-facing configuration (what users write in pen.config.ts).\r\n * All fields are optional and will be merged with defaults.\r\n */\r\nexport type PenConfig = Partial<ResolvedPenConfig>\r\n\r\nexport function defineConfig(config: PenConfig): PenConfig {\r\n return config\r\n}\r\n\r\nconst defaultConfig: ResolvedPenConfig = {\r\n appDir: './src/app',\r\n outDir: './.pen',\r\n emitMetadata: false,\r\n}\r\n\r\n/**\r\n * Loads pen.config.ts from the current directory.\r\n * Falls back to defaults if config file doesn't exist.\r\n * Returns a fully resolved config with all fields guaranteed to be present.\r\n */\r\nexport async function loadConfig(): Promise<ResolvedPenConfig> {\r\n try {\r\n const configPath = resolve(process.cwd(), 'pen.config.ts')\r\n const configUrl = pathToFileURL(configPath).href\r\n\r\n const module = await import(configUrl) as { default?: PenConfig }\r\n const userConfig = module.default || {}\r\n\r\n // Merge with defaults\r\n return { ...defaultConfig, ...userConfig }\r\n }\r\n catch {\r\n // Config file doesn't exist or failed to load - use defaults\r\n return defaultConfig\r\n }\r\n}\r\n","/** Callbacks for tree traversal. */\r\nexport type TraverseCallbacks<TNode> = {\r\n /** Called when visiting each node (pre-order) */\r\n visit?: (node: TNode) => void\r\n /** Returns children for a node (or creates them) */\r\n expand?: (node: TNode) => TNode[]\r\n /** Attaches a child to its parent */\r\n attach?: (child: TNode, parent: TNode) => void\r\n}\r\n\r\n/**\r\n * Depth-first tree traversal with pluggable callbacks.\r\n *\r\n * @template TNode - The type of tree nodes\r\n * @param root - The root node to start traversal from\r\n * @param callbacks - Callbacks for visit, expand, and attach operations\r\n *\r\n * @example\r\n * // Visit existing tree\r\n * traverse(tree, {\r\n * visit: (node) => console.log(node.name),\r\n * expand: (node) => node.children ?? []\r\n * })\r\n *\r\n * @example\r\n * // Build new tree\r\n * traverse(root, {\r\n * expand: (node) => createChildren(node),\r\n * attach: (child, parent) => parent.children.push(child)\r\n * })\r\n */\r\nexport function traverse<TNode>(root: TNode, callbacks: TraverseCallbacks<TNode>) {\r\n const { visit, expand, attach } = callbacks\r\n const stack = [root]\r\n\r\n while (stack.length) {\r\n const node = stack.pop()!\r\n visit?.(node)\r\n\r\n // Process children in reverse so\r\n // they're popped in correct order\r\n const children = expand?.(node) ?? []\r\n\r\n for (let i = children.length-1; i >= 0; i--) {\r\n const child = children[i]!\r\n attach?.(child, node)\r\n stack.push(child)\r\n }\r\n }\r\n}\r\n\r\n/** Callbacks for ancestor traversal. */\r\nexport type AncestorCallbacks<TNode> = {\r\n /** Called when visiting each ancestor node (from leaf to root) */\r\n visit: (node: TNode) => void\r\n /** Returns the parent node for a given node */\r\n parent: (node: TNode) => TNode | undefined\r\n}\r\n\r\n/**\r\n * Traverses the ancestor chain of a given node from leaf to root.\r\n *\r\n * @template TNode - The type of tree nodes\r\n * @param node - The node to start the ancestor traversal from\r\n * @param callbacks - Callbacks for visit and parent operations\r\n *\r\n * @example\r\n * // Visit ancestors in a tree structure\r\n * ancestors(node, {\r\n * visit: (node) => console.log(`Visiting: ${node.name}`),\r\n * parent: (node) => node.parent // Return the parent of the node\r\n * })\r\n *\r\n * @example\r\n * // Collect all ancestors' roles in a tree structure\r\n * const roles = []\r\n * ancestors(node, {\r\n * visit: (node) => roles.push(node.role),\r\n * parent: (node) => node.parent\r\n * })\r\n * console.log(roles) // Logs all ancestor roles from leaf to root\r\n */\r\nexport function ancestors<TNode>(node: TNode, callbacks: AncestorCallbacks<TNode>) {\r\n const { visit, parent } = callbacks\r\n let currentNode: TNode | undefined = node\r\n\r\n while (currentNode) {\r\n visit(currentNode)\r\n currentNode = parent(currentNode)\r\n }\r\n}\r\n","/**\r\n * Base error for all route-builder build errors\r\n */\r\nexport class FileRouterError extends Error {\r\n constructor(message: string) {\r\n super(message)\r\n this.name = 'FileRouterError'\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// File Tree Errors\r\n// ============================================================================\r\n\r\nexport class DirectoryNotFoundError extends FileRouterError {\r\n constructor(public path: string) {\r\n super(`Directory not found: \"${path}\"`)\r\n this.name = 'DirectoryNotFoundError'\r\n }\r\n}\r\n\r\nexport class NotADirectoryError extends FileRouterError {\r\n constructor(public path: string) {\r\n super(`Path is not a directory: \"${path}\"`)\r\n this.name = 'NotADirectoryError'\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Route Tree Errors\r\n// ============================================================================\r\n\r\nexport class RootIsFileError extends FileRouterError {\r\n constructor(public path: string) {\r\n super(\r\n `Root path is a file, not a directory: \"${path}\"\\n\\n` +\r\n 'The app directory must be a directory, not a file.',\r\n )\r\n this.name = 'RootIsFileError'\r\n }\r\n}\r\n\r\nexport class DuplicateScreenError extends FileRouterError {\r\n constructor(\r\n public url: string,\r\n public files: string[],\r\n ) {\r\n super(\r\n `Conflicting screen routes found at \"${url}\":\\n` +\r\n files.map(f => ` - ${f}`).join('\\n') + '\\n\\n' +\r\n 'Each URL can only have one screen file.\\n' +\r\n 'Move one screen to a different directory or rename the route segment.',\r\n )\r\n this.name = 'DuplicateScreenError'\r\n }\r\n}\r\n","import { readdirSync, statSync, type Dirent } from 'fs'\r\nimport { resolve, join, posix } from 'path'\r\nimport { traverse } from '@/lib/tree'\r\nimport { DirectoryNotFoundError, NotADirectoryError } from '../errors'\r\n\r\nexport type FileNode = {\r\n name: string // dirent name\r\n relPath: string\r\n absPath: string\r\n children?: FileNode[] // present only for directories\r\n}\r\n\r\n/**\r\n * Builds a file tree from a directory path.\r\n *\r\n * @param appPath - Path to the app directory\r\n * @returns File tree structure\r\n * @throws {DirectoryNotFoundError} If the directory doesn't exist\r\n * @throws {NotADirectoryError} If the path is not a directory\r\n */\r\nexport function createFileTree(appPath: string): FileNode {\r\n const absPath = resolve(appPath)\r\n validateDirectory(absPath)\r\n\r\n // Root node\r\n const name = 'app'\r\n const relPath = '/app'\r\n const root: FileNode = { name, relPath, absPath, children: [] }\r\n\r\n traverse(root, {\r\n attach: (child, parent) => parent.children!.push(child),\r\n expand: (file) => {\r\n if (!file.children) return []\r\n\r\n return readdirSync(file.absPath, { withFileTypes: true })\r\n .filter(d => d.isFile() || d.isDirectory())\r\n .map(d => createFileNode(d, file))\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n },\r\n })\r\n return root\r\n}\r\n\r\n/** Validates that the path exists and is a directory. */\r\nfunction validateDirectory(path: string) {\r\n const stat = statSync(path, { throwIfNoEntry: false })\r\n if (!stat) throw new DirectoryNotFoundError(path)\r\n if (!stat.isDirectory()) throw new NotADirectoryError(path)\r\n}\r\n\r\nfunction createFileNode(dirent: Dirent, parent: FileNode) {\r\n const relPath = posix.join(parent.relPath, dirent.name)\r\n const absPath = join(parent.absPath, dirent.name)\r\n\r\n return dirent.isDirectory()\r\n ? { name: dirent.name, relPath, absPath, children: [] }\r\n : { name: dirent.name, relPath, absPath }\r\n}\r\n","import type { FileNode } from './file-tree'\r\nimport { parse, posix } from 'path'\r\nimport { traverse } from '@/lib/tree'\r\nimport { RootIsFileError, DuplicateScreenError } from '../errors'\r\n\r\nexport const SEGMENT_ROLES = ['layout', 'screen', 'error', 'not-found'] as const\r\nexport type SegmentRole = typeof SEGMENT_ROLES[number]\r\n\r\nexport type SegmentRoles = Partial<Record<SegmentRole, string>>\r\nexport type SegmentNode = {\r\n segment: string\r\n url: `${string}/`\r\n type: 'page' | 'group'\r\n roles: SegmentRoles\r\n parent?: SegmentNode\r\n children?: SegmentNode[]\r\n file: FileNode\r\n}\r\n\r\n/**\r\n * Creates a segment tree from a file system tree.\r\n *\r\n * @param fileTree - File system tree\r\n * @returns Segment tree with assigned roles and validated structure\r\n * @throws {RootIsFileError} If the root is a file instead of a directory\r\n * @throws {DuplicateScreenError} If multiple screens map to the same URL\r\n */\r\nexport function createSegmentTree(fileTree: FileNode): SegmentNode {\r\n const tree = buildSegmentTree(fileTree) // Builds segment tree structure from file tree\r\n bindSegmentTree(tree) // Binds roles to segments and validates tree structure\r\n return tree\r\n}\r\n\r\nfunction buildSegmentTree(fileTree: FileNode) {\r\n if (fileTree.children === undefined)\r\n throw new RootIsFileError(fileTree.absPath)\r\n\r\n const root: SegmentNode = {\r\n segment: '',\r\n url: '/',\r\n type: 'page',\r\n roles: {},\r\n children: [],\r\n file: fileTree,\r\n }\r\n\r\n traverse(root, {\r\n attach: (child, parent) => parent.children!.push(child),\r\n expand: segment =>\r\n (segment.file.children ?? [])\r\n .filter(file => file.children && !file.name.startsWith('_'))\r\n .map(file => createSegmentNode(file, segment))\r\n .sort((a, b) => a.segment.localeCompare(b.segment)),\r\n })\r\n return root\r\n}\r\n\r\nfunction bindSegmentTree(segmentTree: SegmentNode) {\r\n const screens: Record<SegmentNode['url'], string> = {}\r\n\r\n traverse(segmentTree, {\r\n expand: segment => segment.children ?? [],\r\n visit: segment => {\r\n assignRoles(segment)\r\n validateScreenUniqueness(segment, screens)\r\n },\r\n })\r\n}\r\n\r\nfunction createSegmentNode(file: FileNode, parent: SegmentNode) {\r\n const isGroup = file.name.startsWith('(') && file.name.endsWith(')')\r\n const segmentNode: SegmentNode = {\r\n segment: file.name,\r\n url: isGroup ? parent.url : `${posix.join(parent.url, file.name)}/`,\r\n type: isGroup ? 'group' : 'page',\r\n roles: {},\r\n parent,\r\n children: [],\r\n file,\r\n }\r\n return segmentNode\r\n}\r\n\r\nfunction assignRoles(segment: SegmentNode) {\r\n for (const child of segment.file.children ?? []) {\r\n const { name, ext } = parse(child.name)\r\n if (ext === '.tsx' && SEGMENT_ROLES.includes(name as SegmentRole))\r\n segment.roles[name as SegmentRole] = child.absPath\r\n }\r\n}\r\n\r\nfunction validateScreenUniqueness(segment: SegmentNode, screens: Record<SegmentNode['url'], string>) {\r\n if (!segment.roles.screen) return\r\n\r\n const existing = screens[segment.url]\r\n if (existing)\r\n throw new DuplicateScreenError(segment.url, [existing, segment.file.absPath])\r\n screens[segment.url] = segment.file.absPath\r\n}\r\n","import { parse, join } from 'path'\r\n\r\nexport function removeExtension(filePath: string) {\r\n const parsed = parse(filePath)\r\n return join(parsed.dir, parsed.name) // Use posix for forward slashes\r\n}\r\n","import type { SegmentNode, SegmentRoles } from './segment-tree'\r\nimport { ancestors, traverse } from '@/lib/tree'\r\nimport { relative } from 'path'\r\nimport { removeExtension } from '@/lib/path-utils'\r\n\r\nexport type RouteTable = Record<string, Route>\r\nexport type Route = {\r\n url: string\r\n chain: SegmentRoles[]\r\n}\r\n\r\n/**\r\n * Builds a route manifest from a segment tree.\r\n *\r\n * Flattens the tree into a dictionary mapping URLs to route metadata.\r\n * Only includes routes that have screens.\r\n * Paths are stored as relative import paths from the generated directory.\r\n *\r\n * @param segmentTree - Segment tree with parent pointers\r\n * @param outDir - Output directory (to calculate relative import paths)\r\n * @returns Flat manifest ready for runtime composition\r\n */\r\nexport function createRouteTable(segmentTree: SegmentNode, outDir: string): RouteTable {\r\n const manifest: RouteTable = {}\r\n const genDir = `${outDir}/generated`\r\n\r\n traverse(segmentTree, {\r\n expand: parentSegment => parentSegment.children ?? [],\r\n visit: segment => {\r\n if (!segment.roles.screen) return\r\n const url = segment.url\r\n const chain = createSegmentChain(segment, genDir)\r\n manifest[url] = { url, chain }\r\n },\r\n })\r\n return manifest\r\n}\r\n\r\n/** Builds ancestor chain from leaf → root order. */\r\nfunction createSegmentChain(segment: SegmentNode, genDir: string) {\r\n const chain: SegmentRoles[] = []\r\n\r\n ancestors(segment, {\r\n parent: ancestorSegment => ancestorSegment.parent,\r\n visit: ancestorSegment => {\r\n const roles: SegmentRoles = {}\r\n const entries = Object.entries(ancestorSegment.roles) as [keyof SegmentRoles, string][]\r\n\r\n for (const [name, path] of entries) {\r\n if (name !== 'screen' || ancestorSegment === segment) { // Skip ancestor screens\r\n const importPath = removeExtension(path) // Remove extension\r\n const relPath = relative(genDir, importPath).replace(/\\\\/g, '/') // Convert to relative path\r\n roles[name] = `${relPath}.js` // Add .js for ES modules\r\n }\r\n }\r\n chain.push(roles)\r\n },\r\n })\r\n return chain\r\n}\r\n","import type { RouteTable } from './route-manifest'\r\nimport { SEGMENT_ROLES } from './segment-tree'\r\n\r\n/** Map from import path to component index (keys are in sorted order) */\r\nexport type ComponentMap = Record<string, number>\r\n\r\n/**\r\n * Builds a mapping of import paths to component IDs from the manifest.\r\n * Collects all unique import paths and assigns them indices.\r\n * Keys are stored in sorted order for deterministic output.\r\n */\r\nexport function createComponentMap(manifest: RouteTable): ComponentMap {\r\n const importPaths = new Set<string>()\r\n\r\n // Collect all unique import paths from manifest\r\n for (const route of Object.values(manifest)) {\r\n for (const segment of route.chain) {\r\n for (const role of SEGMENT_ROLES)\r\n if (segment[role]) importPaths.add(segment[role])\r\n }\r\n }\r\n\r\n // Sort for deterministic output\r\n const imports = Array.from(importPaths).sort()\r\n const mapping: ComponentMap = {}\r\n for (let i = 0; i < imports.length; i++)\r\n mapping[imports[i]!] = i\r\n\r\n return mapping\r\n}\r\n","import type { Route, RouteTable } from './route-manifest'\r\nimport type { ComponentMap } from './component-map'\r\n\r\nexport interface ElementTree {\r\n component: string\r\n props: Record<string, unknown>\r\n children?: ElementTree\r\n}\r\n\r\nexport type ElementTreeMap = Record<string, ElementTree>\r\n\r\n/**\r\n * Creates element trees for all routes in the manifest.\r\n * Each tree represents the nested React component structure for a route.\r\n */\r\nexport function createElementTrees(manifest: RouteTable, componentMap: ComponentMap): ElementTreeMap {\r\n const elementTrees: ElementTreeMap = {}\r\n for (const [url, route] of Object.entries(manifest))\r\n elementTrees[url] = createElementTree(route, componentMap)\r\n return elementTrees\r\n}\r\n\r\n/**\r\n * Builds a structured element tree representing nested React components.\r\n *\r\n * This function mirrors the runtime composition logic but creates a structured data tree\r\n * that will be serialized into createElement calls for the generated routes.ts file.\r\n *\r\n * Composition order per segment (inside to outside):\r\n * 1. Screen component (only in leaf segment)\r\n * 2. Not-found boundary (wraps screen if present)\r\n * 3. Layout (wraps content)\r\n * 4. Error boundary (wraps layout + all descendants)\r\n */\r\nfunction createElementTree(route: Route, mapping: ComponentMap): ElementTree {\r\n const imports = Object.keys(mapping)\r\n\r\n // Start with the screen from the first segment\r\n const screenSegment = route.chain[0]!\r\n const screenPath = screenSegment['screen']!\r\n const screenIndex = mapping[screenPath]!\r\n const screenKey = JSON.stringify(imports[screenIndex]!)\r\n\r\n let tree: ElementTree = {\r\n component: `Component${screenIndex}`,\r\n props: { key: screenKey },\r\n }\r\n\r\n // Process segments from leaf → root (same order as runtime composition)\r\n for (const segment of route.chain) {\r\n // Not-found boundary\r\n if (segment['not-found']) {\r\n const path = segment['not-found']\r\n const index = mapping[path]!\r\n const key = JSON.stringify(imports[index]!)\r\n const fallback = `Component${index}`\r\n tree = {\r\n component: 'NotFoundBoundary',\r\n props: { key, fallback },\r\n children: tree,\r\n }\r\n }\r\n // Error boundary\r\n if (segment['error']) {\r\n const path = segment['error']\r\n const index = mapping[path]!\r\n const key = JSON.stringify(imports[index]!)\r\n const fallback = `Component${index}`\r\n tree = {\r\n component: 'ErrorBoundary',\r\n props: { key, fallback },\r\n children: tree,\r\n }\r\n }\r\n // Layout\r\n if (segment['layout']) {\r\n const path = segment['layout']\r\n const index = mapping[path]!\r\n const key = JSON.stringify(imports[index]!)\r\n tree = {\r\n component: `Component${index}`,\r\n props: { key },\r\n children: tree,\r\n }\r\n }\r\n }\r\n return tree\r\n}\r\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -2,8 +2,12 @@ import * as react0 from "react";
2
2
  import { Component, ComponentType, PropsWithChildren, ReactElement } from "react";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
 
5
- //#region src/core/config.d.ts
6
- interface PenConfig {
5
+ //#region src/pen/config.d.ts
6
+ /**
7
+ * Resolved configuration with all fields guaranteed to be present.
8
+ * This is what loadConfig() returns after merging user config with defaults.
9
+ */
10
+ type ResolvedPenConfig = {
7
11
  /**
8
12
  * Directory containing your app routes.
9
13
  * @default './src/app'
@@ -14,16 +18,35 @@ interface PenConfig {
14
18
  * @default './.pen'
15
19
  */
16
20
  outDir: string;
17
- }
18
- declare function defineConfig(config: Partial<PenConfig>): Partial<PenConfig>;
19
- declare const defaultConfig: PenConfig;
21
+ /**
22
+ * Emit metadata files (manifest, components, element-tree) to aid debugging and tooling.
23
+ * These files provide introspection into your app's structure.
24
+ * @default true
25
+ */
26
+ emitMetadata: boolean;
27
+ };
28
+ /**
29
+ * User-facing configuration (what users write in pen.config.ts).
30
+ * All fields are optional and will be merged with defaults.
31
+ */
32
+ type PenConfig = Partial<ResolvedPenConfig>;
33
+ declare function defineConfig(config: PenConfig): PenConfig;
20
34
  /**
21
35
  * Loads pen.config.ts from the current directory.
22
36
  * Falls back to defaults if config file doesn't exist.
37
+ * Returns a fully resolved config with all fields guaranteed to be present.
23
38
  */
24
- declare function loadConfig(): Promise<PenConfig>;
39
+ declare function loadConfig(): Promise<ResolvedPenConfig>;
25
40
  //#endregion
26
- //#region src/core/route-builder/builders/file-tree.d.ts
41
+ //#region src/pen/compiler/types.d.ts
42
+ /**
43
+ * Compiled route elements mapped by URL.
44
+ * This represents the output format of the build process.
45
+ * Routes are compiled once at build time via codegen.
46
+ */
47
+ type CompiledRoutes = Record<string, ReactElement>;
48
+ //#endregion
49
+ //#region src/pen/compiler/builders/file-tree.d.ts
27
50
  type FileNode = {
28
51
  name: string;
29
52
  relPath: string;
@@ -38,18 +61,15 @@ type FileNode = {
38
61
  * @throws {DirectoryNotFoundError} If the directory doesn't exist
39
62
  * @throws {NotADirectoryError} If the path is not a directory
40
63
  */
41
- declare function buildFileTree(appPath: string): FileNode;
64
+ declare function createFileTree(appPath: string): FileNode;
42
65
  //#endregion
43
- //#region src/core/route-builder/builders/segment-tree.d.ts
44
- type SegmentRoles = {
45
- 'layout'?: string;
46
- 'screen'?: string;
47
- 'error'?: string;
48
- 'not-found'?: string;
49
- };
66
+ //#region src/pen/compiler/builders/segment-tree.d.ts
67
+ declare const SEGMENT_ROLES: readonly ["layout", "screen", "error", "not-found"];
68
+ type SegmentRole = typeof SEGMENT_ROLES[number];
69
+ type SegmentRoles = Partial<Record<SegmentRole, string>>;
50
70
  type SegmentNode = {
51
71
  segment: string;
52
- url: string;
72
+ url: `${string}/`;
53
73
  type: 'page' | 'group';
54
74
  roles: SegmentRoles;
55
75
  parent?: SegmentNode;
@@ -57,17 +77,17 @@ type SegmentNode = {
57
77
  file: FileNode;
58
78
  };
59
79
  /**
60
- * Builds a segment tree from a file system tree.
80
+ * Creates a segment tree from a file system tree.
61
81
  *
62
82
  * @param fileTree - File system tree
63
- * @returns Segment tree with computed URLs and validated structure
83
+ * @returns Segment tree with assigned roles and validated structure
64
84
  * @throws {RootIsFileError} If the root is a file instead of a directory
65
85
  * @throws {DuplicateScreenError} If multiple screens map to the same URL
66
86
  */
67
- declare function buildSegmentTree(fileTree: FileNode): SegmentNode;
87
+ declare function createSegmentTree(fileTree: FileNode): SegmentNode;
68
88
  //#endregion
69
- //#region src/core/route-builder/builders/route-manifest.d.ts
70
- type RouteManifest = Record<string, Route>;
89
+ //#region src/pen/compiler/builders/route-manifest.d.ts
90
+ type RouteTable = Record<string, Route>;
71
91
  type Route = {
72
92
  url: string;
73
93
  chain: SegmentRoles[];
@@ -77,21 +97,38 @@ type Route = {
77
97
  *
78
98
  * Flattens the tree into a dictionary mapping URLs to route metadata.
79
99
  * Only includes routes that have screens.
100
+ * Paths are stored as relative import paths from the generated directory.
80
101
  *
81
102
  * @param segmentTree - Segment tree with parent pointers
103
+ * @param outDir - Output directory (to calculate relative import paths)
82
104
  * @returns Flat manifest ready for runtime composition
83
105
  */
84
- declare function buildRouteManifest(segmentTree: SegmentNode): RouteManifest;
106
+ declare function createRouteTable(segmentTree: SegmentNode, outDir: string): RouteTable;
85
107
  //#endregion
86
- //#region src/core/route-builder/builders/component-map.d.ts
87
- type ComponentImportMap = Record<string, string>;
108
+ //#region src/pen/compiler/builders/component-map.d.ts
109
+ /** Map from import path to component index (keys are in sorted order) */
110
+ type ComponentMap = Record<string, number>;
88
111
  /**
89
- * Builds a component map from a route manifest.
90
- * Maps absolute component paths to relative import paths.
112
+ * Builds a mapping of import paths to component IDs from the manifest.
113
+ * Collects all unique import paths and assigns them indices.
114
+ * Keys are stored in sorted order for deterministic output.
91
115
  */
92
- declare function buildComponentMap(manifest: RouteManifest, outDir: string): ComponentImportMap;
116
+ declare function createComponentMap(manifest: RouteTable): ComponentMap;
93
117
  //#endregion
94
- //#region src/core/route-builder/errors.d.ts
118
+ //#region src/pen/compiler/builders/element-tree.d.ts
119
+ interface ElementTree {
120
+ component: string;
121
+ props: Record<string, unknown>;
122
+ children?: ElementTree;
123
+ }
124
+ type ElementTreeMap = Record<string, ElementTree>;
125
+ /**
126
+ * Creates element trees for all routes in the manifest.
127
+ * Each tree represents the nested React component structure for a route.
128
+ */
129
+ declare function createElementTrees(manifest: RouteTable, componentMap: ComponentMap): ElementTreeMap;
130
+ //#endregion
131
+ //#region src/pen/compiler/errors.d.ts
95
132
  /**
96
133
  * Base error for all route-builder build errors
97
134
  */
@@ -116,7 +153,7 @@ declare class DuplicateScreenError extends FileRouterError {
116
153
  constructor(url: string, files: string[]);
117
154
  }
118
155
  //#endregion
119
- //#region src/core/router/RouterProvider.d.ts
156
+ //#region src/pen/router/RouterProvider.d.ts
120
157
  interface RouterContextValue {
121
158
  url: string;
122
159
  data: unknown | undefined;
@@ -136,7 +173,7 @@ declare function RouterProvider({
136
173
  children
137
174
  }: RouterProviderProps): react_jsx_runtime0.JSX.Element;
138
175
  //#endregion
139
- //#region src/core/router/hooks/use-history.d.ts
176
+ //#region src/pen/router/hooks/use-history.d.ts
140
177
  /**
141
178
  * Get the navigation history (URLs only)
142
179
  *
@@ -157,7 +194,7 @@ declare function RouterProvider({
157
194
  */
158
195
  declare function useHistory(): readonly string[];
159
196
  //#endregion
160
- //#region src/core/router/hooks/use-navigate.d.ts
197
+ //#region src/pen/router/hooks/use-navigate.d.ts
161
198
  /**
162
199
  * Get navigation functions without url/data
163
200
  */
@@ -168,7 +205,7 @@ declare function useNavigate(): {
168
205
  forward: () => void;
169
206
  };
170
207
  //#endregion
171
- //#region src/core/router/hooks/use-route-data.d.ts
208
+ //#region src/pen/router/hooks/use-route-data.d.ts
172
209
  /**
173
210
  * Access route data passed via router.push()
174
211
  *
@@ -181,31 +218,30 @@ declare function useNavigate(): {
181
218
  */
182
219
  declare function useRouteData<T = unknown>(): T | undefined;
183
220
  //#endregion
184
- //#region src/core/router/hooks/use-router.d.ts
221
+ //#region src/pen/router/hooks/use-router.d.ts
185
222
  declare function useRouter(): RouterContextValue;
186
223
  //#endregion
187
- //#region src/core/router/hooks/use-url.d.ts
224
+ //#region src/pen/router/hooks/use-url.d.ts
188
225
  /**
189
226
  * Get the current URL
190
227
  */
191
228
  declare function useUrl(): string;
192
229
  //#endregion
193
- //#region src/core/runtime/types.d.ts
194
- type ComponentMap = Record<string, ComponentType>;
195
- //#endregion
196
- //#region src/core/runtime/App.d.ts
230
+ //#region src/pen/runtime/App.d.ts
197
231
  interface AppProps {
198
232
  initialUrl: string;
199
- manifest: RouteManifest;
200
- components: ComponentMap;
233
+ routes: CompiledRoutes;
201
234
  }
235
+ /**
236
+ * Root application component.
237
+ * Routes are compiled at build time via codegen - no runtime composition needed!
238
+ */
202
239
  declare function App({
203
240
  initialUrl,
204
- manifest,
205
- components
241
+ routes
206
242
  }: AppProps): react_jsx_runtime0.JSX.Element;
207
243
  //#endregion
208
- //#region src/core/runtime/boundaries/ErrorBoundary.d.ts
244
+ //#region src/pen/runtime/ui/ErrorBoundary.d.ts
209
245
  /** Props passed to error.tsx components */
210
246
  interface ErrorComponentProps {
211
247
  error: Error;
@@ -243,7 +279,7 @@ declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryS
243
279
  render(): string | number | bigint | boolean | react_jsx_runtime0.JSX.Element | Iterable<react0.ReactNode> | Promise<string | number | bigint | boolean | react0.ReactPortal | react0.ReactElement<unknown, string | react0.JSXElementConstructor<any>> | Iterable<react0.ReactNode> | null | undefined> | null | undefined;
244
280
  }
245
281
  //#endregion
246
- //#region src/core/runtime/boundaries/NotFoundBoundary.d.ts
282
+ //#region src/pen/runtime/ui/NotFoundBoundary.d.ts
247
283
  /** Props passed to not-found.tsx components */
248
284
  interface NotFoundComponentProps {
249
285
  url: string;
@@ -260,14 +296,14 @@ declare function NotFoundBoundary({
260
296
  children
261
297
  }: NotFoundBoundaryProps): react_jsx_runtime0.JSX.Element;
262
298
  //#endregion
263
- //#region src/core/runtime/screens/ErrorScreen.d.ts
299
+ //#region src/pen/runtime/ui/ErrorScreen.d.ts
264
300
  /** Global error fallback that always wraps the app. */
265
301
  declare function ErrorScreen({
266
302
  error,
267
303
  reset
268
304
  }: ErrorComponentProps): react_jsx_runtime0.JSX.Element;
269
305
  //#endregion
270
- //#region src/core/runtime/screens/NotFoundScreen.d.ts
306
+ //#region src/pen/runtime/ui/NotFoundScreen.d.ts
271
307
  /**
272
308
  * Not found error screen displayed when a route is not found.
273
309
  * Shows the URL that was attempted.
@@ -276,47 +312,22 @@ declare function NotFoundScreen({
276
312
  url
277
313
  }: NotFoundComponentProps): react_jsx_runtime0.JSX.Element;
278
314
  //#endregion
279
- //#region src/core/runtime/FileRouter.d.ts
315
+ //#region src/pen/runtime/FileRouter.d.ts
280
316
  /**
281
317
  * Props for the FileRouter component.
282
- * Defines the URL to render, the route manifest, and component map.
283
318
  */
284
319
  interface FileRouterProps {
285
- manifest: RouteManifest;
286
- components: ComponentMap;
320
+ routes: CompiledRoutes;
287
321
  }
288
322
  /**
289
- * Router component that orchestrates route matching and composition.
290
- * Returns the composed route element or throws NotFoundError.
323
+ * Router component that renders compiled route elements.
324
+ * Simply looks up the current URL in the compiled routes map.
291
325
  */
292
326
  declare function FileRouter({
293
- manifest,
294
- components
327
+ routes
295
328
  }: FileRouterProps): ReactElement;
296
329
  //#endregion
297
- //#region src/core/runtime/routing/composer.d.ts
298
- /**
299
- * Composes a route element by processing each segment in the chain.
300
- *
301
- * Composition order per segment (inside to outside):
302
- * 1. Screen component (only in leaf segment)
303
- * 2. Not-found boundary (wraps screen if present)
304
- * 3. Layout (wraps content)
305
- * 4. Error boundary (wraps layout + all descendants)
306
- *
307
- * This ensures error boundaries catch errors at the right level,
308
- * and only layouts above the error remain visible.
309
- */
310
- declare function composeRoute(route: Route, components: ComponentMap): ReactElement;
311
- //#endregion
312
- //#region src/core/runtime/routing/matcher.d.ts
313
- /**
314
- * Matches a URL against the route manifest.
315
- * Returns the matched route metadata or null if no match found.
316
- */
317
- declare function matchRoute(url: string, manifest: RouteManifest): Route | null;
318
- //#endregion
319
- //#region src/core/runtime/errors.d.ts
330
+ //#region src/pen/runtime/errors.d.ts
320
331
  /** Special error thrown to trigger not-found.tsx. */
321
332
  declare class NotFoundError extends Error {
322
333
  readonly url: string;
@@ -333,5 +344,32 @@ declare class ComponentNotFoundError extends Error {
333
344
  constructor(componentPath: string);
334
345
  }
335
346
  //#endregion
336
- export { App, type AppProps, ComponentImportMap, type ComponentMap, ComponentNotFoundError, DirectoryNotFoundError, DuplicateScreenError, EmptyChainError, ErrorBoundary, type ErrorComponentProps, ErrorScreen, FileNode, FileRouter, FileRouterError, type FileRouterProps, NotADirectoryError, NotFoundBoundary, type NotFoundComponentProps, NotFoundError, NotFoundScreen, PenConfig, RootIsFileError, Route, RouteManifest, RouterContext, type RouterContextValue, RouterProvider, type RouterProviderProps, SegmentNode, SegmentRoles, buildComponentMap, buildFileTree, buildRouteManifest, buildSegmentTree, composeRoute, defaultConfig, defineConfig, loadConfig, matchRoute, useHistory, useNavigate, useRouteData, useRouter, useUrl };
347
+ //#region src/pen/utils/use-memory-monitor.d.ts
348
+ interface MemoryUsage {
349
+ /** Heap memory currently used (in MB) */
350
+ heapUsed: number;
351
+ /** Total heap memory allocated (in MB) */
352
+ heapTotal: number;
353
+ /** Resident Set Size - total memory allocated for the process (in MB) */
354
+ rss: number;
355
+ /** Memory used by C++ objects bound to JavaScript (in MB) */
356
+ external: number;
357
+ }
358
+ /**
359
+ * Hook to monitor Node.js memory usage in real-time
360
+ *
361
+ * @param intervalMs - Polling interval in milliseconds (default: 1000ms)
362
+ * @returns Current memory usage statistics in megabytes
363
+ *
364
+ * @example
365
+ * ```tsx
366
+ * function MemoryDisplay() {
367
+ * const { heapUsed, heapTotal, rss } = useMemoryMonitor()
368
+ * return <Text>RAM: {heapUsed}/{heapTotal} MB (RSS: {rss} MB)</Text>
369
+ * }
370
+ * ```
371
+ */
372
+ declare function useMemoryMonitor(intervalMs?: number): MemoryUsage;
373
+ //#endregion
374
+ export { App, type AppProps, type CompiledRoutes, type ComponentMap, ComponentNotFoundError, DirectoryNotFoundError, DuplicateScreenError, type ElementTree, type ElementTreeMap, EmptyChainError, ErrorBoundary, type ErrorComponentProps, ErrorScreen, type FileNode, FileRouter, FileRouterError, type FileRouterProps, MemoryUsage, NotADirectoryError, NotFoundBoundary, type NotFoundComponentProps, NotFoundError, NotFoundScreen, PenConfig, ResolvedPenConfig, RootIsFileError, type Route, type RouteTable, RouterContext, type RouterContextValue, RouterProvider, type RouterProviderProps, type SegmentNode, type SegmentRoles, createComponentMap, createElementTrees, createFileTree, createRouteTable, createSegmentTree, defineConfig, loadConfig, useHistory, useMemoryMonitor, useNavigate, useRouteData, useRouter, useUrl };
337
375
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/config.ts","../src/core/route-builder/builders/file-tree.ts","../src/core/route-builder/builders/segment-tree.ts","../src/core/route-builder/builders/route-manifest.ts","../src/core/route-builder/builders/component-map.ts","../src/core/route-builder/errors.ts","../src/core/router/RouterProvider.tsx","../src/core/router/hooks/use-history.ts","../src/core/router/hooks/use-navigate.ts","../src/core/router/hooks/use-route-data.ts","../src/core/router/hooks/use-router.ts","../src/core/router/hooks/use-url.ts","../src/core/runtime/types.ts","../src/core/runtime/App.tsx","../src/core/runtime/boundaries/ErrorBoundary.tsx","../src/core/runtime/boundaries/NotFoundBoundary.tsx","../src/core/runtime/screens/ErrorScreen.tsx","../src/core/runtime/screens/NotFoundScreen.tsx","../src/core/runtime/FileRouter.tsx","../src/core/runtime/routing/composer.ts","../src/core/runtime/routing/matcher.ts","../src/core/runtime/errors.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/pen/config.ts","../src/pen/compiler/types.ts","../src/pen/compiler/builders/file-tree.ts","../src/pen/compiler/builders/segment-tree.ts","../src/pen/compiler/builders/route-manifest.ts","../src/pen/compiler/builders/component-map.ts","../src/pen/compiler/builders/element-tree.ts","../src/pen/compiler/errors.ts","../src/pen/router/RouterProvider.tsx","../src/pen/router/hooks/use-history.ts","../src/pen/router/hooks/use-navigate.ts","../src/pen/router/hooks/use-route-data.ts","../src/pen/router/hooks/use-router.ts","../src/pen/router/hooks/use-url.ts","../src/pen/runtime/App.tsx","../src/pen/runtime/ui/ErrorBoundary.tsx","../src/pen/runtime/ui/NotFoundBoundary.tsx","../src/pen/runtime/ui/ErrorScreen.tsx","../src/pen/runtime/ui/NotFoundScreen.tsx","../src/pen/runtime/FileRouter.tsx","../src/pen/runtime/errors.ts","../src/pen/utils/use-memory-monitor.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"./component-map-CLP6MqF8.mjs";import{Component as f,createContext as p,createElement as m,useCallback as h,useContext as g,useState as _}from"react";import{jsx as v,jsxs as y}from"react/jsx-runtime";import{Box as b,Text as x,useInput as S}from"ink";function C(e,t,n){return{stack:[...e.stack.slice(0,e.position+1),{url:t,data:n}],position:e.position+1}}function w(e,t){let n=[...e.stack];return n[e.position]={url:t},{...e,stack:n}}function T(e){return e.position>0?{...e,position:e.position-1}:e}function E(e){return e.position<e.stack.length-1?{...e,position:e.position+1}:e}const D=p(null);function O({initialUrl:e,children:t}){let[n,r]=_({stack:[{url:e}],position:0}),{url:i,data:a}=n.stack[n.position]??{url:e},o=h((e,t)=>{r(n=>C(n,e,t))},[]),s=h(e=>{r(t=>w(t,e))},[]),c=h(()=>{r(e=>T(e))},[]),l=h(()=>{r(e=>E(e))},[]);return v(D.Provider,{value:{url:i,data:a,history:n.stack.map(e=>e.url),position:n.position,push:o,replace:s,back:c,forward:l},children:t})}function k(){let e=g(D);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e}function A(){let{history:e}=k();return e}function j(){let{push:e,replace:t,back:n,forward:r}=k();return{push:e,replace:t,back:n,forward:r}}function M(){let{data:e}=k();return e}function N(){let{url:e}=k();return e}var P=class extends f{state;constructor(e){super(e),this.state={error:null},this.reset=this.reset.bind(this)}static getDerivedStateFromError(e){return{error:e}}componentDidCatch(){}reset(){this.setState({error:null})}render(){if(this.state.error){let e=this.props.fallback;return v(e,{error:this.state.error,reset:this.reset})}return this.props.children}},F=class extends Error{url;constructor(e,t=`Not Found`){super(t),this.name=`NotFoundError`,this.url=e}},I=class extends Error{constructor(e){super(`Route ${e} has an empty chain. This indicates a bug in manifest generation - routes must have at least one segment.`),this.url=e,this.name=`EmptyChainError`}},L=class extends Error{constructor(e){super(`Component not found: ${e}. This indicates the component map is out of sync with the manifest. Try running 'pen build' again.`),this.componentPath=e,this.name=`ComponentNotFoundError`}},R=class extends f{state;constructor(e){super(e),this.state={url:null}}static getDerivedStateFromError(e){return e instanceof F?{url:e.url}:null}componentDidCatch(e){if(!(e instanceof F))throw e}componentDidUpdate(e){e.url!==this.props.url&&this.state.url&&this.setState({url:null})}render(){if(this.state.url){let e=this.props.fallback;return v(e,{url:this.state.url})}return this.props.children}};function z({fallback:e,children:t}){let{url:n}=k();return v(R,{fallback:e,url:n,children:t})}function B({error:e,reset:t}){let[n,r]=_(!1);return S(e=>{e===`r`&&t(),e===`s`&&r(e=>!e),e===`q`&&process.exit(1)}),y(b,{flexDirection:`column`,padding:2,borderStyle:`double`,borderColor:`red`,children:[v(b,{marginBottom:1,children:v(x,{bold:!0,color:`red`,children:`💥 Critical Application Error`})}),v(b,{marginBottom:1,children:v(x,{color:`red`,children:`The application encountered a fatal error and cannot continue.`})}),y(b,{flexDirection:`column`,padding:1,borderStyle:`round`,borderColor:`yellow`,marginBottom:1,children:[v(x,{bold:!0,color:`yellow`,children:`Error Details:`}),v(x,{children:e.message})]}),n&&e.stack&&y(b,{flexDirection:`column`,padding:1,borderStyle:`single`,borderColor:`gray`,marginBottom:1,children:[v(x,{bold:!0,dimColor:!0,children:`Stack Trace:`}),v(x,{dimColor:!0,children:e.stack})]}),y(b,{flexDirection:`column`,marginTop:1,children:[v(x,{bold:!0,children:`Options:`}),v(x,{dimColor:!0,children:` [r] Retry [s] Toggle Stack Trace [q] Quit`})]}),v(b,{marginTop:1,children:v(x,{dimColor:!0,children:`If this error persists, please report it.`})})]})}function V({url:e}){let t=k();return S(e=>{e===`b`&&t.back(),e===`q`&&process.exit(0)}),y(b,{flexDirection:`column`,padding:2,borderStyle:`round`,borderColor:`red`,children:[y(b,{flexDirection:`column`,marginBottom:1,children:[v(x,{bold:!0,color:`red`,children:`Route Not Found`}),v(x,{dimColor:!0,children:`The router couldn't match the requested URL.`})]}),v(b,{flexDirection:`column`,paddingLeft:1,marginBottom:1,children:y(x,{children:[`Requested:`,` `,v(x,{color:`yellow`,bold:!0,children:e})]})}),y(b,{flexDirection:`column`,padding:1,borderStyle:`single`,borderColor:`gray`,marginBottom:1,children:[v(x,{bold:!0,children:`Actions`}),v(x,{dimColor:!0,children:` [b] Go back`}),v(x,{dimColor:!0,children:` [q] Quit`})]}),v(x,{dimColor:!0,children:`Tip: Check the route segment name or add a screen.tsx for this path.`})]})}function H(e,t){let n=e[t];if(!n)throw new L(t);return n}function U(e,t){if(!e.chain.length)throw new I(e.url);let n=e.chain[0].screen,r=m(H(t,n),{key:n});for(let n of e.chain){if(n[`not-found`]){let e=n[`not-found`];r=m(z,{key:e,fallback:H(t,e)},r)}if(n.error){let e=n.error;r=m(P,{key:e,fallback:H(t,e)},r)}if(n.layout){let e=n.layout;r=m(H(t,e),{key:e},r)}}return r}function W(e,t){return t[e.endsWith(`/`)?e:`${e}/`]??null}function G({manifest:e,components:t}){let{url:n}=k(),r=W(n,e);if(!r)throw new F(n);return U(r,t)}function K({initialUrl:e,manifest:t,components:n}){return v(P,{fallback:B,children:v(O,{initialUrl:e,children:v(z,{fallback:V,children:v(G,{manifest:t,components:n})})})})}export{K as App,L as ComponentNotFoundError,e as DirectoryNotFoundError,s as DuplicateScreenError,I as EmptyChainError,P as ErrorBoundary,B as ErrorScreen,G as FileRouter,l as FileRouterError,t as NotADirectoryError,z as NotFoundBoundary,F as NotFoundError,V as NotFoundScreen,a as RootIsFileError,D as RouterContext,O as RouterProvider,u as buildComponentMap,i as buildFileTree,o as buildRouteManifest,c as buildSegmentTree,U as composeRoute,d as defaultConfig,n as defineConfig,r as loadConfig,W as matchRoute,A as useHistory,j as useNavigate,M as useRouteData,k as useRouter,N as useUrl};
1
+ import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"./element-tree-DoH7UgRL.mjs";import{Component as f,createContext as p,useCallback as m,useContext as h,useEffect as g,useState as _}from"react";import{jsx as v,jsxs as y}from"react/jsx-runtime";import{Box as b,Text as x,useInput as S}from"ink";function C(e){return e.endsWith(`/`)?e:`${e}/`}function w(e,t,n){return{stack:[...e.stack.slice(0,e.position+1),{url:C(t),data:n}],position:e.position+1}}function T(e,t){let n=[...e.stack];return n[e.position]={url:C(t)},{...e,stack:n}}function E(e){return e.position>0?{...e,position:e.position-1}:e}function D(e){return e.position<e.stack.length-1?{...e,position:e.position+1}:e}const O=p(null);function k({initialUrl:e,children:t}){let n=C(e),[r,i]=_({stack:[{url:n}],position:0}),{url:a,data:o}=r.stack[r.position]??{url:n},s=m((e,t)=>{i(n=>w(n,e,t))},[]),c=m(e=>{i(t=>T(t,e))},[]),l=m(()=>{i(e=>E(e))},[]),u=m(()=>{i(e=>D(e))},[]);return v(O.Provider,{value:{url:a,data:o,history:r.stack.map(e=>e.url),position:r.position,push:s,replace:c,back:l,forward:u},children:t})}function A(){let e=h(O);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e}function j(){let{history:e}=A();return e}function M(){let{push:e,replace:t,back:n,forward:r}=A();return{push:e,replace:t,back:n,forward:r}}function N(){let{data:e}=A();return e}function P(){let{url:e}=A();return e}var F=class extends f{state;constructor(e){super(e),this.state={error:null},this.reset=this.reset.bind(this)}static getDerivedStateFromError(e){return{error:e}}componentDidCatch(){}reset(){this.setState({error:null})}render(){if(this.state.error){let e=this.props.fallback;return v(e,{error:this.state.error,reset:this.reset})}return this.props.children}},I=class extends Error{url;constructor(e,t=`Not Found`){super(t),this.name=`NotFoundError`,this.url=e}},L=class extends Error{constructor(e){super(`Route ${e} has an empty chain. This indicates a bug in manifest generation - routes must have at least one segment.`),this.url=e,this.name=`EmptyChainError`}},R=class extends Error{constructor(e){super(`Component not found: ${e}. This indicates the component map is out of sync with the manifest. Try running 'pen build' again.`),this.componentPath=e,this.name=`ComponentNotFoundError`}},z=class extends f{state;constructor(e){super(e),this.state={url:null}}static getDerivedStateFromError(e){return e instanceof I?{url:e.url}:null}componentDidCatch(e){if(!(e instanceof I))throw e}componentDidUpdate(e){e.url!==this.props.url&&this.state.url&&this.setState({url:null})}render(){if(this.state.url){let e=this.props.fallback;return v(e,{url:this.state.url})}return this.props.children}};function B({fallback:e,children:t}){let{url:n}=A();return v(z,{fallback:e,url:n,children:t})}function V({error:e,reset:t}){let[n,r]=_(!1);return S(e=>{e===`r`&&t(),e===`s`&&r(e=>!e),e===`q`&&process.exit(1)}),y(b,{flexDirection:`column`,padding:2,borderStyle:`double`,borderColor:`red`,children:[v(b,{marginBottom:1,children:v(x,{bold:!0,color:`red`,children:`💥 Critical Application Error`})}),v(b,{marginBottom:1,children:v(x,{color:`red`,children:`The application encountered a fatal error and cannot continue.`})}),y(b,{flexDirection:`column`,padding:1,borderStyle:`round`,borderColor:`yellow`,marginBottom:1,children:[v(x,{bold:!0,color:`yellow`,children:`Error Details:`}),v(x,{children:e.message})]}),n&&e.stack&&y(b,{flexDirection:`column`,padding:1,borderStyle:`single`,borderColor:`gray`,marginBottom:1,children:[v(x,{bold:!0,dimColor:!0,children:`Stack Trace:`}),v(x,{dimColor:!0,children:e.stack})]}),y(b,{flexDirection:`column`,marginTop:1,children:[v(x,{bold:!0,children:`Options:`}),v(x,{dimColor:!0,children:` [r] Retry [s] Toggle Stack Trace [q] Quit`})]}),v(b,{marginTop:1,children:v(x,{dimColor:!0,children:`If this error persists, please report it.`})})]})}function H({url:e}){let t=A();return S(e=>{e===`b`&&t.back(),e===`q`&&process.exit(0)}),y(b,{flexDirection:`column`,padding:2,borderStyle:`round`,borderColor:`red`,children:[y(b,{flexDirection:`column`,marginBottom:1,children:[v(x,{bold:!0,color:`red`,children:`Route Not Found`}),v(x,{dimColor:!0,children:`The router couldn't match the requested URL.`})]}),v(b,{flexDirection:`column`,paddingLeft:1,marginBottom:1,children:y(x,{children:[`Requested:`,` `,v(x,{color:`yellow`,bold:!0,children:e})]})}),y(b,{flexDirection:`column`,padding:1,borderStyle:`single`,borderColor:`gray`,marginBottom:1,children:[v(x,{bold:!0,children:`Actions`}),v(x,{dimColor:!0,children:` [b] Go back`}),v(x,{dimColor:!0,children:` [q] Quit`})]}),v(x,{dimColor:!0,children:`Tip: Check the route segment name or add a screen.tsx for this path.`})]})}function U({routes:e}){let{url:t}=A(),n=e[t];if(!n)throw new I(t);return n}function W({initialUrl:e,routes:t}){return v(F,{fallback:V,children:v(k,{initialUrl:e,children:v(B,{fallback:H,children:v(U,{routes:t})})})})}function G(e=1e3){let[t,n]=_({heapUsed:0,heapTotal:0,rss:0,external:0});return g(()=>{let t=()=>{let e=process.memoryUsage();n({heapUsed:Math.round(e.heapUsed/1024/1024),heapTotal:Math.round(e.heapTotal/1024/1024),rss:Math.round(e.rss/1024/1024),external:Math.round(e.external/1024/1024)})};t();let r=setInterval(t,e);return()=>clearInterval(r)},[e]),t}export{W as App,R as ComponentNotFoundError,s as DirectoryNotFoundError,l as DuplicateScreenError,L as EmptyChainError,F as ErrorBoundary,V as ErrorScreen,U as FileRouter,t as FileRouterError,a as NotADirectoryError,B as NotFoundBoundary,I as NotFoundError,H as NotFoundScreen,d as RootIsFileError,O as RouterContext,k as RouterProvider,o as createComponentMap,u as createElementTrees,e as createFileTree,c as createRouteTable,i as createSegmentTree,n as defineConfig,r as loadConfig,j as useHistory,G as useMemoryMonitor,M as useNavigate,N as useRouteData,A as useRouter,P as useUrl};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["push","actions.push","replace","actions.replace","back","actions.back","forward","actions.forward"],"sources":["../src/core/router/actions.ts","../src/core/router/RouterProvider.tsx","../src/core/router/hooks/use-router.ts","../src/core/router/hooks/use-history.ts","../src/core/router/hooks/use-navigate.ts","../src/core/router/hooks/use-route-data.ts","../src/core/router/hooks/use-url.ts","../src/core/runtime/boundaries/ErrorBoundary.tsx","../src/core/runtime/errors.ts","../src/core/runtime/boundaries/NotFoundBoundary.tsx","../src/core/runtime/screens/ErrorScreen.tsx","../src/core/runtime/screens/NotFoundScreen.tsx","../src/core/runtime/routing/composer.ts","../src/core/runtime/routing/matcher.ts","../src/core/runtime/FileRouter.tsx","../src/core/runtime/App.tsx"],"sourcesContent":["import type { NavigationHistory } from './types'\r\n\r\nexport function push(prev: NavigationHistory, newUrl: string, newData?: unknown): NavigationHistory {\r\n return {\r\n stack: [...prev.stack.slice(0, prev.position + 1), { url: newUrl, data: newData }],\r\n position: prev.position + 1,\r\n }\r\n}\r\n\r\nexport function replace(prev: NavigationHistory, newUrl: string): NavigationHistory {\r\n const newStack = [...prev.stack]\r\n newStack[prev.position] = { url: newUrl }\r\n return { ...prev, stack: newStack }\r\n}\r\n\r\nexport function back(prev: NavigationHistory): NavigationHistory {\r\n return prev.position > 0\r\n ? { ...prev, position: prev.position - 1 }\r\n : prev\r\n}\r\n\r\nexport function forward(prev: NavigationHistory): NavigationHistory {\r\n return prev.position < prev.stack.length - 1\r\n ? { ...prev, position: prev.position + 1 }\r\n : prev\r\n}\r\n","import { createContext, useState, useCallback, type PropsWithChildren } from 'react'\r\nimport * as actions from './actions'\r\nimport type { NavigationHistory } from './types'\r\n\r\nexport interface RouterContextValue {\r\n url: string\r\n data: unknown | undefined\r\n history: readonly string[] // Expose as readonly\r\n position: number // Expose current position\r\n push: (url: string, data?: unknown) => void\r\n replace: (url: string) => void\r\n back: () => void\r\n forward: () => void\r\n}\r\n\r\nexport interface RouterProviderProps extends PropsWithChildren {\r\n initialUrl: string\r\n}\r\n\r\n// Step 1: Define what data exists on the channel\r\nexport const RouterContext = createContext<RouterContextValue | null>(null)\r\n\r\n// Step 2: Broadcast the data\r\nexport function RouterProvider({ initialUrl, children }: RouterProviderProps) {\r\n const [history, setHistory] = useState<NavigationHistory>({\r\n stack: [{ url: initialUrl }],\r\n position: 0,\r\n })\r\n\r\n // Grab the current url and data\r\n const { url, data } = history.stack[history.position] ?? { url: initialUrl }\r\n\r\n // Push new URL and data to history\r\n const push = useCallback((newUrl: string, newData?: unknown) => {\r\n setHistory(prev => actions.push(prev, newUrl, newData))\r\n }, [])\r\n\r\n // Replace current URL without adding to history\r\n const replace = useCallback((newUrl: string) => {\r\n setHistory(prev => actions.replace(prev, newUrl))\r\n }, [])\r\n\r\n // Navigate backwards\r\n const back = useCallback(() => {\r\n setHistory(prev => actions.back(prev))\r\n }, [])\r\n\r\n // Navigate forwards\r\n const forward = useCallback(() => {\r\n setHistory(prev => actions.forward(prev))\r\n }, [])\r\n\r\n return (\r\n <RouterContext.Provider\r\n value={{\r\n url,\r\n data,\r\n history: history.stack.map(entry => entry.url),\r\n position: history.position,\r\n push,\r\n replace,\r\n back,\r\n forward,\r\n }}\r\n >\r\n {children}\r\n </RouterContext.Provider>\r\n )\r\n}\r\n","import { useContext } from 'react'\r\nimport { RouterContext } from '../RouterProvider'\r\n\r\n// Step 3: Tune into the channel and receive the broadcast\r\nexport function useRouter() {\r\n const context = useContext(RouterContext)\r\n if (!context)\r\n throw new Error('useRouter must be used within a RouterProvider')\r\n return context\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Get the navigation history (URLs only)\r\n *\r\n * @returns readonly array of visited URLs\r\n *\r\n * @example\r\n * ```tsx\r\n * const history = useHistory()\r\n * return (\r\n * <Box>\r\n * <Text>Visited pages:</Text>\r\n * {history.map((url, i) => (\r\n * <Text key={i}>{url}</Text>\r\n * ))}\r\n * </Box>\r\n * )\r\n * ```\r\n */\r\nexport function useHistory(): readonly string[] {\r\n const { history } = useRouter()\r\n return history\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Get navigation functions without url/data\r\n */\r\nexport function useNavigate() {\r\n const { push, replace, back, forward } = useRouter()\r\n return { push, replace, back, forward }\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Access route data passed via router.push()\r\n *\r\n * @example\r\n * ```tsx\r\n * const user = useRouteData<User>()\r\n * if (!user) return <Text>No user</Text>\r\n * return <Text>{user.name}</Text>\r\n * ```\r\n */\r\nexport function useRouteData<T = unknown>() {\r\n const { data } = useRouter()\r\n return data as T | undefined\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Get the current URL\r\n */\r\nexport function useUrl(): string {\r\n const { url } = useRouter()\r\n return url\r\n}\r\n","import { Component, type ComponentType, type PropsWithChildren } from 'react'\r\n\r\n/** Props passed to error.tsx components */\r\nexport interface ErrorComponentProps {\r\n error: Error\r\n reset: () => void\r\n}\r\n\r\ninterface ErrorBoundaryProps extends PropsWithChildren {\r\n fallback: ComponentType<ErrorComponentProps>\r\n}\r\n\r\ninterface ErrorBoundaryState {\r\n error: Error | null\r\n}\r\n\r\n/**\r\n * React Error Boundary that catches errors in child components.\r\n * Renders the provided ErrorComponent when an error occurs.\r\n *\r\n * Catches:\r\n * - Errors during rendering\r\n * - Errors in lifecycle methods\r\n * - Errors in constructors\r\n *\r\n * Does NOT catch:\r\n * - Event handlers (use try-catch)\r\n * - Async code (use try-catch)\r\n * - Errors in the error boundary itself\r\n */\r\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\r\n state: ErrorBoundaryState\r\n\r\n constructor(props: ErrorBoundaryProps) {\r\n super(props)\r\n this.state = { error: null }\r\n this.reset = this.reset.bind(this) // Bind all methods that will be passed as callbacks\r\n }\r\n\r\n /** Called when a child component throws during render. */\r\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\r\n return { error }\r\n }\r\n\r\n /** Called after error is caught. */\r\n componentDidCatch() {\r\n // Error is passed to error.tsx component via props\r\n }\r\n\r\n /** Resets error state and attempts to re-render children. */\r\n reset() {\r\n this.setState({ error: null })\r\n }\r\n\r\n render() {\r\n if (this.state.error) {\r\n const ErrorComponent = this.props.fallback\r\n return <ErrorComponent error={this.state.error} reset={this.reset} />\r\n }\r\n return this.props.children\r\n }\r\n}\r\n","/** Special error thrown to trigger not-found.tsx. */\r\nexport class NotFoundError extends Error {\r\n public readonly url: string\r\n\r\n constructor(url: string, message: string = 'Not Found') {\r\n super(message)\r\n this.name = 'NotFoundError'\r\n this.url = url\r\n }\r\n}\r\n\r\n/** Error thrown when a route has an empty chain. */\r\nexport class EmptyChainError extends Error {\r\n constructor(public url: string) {\r\n super(\r\n `Route ${url} has an empty chain. ` +\r\n 'This indicates a bug in manifest generation - ' +\r\n 'routes must have at least one segment.',\r\n )\r\n this.name = 'EmptyChainError'\r\n }\r\n}\r\n\r\n/** Error thrown when a component is missing from the component map. */\r\nexport class ComponentNotFoundError extends Error {\r\n constructor(public componentPath: string) {\r\n super(\r\n `Component not found: ${componentPath}. ` +\r\n 'This indicates the component map is out of sync with the manifest. ' +\r\n 'Try running \\'pen build\\' again.',\r\n )\r\n this.name = 'ComponentNotFoundError'\r\n }\r\n}\r\n","import { Component, type ComponentType, type PropsWithChildren } from 'react'\r\nimport { useRouter } from '@/core/router'\r\nimport { NotFoundError } from '../errors'\r\n\r\n/** Props passed to not-found.tsx components */\r\nexport interface NotFoundComponentProps {\r\n url: string\r\n}\r\n\r\ninterface NotFoundErrorBoundaryProps extends PropsWithChildren {\r\n fallback: ComponentType<NotFoundComponentProps>\r\n url: string\r\n}\r\n\r\ninterface NotFoundBoundaryState {\r\n url: string | null\r\n}\r\n\r\n/**\r\n * Catches NotFoundError thrown by notFound() function.\r\n * Renders fallback component when caught.\r\n *\r\n * Does NOT catch:\r\n * - Non-NotFoundError errors (they bubble)\r\n */\r\nclass NotFoundErrorBoundary extends Component<NotFoundErrorBoundaryProps, NotFoundBoundaryState> {\r\n state: NotFoundBoundaryState\r\n\r\n constructor(props: NotFoundErrorBoundaryProps) {\r\n super(props)\r\n this.state = { url: null }\r\n }\r\n\r\n /** Called when a child component throws during render. */\r\n static getDerivedStateFromError(error: Error) {\r\n // Only catch NotFoundError, other errors bubble\r\n return error instanceof NotFoundError ? { url: error.url } : null\r\n }\r\n\r\n /** Called after error is caught. */\r\n componentDidCatch(error: Error) {\r\n if (!(error instanceof NotFoundError)) // Only handle NotFoundError\r\n throw error // Re-throw other errors\r\n }\r\n\r\n /** Called after updates. Clears the not-found state when the URL prop changes. */\r\n componentDidUpdate(prevProps: NotFoundErrorBoundaryProps) {\r\n if (prevProps.url !== this.props.url && this.state.url) // URL changed → clear not-found state\r\n this.setState({ url: null })\r\n }\r\n\r\n render() {\r\n if (this.state.url) {\r\n const NotFoundComponent = this.props.fallback\r\n return <NotFoundComponent url={this.state.url} />\r\n }\r\n return this.props.children\r\n }\r\n}\r\n\r\nexport interface NotFoundBoundaryProps extends PropsWithChildren {\r\n fallback: ComponentType<NotFoundComponentProps>\r\n}\r\n\r\n/**\r\n * Router-aware wrapper for NotFoundErrorBoundary.\r\n * Injects the current URL so the boundary can reset on navigation.\r\n */\r\nexport function NotFoundBoundary({ fallback, children }: NotFoundBoundaryProps) {\r\n const { url } = useRouter()\r\n return (\r\n <NotFoundErrorBoundary fallback={fallback} url={url}>\r\n {children}\r\n </NotFoundErrorBoundary>\r\n )\r\n}\r\n","// src/core/router/components/GlobalErrorFallback.tsx\r\nimport { Box, Text, useInput } from 'ink'\r\nimport { useState } from 'react'\r\nimport { type ErrorComponentProps } from '../boundaries/ErrorBoundary'\r\n\r\n/** Global error fallback that always wraps the app. */\r\nexport function ErrorScreen({ error, reset }: ErrorComponentProps) {\r\n const [showStack, setShowStack] = useState(false)\r\n\r\n useInput((input) => {\r\n if (input === 'r') reset()\r\n if (input === 's') setShowStack(prev => !prev)\r\n if (input === 'q') process.exit(1)\r\n })\r\n\r\n return (\r\n <Box\r\n flexDirection=\"column\"\r\n padding={2}\r\n borderStyle=\"double\"\r\n borderColor=\"red\"\r\n >\r\n <Box marginBottom={1}>\r\n <Text bold color=\"red\">💥 Critical Application Error</Text>\r\n </Box>\r\n\r\n <Box marginBottom={1}>\r\n <Text color=\"red\">The application encountered a fatal error and cannot continue.</Text>\r\n </Box>\r\n\r\n <Box\r\n flexDirection=\"column\"\r\n padding={1}\r\n borderStyle=\"round\"\r\n borderColor=\"yellow\"\r\n marginBottom={1}\r\n >\r\n <Text bold color=\"yellow\">Error Details:</Text>\r\n <Text>{error.message}</Text>\r\n </Box>\r\n\r\n {showStack && error.stack && (\r\n <Box\r\n flexDirection=\"column\"\r\n padding={1}\r\n borderStyle=\"single\"\r\n borderColor=\"gray\"\r\n marginBottom={1}\r\n >\r\n <Text bold dimColor>Stack Trace:</Text>\r\n <Text dimColor>{error.stack}</Text>\r\n </Box>\r\n )}\r\n\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold>Options:</Text>\r\n <Text dimColor> [r] Retry [s] Toggle Stack Trace [q] Quit</Text>\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n If this error persists, please report it.\r\n </Text>\r\n </Box>\r\n </Box>\r\n )\r\n}\r\n","import { Box, Text, useInput } from 'ink'\r\nimport { useRouter } from '@/core/router'\r\nimport { type NotFoundComponentProps } from '../boundaries/NotFoundBoundary'\r\n\r\n/**\r\n * Not found error screen displayed when a route is not found.\r\n * Shows the URL that was attempted.\r\n */\r\nexport function NotFoundScreen({ url }: NotFoundComponentProps) {\r\n const router = useRouter()\r\n\r\n useInput((input) => {\r\n if (input === 'b') router.back()\r\n if (input === 'q') process.exit(0)\r\n })\r\n\r\n return (\r\n <Box flexDirection=\"column\" padding={2} borderStyle=\"round\" borderColor=\"red\">\r\n <Box flexDirection=\"column\" marginBottom={1}>\r\n <Text bold color=\"red\">Route Not Found</Text>\r\n <Text dimColor>{'The router couldn\\'t match the requested URL.'}</Text>\r\n </Box>\r\n\r\n <Box flexDirection=\"column\" paddingLeft={1} marginBottom={1}>\r\n <Text>\r\n Requested:{' '}\r\n <Text color=\"yellow\" bold>\r\n {url}\r\n </Text>\r\n </Text>\r\n </Box>\r\n\r\n <Box\r\n flexDirection=\"column\"\r\n padding={1}\r\n borderStyle=\"single\"\r\n borderColor=\"gray\"\r\n marginBottom={1}\r\n >\r\n <Text bold>Actions</Text>\r\n <Text dimColor> [b] Go back</Text>\r\n <Text dimColor> [q] Quit</Text>\r\n </Box>\r\n\r\n <Text dimColor>\r\n Tip: Check the route segment name or add a screen.tsx for this path.\r\n </Text>\r\n </Box>\r\n )\r\n}\r\n","import { createElement } from 'react'\r\n\r\nimport { ErrorBoundary } from '../boundaries/ErrorBoundary'\r\nimport { NotFoundBoundary } from '../boundaries/NotFoundBoundary'\r\nimport { ComponentNotFoundError, EmptyChainError } from '../errors'\r\n\r\nimport type { ComponentType, ReactElement } from 'react'\r\nimport type { Route } from '@/core/route-builder'\r\nimport type { ErrorComponentProps } from '../boundaries/ErrorBoundary'\r\nimport type { NotFoundComponentProps } from '../boundaries/NotFoundBoundary'\r\nimport type { ComponentMap } from '../types'\r\n\r\n/**\r\n * Gets a component from the component map or throws.\r\n */\r\nfunction getComponent(components: ComponentMap, path: string): ComponentType {\r\n const component = components[path]\r\n if (!component) throw new ComponentNotFoundError(path)\r\n return component\r\n}\r\n\r\n/**\r\n * Composes a route element by processing each segment in the chain.\r\n *\r\n * Composition order per segment (inside to outside):\r\n * 1. Screen component (only in leaf segment)\r\n * 2. Not-found boundary (wraps screen if present)\r\n * 3. Layout (wraps content)\r\n * 4. Error boundary (wraps layout + all descendants)\r\n *\r\n * This ensures error boundaries catch errors at the right level,\r\n * and only layouts above the error remain visible.\r\n */\r\nexport function composeRoute(route: Route, components: ComponentMap): ReactElement {\r\n if (!route.chain.length)\r\n throw new EmptyChainError(route.url)\r\n\r\n // Start with the screen (guaranteed in first segment)\r\n const leafSegment = route.chain[0]!\r\n const screenPath = leafSegment['screen']!\r\n let element = createElement(getComponent(components, screenPath), { key: screenPath })\r\n\r\n // Process segments from leaf → root\r\n for (const segment of route.chain) {\r\n // Not-found boundary\r\n if (segment['not-found']) {\r\n const path = segment['not-found']\r\n const fallback = getComponent(components, path) as ComponentType<NotFoundComponentProps>\r\n element = createElement(NotFoundBoundary, { key: path, fallback }, element)\r\n }\r\n\r\n // Error boundary\r\n if (segment['error']) {\r\n const path = segment['error']\r\n const fallback = getComponent(components, path) as ComponentType<ErrorComponentProps>\r\n element = createElement(ErrorBoundary, { key: path, fallback }, element)\r\n }\r\n\r\n // Layout\r\n if (segment['layout']) {\r\n const path = segment['layout']\r\n element = createElement(getComponent(components, path), { key: path }, element)\r\n }\r\n }\r\n\r\n return element\r\n}\r\n","import type { Route, RouteManifest } from '@/core/route-builder'\r\n\r\n/**\r\n * Matches a URL against the route manifest.\r\n * Returns the matched route metadata or null if no match found.\r\n */\r\nexport function matchRoute(url: string, manifest: RouteManifest): Route | null {\r\n // Normalize URL (ensure trailing slash)\r\n const normalizedUrl = url.endsWith('/') ? url : `${url}/`\r\n\r\n // Direct lookup\r\n return manifest[normalizedUrl] ?? null\r\n}\r\n","import { useRouter } from '@/core/router'\r\nimport { composeRoute } from './routing/composer'\r\nimport { matchRoute } from './routing/matcher'\r\nimport { NotFoundError } from './errors'\r\n\r\nimport type { ReactElement } from 'react'\r\nimport type { RouteManifest } from '@/core/route-builder'\r\nimport type { ComponentMap } from './types'\r\n\r\n/**\r\n * Props for the FileRouter component.\r\n * Defines the URL to render, the route manifest, and component map.\r\n */\r\nexport interface FileRouterProps {\r\n manifest: RouteManifest\r\n components: ComponentMap\r\n}\r\n\r\n/**\r\n * Router component that orchestrates route matching and composition.\r\n * Returns the composed route element or throws NotFoundError.\r\n */\r\nexport function FileRouter({ manifest, components }: FileRouterProps): ReactElement {\r\n const { url } = useRouter()\r\n const route = matchRoute(url, manifest)\r\n\r\n if (!route)\r\n throw new NotFoundError(url)\r\n\r\n return composeRoute(route, components)\r\n}\r\n","import { RouterProvider } from '@/core/router'\r\nimport { ErrorBoundary } from './boundaries/ErrorBoundary'\r\nimport { NotFoundBoundary } from './boundaries/NotFoundBoundary'\r\nimport { ErrorScreen } from './screens/ErrorScreen'\r\nimport { NotFoundScreen } from './screens/NotFoundScreen'\r\nimport { FileRouter } from './FileRouter'\r\n\r\nimport type { RouteManifest } from '@/core/route-builder'\r\nimport type { ComponentMap } from './types'\r\n\r\nexport interface AppProps {\r\n initialUrl: string\r\n manifest: RouteManifest\r\n components: ComponentMap\r\n}\r\n\r\nexport function App({ initialUrl, manifest, components }: AppProps) {\r\n return (\r\n <ErrorBoundary fallback={ErrorScreen}>\r\n <RouterProvider initialUrl={initialUrl}>\r\n <NotFoundBoundary fallback={NotFoundScreen}>\r\n <FileRouter manifest={manifest} components={components} />\r\n </NotFoundBoundary>\r\n </RouterProvider>\r\n </ErrorBoundary>\r\n )\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["push","actions.push","replace","actions.replace","back","actions.back","forward","actions.forward"],"sources":["../src/pen/router/actions.ts","../src/pen/router/RouterProvider.tsx","../src/pen/router/hooks/use-router.ts","../src/pen/router/hooks/use-history.ts","../src/pen/router/hooks/use-navigate.ts","../src/pen/router/hooks/use-route-data.ts","../src/pen/router/hooks/use-url.ts","../src/pen/runtime/ui/ErrorBoundary.tsx","../src/pen/runtime/errors.ts","../src/pen/runtime/ui/NotFoundBoundary.tsx","../src/pen/runtime/ui/ErrorScreen.tsx","../src/pen/runtime/ui/NotFoundScreen.tsx","../src/pen/runtime/FileRouter.tsx","../src/pen/runtime/App.tsx","../src/pen/utils/use-memory-monitor.ts"],"sourcesContent":["import type { NavigationHistory } from './types'\r\n\r\n/**\r\n * Normalizes a URL to include a trailing slash.\r\n * Routes are keyed with trailing slashes in the manifest.\r\n */\r\nexport function normalizeUrl(url: string): string {\r\n return url.endsWith('/') ? url : `${url}/`\r\n}\r\n\r\nexport function push(prev: NavigationHistory, newUrl: string, newData?: unknown): NavigationHistory {\r\n return {\r\n stack: [...prev.stack.slice(0, prev.position + 1), { url: normalizeUrl(newUrl), data: newData }],\r\n position: prev.position + 1,\r\n }\r\n}\r\n\r\nexport function replace(prev: NavigationHistory, newUrl: string): NavigationHistory {\r\n const newStack = [...prev.stack]\r\n newStack[prev.position] = { url: normalizeUrl(newUrl) }\r\n return { ...prev, stack: newStack }\r\n}\r\n\r\nexport function back(prev: NavigationHistory): NavigationHistory {\r\n return prev.position > 0\r\n ? { ...prev, position: prev.position - 1 }\r\n : prev\r\n}\r\n\r\nexport function forward(prev: NavigationHistory): NavigationHistory {\r\n return prev.position < prev.stack.length - 1\r\n ? { ...prev, position: prev.position + 1 }\r\n : prev\r\n}\r\n","import { createContext, useState, useCallback, type PropsWithChildren } from 'react'\r\nimport * as actions from './actions'\r\nimport { normalizeUrl } from './actions'\r\nimport type { NavigationHistory } from './types'\r\n\r\nexport interface RouterContextValue {\r\n url: string\r\n data: unknown | undefined\r\n history: readonly string[] // Expose as readonly\r\n position: number // Expose current position\r\n push: (url: string, data?: unknown) => void\r\n replace: (url: string) => void\r\n back: () => void\r\n forward: () => void\r\n}\r\n\r\nexport interface RouterProviderProps extends PropsWithChildren {\r\n initialUrl: string\r\n}\r\n\r\n// Step 1: Define what data exists on the channel\r\nexport const RouterContext = createContext<RouterContextValue | null>(null)\r\n\r\n// Step 2: Broadcast the data\r\nexport function RouterProvider({ initialUrl, children }: RouterProviderProps) {\r\n const normalizedInitialUrl = normalizeUrl(initialUrl)\r\n const [history, setHistory] = useState<NavigationHistory>({\r\n stack: [{ url: normalizedInitialUrl }],\r\n position: 0,\r\n })\r\n\r\n // Grab the current url and data\r\n const { url, data } = history.stack[history.position] ?? { url: normalizedInitialUrl }\r\n\r\n // Push new URL and data to history\r\n const push = useCallback((newUrl: string, newData?: unknown) => {\r\n setHistory(prev => actions.push(prev, newUrl, newData))\r\n }, [])\r\n\r\n // Replace current URL without adding to history\r\n const replace = useCallback((newUrl: string) => {\r\n setHistory(prev => actions.replace(prev, newUrl))\r\n }, [])\r\n\r\n // Navigate backwards\r\n const back = useCallback(() => {\r\n setHistory(prev => actions.back(prev))\r\n }, [])\r\n\r\n // Navigate forwards\r\n const forward = useCallback(() => {\r\n setHistory(prev => actions.forward(prev))\r\n }, [])\r\n\r\n return (\r\n <RouterContext.Provider\r\n value={{\r\n url,\r\n data,\r\n history: history.stack.map(entry => entry.url),\r\n position: history.position,\r\n push,\r\n replace,\r\n back,\r\n forward,\r\n }}\r\n >\r\n {children}\r\n </RouterContext.Provider>\r\n )\r\n}\r\n","import { useContext } from 'react'\r\nimport { RouterContext } from '../RouterProvider'\r\n\r\n// Step 3: Tune into the channel and receive the broadcast\r\nexport function useRouter() {\r\n const context = useContext(RouterContext)\r\n if (!context)\r\n throw new Error('useRouter must be used within a RouterProvider')\r\n return context\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Get the navigation history (URLs only)\r\n *\r\n * @returns readonly array of visited URLs\r\n *\r\n * @example\r\n * ```tsx\r\n * const history = useHistory()\r\n * return (\r\n * <Box>\r\n * <Text>Visited pages:</Text>\r\n * {history.map((url, i) => (\r\n * <Text key={i}>{url}</Text>\r\n * ))}\r\n * </Box>\r\n * )\r\n * ```\r\n */\r\nexport function useHistory(): readonly string[] {\r\n const { history } = useRouter()\r\n return history\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Get navigation functions without url/data\r\n */\r\nexport function useNavigate() {\r\n const { push, replace, back, forward } = useRouter()\r\n return { push, replace, back, forward }\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Access route data passed via router.push()\r\n *\r\n * @example\r\n * ```tsx\r\n * const user = useRouteData<User>()\r\n * if (!user) return <Text>No user</Text>\r\n * return <Text>{user.name}</Text>\r\n * ```\r\n */\r\nexport function useRouteData<T = unknown>() {\r\n const { data } = useRouter()\r\n return data as T | undefined\r\n}\r\n","import { useRouter } from './use-router'\r\n\r\n/**\r\n * Get the current URL\r\n */\r\nexport function useUrl(): string {\r\n const { url } = useRouter()\r\n return url\r\n}\r\n","import { Component, type ComponentType, type PropsWithChildren } from 'react'\r\n\r\n/** Props passed to error.tsx components */\r\nexport interface ErrorComponentProps {\r\n error: Error\r\n reset: () => void\r\n}\r\n\r\ninterface ErrorBoundaryProps extends PropsWithChildren {\r\n fallback: ComponentType<ErrorComponentProps>\r\n}\r\n\r\ninterface ErrorBoundaryState {\r\n error: Error | null\r\n}\r\n\r\n/**\r\n * React Error Boundary that catches errors in child components.\r\n * Renders the provided ErrorComponent when an error occurs.\r\n *\r\n * Catches:\r\n * - Errors during rendering\r\n * - Errors in lifecycle methods\r\n * - Errors in constructors\r\n *\r\n * Does NOT catch:\r\n * - Event handlers (use try-catch)\r\n * - Async code (use try-catch)\r\n * - Errors in the error boundary itself\r\n */\r\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\r\n state: ErrorBoundaryState\r\n\r\n constructor(props: ErrorBoundaryProps) {\r\n super(props)\r\n this.state = { error: null }\r\n this.reset = this.reset.bind(this) // Bind all methods that will be passed as callbacks\r\n }\r\n\r\n /** Called when a child component throws during render. */\r\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\r\n return { error }\r\n }\r\n\r\n /** Called after error is caught. */\r\n componentDidCatch() {\r\n // Error is passed to error.tsx component via props\r\n }\r\n\r\n /** Resets error state and attempts to re-render children. */\r\n reset() {\r\n this.setState({ error: null })\r\n }\r\n\r\n render() {\r\n if (this.state.error) {\r\n const ErrorComponent = this.props.fallback\r\n return <ErrorComponent error={this.state.error} reset={this.reset} />\r\n }\r\n return this.props.children\r\n }\r\n}\r\n","/** Special error thrown to trigger not-found.tsx. */\r\nexport class NotFoundError extends Error {\r\n public readonly url: string\r\n\r\n constructor(url: string, message: string = 'Not Found') {\r\n super(message)\r\n this.name = 'NotFoundError'\r\n this.url = url\r\n }\r\n}\r\n\r\n/** Error thrown when a route has an empty chain. */\r\nexport class EmptyChainError extends Error {\r\n constructor(public url: string) {\r\n super(\r\n `Route ${url} has an empty chain. ` +\r\n 'This indicates a bug in manifest generation - ' +\r\n 'routes must have at least one segment.',\r\n )\r\n this.name = 'EmptyChainError'\r\n }\r\n}\r\n\r\n/** Error thrown when a component is missing from the component map. */\r\nexport class ComponentNotFoundError extends Error {\r\n constructor(public componentPath: string) {\r\n super(\r\n `Component not found: ${componentPath}. ` +\r\n 'This indicates the component map is out of sync with the manifest. ' +\r\n 'Try running \\'pen build\\' again.',\r\n )\r\n this.name = 'ComponentNotFoundError'\r\n }\r\n}\r\n","import { Component, type ComponentType, type PropsWithChildren } from 'react'\r\nimport { useRouter } from '@/pen/router'\r\nimport { NotFoundError } from '../errors'\r\n\r\n/** Props passed to not-found.tsx components */\r\nexport interface NotFoundComponentProps {\r\n url: string\r\n}\r\n\r\ninterface NotFoundErrorBoundaryProps extends PropsWithChildren {\r\n fallback: ComponentType<NotFoundComponentProps>\r\n url: string\r\n}\r\n\r\ninterface NotFoundBoundaryState {\r\n url: string | null\r\n}\r\n\r\n/**\r\n * Catches NotFoundError thrown by notFound() function.\r\n * Renders fallback component when caught.\r\n *\r\n * Does NOT catch:\r\n * - Non-NotFoundError errors (they bubble)\r\n */\r\nclass NotFoundErrorBoundary extends Component<NotFoundErrorBoundaryProps, NotFoundBoundaryState> {\r\n state: NotFoundBoundaryState\r\n\r\n constructor(props: NotFoundErrorBoundaryProps) {\r\n super(props)\r\n this.state = { url: null }\r\n }\r\n\r\n /** Called when a child component throws during render. */\r\n static getDerivedStateFromError(error: Error) {\r\n // Only catch NotFoundError, other errors bubble\r\n return error instanceof NotFoundError ? { url: error.url } : null\r\n }\r\n\r\n /** Called after error is caught. */\r\n componentDidCatch(error: Error) {\r\n if (!(error instanceof NotFoundError)) // Only handle NotFoundError\r\n throw error // Re-throw other errors\r\n }\r\n\r\n /** Called after updates. Clears the not-found state when the URL prop changes. */\r\n componentDidUpdate(prevProps: NotFoundErrorBoundaryProps) {\r\n if (prevProps.url !== this.props.url && this.state.url) // URL changed → clear not-found state\r\n this.setState({ url: null })\r\n }\r\n\r\n render() {\r\n if (this.state.url) {\r\n const NotFoundComponent = this.props.fallback\r\n return <NotFoundComponent url={this.state.url} />\r\n }\r\n return this.props.children\r\n }\r\n}\r\n\r\nexport interface NotFoundBoundaryProps extends PropsWithChildren {\r\n fallback: ComponentType<NotFoundComponentProps>\r\n}\r\n\r\n/**\r\n * Router-aware wrapper for NotFoundErrorBoundary.\r\n * Injects the current URL so the boundary can reset on navigation.\r\n */\r\nexport function NotFoundBoundary({ fallback, children }: NotFoundBoundaryProps) {\r\n const { url } = useRouter()\r\n return (\r\n <NotFoundErrorBoundary fallback={fallback} url={url}>\r\n {children}\r\n </NotFoundErrorBoundary>\r\n )\r\n}\r\n","// src/core/router/components/GlobalErrorFallback.tsx\r\nimport { Box, Text, useInput } from 'ink'\r\nimport { useState } from 'react'\r\nimport { type ErrorComponentProps } from './ErrorBoundary'\r\n\r\n/** Global error fallback that always wraps the app. */\r\nexport function ErrorScreen({ error, reset }: ErrorComponentProps) {\r\n const [showStack, setShowStack] = useState(false)\r\n\r\n useInput((input) => {\r\n if (input === 'r') reset()\r\n if (input === 's') setShowStack(prev => !prev)\r\n if (input === 'q') process.exit(1)\r\n })\r\n\r\n return (\r\n <Box\r\n flexDirection=\"column\"\r\n padding={2}\r\n borderStyle=\"double\"\r\n borderColor=\"red\"\r\n >\r\n <Box marginBottom={1}>\r\n <Text bold color=\"red\">💥 Critical Application Error</Text>\r\n </Box>\r\n\r\n <Box marginBottom={1}>\r\n <Text color=\"red\">The application encountered a fatal error and cannot continue.</Text>\r\n </Box>\r\n\r\n <Box\r\n flexDirection=\"column\"\r\n padding={1}\r\n borderStyle=\"round\"\r\n borderColor=\"yellow\"\r\n marginBottom={1}\r\n >\r\n <Text bold color=\"yellow\">Error Details:</Text>\r\n <Text>{error.message}</Text>\r\n </Box>\r\n\r\n {showStack && error.stack && (\r\n <Box\r\n flexDirection=\"column\"\r\n padding={1}\r\n borderStyle=\"single\"\r\n borderColor=\"gray\"\r\n marginBottom={1}\r\n >\r\n <Text bold dimColor>Stack Trace:</Text>\r\n <Text dimColor>{error.stack}</Text>\r\n </Box>\r\n )}\r\n\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold>Options:</Text>\r\n <Text dimColor> [r] Retry [s] Toggle Stack Trace [q] Quit</Text>\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n If this error persists, please report it.\r\n </Text>\r\n </Box>\r\n </Box>\r\n )\r\n}\r\n","import { Box, Text, useInput } from 'ink'\r\nimport { useRouter } from '@/pen/router'\r\nimport { type NotFoundComponentProps } from './NotFoundBoundary'\r\n\r\n/**\r\n * Not found error screen displayed when a route is not found.\r\n * Shows the URL that was attempted.\r\n */\r\nexport function NotFoundScreen({ url }: NotFoundComponentProps) {\r\n const router = useRouter()\r\n\r\n useInput((input) => {\r\n if (input === 'b') router.back()\r\n if (input === 'q') process.exit(0)\r\n })\r\n\r\n return (\r\n <Box flexDirection=\"column\" padding={2} borderStyle=\"round\" borderColor=\"red\">\r\n <Box flexDirection=\"column\" marginBottom={1}>\r\n <Text bold color=\"red\">Route Not Found</Text>\r\n <Text dimColor>{'The router couldn\\'t match the requested URL.'}</Text>\r\n </Box>\r\n\r\n <Box flexDirection=\"column\" paddingLeft={1} marginBottom={1}>\r\n <Text>\r\n Requested:{' '}\r\n <Text color=\"yellow\" bold>\r\n {url}\r\n </Text>\r\n </Text>\r\n </Box>\r\n\r\n <Box\r\n flexDirection=\"column\"\r\n padding={1}\r\n borderStyle=\"single\"\r\n borderColor=\"gray\"\r\n marginBottom={1}\r\n >\r\n <Text bold>Actions</Text>\r\n <Text dimColor> [b] Go back</Text>\r\n <Text dimColor> [q] Quit</Text>\r\n </Box>\r\n\r\n <Text dimColor>\r\n Tip: Check the route segment name or add a screen.tsx for this path.\r\n </Text>\r\n </Box>\r\n )\r\n}\r\n","import { useRouter } from '@/pen/router'\r\nimport { NotFoundError } from './errors'\r\n\r\nimport type { ReactElement } from 'react'\r\nimport type { CompiledRoutes } from '../compiler/types'\r\n\r\n/**\r\n * Props for the FileRouter component.\r\n */\r\nexport interface FileRouterProps {\r\n routes: CompiledRoutes\r\n}\r\n\r\n/**\r\n * Router component that renders compiled route elements.\r\n * Simply looks up the current URL in the compiled routes map.\r\n */\r\nexport function FileRouter({ routes }: FileRouterProps): ReactElement {\r\n const { url } = useRouter()\r\n const element = routes[url]\r\n\r\n if (!element) throw new NotFoundError(url)\r\n return element\r\n}\r\n","import { RouterProvider } from '@/pen/router'\r\nimport { ErrorBoundary } from './ui/ErrorBoundary'\r\nimport { NotFoundBoundary } from './ui/NotFoundBoundary'\r\nimport { ErrorScreen } from './ui/ErrorScreen'\r\nimport { NotFoundScreen } from './ui/NotFoundScreen'\r\nimport { FileRouter } from './FileRouter'\r\nimport type { CompiledRoutes } from '../compiler/types'\r\n\r\nexport interface AppProps {\r\n initialUrl: string\r\n routes: CompiledRoutes\r\n}\r\n\r\n/**\r\n * Root application component.\r\n * Routes are compiled at build time via codegen - no runtime composition needed!\r\n */\r\nexport function App({ initialUrl, routes }: AppProps) {\r\n return (\r\n <ErrorBoundary fallback={ErrorScreen}>\r\n <RouterProvider initialUrl={initialUrl}>\r\n <NotFoundBoundary fallback={NotFoundScreen}>\r\n <FileRouter routes={routes} />\r\n </NotFoundBoundary>\r\n </RouterProvider>\r\n </ErrorBoundary>\r\n )\r\n}\r\n","import { useEffect, useState } from 'react'\r\n\r\nexport interface MemoryUsage {\r\n /** Heap memory currently used (in MB) */\r\n heapUsed: number\r\n /** Total heap memory allocated (in MB) */\r\n heapTotal: number\r\n /** Resident Set Size - total memory allocated for the process (in MB) */\r\n rss: number\r\n /** Memory used by C++ objects bound to JavaScript (in MB) */\r\n external: number\r\n}\r\n\r\n/**\r\n * Hook to monitor Node.js memory usage in real-time\r\n *\r\n * @param intervalMs - Polling interval in milliseconds (default: 1000ms)\r\n * @returns Current memory usage statistics in megabytes\r\n *\r\n * @example\r\n * ```tsx\r\n * function MemoryDisplay() {\r\n * const { heapUsed, heapTotal, rss } = useMemoryMonitor()\r\n * return <Text>RAM: {heapUsed}/{heapTotal} MB (RSS: {rss} MB)</Text>\r\n * }\r\n * ```\r\n */\r\nexport function useMemoryMonitor(intervalMs = 1000): MemoryUsage {\r\n const [memory, setMemory] = useState<MemoryUsage>({\r\n heapUsed: 0,\r\n heapTotal: 0,\r\n rss: 0,\r\n external: 0,\r\n })\r\n\r\n useEffect(() => {\r\n const updateMemory = () => {\r\n const usage = process.memoryUsage()\r\n setMemory({\r\n heapUsed: Math.round(usage.heapUsed / 1024 / 1024),\r\n heapTotal: Math.round(usage.heapTotal / 1024 / 1024),\r\n rss: Math.round(usage.rss / 1024 / 1024),\r\n external: Math.round(usage.external / 1024 / 1024),\r\n })\r\n }\r\n\r\n // Get initial reading immediately\r\n updateMemory()\r\n\r\n const interval = setInterval(updateMemory, intervalMs)\r\n\r\n return () => clearInterval(interval)\r\n }, [intervalMs])\r\n\r\n return memory\r\n}\r\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idlesummer/pen",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "File-based routing for React Ink apps (experimental)",
5
5
  "keywords": [
6
6
  "cli",
@@ -11,6 +11,7 @@
11
11
  "experimental",
12
12
  "learning-project"
13
13
  ],
14
+ "homepage": "https://github.com/idlesummer/cogni#readme",
14
15
  "repository": {
15
16
  "type": "git",
16
17
  "url": "https://github.com/idlesummer/pen.git"
@@ -34,7 +35,7 @@
34
35
  "examples/*"
35
36
  ],
36
37
  "scripts": {
37
- "build": "tsdown",
38
+ "build": "tsc --noEmit && tsdown",
38
39
  "lint": "eslint",
39
40
  "prepare": "npm run build",
40
41
  "test": "vitest",
@@ -43,7 +44,7 @@
43
44
  "test:ui": "vitest --ui"
44
45
  },
45
46
  "dependencies": {
46
- "@idlesummer/tasker": "^0.1.2",
47
+ "@idlesummer/tasker": "^0.2.0",
47
48
  "commander": "^14.0.2",
48
49
  "ora": "^9.0.0",
49
50
  "picomatch": "^4.0.3",
@@ -1,6 +0,0 @@
1
- import{join as e,parse as t,posix as n,relative as r,resolve as i}from"path";import{pathToFileURL as a}from"url";import{readdirSync as o,statSync as s}from"fs";function c(e){return e}const l={appDir:`./src/app`,outDir:`./.pen`};async function u(){try{let e=(await import(a(i(process.cwd(),`pen.config.ts`)).href)).default||{};return{...l,...e}}catch{return l}}function d(e){let{root:t,visit:n,expand:r,attach:i,filter:a}=e,o=[t];for(let e of o){n?.(e);let t=r?.(e);if(t)for(let n of t)i?.(n,e),(!a||a(n))&&o.push(n)}return t}function f(e){let{root:t,visit:n,expand:r,attach:i,filter:a}=e,o=[t];for(;o.length;){let e=o.pop();n?.(e);let t=r?.(e);if(!t)continue;let s=t.length;for(let n=s-1;n>=0;n--){let r=t[n];i?.(r,e),(!a||a(r))&&o.push(r)}}return t}function p(e,t,n){let r=[],i=e;for(;i;)r.push(n(i)),i=t(i);return r}var m=class extends Error{constructor(e){super(e),this.name=`FileRouterError`}},h=class extends m{constructor(e){super(`Directory not found: "${e}"`),this.path=e,this.name=`DirectoryNotFoundError`}},g=class extends m{constructor(e){super(`Path is not a directory: "${e}"`),this.path=e,this.name=`NotADirectoryError`}},_=class extends m{constructor(e){super(`Root path is a file, not a directory: "${e}"\n\nThe app directory must be a directory, not a file.`),this.path=e,this.name=`RootIsFileError`}},v=class extends m{constructor(e,t){super(`Conflicting screen routes found at "${e}":\n`+t.map(e=>` - ${e}`).join(`
2
- `)+`
3
-
4
- Each URL can only have one screen file.
5
- Move one screen to a different directory or rename the route segment.`),this.url=e,this.files=t,this.name=`DuplicateScreenError`}};function y(t){let r=i(t),a=s(r,{throwIfNoEntry:!1});if(!a)throw new h(r);if(!a.isDirectory())throw new g(r);let c={name:`app`,relPath:`/app`,absPath:r,children:[]};function l(t){let r=o(t.absPath,{withFileTypes:!0}),i=[];for(let a of r){if(!a.isFile()&&!a.isDirectory())continue;let r=n.join(t.relPath,a.name),o=e(t.absPath,a.name),s=a.isDirectory()?{name:a.name,relPath:r,absPath:o,children:[]}:{name:a.name,relPath:r,absPath:o};i.push(s)}return i.sort((e,t)=>e.name.localeCompare(t.name))}return d({root:c,expand:l,attach:(e,t)=>t.children.push(e),filter:e=>e.children!==void 0})}function b(e){if(e.children===void 0)throw new _(e.absPath);let r={segment:``,url:`/`,type:`page`,roles:{},children:[],file:e};if(!e.children.length)return r;let i={};function a(e){let n=e.file;if(!n.children?.length)return;for(let r of n.children){let{name:n,ext:i}=t(r.name);if(i===`.tsx`)switch(n){case`screen`:e.roles.screen=r.absPath;break;case`not-found`:e.roles[`not-found`]=r.absPath;break;case`error`:e.roles.error=r.absPath;break;case`layout`:e.roles.layout=r.absPath;break}}if(!e.roles.screen)return;let r=i[e.url];if(r)throw new v(e.url,[r,n.absPath]);i[e.url]=n.absPath}function o(e){let t=e.file;if(!t.children)return;let r=[];for(let i of t.children){if(!i.children||i.name.startsWith(`_`))continue;let t=i.name.startsWith(`(`)&&i.name.endsWith(`)`);r.push({segment:i.name,url:t?e.url:`${n.join(e.url,i.name)}/`,type:t?`group`:`page`,roles:{},parent:e,children:[],file:i})}return r.sort((e,t)=>e.segment.localeCompare(t.segment))}return f({root:r,visit:a,expand:o,attach:(e,t)=>t.children.push(e)})}function x(n){let r=t(n);return e(r.dir,r.name)}function S(e){let t={};function n(e){if(!e.roles.screen)return;let n=p(e,e=>e.parent,t=>{let n={},r=Object.entries(t.roles);for(let[i,a]of r)(i!==`screen`||t===e)&&(n[i]=x(a));return n});t[e.url]={url:e.url,chain:n}}return f({root:e,visit:n,expand:e=>e.children}),t}function C(e,t){let n=new Set;for(let t of Object.values(e))for(let e of t.chain)e.screen&&n.add(e.screen),e[`not-found`]&&n.add(e[`not-found`]),e.error&&n.add(e.error),e.layout&&n.add(e.layout);let i={},a=`${t}/generated`;for(let e of n)i[e]=r(a,e).replace(/\\/g,`/`)+`.js`;return i}export{h as a,g as c,c as d,u as f,y as i,_ as l,S as n,v as o,b as r,m as s,C as t,l as u};
6
- //# sourceMappingURL=component-map-CLP6MqF8.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"component-map-CLP6MqF8.mjs","names":[],"sources":["../src/core/config.ts","../src/lib/tree-utils.ts","../src/core/route-builder/errors.ts","../src/core/route-builder/builders/file-tree.ts","../src/core/route-builder/builders/segment-tree.ts","../src/lib/path-utils.ts","../src/core/route-builder/builders/route-manifest.ts","../src/core/route-builder/builders/component-map.ts"],"sourcesContent":["import { resolve } from 'path'\r\nimport { pathToFileURL } from 'url'\r\n\r\nexport interface PenConfig {\r\n /**\r\n * Directory containing your app routes.\r\n * @default './src/app'\r\n */\r\n appDir: string\r\n\r\n /**\r\n * Output directory for generated files and build artifacts.\r\n * @default './.pen'\r\n */\r\n outDir: string\r\n}\r\n\r\nexport function defineConfig(config: Partial<PenConfig>): Partial<PenConfig> {\r\n return config\r\n}\r\n\r\nexport const defaultConfig: PenConfig = {\r\n appDir: './src/app',\r\n outDir: './.pen',\r\n}\r\n\r\n/**\r\n * Loads pen.config.ts from the current directory.\r\n * Falls back to defaults if config file doesn't exist.\r\n */\r\nexport async function loadConfig(): Promise<PenConfig> {\r\n try {\r\n const configPath = resolve(process.cwd(), 'pen.config.ts')\r\n const configUrl = pathToFileURL(configPath).href\r\n\r\n const module = await import(configUrl) as { default?: Partial<PenConfig> }\r\n const userConfig = module.default || {}\r\n\r\n // Merge with defaults\r\n return { ...defaultConfig, ...userConfig }\r\n }\r\n catch {\r\n // Config file doesn't exist or failed to load - use defaults\r\n return defaultConfig\r\n }\r\n}\r\n","export type TraversalOptions<TNode> = {\r\n root: TNode\r\n visit?: (node: TNode) => void // Inspect/process current node\r\n expand?: (node: TNode) => TNode[] | undefined // Get/create child nodes\r\n attach?: (child: TNode, parent: TNode) => void // Link child to parent\r\n filter?: (child: TNode) => boolean // Control child traversal\r\n}\r\n\r\n/**\r\n * Traverse tree breadth-first (level by level).\r\n * Processes all siblings before any children.\r\n */\r\nexport function traverseBreadthFirst<TNode>(options: TraversalOptions<TNode>) {\r\n const { root, visit, expand, attach, filter } = options\r\n const queue = [root]\r\n\r\n for (const node of queue) {\r\n visit?.(node)\r\n\r\n const children = expand?.(node)\r\n if (!children) continue\r\n\r\n for (const child of children) {\r\n attach?.(child, node)\r\n\r\n if (!filter || filter(child))\r\n queue.push(child)\r\n }\r\n }\r\n return root\r\n}\r\n\r\n/**\r\n * Traverse tree depth-first with preorder traversal.\r\n * Processes parent before children, going deep before wide.\r\n */\r\nexport function traverseDepthFirst<TNode>(options: TraversalOptions<TNode>) {\r\n const { root, visit, expand, attach, filter } = options\r\n const stack = [root]\r\n\r\n while (stack.length) {\r\n const node = stack.pop()!\r\n visit?.(node)\r\n\r\n const children = expand?.(node)\r\n if (!children) continue\r\n\r\n // Process in reverse to maintain left-to-right\r\n // order when popping from stack\r\n const len = children.length\r\n for (let i = len-1; i >= 0; i--) {\r\n const child = children[i]!\r\n attach?.(child, node)\r\n\r\n if (!filter || filter(child))\r\n stack.push(child)\r\n }\r\n }\r\n return root\r\n}\r\n\r\nexport function collectAncestors<T, R>(\r\n node: T,\r\n parent: (node: T) => T | undefined,\r\n mapper: (node: T) => R,\r\n): R[] {\r\n const results: R[] = []\r\n let currentNode: T | undefined = node\r\n\r\n while (currentNode) {\r\n results.push(mapper(currentNode))\r\n currentNode = parent(currentNode)\r\n }\r\n\r\n return results\r\n}\r\n","/**\r\n * Base error for all route-builder build errors\r\n */\r\nexport class FileRouterError extends Error {\r\n constructor(message: string) {\r\n super(message)\r\n this.name = 'FileRouterError'\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// File Tree Errors\r\n// ============================================================================\r\n\r\nexport class DirectoryNotFoundError extends FileRouterError {\r\n constructor(public path: string) {\r\n super(`Directory not found: \"${path}\"`)\r\n this.name = 'DirectoryNotFoundError'\r\n }\r\n}\r\n\r\nexport class NotADirectoryError extends FileRouterError {\r\n constructor(public path: string) {\r\n super(`Path is not a directory: \"${path}\"`)\r\n this.name = 'NotADirectoryError'\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Route Tree Errors\r\n// ============================================================================\r\n\r\nexport class RootIsFileError extends FileRouterError {\r\n constructor(public path: string) {\r\n super(\r\n `Root path is a file, not a directory: \"${path}\"\\n\\n` +\r\n 'The app directory must be a directory, not a file.',\r\n )\r\n this.name = 'RootIsFileError'\r\n }\r\n}\r\n\r\nexport class DuplicateScreenError extends FileRouterError {\r\n constructor(\r\n public url: string,\r\n public files: string[],\r\n ) {\r\n super(\r\n `Conflicting screen routes found at \"${url}\":\\n` +\r\n files.map(f => ` - ${f}`).join('\\n') + '\\n\\n' +\r\n 'Each URL can only have one screen file.\\n' +\r\n 'Move one screen to a different directory or rename the route segment.',\r\n )\r\n this.name = 'DuplicateScreenError'\r\n }\r\n}\r\n","import { readdirSync, statSync } from 'fs'\r\nimport { resolve, join, posix } from 'path'\r\nimport { traverseBreadthFirst } from '@/lib/tree-utils'\r\nimport { DirectoryNotFoundError, NotADirectoryError } from '../errors'\r\n\r\nexport type FileNode = {\r\n name: string // dirent name\r\n relPath: string\r\n absPath: string\r\n children?: FileNode[] // present only for directories\r\n}\r\n\r\n/**\r\n * Builds a file tree from a directory path.\r\n *\r\n * @param appPath - Path to the app directory\r\n * @returns File tree structure\r\n * @throws {DirectoryNotFoundError} If the directory doesn't exist\r\n * @throws {NotADirectoryError} If the path is not a directory\r\n */\r\nexport function buildFileTree(appPath: string): FileNode {\r\n const rootPath = resolve(appPath)\r\n const stat = statSync(rootPath, { throwIfNoEntry: false })\r\n\r\n // Validation\r\n if (!stat)\r\n throw new DirectoryNotFoundError(rootPath)\r\n\r\n if (!stat.isDirectory())\r\n throw new NotADirectoryError(rootPath)\r\n\r\n // Root node\r\n const root: FileNode = {\r\n name: 'app',\r\n relPath: '/app',\r\n absPath: rootPath,\r\n children: [],\r\n }\r\n\r\n function expand(parentFile: FileNode) {\r\n const dirents = readdirSync(parentFile.absPath, { withFileTypes: true })\r\n const children: FileNode[] = []\r\n\r\n for (const dirent of dirents) {\r\n if (!dirent.isFile() && !dirent.isDirectory()) // Traverse only dirs and files\r\n continue\r\n\r\n const relPath = posix.join(parentFile.relPath, dirent.name)\r\n const absPath = join(parentFile.absPath, dirent.name)\r\n\r\n const child: FileNode = dirent.isDirectory()\r\n ? { name: dirent.name, relPath, absPath, children: [] }\r\n : { name: dirent.name, relPath, absPath }\r\n children.push(child)\r\n }\r\n return children.sort((a, b) => a.name.localeCompare(b.name))\r\n }\r\n\r\n return traverseBreadthFirst({\r\n root,\r\n expand,\r\n attach: (child, parent) => parent.children!.push(child),\r\n filter: (file) => file.children !== undefined,\r\n })\r\n}\r\n","import { parse, posix } from 'path'\r\nimport { traverseDepthFirst } from '@/lib/tree-utils'\r\nimport { RootIsFileError, DuplicateScreenError } from '../errors'\r\nimport type { FileNode } from './file-tree'\r\n\r\nexport type SegmentRoles = {\r\n 'layout'?: string\r\n 'screen'?: string\r\n 'error'?: string\r\n 'not-found'?: string\r\n}\r\n\r\nexport type SegmentNode = {\r\n segment: string // directory name\r\n url: string\r\n type: 'page' | 'group'\r\n roles: SegmentRoles\r\n parent?: SegmentNode\r\n children?: SegmentNode[]\r\n file: FileNode\r\n}\r\n\r\n/**\r\n * Builds a segment tree from a file system tree.\r\n *\r\n * @param fileTree - File system tree\r\n * @returns Segment tree with computed URLs and validated structure\r\n * @throws {RootIsFileError} If the root is a file instead of a directory\r\n * @throws {DuplicateScreenError} If multiple screens map to the same URL\r\n */\r\nexport function buildSegmentTree(fileTree: FileNode): SegmentNode {\r\n if (fileTree.children === undefined)\r\n throw new RootIsFileError(fileTree.absPath)\r\n\r\n // Special case: Root has \"/\" as url instead of \"/app\",\r\n // and empty segment instead of \"app\"\r\n const root: SegmentNode = {\r\n segment: '',\r\n url: '/',\r\n type: 'page',\r\n roles: {},\r\n children: [],\r\n file: fileTree,\r\n }\r\n\r\n // Early return if root has no children\r\n if (!fileTree.children.length)\r\n return root\r\n\r\n // For tracking duplicate screens\r\n const screenSegments: Record<string, string> = {}\r\n\r\n function visit(parentSegment: SegmentNode) {\r\n const parentFile = parentSegment.file\r\n if (!parentFile.children?.length) return\r\n\r\n // Step 1: Assign file to segment role\r\n for (const file of parentFile.children) {\r\n const { name, ext } = parse(file.name)\r\n if (ext !== '.tsx') continue\r\n\r\n switch (name) {\r\n case 'screen': parentSegment.roles['screen'] = file.absPath; break\r\n case 'not-found': parentSegment.roles['not-found'] = file.absPath; break\r\n case 'error': parentSegment.roles['error'] = file.absPath; break\r\n case 'layout': parentSegment.roles['layout'] = file.absPath; break\r\n }\r\n }\r\n\r\n // Step 2: Validate that screen URLs are unique across the entire tree\r\n if (!parentSegment.roles.screen)\r\n return // No screen to validate\r\n\r\n // Check if another screen already claimed this URL\r\n const existingFilePath = screenSegments[parentSegment.url]\r\n if (existingFilePath)\r\n throw new DuplicateScreenError(parentSegment.url, [existingFilePath, parentFile.absPath])\r\n\r\n screenSegments[parentSegment.url] = parentFile.absPath\r\n }\r\n\r\n function expand(parentSegment: SegmentNode) {\r\n const parentFile = parentSegment.file\r\n if (!parentFile.children) return // Skip expand if route has no children\r\n\r\n // Create container for route children\r\n const segments: SegmentNode[] = []\r\n\r\n for (const file of parentFile.children) {\r\n if (!file.children) continue // Skip if file (only dirs pass through)\r\n if (file.name.startsWith('_')) continue // Skip if private directory\r\n\r\n const isGroup = file.name.startsWith('(') && file.name.endsWith(')')\r\n segments.push({\r\n segment: file.name,\r\n url: isGroup ? parentSegment.url : `${posix.join(parentSegment.url, file.name)}/`,\r\n type: isGroup ? 'group' : 'page',\r\n roles: {},\r\n parent: parentSegment,\r\n children: [],\r\n file,\r\n })\r\n }\r\n\r\n return segments.sort((a, b) => a.segment.localeCompare(b.segment))\r\n }\r\n\r\n return traverseDepthFirst({\r\n root,\r\n visit,\r\n expand,\r\n attach: (child, parent) => parent.children!.push(child),\r\n })\r\n}\r\n","import { parse, join } from 'path'\r\n\r\nexport function removeExtension(filePath: string) {\r\n const parsed = parse(filePath)\r\n return join(parsed.dir, parsed.name) // Use posix for forward slashes\r\n}\r\n","import { removeExtension } from '@/lib/path-utils'\r\nimport { collectAncestors, traverseDepthFirst } from '@/lib/tree-utils'\r\nimport type { SegmentNode, SegmentRoles } from './segment-tree'\r\n\r\nexport type RouteManifest = Record<string, Route>\r\nexport type Route = {\r\n url: string\r\n chain: SegmentRoles[]\r\n}\r\n\r\n/**\r\n * Builds a route manifest from a segment tree.\r\n *\r\n * Flattens the tree into a dictionary mapping URLs to route metadata.\r\n * Only includes routes that have screens.\r\n *\r\n * @param segmentTree - Segment tree with parent pointers\r\n * @returns Flat manifest ready for runtime composition\r\n */\r\nexport function buildRouteManifest(segmentTree: SegmentNode): RouteManifest {\r\n const manifest: RouteManifest = {}\r\n\r\n function visit(segment: SegmentNode) {\r\n // Only create manifest entry if this node has a screen\r\n if (!segment.roles.screen) return\r\n\r\n // Build ancestor chain (leaf → root order)\r\n const segmentChain = collectAncestors(\r\n segment,\r\n node => node.parent,\r\n ancestorSegment => {\r\n const roles: SegmentRoles = {}\r\n const entries = Object.entries(ancestorSegment.roles) as [keyof SegmentRoles, string][]\r\n\r\n for (const [roleName, path] of entries)\r\n if (roleName !== 'screen' || ancestorSegment === segment) // Skip ancestor screens\r\n roles[roleName] = removeExtension(path)\r\n\r\n return roles\r\n },\r\n )\r\n\r\n manifest[segment.url] = {\r\n url: segment.url,\r\n chain: segmentChain,\r\n }\r\n }\r\n\r\n traverseDepthFirst({\r\n root: segmentTree,\r\n visit,\r\n expand: parentSegment => parentSegment.children,\r\n })\r\n\r\n return manifest\r\n}\r\n","import { relative } from 'path'\r\nimport type { RouteManifest } from './route-manifest'\r\n\r\nexport type ComponentImportMap = Record<string, string>\r\n\r\n/**\r\n * Builds a component map from a route manifest.\r\n * Maps absolute component paths to relative import paths.\r\n */\r\nexport function buildComponentMap(manifest: RouteManifest, outDir: string): ComponentImportMap {\r\n const segmentPaths = new Set<string>()\r\n\r\n // Collect all unique absolute paths from manifest\r\n for (const route of Object.values(manifest)) {\r\n for (const segment of route.chain) {\r\n if (segment['screen']) segmentPaths.add(segment['screen'])\r\n if (segment['not-found']) segmentPaths.add(segment['not-found'])\r\n if (segment['error']) segmentPaths.add(segment['error'])\r\n if (segment['layout']) segmentPaths.add(segment['layout'])\r\n }\r\n }\r\n\r\n // Build map: absolute path → relative import path\r\n const componentMap: ComponentImportMap = {}\r\n const genDir = `${outDir}/generated`\r\n\r\n for (const absPath of segmentPaths) {\r\n // Calculate relative path and normalize to forward slashes for ES modules\r\n const relPath = relative(genDir, absPath).replace(/\\\\/g, '/')\r\n const importPath = relPath + '.js'\r\n componentMap[absPath] = importPath\r\n }\r\n\r\n return componentMap\r\n}\r\n"],"mappings":"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"}