@wular/pnext 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/server/_chunks/{aliases-pycbtjt1.js → aliases-s49gt0bb.js} +5 -5
  2. package/dist/server/_chunks/{analyze-4d9e00g9.js → analyze-c45xjea8.js} +8 -8
  3. package/dist/server/_chunks/{boot-bhemypxz.js → boot-exjk45ry.js} +8 -8
  4. package/dist/server/_chunks/{build-c991dv65.js → build-m49fem3j.js} +4 -4
  5. package/dist/server/_chunks/{build-kh5e3x48.js → build-ntq282xh.js} +82 -40
  6. package/dist/server/_chunks/{build-tier-0a96kmx3.js → build-tier-byqfyz34.js} +17 -17
  7. package/dist/server/_chunks/{config-27rj7830.js → config-1hdw96je.js} +2 -2
  8. package/dist/server/_chunks/{config-0tvz03a6.js → config-m2yaayer.js} +3 -3
  9. package/dist/server/_chunks/{create-9rvwshsp.js → create-k089tdt7.js} +3 -3
  10. package/dist/server/_chunks/{dev-ex9exynt.js → dev-kwp1t8b3.js} +2 -2
  11. package/dist/server/_chunks/{generate-myw9hef7.js → generate-ap2zs26g.js} +3 -3
  12. package/dist/server/_chunks/{index-x2m17v9c.js → index-0f5t5730.js} +16 -13
  13. package/dist/server/_chunks/{index-xh1nbk46.js → index-1q52cw31.js} +7 -7
  14. package/dist/server/_chunks/{index-nf3df4jy.js → index-1tmqkr6x.js} +3 -3
  15. package/dist/server/_chunks/index-1xrvw3zp.js +92 -0
  16. package/dist/server/_chunks/{index-yrjkqtfj.js → index-4g6amwtg.js} +2 -2
  17. package/dist/server/_chunks/{index-11xpbfej.js → index-4hspm5ez.js} +1 -1
  18. package/dist/server/_chunks/{index-qcrvt6er.js → index-56jw1658.js} +2 -2
  19. package/dist/server/_chunks/{index-d158y67j.js → index-6w807g31.js} +12 -4
  20. package/dist/server/_chunks/{index-29pq0kax.js → index-72wjyacg.js} +2 -2
  21. package/dist/server/_chunks/{index-3e075kny.js → index-81qnfmzz.js} +1 -1
  22. package/dist/server/_chunks/{index-3ycf5ckv.js → index-8hy8qskn.js} +3 -3
  23. package/dist/server/_chunks/{index-he6n2bvp.js → index-8y2yft9b.js} +1 -1
  24. package/dist/server/_chunks/{index-8ct7rqy3.js → index-a4asdd4j.js} +1 -1
  25. package/dist/server/_chunks/{index-1pzm1hdy.js → index-cqft8ae4.js} +7 -7
  26. package/dist/server/_chunks/{index-9hz3s0m8.js → index-dd4tb5vy.js} +1 -1
  27. package/dist/server/_chunks/{index-tq3b39a5.js → index-drfxdvvh.js} +4 -4
  28. package/dist/server/_chunks/{index-4da76t62.js → index-e63ma63t.js} +1 -1
  29. package/dist/server/_chunks/{index-rgj5mx84.js → index-ed6rwv1x.js} +1 -1
  30. package/dist/server/_chunks/{index-dgr390q7.js → index-f76517s5.js} +23 -23
  31. package/dist/server/_chunks/{index-b7j671dj.js → index-fdc7wz3a.js} +1 -1
  32. package/dist/server/_chunks/{index-0yjv5v47.js → index-g39z5s9d.js} +3 -3
  33. package/dist/server/_chunks/{index-cvr4z22m.js → index-hw5k2sgj.js} +1 -1
  34. package/dist/server/_chunks/{index-evhd413j.js → index-jh486fye.js} +1 -1
  35. package/dist/server/_chunks/{index-fjv96re1.js → index-mjvq5c7e.js} +8 -8
  36. package/dist/server/_chunks/{index-kdhxpwyq.js → index-mph0cmh9.js} +3 -3
  37. package/dist/server/_chunks/{index-gkndxkxf.js → index-nxg8k7cg.js} +2 -2
  38. package/dist/server/_chunks/{index-jn9rbhrx.js → index-p79ck9cd.js} +4 -4
  39. package/dist/server/_chunks/{index-hdbwtdv2.js → index-pshjr3qm.js} +1 -1
  40. package/dist/server/_chunks/{index-n629dy38.js → index-r1kzsnyp.js} +4 -4
  41. package/dist/server/_chunks/{index-cn86z041.js → index-ra6fsg5v.js} +1 -1
  42. package/dist/server/_chunks/{index-38xpdxwd.js → index-tgwt3kwf.js} +4 -4
  43. package/dist/server/_chunks/{index-71885zaq.js → index-x3cvacmf.js} +4 -4
  44. package/dist/server/_chunks/{index-j681c48a.js → index-zq077xjc.js} +1 -1
  45. package/dist/server/_chunks/{index-rjegyfx4.js → index-zvy5134a.js} +5 -5
  46. package/dist/server/_chunks/{loader-5en7wpt5.js → loader-5j9bjhw7.js} +6 -6
  47. package/dist/server/_chunks/{modules-t92favpj.js → modules-gy48pdw3.js} +8 -8
  48. package/dist/server/_chunks/{pipeline-4t9q43ht.js → pipeline-m8ac90dn.js} +11 -11
  49. package/dist/server/_chunks/{production-385bw8df.js → production-26vqtxvg.js} +1 -1
  50. package/dist/server/_chunks/{proxy-gfhmktbk.js → proxy-74a1mg4v.js} +7 -7
  51. package/dist/server/_chunks/{proxy-config-60gprphe.js → proxy-config-7hnexa7b.js} +5 -5
  52. package/dist/server/_chunks/{router-xnrva921.js → router-frkywyzb.js} +4 -4
  53. package/dist/server/_chunks/{routes-ht868pdc.js → routes-y7de16ja.js} +7 -7
  54. package/dist/server/_chunks/{run-d40d63ag.js → run-q8aq4cez.js} +4 -4
  55. package/dist/server/_chunks/{server-2p8fgqq2.js → server-21t1fzp7.js} +19 -19
  56. package/dist/server/_chunks/{tsconfig-defaults-bcn34eqb.js → tsconfig-defaults-bcq068bd.js} +3 -3
  57. package/dist/server/_chunks/{typegen-dvtcjtaw.js → typegen-wdqj67rx.js} +10 -10
  58. package/dist/server/cli/index.js +11 -11
  59. package/dist/server/cli/start.js +5 -5
  60. package/dist/server/compat/next/config-headers.js +4 -4
  61. package/dist/server/compat/next/font/cache.js +4 -4
  62. package/dist/server/compat/next/font/local.js +5 -5
  63. package/dist/server/compat/next/font/runtime.js +5 -5
  64. package/dist/server/compat/next/navigation.js +4 -4
  65. package/dist/server/compat/next/redirects.js +4 -4
  66. package/dist/server/compat/next/rewrites.js +3 -3
  67. package/dist/server/generation.json +1 -1
  68. package/package.json +1 -1
  69. package/src/cli/adapters/vercel-warm.ts +2 -1
  70. package/src/cli/adapters/vercel.ts +43 -4
  71. package/src/cli/serve/standalone.ts +49 -17
  72. package/src/css/assets.ts +9 -1
  73. package/src/render/renderer.ts +6 -3
  74. package/dist/server/_chunks/index-gcmj16xy.js +0 -92
