@timber-js/app 0.2.0-alpha.182 → 0.2.0-alpha.183
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{build-output-helper-DF5uScqS.js → build-output-helper-BC-Zg0_w.js} +38 -8
- package/dist/_chunks/build-output-helper-BC-Zg0_w.js.map +1 -0
- package/dist/_chunks/{cloudflare-AHoWYTYr.js → cloudflare-nlD9KhDj.js} +21 -3
- package/dist/_chunks/{cloudflare-AHoWYTYr.js.map → cloudflare-nlD9KhDj.js.map} +1 -1
- package/dist/_chunks/plugin-context---kTF5v8.js.map +1 -1
- package/dist/adapters/build-output-helper.d.ts +6 -0
- package/dist/adapters/build-output-helper.d.ts.map +1 -1
- package/dist/adapters/cloudflare-dev.js +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +1 -1
- package/dist/adapters/cloudflare.d.ts +10 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +2 -2
- package/dist/adapters/nitro.d.ts +14 -0
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +14 -1
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +1 -1
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/types.d.ts +14 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/client/internal.js +73 -11
- package/dist/client/internal.js.map +1 -1
- package/dist/client/rsc-fetch.d.ts +2 -0
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/index.js +324 -196
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +6 -0
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +4 -3
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +26 -2
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +12 -0
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/sitemap-generator.d.ts +1 -1
- package/dist/server/sitemap-generator.d.ts.map +1 -1
- package/dist/server/static-generator.d.ts +106 -0
- package/dist/server/static-generator.d.ts.map +1 -0
- package/docs/learn/13-configuration.mdx +5 -1
- package/package.json +1 -1
- package/src/adapters/build-output-helper.ts +34 -0
- package/src/adapters/cloudflare.ts +43 -1
- package/src/adapters/nitro.ts +58 -2
- package/src/adapters/shared.ts +38 -10
- package/src/adapters/types.ts +14 -0
- package/src/client/browser-entry/index.ts +7 -1
- package/src/client/rsc-fetch.ts +132 -19
- package/src/index.ts +1 -1
- package/src/plugin-context.ts +6 -0
- package/src/plugins/adapter-build.ts +5 -0
- package/src/plugins/build-report.ts +6 -12
- package/src/plugins/static-build.ts +264 -2
- package/src/server/rsc-entry/index.ts +24 -0
- package/src/server/sitemap-generator.ts +1 -1
- package/src/server/static-generator.ts +692 -0
- package/dist/_chunks/build-output-helper-DF5uScqS.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-output-helper-DF5uScqS.js","names":[],"sources":["../../src/adapters/shared.ts","../../src/adapters/build-output-helper.ts"],"sourcesContent":["// Shared adapter utilities — leaf module with zero deps on server/, client/,\n// or any Vite-dependent code.\n//\n// Adapters are loaded by Node at Vite startup time, before Vite's module\n// resolver is available. This module uses only Node built-ins and can be\n// imported by both adapters via a relative path.\n//\n// IMPORTANT: Do NOT add imports from ../server/, ../client/, ../plugins/,\n// or any module that transitively depends on Vite. A test in\n// tests/adapters/shared-no-forbidden-imports.test.ts enforces this.\n\n// ─── Static asset cache headers ──────────────────────────────────────────\n\n/** Cache-Control value for hashed (immutable) assets. */\nexport const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';\n\n/** Cache-Control value for unhashed static assets. */\nexport const STATIC_CACHE = 'public, max-age=3600, must-revalidate';\n\n/**\n * Generate a `_headers` file for static asset cache control.\n *\n * The `_headers` file is a platform convention supported by Cloudflare Workers\n * Static Assets, Cloudflare Pages, and Netlify. It maps URL patterns to\n * HTTP response headers.\n *\n * Only hashed (immutable) asset paths are listed here by default. When\n * `unhashedPaths` is provided, per-file rules are appended for unhashed\n * static assets (favicon.ico, robots.txt, etc.) so they get Cache-Control\n * on platforms like Cloudflare Workers Static Assets where the server\n * handler never sees static file requests.\n *\n * A `/*` catch-all is NOT used — it would be additive on Cloudflare,\n * producing duplicate Cache-Control directives on hashed assets\n * (invalid per RFC 9111).\n */\n// Cloudflare Workers Static Assets caps _headers at 100 rules.\n// Reserve 2 for the wildcard hashed-asset rules.\nconst MAX_UNHASHED_RULES = 98;\n\nexport function generateHeadersFile(unhashedPaths?: string[]): string {\n let content = `# Auto-generated by @timber-js/app — static asset cache headers.\n# See design/25-production-deployments.md §\"CDN / Edge Cache\"\n\n/assets/*\n Cache-Control: ${IMMUTABLE_CACHE}\n\n/_timber/fonts/*\n Cache-Control: ${IMMUTABLE_CACHE}\n`;\n\n if (unhashedPaths && unhashedPaths.length > 0) {\n const capped = unhashedPaths.slice(0, MAX_UNHASHED_RULES);\n if (unhashedPaths.length > MAX_UNHASHED_RULES) {\n console.warn(\n `[timber] _headers: ${unhashedPaths.length} unhashed static files found, ` +\n `but Cloudflare limits _headers to 100 rules. Only the first ${MAX_UNHASHED_RULES} ` +\n `unhashed files will get Cache-Control headers.`\n );\n }\n content += `\\n# Unhashed static assets — short-lived cache with revalidation\\n`;\n for (const p of capped) {\n content += `\\n${p}\\n Cache-Control: ${STATIC_CACHE}\\n`;\n }\n }\n\n return content;\n}\n","// Shared build-output helper for adapter buildOutput() methods.\n//\n// Steps 1–5 of every adapter's buildOutput are platform-agnostic:\n// 1. Copy client assets to the public/static directory\n// 2. Write _headers file for static asset cache control\n// 3. Copy RSC + SSR server bundles\n// 4. Write manifest-init module (if present)\n//\n// Only the final steps (entry codegen, platform config) vary per adapter.\n//\n// IMPORTANT: This module must remain a leaf — no imports from ../server/,\n// ../client/, or Vite-dependent code. See adapters/shared.ts header.\n\nimport { writeFile, mkdir, cp, readdir, readFile } from 'node:fs/promises';\nimport { join, basename, relative } from 'node:path';\nimport { generateHeadersFile } from './shared.js';\nimport type { TimberConfig } from './types';\n\n/** Options for the shared build output steps. */\nexport interface BuildOutputBaseOptions {\n /** Resolved timber config. */\n config: TimberConfig;\n /** Root build directory (e.g. `.timber/build`). */\n buildDir: string;\n /** Adapter-specific output directory (e.g. `.timber/build/nitro`). */\n outDir: string;\n /**\n * Name of the public/static directory within outDir.\n * Nitro uses 'public', Cloudflare uses 'static'.\n */\n publicDirName: string;\n}\n\n/**\n * Run the platform-agnostic build output steps shared by all adapters:\n *\n * 1. Create the output directory\n * 2. Copy client assets to the public/static directory\n * 3. Write the `_headers` file for static asset cache control\n * 4. Copy RSC and SSR server bundles into the output directory\n * 5. Write the manifest-init module if present\n *\n * Returns the resolved public directory path for further adapter-specific use.\n */\nexport async function runSharedBuildSteps(opts: BuildOutputBaseOptions): Promise<string> {\n const { config, buildDir, outDir, publicDirName } = opts;\n\n // 1. Create the output directory.\n await mkdir(outDir, { recursive: true });\n\n // 2. Copy client assets to public/static directory.\n // When client JavaScript is disabled, skip .js files — only CSS,\n // fonts, images, and other static assets are needed.\n const clientDir = join(buildDir, 'client');\n const publicDir = join(outDir, publicDirName);\n await mkdir(publicDir, { recursive: true });\n // Find WASM files that are referenced by client JS (legitimate client WASM)\n // vs orphaned server-only WASM (e.g. @takumi-rs for OG image generation).\n // Only strip orphaned WASM to avoid breaking client-side WASM usage.\n const referencedWasm = await findReferencedWasm(clientDir);\n\n await cp(clientDir, publicDir, {\n recursive: true,\n filter: (src: string) => {\n if (src.endsWith('.wasm') && !referencedWasm.has(basename(src))) {\n return false;\n }\n if (config.clientJavascriptDisabled && src.endsWith('.js')) return false;\n return true;\n },\n }).catch((e: NodeJS.ErrnoException) => {\n // Client dir may not exist when client JavaScript is disabled\n if (e.code !== 'ENOENT') throw e;\n });\n\n // 3. Write _headers file for static asset cache control.\n // Scan for unhashed files so they get per-file Cache-Control rules in the\n // _headers file. On platforms like Cloudflare Workers Static Assets, the\n // worker never sees static file requests — _headers is the only mechanism.\n const unhashedPaths = await findUnhashedStaticFiles(publicDir);\n await writeFile(join(publicDir, '_headers'), generateHeadersFile(unhashedPaths));\n\n // 4. Copy RSC + SSR server bundles into the output directory.\n await cp(join(buildDir, 'rsc'), join(outDir, 'rsc'), { recursive: true });\n await cp(join(buildDir, 'ssr'), join(outDir, 'ssr'), { recursive: true }).catch(\n (e: NodeJS.ErrnoException) => {\n // SSR dir may not exist for static-only builds\n if (e.code !== 'ENOENT') throw e;\n }\n );\n\n // 4b. Copy prebuilt/ flight payloads if present (cache.component artifacts).\n // These are produced by the post-build capture pass (design/44) and read\n // at request time via the adapter's PrebuiltPayloadSource.\n await cp(join(buildDir, 'prebuilt'), join(outDir, 'prebuilt'), { recursive: true }).catch(\n (e: NodeJS.ErrnoException) => {\n // No prebuilt dir when the app has no cache.component calls\n if (e.code !== 'ENOENT') throw e;\n }\n );\n\n // 5. Write manifest-init module if present.\n if (config.manifestInit) {\n await writeFile(join(outDir, '_timber-manifest-init.js'), config.manifestInit);\n }\n\n return publicDir;\n}\n\n/**\n * Find unhashed static files in the public directory that need per-file\n * Cache-Control rules in the `_headers` file.\n *\n * Excludes:\n * - Files under `assets/` and `_timber/` (covered by wildcard rules)\n * - Underscore-prefixed files (`_headers`, `_worker.js`, etc.)\n *\n * @internal Exported for testing.\n */\n// Well-known metadata files that should be prioritized when capping\n// _headers rules, so they're never dropped in favor of less important files.\nconst PRIORITY_FILES = new Set([\n '/favicon.ico',\n '/robots.txt',\n '/sitemap.xml',\n '/manifest.json',\n '/manifest.webmanifest',\n '/apple-touch-icon.png',\n]);\n\nexport async function findUnhashedStaticFiles(publicDir: string): Promise<string[]> {\n let files: string[];\n try {\n files = await collectFiles(publicDir);\n } catch {\n return [];\n }\n\n const priority: string[] = [];\n const rest: string[] = [];\n for (const file of files) {\n const rel = relative(publicDir, file).replace(/\\\\/g, '/');\n if (rel.startsWith('assets/') || rel.startsWith('_timber/')) continue;\n if (basename(rel).startsWith('_')) continue;\n const urlPath = '/' + rel;\n if (PRIORITY_FILES.has(urlPath)) {\n priority.push(urlPath);\n } else {\n rest.push(urlPath);\n }\n }\n\n return [...priority.sort(), ...rest.sort()];\n}\n\n/**\n * Scan client JS files for references to .wasm filenames.\n * Returns the set of WASM basenames that are actually imported/referenced\n * by client code and must be preserved in the static output.\n */\nasync function findReferencedWasm(clientDir: string): Promise<Set<string>> {\n const referenced = new Set<string>();\n\n let files: string[];\n try {\n files = await collectFiles(clientDir);\n } catch {\n return referenced;\n }\n\n const jsFiles = files.filter((f) => f.endsWith('.js'));\n const wasmFiles = files.filter((f) => f.endsWith('.wasm')).map((f) => basename(f));\n if (wasmFiles.length === 0) return referenced;\n\n for (const jsFile of jsFiles) {\n const content = await readFile(jsFile, 'utf-8');\n for (const wasmName of wasmFiles) {\n if (content.includes(wasmName)) {\n referenced.add(wasmName);\n }\n }\n }\n\n return referenced;\n}\n\nasync function collectFiles(dir: string): Promise<string[]> {\n const result: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n result.push(...(await collectFiles(fullPath)));\n } else {\n result.push(fullPath);\n }\n }\n return result;\n}\n"],"mappings":"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"}
|