@@ -3,10 +3,10 @@ import {
3
3
  currentLayoutSegments,
4
4
  currentParamsSnapshot,
5
5
  hasLayoutSegmentScope
6
- } from "../../_chunks/index-29pq0kax.js";
7
- import"../../_chunks/index-cn86z041.js";
8
- import"../../_chunks/index-4da76t62.js";
9
- import"../../_chunks/index-hdbwtdv2.js";
6
+ } from "../../_chunks/index-72wjyacg.js";
7
+ import"../../_chunks/index-ra6fsg5v.js";
8
+ import"../../_chunks/index-e63ma63t.js";
9
+ import"../../_chunks/index-pshjr3qm.js";
10
10
  import {
11
11
  canonicalUrlHref
12
12
  } from "../../_chunks/index-en7n9f7m.js";
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  loadCompatRedirects,
4
4
  resolveCompatRedirect
5
- } from "../../_chunks/index-cvr4z22m.js";
6
- import"../../_chunks/index-he6n2bvp.js";
5
+ } from "../../_chunks/index-hw5k2sgj.js";
6
+ import"../../_chunks/index-8y2yft9b.js";
7
7
  import"../../_chunks/index-6q7zvrap.js";
8
8
  import"../../_chunks/index-79ngvam8.js";
9
9
  import"../../_chunks/index-akkrvjn6.js";
@@ -15,9 +15,9 @@ import"../../_chunks/index-6b009mt0.js";
15
15
  import"../../_chunks/index-4zzebszd.js";
16
16
  import"../../_chunks/index-sq3kvz3g.js";
17
17
  import"../../_chunks/index-ckkdpht5.js";
18
- import"../../_chunks/index-71885zaq.js";
18
+ import"../../_chunks/index-x3cvacmf.js";
19
19
  import"../../_chunks/index-bzd2ef7t.js";
20
- import"../../_chunks/index-hdbwtdv2.js";
20
+ import"../../_chunks/index-pshjr3qm.js";
21
21
  import"../../_chunks/index-grta8ap3.js";
22
22
  import"../../_chunks/index-kresp2n8.js";
23
23
  import"../../_chunks/index-e3a6yx84.js";
@@ -8,7 +8,7 @@ import {
8
8
  resolveCompatRewrite,
9
9
  splitDestination,
10
10
  substitute
11
- } from "../../_chunks/index-he6n2bvp.js";
11
+ } from "../../_chunks/index-8y2yft9b.js";
12
12
  import"../../_chunks/index-6q7zvrap.js";
13
13
  import"../../_chunks/index-79ngvam8.js";
14
14
  import"../../_chunks/index-akkrvjn6.js";
@@ -20,9 +20,9 @@ import"../../_chunks/index-6b009mt0.js";
20
20
  import"../../_chunks/index-4zzebszd.js";
21
21
  import"../../_chunks/index-sq3kvz3g.js";
22
22
  import"../../_chunks/index-ckkdpht5.js";
23
- import"../../_chunks/index-71885zaq.js";
23
+ import"../../_chunks/index-x3cvacmf.js";
24
24
  import"../../_chunks/index-bzd2ef7t.js";
25
- import"../../_chunks/index-hdbwtdv2.js";
25
+ import"../../_chunks/index-pshjr3qm.js";
26
26
  import"../../_chunks/index-grta8ap3.js";
27
27
  import"../../_chunks/index-kresp2n8.js";
28
28
  import"../../_chunks/index-e3a6yx84.js";
@@ -1 +1 @@
1
- {"version":"0.1.8","fingerprint":"64aaa993cbaa58df","source":["src/api/cache.ts","src/api/client-navigation.ts","src/api/dynamic.tsx","src/api/link.tsx","src/api/navigation.ts","src/api/server.ts","src/api/suspense.ts","src/client/router/events.ts","src/client/router/history.ts","src/client/router/hub.ts","src/client/router/index.ts","src/client/router/page-slot.ts","src/client/router/policies.ts","src/client/router/runtime.ts","src/index.ts","src/internal.ts","src/request/cache.ts","src/routing/href.ts","src/utils/decode.ts"]}
1
+ {"version":"0.1.10","fingerprint":"db18aac8a57bc886","source":["src/api/cache.ts","src/api/client-navigation.ts","src/api/dynamic.tsx","src/api/link.tsx","src/api/navigation.ts","src/api/server.ts","src/api/suspense.ts","src/client/router/events.ts","src/client/router/history.ts","src/client/router/hub.ts","src/client/router/index.ts","src/client/router/page-slot.ts","src/client/router/policies.ts","src/client/router/runtime.ts","src/index.ts","src/internal.ts","src/request/cache.ts","src/routing/href.ts","src/utils/decode.ts"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wular/pnext",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A fast little framework for server-first React apps, fully compatible with Next.js",
5
5
  "type": "module",
6
6
  "bin": {
@@ -614,7 +614,8 @@ async function importClientModules(
614
614
  ) {
615
615
  if (!getCompatModeExtensions().reactEnabled(config)) return
616
616
  for (const file of clientFiles) {
617
- if (skip.has(file)) continue
617
+ // A package's client modules serve from the linked dependency graph; a vendor href is dev-only.
618
+ if (skip.has(file) || file.includes(`${path.sep}node_modules${path.sep}`)) continue
618
619
  const href = await devClientModuleHref(config, file, 'build')
619
620
  report(file, fileURLToPath(href))
620
621
  try {
@@ -74,6 +74,16 @@ export async function writeVercelOutput(
74
74
  ),
75
75
  )
76
76
  const overrides = await staticOverrides(staticPath, staticFiles)
77
+ const proxy = await proxyRoutes(config)
78
+ await log.step('drop public files from the function', () =>
79
+ dropPublicFiles(
80
+ config.publicPath,
81
+ path.join(functionPath, 'static'),
82
+ staticFiles,
83
+ proxy.map(route => new RegExp(route.src)),
84
+ config.basePath,
85
+ ),
86
+ )
77
87
 
78
88
  const routes: NonNullable<VercelConfig['routes']> = [
79
89
  // next-compat documents reference the build output under Next's static path (assetPathname),
@@ -85,7 +95,7 @@ export async function writeVercelOutput(
85
95
  ...(await immutableAssetRoutes(staticPath, manifest.publicAssets)),
86
96
  // Proxy-matched paths go to the server function before the CDN filesystem
87
97
  // check — `pnext start` runs the proxy ahead of static files too.
88
- ...(await proxyRoutes(config)),
98
+ ...proxy,
89
99
  ...staticHeaderRoutes(staticFiles),
90
100
  { handle: 'filesystem' },
91
101
  { src: '^/.*$', dest: `/${SERVER_FUNCTION}` },
@@ -448,6 +458,7 @@ async function staticOverrides(
448
458
  }
449
459
 
450
460
  // `important` lets route headers win over the CDN's own static-file cache-control, as Next's builder does.
461
+ // Vercel anchors every `src` at both ends, so a prefix must match the rest of the path.
451
462
  async function immutableAssetRoutes(publicDir: string, publicAssets: string[] = []) {
452
463
  const app = new Set(publicAssets)
453
464
  const built =
@@ -457,7 +468,7 @@ async function immutableAssetRoutes(publicDir: string, publicAssets: string[] =
457
468
  .filter(relative => !app.has(relative) && immutableAssetPath(relative))
458
469
  : []
459
470
  const patterns = immutableAssetPatterns(built, publicAssets)
460
- return routeSources(patterns, source => `^/(?:${source})`).map(src => ({
471
+ return routeSources(patterns, source => `^/(?:${source})$`).map(src => ({
461
472
  src,
462
473
  headers: { 'cache-control': immutableCacheControl },
463
474
  continue: true,
@@ -472,12 +483,14 @@ async function immutableAssetRoutes(publicDir: string, publicAssets: string[] =
472
483
  function immutableAssetPatterns(built: string[], publicAssets: string[]) {
473
484
  const appDirs = new Set(publicAssets.flatMap(ancestorDirs))
474
485
  const prefixes = [...new Set(immutableAssetPrefixes())]
475
- const patterns = prefixes.filter(prefix => !appDirs.has(prefix)).map(escapeRegex)
486
+ const patterns = prefixes
487
+ .filter(prefix => !appDirs.has(prefix))
488
+ .map(prefix => `${escapeRegex(prefix)}.+`)
476
489
  for (const relative of built) {
477
490
  const prefix = prefixes.find(prefix => relative.startsWith(prefix))
478
491
  if (!prefix || !appDirs.has(prefix)) continue
479
492
  const dir = ancestorDirs(relative).find(dir => dir.length > prefix.length && !appDirs.has(dir))
480
- patterns.push(dir ? escapeRegex(requestPath(dir)) : `${escapeRegex(requestPath(relative))}$`)
493
+ patterns.push(dir ? `${escapeRegex(requestPath(dir))}.+` : escapeRegex(requestPath(relative)))
481
494
  }
482
495
  return [...new Set(patterns)]
483
496
  }
@@ -502,6 +515,32 @@ const functionOnlyHeaders = new Set([
502
515
  'set-cookie',
503
516
  ])
504
517
 
518
+ /**
519
+ * The CDN answers the app's `public/` files, so the function drops them: all but pages, files the CDN
520
+ * cannot serve, and paths a proxy route or an encoded name sends to it. Build assets stay: a render
521
+ * reads them (font CSS, inlined CSS).
522
+ */
523
+ async function dropPublicFiles(
524
+ publicDir: string,
525
+ dir: string,
526
+ staticFiles: Record<string, StaticFileMetadata>,
527
+ proxied: RegExp[],
528
+ basePath: string,
529
+ ) {
530
+ const publicFiles = existsSync(publicDir) ? await listFiles(publicDir) : []
531
+ const dropped = publicFiles
532
+ .map(source => toPosixPath(path.relative(publicDir, source)))
533
+ .filter(relative => {
534
+ const metadata = staticFiles[relative]
535
+ if (relative.endsWith('.html') || (metadata && !canServeStaticOnVercel(metadata)))
536
+ return false
537
+ if (requestPath(relative) !== relative) return false
538
+ const paths = [`/${relative}`, `${basePath}/${relative}`]
539
+ return !proxied.some(src => paths.some(pathname => src.test(pathname)))
540
+ })
541
+ await Promise.all(dropped.map(relative => rm(path.join(dir, relative), { force: true })))
542
+ }
543
+
505
544
  function canServeStaticOnVercel(metadata: StaticFileMetadata) {
506
545
  if (metadata.status !== 200) return false
507
546
  const headers = routeHeaders(metadata)
@@ -13,7 +13,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
13
13
  import { frameworkRuntimeSpecifiers, type ResolvedConfig } from '../../config'
14
14
  import { frameworkFingerprint } from '../../runtime/fingerprint'
15
15
  import { cacheRoot, compiledSourceGraph, flushDevModuleCaches } from '../../runtime/module-cache'
16
- import { PREBUNDLE_STAMP, prebundledFile, readPrebundleStamp } from '../../runtime/prebundle'
16
+ import { PREBUNDLE_STAMP, readPrebundleStamp } from '../../runtime/prebundle'
17
17
  import {
18
18
  buildIndexFile,
19
19
  type BuildIndex,
@@ -40,9 +40,15 @@ const buildOnlyModule = /^(?:build|modules|loader|compile|esbuild|dev|run|typege
40
40
  const builtins = new Set(builtinModules)
41
41
  const scriptFile = /\.(?:[cm]?[jt]sx?)$/
42
42
 
43
+ let prebundled: boolean | undefined
44
+ /** Whether a release serves from the framework's prebundle: one matches this source. */
45
+ function servesPrebundle() {
46
+ return (prebundled ??= readPrebundleStamp(frameworkRoot, frameworkFingerprint()) !== undefined)
47
+ }
48
+
43
49
  /** The framework module a release serves from: its prebundle when one matches this source. */
44
50
  function frameworkStartEntry() {
45
- return readPrebundleStamp(frameworkRoot, frameworkFingerprint())
51
+ return servesPrebundle()
46
52
  ? path.join(frameworkRoot, 'dist', 'server', 'cli', 'start.js')
47
53
  : path.join(frameworkRoot, 'src', 'cli', 'start.ts')
48
54
  }
@@ -299,7 +305,11 @@ function createTracer(config: TraceConfig, from?: TraceSnapshot, onReady?: (dest
299
305
  release && !isInside(outPath, target) && !isInside(cache, target) && !shipped(target)
300
306
  // An app source named by URL is its identity: it resolves to the app root, never read.
301
307
  if (appFile && scriptFile.test(target)) continue
302
- if (!statSync(target, { throwIfNoEntry: false })?.isFile()) continue
308
+ const stat = statSync(target, { throwIfNoEntry: false })
309
+ // A package directory it reads by URL (migrations, data) ships whole.
310
+ if (stat?.isDirectory() && !release && packageDataDir(target, file))
311
+ for (const asset of dataFiles(target)) setFile(trace, place(asset), asset)
312
+ if (!stat?.isFile()) continue
303
313
  // A file it reads by URL (a font, data) ships, under the app's own path.
304
314
  const at = appFile
305
315
  ? toPosixPath(
@@ -448,15 +458,8 @@ function createPlacer(outPath: string, cache: string, claims = new Map<string, s
448
458
  const dir = packageDir(frameworkRoot, frameworkPackage.name)
449
459
  return `${dir}/${toPosixPath(path.relative(frameworkRoot, file))}`
450
460
  }
451
- const posix = toPosixPath(file)
452
- const at = posix.lastIndexOf('/node_modules/')
453
- if (at !== -1) {
454
- const rest = posix.slice(at + '/node_modules/'.length).split('/')
455
- const length = rest[0]!.startsWith('@') ? 2 : 1
456
- const name = rest.slice(0, length).join('/')
457
- const root = `${posix.slice(0, at)}/node_modules/${name}`
458
- return [packageDir(root, name), ...rest.slice(length)].join('/')
459
- }
461
+ const owner = installedPackage(file)
462
+ if (owner) return [packageDir(owner.root, owner.name), ...owner.rest].join('/')
460
463
  // Workspace source reached through a link: it ships under its package name.
461
464
  const root = packageRoot(path.dirname(file))
462
465
  const name = root && readPackageName(root)
@@ -498,6 +501,17 @@ function addTree(root: string, dest: string, trace: Trace) {
498
501
  visit(root, new Set())
499
502
  }
500
503
 
504
+ /** The installed package holding `file`: its `node_modules/<name>` dir, name, and path inside. */
505
+ function installedPackage(file: string) {
506
+ const posix = toPosixPath(file)
507
+ const at = posix.lastIndexOf('/node_modules/')
508
+ if (at === -1) return undefined
509
+ const rest = posix.slice(at + '/node_modules/'.length).split('/')
510
+ const length = rest[0]!.startsWith('@') ? 2 : 1
511
+ const name = rest.slice(0, length).join('/')
512
+ return { root: `${posix.slice(0, at)}/node_modules/${name}`, name, rest: rest.slice(length) }
513
+ }
514
+
501
515
  function installed(file: string) {
502
516
  return toPosixPath(file).includes('/node_modules/')
503
517
  }
@@ -572,11 +586,13 @@ function resolveEdge(specifier: string, from: string, require = false) {
572
586
  return target
573
587
  }
574
588
 
575
- /** The prebundled module a framework source file is served by, when it has one. */
589
+ /** The prebundled module a framework source file is served by, when the release serves the prebundle. */
576
590
  function frameworkCounterparts(file: string) {
577
- const counterpart = prebundledFile(file)
578
- if (counterpart === file) return []
579
- return [counterpart, path.join(frameworkRoot, PREBUNDLE_STAMP)]
591
+ const src = path.join(frameworkRoot, 'src')
592
+ if (!servesPrebundle() || !isInside(src, file)) return []
593
+ const relative = path.relative(src, file).replace(/\.tsx?$/, '.js')
594
+ const counterpart = path.join(frameworkRoot, 'dist', 'server', relative)
595
+ return existsSync(counterpart) ? [counterpart, path.join(frameworkRoot, PREBUNDLE_STAMP)] : []
580
596
  }
581
597
 
582
598
  /** `_chunks/name-<hash>.js` chunk or `name.ts` module -> `name`. */
@@ -616,6 +632,22 @@ function addPackageFiles(file: string, trace: Trace, place: (file: string) => st
616
632
  }
617
633
  }
618
634
 
635
+ /** Whether `dir` is data of `from`'s package: inside it, not the module's own dir or above. */
636
+ function packageDataDir(dir: string, from: string) {
637
+ const root = installedPackage(from)?.root ?? packageRoot(path.dirname(from))
638
+ if (!root || !isInside(root, dir) || isInside(dir, path.dirname(from))) return false
639
+ return !path.relative(root, dir).split(path.sep).includes('node_modules')
640
+ }
641
+
642
+ /** Every file under `dir`, nested packages aside. */
643
+ function dataFiles(dir: string): string[] {
644
+ return readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
645
+ const file = path.join(dir, entry.name)
646
+ if (entry.isDirectory()) return entry.name === 'node_modules' ? [] : dataFiles(file)
647
+ return entry.isFile() ? [file] : []
648
+ })
649
+ }
650
+
619
651
  function packageRoot(dir: string): string | undefined {
620
652
  for (; dir !== path.dirname(dir); dir = path.dirname(dir)) {
621
653
  if (existsSync(path.join(dir, 'package.json'))) return dir
@@ -655,7 +687,7 @@ function rewriteSpecifiers(file: string, source: string, edits: Map<string, stri
655
687
  function urlTarget(specifier: string, from: string) {
656
688
  try {
657
689
  const url = new URL(specifier, pathToFileURL(from))
658
- return url.protocol === 'file:' ? fileURLToPath(url) : undefined
690
+ return url.protocol === 'file:' ? path.resolve(fileURLToPath(url)) : undefined
659
691
  } catch {
660
692
  return undefined
661
693
  }
package/src/css/assets.ts CHANGED
@@ -126,11 +126,19 @@ export async function emitFontCssStylesheet(
126
126
  : `${routeId}.css`
127
127
  const name = hashedAssetName(logicalName, contents)
128
128
  const file = path.join(outDir, name)
129
- if (!existsSync(file) || (await readFile(file, 'utf8')) !== contents)
129
+ if (existsSync(file) && (await readFile(file, 'utf8')) === contents) return name
130
+ try {
130
131
  await writeFile(file, contents)
132
+ } catch (error) {
133
+ // A read-only deployment cannot add the sheet; the render inlines the font rules instead.
134
+ if (options.dev || !readOnlyErrors.has((error as NodeJS.ErrnoException).code ?? '')) throw error
135
+ return undefined
136
+ }
131
137
  return name
132
138
  }
133
139
 
140
+ const readOnlyErrors = new Set(['EROFS', 'EACCES', 'EPERM'])
141
+
134
142
  // Prepend the configured assetPrefix (a CDN origin or path) to an app-absolute
135
143
  // asset URL. Link hrefs use basePath instead and must NOT go through here.
136
144
  export function withAssetPrefix(
@@ -3987,14 +3987,17 @@ async function renderTreeInFrame(
3987
3987
  ...(nonce ? { nonce } : {}),
3988
3988
  }),
3989
3989
  )
3990
+ let fontStyle = ''
3990
3991
  if (!inlineStylesheets && fonts.css) {
3991
3992
  const fontAsset = await profileRenderStep(profile, 'font stylesheet', () =>
3992
3993
  emitFontCssStylesheet(options.config, options.route.id, fonts.css, stylesheetAssets[0], {
3993
3994
  dev: Boolean(options.dev),
3994
3995
  }),
3995
3996
  )
3996
- const fontHref = assetHref(options.config, fontAsset)
3997
- if (globalStylesheet) globalStylesheet = fontHref
3997
+ const fontHref = fontAsset && assetHref(options.config, fontAsset)
3998
+ if (!fontHref)
3999
+ fontStyle = `<style${nonce ? ` nonce="${escapeHtml(nonce)}"` : ''}>${fonts.css}</style>`
4000
+ else if (globalStylesheet) globalStylesheet = fontHref
3998
4001
  else if (Array.isArray(routeStylesheet))
3999
4002
  routeStylesheet = [fontHref, ...routeStylesheet.slice(1)]
4000
4003
  else routeStylesheet = fontHref
@@ -4039,7 +4042,7 @@ async function renderTreeInFrame(
4039
4042
  : ''
4040
4043
  // Inlined CSS is a stylesheet, not a script: it belongs at the stylesheet slot so the cascade
4041
4044
  // (and the emitted order) matches the linked path.
4042
- const headStyles = inlineStylesheets?.join('\n') ?? ''
4045
+ const headStyles = [fontStyle, ...(inlineStylesheets ?? [])].filter(Boolean).join('\n')
4043
4046
  const headScripts = profileRenderSyncStep(profile, 'head scripts', () =>
4044
4047
  [
4045
4048
  renderCollectedHeadScripts(),
@@ -1,92 +0,0 @@
1
- // @bun
2
- import { join as __pnextJoin } from "node:path";
3
- const __PNEXT_PACKAGE_ROOT__ = __pnextJoin(import.meta.dirname, "../../..");
4
- // src/runtime/fingerprint.ts
5
- import { createHash } from "crypto";
6
- import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
7
- import path from "path";
8
- import { fileURLToPath } from "url";
9
- var prebundleFingerprint = typeof { version: "0.1.8", fingerprint: "64aaa993cbaa58df", entries: ["cli/index", "cli/start", "client/reference", "api/client-cache", "compat/next/canonical-url", "compat/next/link-transform", "compat/next/client-navigation", "compat/next/og", "compat/next/link", "compat/next/usage", "compat/next/client-script", "compat/next/headers", "compat/next/custom-server", "compat/next/legacy-image", "compat/next/router", "compat/next/navigation", "compat/next/dist/server/web/spec-extension/unstable-no-store", "compat/next/dist/server/web/spec-extension/unstable-cache", "compat/next/dist/server/web/spec-extension/revalidate", "compat/next/dist/client/components/app-router-headers", "compat/next/web-vitals", "compat/next/root-params", "compat/next/route-state", "compat/next/server-only", "compat/next/client-server", "compat/next/legacy-request-apis", "compat/next/redirects", "compat/next/resource-hints", "compat/next/svgr", "compat/next/client-only", "compat/next/image/props", "compat/next/image/optimizer", "compat/next/image/validate", "compat/next/image/shared", "compat/next/image/patterns", "compat/next/image/config", "compat/next/image/client", "compat/next/image/static-metadata", "compat/next/user-agent", "compat/next/client-cache", "compat/next/head", "compat/next/dynamic", "compat/next/telemetry", "compat/next/offline", "compat/next/cache", "compat/next/constants", "compat/next/error", "compat/next/script", "compat/next/index", "compat/next/font/shared", "compat/next/font/runtime", "compat/next/font/google", "compat/next/font/cache", "compat/next/font/runtime-client", "compat/next/font/local", "compat/next/font/index", "compat/next/config-loader", "compat/next/config", "compat/next/image", "compat/next/server", "compat/next/rewrites", "compat/next/preferred-region", "compat/next/config-headers", "compat/next/form", "compat/react/dom", "compat/react/hooks-extra", "compat/react/react-server", "compat/react/dom-client", "compat/react/style-values", "compat/react/compiler-runtime", "compat/react/router-shim", "compat/react/dom-server", "compat/react/dom-react-server", "compat/react/server-component-use", "compat/react/action-state", "compat/react/client", "compat/react/client-lite", "compat/react/server-inserted-html", "compat/react/parity", "compat/react/preact", "compat/react/use", "compat/react/view-transition", "compat/react/internals", "compat/react/server"] } === "object" ? { version: "0.1.8", fingerprint: "64aaa993cbaa58df", entries: ["cli/index", "cli/start", "client/reference", "api/client-cache", "compat/next/canonical-url", "compat/next/link-transform", "compat/next/client-navigation", "compat/next/og", "compat/next/link", "compat/next/usage", "compat/next/client-script", "compat/next/headers", "compat/next/custom-server", "compat/next/legacy-image", "compat/next/router", "compat/next/navigation", "compat/next/dist/server/web/spec-extension/unstable-no-store", "compat/next/dist/server/web/spec-extension/unstable-cache", "compat/next/dist/server/web/spec-extension/revalidate", "compat/next/dist/client/components/app-router-headers", "compat/next/web-vitals", "compat/next/root-params", "compat/next/route-state", "compat/next/server-only", "compat/next/client-server", "compat/next/legacy-request-apis", "compat/next/redirects", "compat/next/resource-hints", "compat/next/svgr", "compat/next/client-only", "compat/next/image/props", "compat/next/image/optimizer", "compat/next/image/validate", "compat/next/image/shared", "compat/next/image/patterns", "compat/next/image/config", "compat/next/image/client", "compat/next/image/static-metadata", "compat/next/user-agent", "compat/next/client-cache", "compat/next/head", "compat/next/dynamic", "compat/next/telemetry", "compat/next/offline", "compat/next/cache", "compat/next/constants", "compat/next/error", "compat/next/script", "compat/next/index", "compat/next/font/shared", "compat/next/font/runtime", "compat/next/font/google", "compat/next/font/cache", "compat/next/font/runtime-client", "compat/next/font/local", "compat/next/font/index", "compat/next/config-loader", "compat/next/config", "compat/next/image", "compat/next/server", "compat/next/rewrites", "compat/next/preferred-region", "compat/next/config-headers", "compat/next/form", "compat/react/dom", "compat/react/hooks-extra", "compat/react/react-server", "compat/react/dom-client", "compat/react/style-values", "compat/react/compiler-runtime", "compat/react/router-shim", "compat/react/dom-server", "compat/react/dom-react-server", "compat/react/server-component-use", "compat/react/action-state", "compat/react/client", "compat/react/client-lite", "compat/react/server-inserted-html", "compat/react/parity", "compat/react/preact", "compat/react/use", "compat/react/view-transition", "compat/react/internals", "compat/react/server"] }.fingerprint : undefined;
10
- var SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".cjs", ".mjs"]);
11
- var DECLARATION = /\.d\.[cm]?ts$/;
12
- var frameworkSrc = fileURLToPath(new URL("..", Bun.pathToFileURL(__PNEXT_PACKAGE_ROOT__ + "/src/runtime/fingerprint.ts").href));
13
- function sourceFiles(dir, found = []) {
14
- let entries;
15
- try {
16
- entries = readdirSync(dir, { withFileTypes: true });
17
- } catch {
18
- return found;
19
- }
20
- for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : 1)) {
21
- const full = path.join(dir, entry.name);
22
- if (entry.isDirectory())
23
- sourceFiles(full, found);
24
- else if (SOURCE_EXTENSIONS.has(path.extname(entry.name)) && !DECLARATION.test(entry.name))
25
- found.push(full);
26
- }
27
- return found;
28
- }
29
- function contentFingerprint(files, dir) {
30
- const hash = createHash("sha256");
31
- for (const file of files) {
32
- hash.update(path.relative(dir, file));
33
- try {
34
- hash.update(Bun.hash(readFileSync(file)).toString(36));
35
- } catch {}
36
- hash.update("\x00");
37
- }
38
- return hash.digest("hex").slice(0, 16);
39
- }
40
- var RECORD_VERSION = 1;
41
- function statSignature(files, dir) {
42
- const hash = createHash("sha256");
43
- for (const file of files) {
44
- const stats = statSync(file, { throwIfNoEntry: false });
45
- hash.update(`${path.relative(dir, file)}:${stats?.size ?? -1}:${stats?.mtimeMs ?? -1}\x00`);
46
- }
47
- return hash.digest("hex").slice(0, 16);
48
- }
49
- function recordFile(dir) {
50
- return path.join(dir, "framework.json");
51
- }
52
- function readRecord(dir) {
53
- try {
54
- const parsed = JSON.parse(readFileSync(recordFile(dir), "utf8"));
55
- return parsed.version === RECORD_VERSION && parsed.stats && parsed.fingerprint ? parsed : undefined;
56
- } catch {
57
- return;
58
- }
59
- }
60
- function writeRecord(dir, record) {
61
- try {
62
- mkdirSync(dir, { recursive: true });
63
- writeFileSync(recordFile(dir), JSON.stringify(record));
64
- } catch {}
65
- }
66
- var fingerprint;
67
- var fingerprintStats;
68
- var scanned = false;
69
- var recorded = new Set;
70
- function frameworkFingerprint(recordDir) {
71
- const pinned = process.env.PNEXT_FRAMEWORK_FINGERPRINT ?? prebundleFingerprint;
72
- if (pinned)
73
- return pinned;
74
- if (fingerprint !== undefined && (!recordDir || recorded.has(recordDir)))
75
- return fingerprint;
76
- const previous = recordDir ? readRecord(recordDir) : undefined;
77
- if (!scanned || fingerprint === undefined || fingerprintStats === undefined) {
78
- const files = sourceFiles(frameworkSrc);
79
- fingerprintStats = statSignature(files, frameworkSrc);
80
- const recordedFingerprint = previous?.stats === fingerprintStats ? previous.fingerprint : undefined;
81
- scanned = recordedFingerprint === undefined;
82
- fingerprint = recordedFingerprint ?? contentFingerprint(files, frameworkSrc);
83
- }
84
- if (recordDir) {
85
- recorded.add(recordDir);
86
- if (previous?.stats !== fingerprintStats)
87
- writeRecord(recordDir, { version: RECORD_VERSION, stats: fingerprintStats, fingerprint });
88
- }
89
- return fingerprint;
90
- }
91
-
92
- export { frameworkFingerprint };