@thomas.f/prnext 0.1.16-alpha → 0.1.18-alpha

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 (215) hide show
  1. package/AI.md +63 -0
  2. package/README.md +35 -503
  3. package/build/ADAPTERS.md +27 -2
  4. package/build/actions.mjs +6 -1
  5. package/build/analyze.mjs +340 -0
  6. package/build/app-config.mjs +20 -8
  7. package/build/app-static.mjs +41 -14
  8. package/build/app-styles.mjs +79 -0
  9. package/build/app.mjs +99 -31
  10. package/build/build-lock.mjs +6 -6
  11. package/build/bundle-runtime.mjs +39 -11
  12. package/build/cache-components.mjs +1 -1
  13. package/build/cache-handlers.mjs +1 -1
  14. package/build/check.mjs +56 -11
  15. package/build/compact-css.mjs +53 -0
  16. package/build/compilation-cache.mjs +269 -0
  17. package/build/compilation-store.mjs +142 -0
  18. package/build/compiler.mjs +51 -4
  19. package/build/concurrent.mjs +12 -0
  20. package/build/config.mjs +50 -13
  21. package/build/create.mjs +239 -0
  22. package/build/css-modules.mjs +65 -10
  23. package/build/deployment-assets.mjs +89 -0
  24. package/build/deployment-config.mjs +18 -0
  25. package/build/dev-compiler-health.mjs +11 -0
  26. package/build/dev-css-update.mjs +85 -0
  27. package/build/dev-dependencies.mjs +44 -0
  28. package/build/dev-discovery.mjs +28 -0
  29. package/build/dev-worker.mjs +14 -5
  30. package/build/dev.mjs +64 -6
  31. package/build/dynamic.mjs +1 -1
  32. package/build/edge.mjs +5 -4
  33. package/build/fonts.mjs +1 -1
  34. package/build/framework-imports.mjs +50 -0
  35. package/build/html-only-config.mjs +53 -0
  36. package/build/html-only-graph.mjs +67 -0
  37. package/build/incremental.mjs +8 -4
  38. package/build/index.mjs +140 -112
  39. package/build/inspect.mjs +3 -0
  40. package/build/inspection-summary.mjs +37 -4
  41. package/build/instrumentation.mjs +30 -0
  42. package/build/loader-cache.mjs +53 -26
  43. package/build/metadata-routes.mjs +3 -4
  44. package/build/migrate.mjs +60 -57
  45. package/build/migration-audit.mjs +1 -1
  46. package/build/native-type-config.mjs +37 -0
  47. package/build/npm-packages.mjs +3 -18
  48. package/build/package-manager.mjs +308 -0
  49. package/build/persistent-cache.mjs +112 -0
  50. package/build/precompress.mjs +62 -30
  51. package/build/prerender-cache.mjs +70 -0
  52. package/build/prerender-config.mjs +29 -0
  53. package/build/prerender-pages.mjs +100 -0
  54. package/build/prerender-pool.mjs +165 -0
  55. package/build/prerender-process-worker.mjs +58 -0
  56. package/build/prerender-worker.mjs +10 -0
  57. package/build/production-builder.mjs +129 -0
  58. package/build/react-compiler-report.mjs +225 -0
  59. package/build/react-compiler.mjs +139 -0
  60. package/build/runtime-contract.mjs +2 -2
  61. package/build/scan.mjs +4 -4
  62. package/build/source-analysis.mjs +42 -0
  63. package/build/static-adapter.mjs +13 -5
  64. package/build/static-export.mjs +4 -4
  65. package/build/static-input.mjs +48 -0
  66. package/build/styles.mjs +17 -3
  67. package/build/transform-cache.mjs +37 -4
  68. package/build/transform.mjs +1 -1
  69. package/build/type-command.mjs +41 -0
  70. package/build/typed-routes.mjs +95 -0
  71. package/build/typescript-dev-worker.mjs +18 -0
  72. package/build/typescript-dev.mjs +49 -0
  73. package/build/typescript-native-worker.mjs +55 -0
  74. package/build/typescript-native.mjs +18 -0
  75. package/build/typescript-toolchain.mjs +19 -0
  76. package/build/typescript-watch.mjs +22 -0
  77. package/build/typescript.mjs +11 -8
  78. package/build/webpack-graph.mjs +170 -30
  79. package/build/worker-memory.mjs +9 -0
  80. package/build/worker-preload.mjs +9 -0
  81. package/cli.mjs +162 -122
  82. package/compat/activity-cache.cjs +26 -0
  83. package/compat/after.cjs +6 -4
  84. package/compat/app-layout-context.cjs +52 -2
  85. package/compat/base64.cjs +10 -0
  86. package/compat/cache-handlers.cjs +12 -2
  87. package/compat/cache-wire.cjs +28 -0
  88. package/compat/cache.cjs +2 -1
  89. package/compat/cache.d.cts +4 -0
  90. package/compat/cookies.cjs +21 -5
  91. package/compat/data-cache.cjs +12 -8
  92. package/compat/flight-delta.cjs +150 -0
  93. package/compat/headers.cjs +4 -2
  94. package/compat/incremental-cache.cjs +20 -7
  95. package/compat/index.d.ts +89 -10
  96. package/compat/link.cjs +12 -9
  97. package/compat/link.d.cts +4 -3
  98. package/compat/navigation-authorization.cjs +9 -0
  99. package/compat/navigation-stages.cjs +19 -0
  100. package/compat/navigation.d.cts +4 -3
  101. package/compat/offline-store.cjs +11 -0
  102. package/compat/offline.cjs +5 -0
  103. package/compat/offline.d.cts +2 -0
  104. package/compat/redis-cache.cjs +201 -0
  105. package/compat/redis-cache.d.cts +37 -0
  106. package/compat/router.d.cts +4 -3
  107. package/compat/server-primitives.cjs +31 -11
  108. package/compat/server.d.cts +15 -2
  109. package/compat/static-generation.cjs +7 -0
  110. package/compat/typed-route.d.cts +17 -0
  111. package/compat/use-cache.cjs +3 -0
  112. package/docs/ai/compatibility.md +275 -0
  113. package/docs/ai/data.md +288 -0
  114. package/docs/ai/deployment.md +269 -0
  115. package/docs/ai/manifest.json +11 -0
  116. package/docs/ai/quickstart.md +171 -0
  117. package/docs/ai/routing.md +214 -0
  118. package/docs/ai/troubleshooting.md +155 -0
  119. package/index.mjs +2 -0
  120. package/native/diagnostics.mjs +34 -0
  121. package/native/resolve.mjs +7 -2
  122. package/package.json +44 -7
  123. package/release/ai-docs.mjs +48 -0
  124. package/release/verify.mjs +2 -0
  125. package/runtime/action-client.mjs +10 -4
  126. package/runtime/action-server.mjs +2 -0
  127. package/runtime/ai-docs.mjs +202 -0
  128. package/runtime/app-client.mjs +226 -68
  129. package/runtime/app-content.mjs +4 -3
  130. package/runtime/app-partial-client.mjs +3 -0
  131. package/runtime/app-partial.mjs +1 -1
  132. package/runtime/app-prefetch-constants.mjs +4 -0
  133. package/runtime/app-prefetch.mjs +2 -3
  134. package/runtime/app-render-key.mjs +5 -0
  135. package/runtime/app-render.mjs +100 -28
  136. package/runtime/app-routing.mjs +22 -9
  137. package/runtime/app-static.mjs +18 -8
  138. package/runtime/app-stream-html.mjs +10 -53
  139. package/runtime/app-style-selection.mjs +34 -0
  140. package/runtime/app-styles.mjs +154 -0
  141. package/runtime/cache-components-rsc.mjs +4 -1
  142. package/runtime/cache-request.mjs +2 -1
  143. package/runtime/cloud/archive.mjs +5 -6
  144. package/runtime/compile-cache.mjs +8 -0
  145. package/runtime/deployment-client.mjs +19 -0
  146. package/runtime/dev-ai-brands.mjs +26 -0
  147. package/runtime/dev-ai-style.mjs +99 -0
  148. package/runtime/dev-build-reasons.mjs +25 -0
  149. package/runtime/dev-cache-control.mjs +53 -0
  150. package/runtime/dev-client.mjs +245 -46
  151. package/runtime/dev-compiler-broker.mjs +42 -0
  152. package/runtime/dev-console-copy.mjs +19 -0
  153. package/runtime/dev-console.mjs +106 -0
  154. package/runtime/dev-dependency-graph.mjs +40 -0
  155. package/runtime/dev-element-picker-style.mjs +27 -0
  156. package/runtime/dev-element-picker.mjs +213 -0
  157. package/runtime/dev-error-overlay-style.mjs +41 -0
  158. package/runtime/dev-error-overlay.mjs +167 -0
  159. package/runtime/dev-error-report.mjs +121 -0
  160. package/runtime/dev-file-changes.mjs +1 -1
  161. package/runtime/dev-gateway.mjs +44 -0
  162. package/runtime/dev-host-relay.mjs +66 -0
  163. package/runtime/dev-indicator.mjs +191 -0
  164. package/runtime/dev-issue-motion.mjs +214 -0
  165. package/runtime/dev-lazy-tool.mjs +34 -0
  166. package/runtime/dev-logo.mjs +2 -0
  167. package/runtime/dev-notification.mjs +81 -0
  168. package/runtime/dev-popout.mjs +34 -0
  169. package/runtime/dev-position.mjs +107 -0
  170. package/runtime/dev-preview-touch.mjs +122 -0
  171. package/runtime/dev-runtime-pool.mjs +45 -0
  172. package/runtime/dev-runtime-spare.mjs +57 -0
  173. package/runtime/dev-tools-client.mjs +155 -0
  174. package/runtime/dev-tools-mcp.mjs +126 -0
  175. package/runtime/dev-tools-metrics.mjs +31 -0
  176. package/runtime/dev-tools-model.mjs +42 -0
  177. package/runtime/dev-tools-panel.mjs +341 -0
  178. package/runtime/dev-tools-result.mjs +37 -0
  179. package/runtime/dev-tools-server.mjs +256 -0
  180. package/runtime/dev-tools-source.mjs +104 -0
  181. package/runtime/dev-tooltip.mjs +94 -0
  182. package/runtime/dev-ui-icons.mjs +8 -0
  183. package/runtime/dev-workbench-style.mjs +106 -0
  184. package/runtime/dev-workbench.mjs +428 -0
  185. package/runtime/dev-worker-logs.mjs +31 -0
  186. package/runtime/development.mjs +324 -0
  187. package/runtime/document.mjs +1 -1
  188. package/runtime/edge.mjs +6 -5
  189. package/runtime/env.mjs +2 -1
  190. package/runtime/fetch-cache.mjs +10 -8
  191. package/runtime/flight-delta.mjs +50 -0
  192. package/runtime/flight-tap.mjs +28 -0
  193. package/runtime/html-only.mjs +27 -0
  194. package/runtime/http.mjs +2 -0
  195. package/runtime/incremental-cache.mjs +25 -4
  196. package/runtime/instrumentation.mjs +49 -0
  197. package/runtime/navigation-recovery.mjs +36 -0
  198. package/runtime/navigation.mjs +7 -0
  199. package/runtime/node-html-stream.mjs +47 -0
  200. package/runtime/pages-client.mjs +24 -8
  201. package/runtime/pages-navigation.mjs +54 -64
  202. package/runtime/persistent/client.mjs +6 -5
  203. package/runtime/persistent/daemon.mjs +66 -56
  204. package/runtime/persistent/native-proxy.mjs +63 -0
  205. package/runtime/prefetch-scheduler.mjs +110 -0
  206. package/runtime/preload.mjs +12 -0
  207. package/runtime/profile.mjs +14 -0
  208. package/runtime/profiles.json +2 -2
  209. package/runtime/render.mjs +3 -0
  210. package/runtime/route-pattern.mjs +44 -0
  211. package/runtime/rsc-worker.mjs +41 -12
  212. package/runtime/trace.mjs +78 -0
  213. package/runtime/typed-route.mjs +19 -0
  214. package/runtime/watch-project.mjs +29 -18
  215. package/runtime/worker.mjs +102 -24
@@ -1,11 +1,12 @@
1
1
  import {build as esbuild, transform} from 'esbuild';
2
- import {mkdir, mkdtemp, readFile, writeFile, rm, realpath} from 'node:fs/promises';
2
+ import {mkdir, mkdtemp, readFile, writeFile, rm, rmdir, realpath} from 'node:fs/promises';
3
3
  import {createHash, randomUUID} from 'node:crypto';
4
4
  import {createRequire} from 'node:module';
5
5
  import {fileURLToPath} from 'node:url';
6
6
  import path from 'node:path';
7
7
  import { cachedAsyncTransform } from './transform-cache.mjs';
8
8
  import {inlineSourceMap} from './source-maps.mjs';
9
+ import {buildCacheIdentity} from './persistent-cache.mjs';
9
10
 
10
11
  const require = createRequire(import.meta.url);
11
12
  const {active} = require('./webpack-bridge.cjs');
@@ -13,6 +14,107 @@ const loaderPath = fileURLToPath(new URL('./webpack-loader.cjs', import.meta.url
13
14
  const hash = value => createHash('sha256').update(value).digest('hex').slice(0, 20);
14
15
  const slash = value => value.replaceAll(path.sep, '/');
15
16
  const safeAsset = name => { if (path.isAbsolute(name) || /[\\\0?#]/.test(name) || name.split('/').some(part => !part || part === '.' || part === '..')) throw new Error(`Unsafe webpack asset path: ${name}`); };
17
+ async function discardPrivateFile(file,boundary){
18
+ await rm(file,{force:true});
19
+ for(let directory=path.dirname(file);directory!==boundary&&directory.startsWith(boundary+path.sep);directory=path.dirname(directory)){
20
+ try{await rmdir(directory);}catch(error){if(['ENOTEMPTY','EEXIST'].includes(error.code))break;if(error.code!=='ENOENT')throw error;}
21
+ }
22
+ }
23
+ const sessions=new Map(),sessionLimit=6,sessionBuildLimit=64,objectIds=new WeakMap();
24
+ let nextId=0,created=0,reused=0,graphQueue=Promise.resolve();
25
+ const objectId=value=>{if(!objectIds.has(value))objectIds.set(value,++nextId);return objectIds.get(value);};
26
+ // Arbitrary plugin closures are not interchangeable just because their source
27
+ // text matches. Only known webpack constructors with the same options may be
28
+ // recreated; other plugins must retain their actual instance identity.
29
+ function fingerprint(value,seen=new Set()){
30
+ if(typeof value==='function')return ['function',objectId(value)];
31
+ if(value instanceof RegExp)return ['regexp',value.source,value.flags];
32
+ if(value===undefined)return ['undefined'];
33
+ if(typeof value==='symbol')throw new Error('Opaque symbol in webpack configuration');
34
+ if(typeof value==='bigint')return ['bigint',String(value)];
35
+ if(typeof value==='number'&&(!Number.isFinite(value)||Object.is(value,-0)))return ['number',String(value),Object.is(value,-0)];
36
+ if(!value||typeof value!=='object')return value;
37
+ if(seen.has(value))return ['object',objectId(value)];
38
+ seen.add(value);
39
+ const properties=(skipIndices=false)=>{
40
+ const keys=Reflect.ownKeys(value);
41
+ if(keys.length>10000||keys.some(key=>typeof key==='symbol'))throw new Error('Opaque or excessive webpack configuration');
42
+ return keys.filter(key=>!skipIndices||!/^(?:0|[1-9]\d*)$/.test(key)).sort().map(key=>{
43
+ const descriptor=Object.getOwnPropertyDescriptor(value,key);if(descriptor.get||descriptor.set)throw new Error('Accessor in webpack configuration');
44
+ return[key,descriptor.enumerable,descriptor.configurable,descriptor.writable,fingerprint(descriptor.value,seen)];
45
+ });
46
+ };
47
+ const prototype=Object.getPrototypeOf(value);let result;
48
+ if(Array.isArray(value))result=['array',properties()];
49
+ else if(prototype===Map.prototype){
50
+ const extras=properties();
51
+ if(value.size>10000)throw new Error('Excessive webpack Map configuration');
52
+ const key=item=>item&&(typeof item==='object'||typeof item==='function')?['identity',objectId(item),fingerprint(item,seen)]:fingerprint(item,seen);
53
+ result=['map',[...Map.prototype.entries.call(value)].map(([name,item])=>[key(name),fingerprint(item,seen)]),extras];
54
+ }else if(prototype===Set.prototype){
55
+ const extras=properties();
56
+ if(value.size>10000)throw new Error('Excessive webpack Set configuration');
57
+ result=['set',[...Set.prototype.values.call(value)].map(item=>item&&(typeof item==='object'||typeof item==='function')?['identity',objectId(item),fingerprint(item,seen)]:fingerprint(item,seen)),extras];
58
+ }else if(prototype===Date.prototype)result=['date',String(Date.prototype.getTime.call(value)),properties()];
59
+ else if(prototype===ArrayBuffer.prototype){
60
+ if(value.byteLength>1024*1024)throw new Error('Excessive webpack binary configuration');
61
+ result=['array-buffer',Buffer.from(value).toString('base64'),value.resizable,value.maxByteLength,properties()];
62
+ }else if(ArrayBuffer.isView(value)){
63
+ if(value.byteLength>10000)throw new Error('Excessive webpack binary view configuration');
64
+ const types=[Buffer,Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array,DataView];
65
+ const type=types.find(type=>prototype===type.prototype);
66
+ if(!type)throw new Error('Opaque webpack binary configuration');
67
+ result=[type.name,fingerprint(value.buffer,seen),value.byteOffset,value.byteLength,properties(true)];
68
+ }else if(prototype===Object.prototype||prototype===null)result=['record',prototype===null,properties()];
69
+ else throw new Error('Opaque webpack configuration instance');
70
+ seen.delete(value);return result;
71
+ }
72
+ function pluginIdentity(plugin,webpack){
73
+ const fields={NormalModuleReplacementPlugin:['resourceRegExp','newResource'],IgnorePlugin:['options'],ProvidePlugin:['definitions'],DefinePlugin:['definitions'],BannerPlugin:['options']};
74
+ for(const[name,keys]of Object.entries(fields))if(plugin.constructor===webpack[name])return[name,fingerprint(Object.fromEntries(keys.map(key=>[key,plugin[key]])))];
75
+ return ['instance',objectId(plugin),fingerprint(plugin)];
76
+ }
77
+ export const webpackCompilerStats=()=>({contexts:sessions.size,created,reused,limit:sessionLimit,buildLimit:sessionBuildLimit});
78
+ function queued(callback){const task=graphQueue.then(callback);graphQueue=task.catch(()=>{});return task;}
79
+ export function closeWebpackCompiler(root){return queued(async()=>{
80
+ const closing=[];for(const[key,session]of sessions)if(root===undefined||session.root===root){sessions.delete(key);closing.push(session.dispose());}
81
+ const results=await Promise.allSettled(closing),errors=results.filter(result=>result.status==='rejected').map(result=>result.reason);
82
+ if(errors.length)throw new AggregateError(errors,'Unable to dispose webpack compiler sessions');
83
+ });}
84
+
85
+ async function compileGraph(resolver,loads,options,{state,config,target}){
86
+ return queued(async()=>{
87
+ const webpack=(await import('webpack')).default;
88
+ let reusable=state.dev&&state.incremental,identity;
89
+ if(reusable){try{identity=await buildCacheIdentity(state.root);}catch{}if(!identity)reusable=false;}
90
+ let base,key;
91
+ if(reusable)try{
92
+ const normalize=value=>typeof value==='string'?value.replaceAll(state.stage,'<stage>'):Array.isArray(value)?value.map(normalize):value&&typeof value==='object'&&[Object.prototype,null].includes(Object.getPrototypeOf(value))?Object.fromEntries(Object.entries(value).map(([key,item])=>[key,normalize(item)])):value;
93
+ const normalized=normalize({...options,plugins:(options.plugins||[]).map(plugin=>plugin.name),stdin:options.stdin&&{...options.stdin,contents:undefined}});
94
+ base=hash(JSON.stringify(fingerprint([state.root,target,normalized])));
95
+ key=hash(JSON.stringify([base,identity,fingerprint({...config,graphPlugins:undefined}),config.graphPlugins.map(plugin=>pluginIdentity(plugin,webpack))]));
96
+ }catch{reusable=false;}
97
+ // Unknown data structures, safe mode and unavailable identities all keep
98
+ // compilation correct through a one-shot compiler. Hidden mutable closure
99
+ // state cannot be fingerprinted: replace the plugin instance when it changes.
100
+ if(state.dev&&!reusable)for(const[key,session]of sessions)if(session.root===state.root){sessions.delete(key);await session.dispose();}
101
+ let session=reusable&&sessions.get(key);
102
+ if(session&&session.builds>=sessionBuildLimit){sessions.delete(key);await session.dispose();session=undefined;}
103
+ if(reusable){
104
+ for(const[old,previous]of sessions)if(previous.root===state.root&&(previous.identity!==identity||previous.base===base&&old!==key)){sessions.delete(old);await previous.dispose();}
105
+ }
106
+ if(session){sessions.delete(key);reused++;}
107
+ else{
108
+ if(reusable)while(sessions.size>=sessionLimit){const[old,previous]=sessions.entries().next().value;sessions.delete(old);await previous.dispose();}
109
+ session=await createGraphSession(state.root,{webpack,reusable,stage:state.stage});created++;
110
+ Object.assign(session,{root:state.root,base,identity,builds:0});
111
+ }
112
+ if(reusable)sessions.set(key,session);
113
+ try{session.builds++;return await session.run(resolver,loads,options,{state,config,target});}
114
+ catch(error){if(reusable)sessions.delete(key);await session.dispose();throw error;}
115
+ finally{if(!reusable)await session.dispose();}
116
+ });
117
+ }
16
118
 
17
119
  /** Webpack owns the actual module/chunk graph. PRNext's resolve/load transforms
18
120
  * remain authoritative for React boundaries and the Node/Edge/browser targets.
@@ -37,27 +139,41 @@ export async function webpackGraph(options, {state, config, target}) {
37
139
  finally { for (const dispose of disposes) await dispose(); }
38
140
  }
39
141
 
40
- async function compileGraph(resolver, loads, options, {state,config,target}) {
41
- const webpack = (await import('webpack')).default;
142
+ async function createGraphSession(root,{webpack,reusable,stage}) {
42
143
  const bridgeId = randomUUID();
43
- await mkdir(path.join(state.stage,'.entries'),{recursive:true});
44
- const virtualDirectory = await realpath(await mkdtemp(path.join(state.stage,'.entries/webpack-')));
45
- const temporary = await mkdtemp(path.join(state.stage,'.webpack-output-'));
46
- const outdir = options.outdir || (options.outfile ? path.dirname(options.outfile) : temporary);
47
- const actualOut = options.write === false ? temporary : outdir;
48
- const extension = options.outExtension?.['.js'] || '.js';
49
- const modules = new Map();
50
- const projectRequire = createRequire(path.join(state.root,'package.json'));
51
- let compiler;
52
- const entries = options.stdin ? {entry:path.join(virtualDirectory,'entry.js')}
53
- : Array.isArray(options.entryPoints) ? Object.fromEntries(options.entryPoints.map(file=>[path.basename(file,path.extname(file)),file])) : options.entryPoints;
144
+ await mkdir(path.join(stage,'.entries'),{recursive:true});
145
+ const home=await realpath(await mkdtemp(path.join(reusable?root:path.join(stage,'.entries'),'.prnext-webpack-')));
146
+ const virtualDirectory=path.join(home,'modules'),virtualStage=path.join(home,'stage'),scratchOut=path.join(home,'output');
147
+ await mkdir(virtualDirectory,{recursive:true});
148
+ const projectRequire = createRequire(path.join(root,'package.json'));
149
+ let compiler,resolver,loads,options,state,config,target,outdir,actualOut,extension,modules,entries,runtime,disposed=false;
150
+ const virtualFiles=new Set(),loaderFiles=new Set(),previousOutputs=new Set();let usedVirtualFiles;
151
+ const fromVirtual=value=>typeof value==='string'&&(value===virtualStage||value.startsWith(virtualStage+path.sep))?state.stage+value.slice(virtualStage.length):value;
152
+ const toVirtual=value=>reusable&&typeof value==='string'&&(value===state.stage||value.startsWith(state.stage+path.sep))?virtualStage+value.slice(state.stage.length):value;
153
+ const stageFile=async file=>{
154
+ const mapped=toVirtual(file);if(mapped===file)return file;
155
+ virtualFiles.add(mapped);usedVirtualFiles.add(mapped);
156
+ const contents=await readFile(file);await mkdir(path.dirname(mapped),{recursive:true});
157
+ let previous;try{previous=await readFile(mapped);}catch(error){if(error.code!=='ENOENT')throw error;}
158
+ if(!previous?.equals(contents))await writeFile(mapped,contents);return mapped;
159
+ };
160
+ const run=async(nextResolver,nextLoads,nextOptions,next)=>{
161
+ resolver=nextResolver;loads=nextLoads;options=nextOptions;({state,config,target}=next);
162
+ usedVirtualFiles=new Set();
163
+ outdir=options.outdir||(options.outfile?path.dirname(options.outfile):scratchOut);actualOut=reusable||options.write===false?scratchOut:outdir;extension=options.outExtension?.['.js']||'.js';modules=new Map();
164
+ // Webpack may retain SizeOnlySource for unchanged assets and assume the last
165
+ // emitted file still exists. Keep current outputs until the next run finishes.
166
+ await mkdir(actualOut,{recursive:true});
167
+ entries=options.stdin?{entry:path.join(virtualDirectory,'entry.js')}:Array.isArray(options.entryPoints)?Object.fromEntries(options.entryPoints.map(file=>[path.basename(file,path.extname(file)),toVirtual(file)])):Object.fromEntries(Object.entries(options.entryPoints).map(([name,file])=>[name,toVirtual(file)]));
168
+ for(const file of Object.values(entries))if(file.startsWith(virtualStage+path.sep))await stageFile(fromVirtual(file));
54
169
  if (options.stdin) {
55
170
  const file = entries.entry;
56
171
  await writeFile(file,options.stdin.contents);
172
+ virtualFiles.add(file);usedVirtualFiles.add(file);
57
173
  modules.set(file,{path:file,namespace:'file',resolveDir:options.stdin.resolveDir || state.root,stdin:true});
58
174
  }
59
175
  const identify = record => record.namespace === 'file' ? slash(path.relative(state.root,record.path)) : record.namespace+':'+slash(path.relative(state.root,record.path));
60
- const lookup = resource => modules.get(resource) || modules.get(resource?.split('?')[0]) || {path:resource?.split('?')[0],namespace:'file',suffix:resource?.includes('?')?'?'+resource.split('?').slice(1).join('?'):''};
176
+ const lookup = resource => modules.get(resource) || modules.get(resource?.split('?')[0]) || {path:fromVirtual(resource?.split('?')[0]),namespace:'file',suffix:resource?.includes('?')?'?'+resource.split('?').slice(1).join('?'):''};
61
177
  const load = async(record,source,webpackContext,sourceMap) => {
62
178
  if (record.stdin) return {contents:source,loader:options.stdin.loader || 'js',resolveDir:record.resolveDir};
63
179
  for (const {settings,callback,framework,configuredLoaders} of loads) {
@@ -106,18 +222,18 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
106
222
  // A changed synchronous chunk may leave webpack's runtime bytes unchanged.
107
223
  // Give each published build its own module cache; React's dev singletons
108
224
  // still retain their identities across the new runtime instance.
109
- const runtime = 'webpack-runtime-'+hash(JSON.stringify([state.stage,Object.keys(entries),options.conditions,target]));
225
+ runtime = 'webpack-runtime-'+hash(JSON.stringify([state.stage,Object.keys(entries),options.conditions,target]));
110
226
  try {
111
- compiler = webpack({name:target,context:state.root,mode:state.dev?'development':'production',
227
+ if(!compiler)compiler = webpack({name:target,context:state.root,mode:state.dev?'development':'production',
112
228
  ...(config.watchOptions ? {watchOptions:config.watchOptions} : {}),
113
- target:target==='node'?'node22':['web','es2022'],entry:entries,cache:false,devtool:options.sourcemap?'source-map':false,
229
+ target:target==='node'?'node22':['web','es2022'],entry:entries,cache:reusable?{type:'memory',maxGenerations:1}:false,devtool:options.sourcemap?'source-map':false,
114
230
  experiments:{outputModule:true,css:true},externalsType:'module',
115
231
  output:{path:actualOut,filename:options.outfile?path.basename(options.outfile):naming(options.entryNames)+extension,
116
232
  chunkFilename:naming(options.chunkNames || 'chunk-[hash]')+extension,cssFilename:naming(options.entryNames)+'.css',
117
233
  cssChunkFilename:'style-[contenthash:16].css',module:true,library:{type:'module'},chunkFormat:'module',chunkLoading:'import',
118
- publicPath:target==='node'?'./':state.assetBase+'/',environment:{module:true,dynamicImport:true},clean:false},
234
+ publicPath:target==='node'?'./':state.assetBase+'/',environment:{module:true,dynamicImport:true},compareBeforeEmit:false,clean:false},
119
235
  performance:false,
120
- optimization:{minimize:!!options.minify,minimizer:[{apply(compiler){
236
+ optimization:{minimize:!!options.minify,mangleExports:options.minify?'size':false,minimizer:[{apply(compiler){
121
237
  compiler.hooks.compilation.tap('PRNextMinify',compilation=>{
122
238
  compilation.hooks.processAssets.tapPromise({name:'PRNextMinify',stage:webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE},async()=>{
123
239
  // Sequential native esbuild transforms avoid a Terser worker pool.
@@ -130,10 +246,10 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
130
246
  }
131
247
  });
132
248
  });
133
- }}],runtimeChunk:options.splitting?{name:runtime}:false,
134
- splitChunks:options.splitting?{chunks:'all',minSize:0,cacheGroups:{default:false,defaultVendors:false,shared:{minChunks:2,test:module=>!module.type.startsWith('css'),name:false}}}:false},
249
+ }}],runtimeChunk:options.splitting?{name:()=>runtime}:false,
250
+ splitChunks:options.splitting?{chunks:'all',minSize:0,cacheGroups:{default:false,defaultVendors:false,shared:{minChunks:2,reuseExistingChunk:true,test:module=>!module.type.startsWith('css'),name:false}}}:false},
135
251
  resolve:{alias:config.resolution.resolveAlias || {},conditionNames:[...(options.conditions || []),'...'],extensions:config.resolution.resolveExtensions || ['.tsx','.ts','.jsx','.js','.mjs','.json'],fullySpecified:false},
136
- module:{rules:[{test:/./,type:'javascript/auto',enforce:'post',use:[{loader:loaderPath,options:{bridge:bridgeId}}]},
252
+ module:{unsafeCache:false,rules:[{test:/./,type:'javascript/auto',enforce:'post',use:[{loader:loaderPath,options:{bridge:bridgeId}}]},
137
253
  {test:/\.json$/,type:'json'},{test:/\.(?:css|scss|sass)$/,type:'css'},
138
254
  {dependency:'url',type:'asset/resource'}]},
139
255
  plugins:[...config.graphPlugins,
@@ -143,7 +259,7 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
143
259
  {apply(compiler) {
144
260
  if (state.devInputs && config.graphPlugins.some(plugin=>plugin.constructor?.name==='ContentlayerWebpackPlugin')) {
145
261
  compiler.hooks.beforeCompile.tapPromise({name:'PRNextGeneratedInputs',stage:Number.MAX_SAFE_INTEGER},()=>
146
- state.generatedInputs ||= state.devInputs.captureDirectory('.contentlayer/generated'));
262
+ state.devInputs&&(state.generatedInputs ||= state.devInputs.captureDirectory('.contentlayer/generated')));
147
263
  }
148
264
  compiler.hooks.normalModuleFactory.tap('PRNextFramework',factory=>{
149
265
  factory.hooks.beforeResolve.tapPromise({name:'PRNextFramework',stage:10000},async data=>{
@@ -151,12 +267,12 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
151
267
  const importer = lookup(data.contextInfo.issuer);
152
268
  const dependency = data.dependencies[0];
153
269
  const kind = dependency?.type?.includes('url') ? 'url-token' : dependency?.type==='css @import'?'import-rule':data.dependencyType==='commonjs'?'require-call':dependency?.type==='import()'?'dynamic-import':data.contextInfo.issuer?'import-statement':'entry-point';
154
- const settings={resolveDir:importer.resolveDir || data.context || state.root,importer:importer.path || '',namespace:importer.namespace || 'file',kind};
270
+ const settings={resolveDir:fromVirtual(importer.resolveDir || data.context || state.root),importer:importer.path || '',namespace:importer.namespace || 'file',kind};
155
271
  // Let webpack resolve/execute the inline loader chain itself. Only
156
272
  // its resource passes through framework resolution.
157
273
  const bang=data.request.lastIndexOf('!');
158
274
  const inline=bang<0?'':data.request.slice(0,bang+1);
159
- const request=bang<0?data.request:data.request.slice(bang+1);
275
+ const request=fromVirtual(bang<0?data.request:data.request.slice(bang+1));
160
276
  const resolved = await resolver.resolve(request,settings);
161
277
  if (resolved.errors.length) throw new Error(resolved.errors.map(error=>error.text).join('\n'));
162
278
  if (resolved.external) {
@@ -174,6 +290,9 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
174
290
  if(record.namespace!=='file'){
175
291
  resource=path.join(virtualDirectory,hash(JSON.stringify([record.namespace,record.path,record.suffix]))+(/css$/.test(record.namespace)?'.css':'.js'));
176
292
  if(!modules.has(resource))await writeFile(resource,'');
293
+ virtualFiles.add(resource);usedVirtualFiles.add(resource);
294
+ }else if(toVirtual(record.path)!==record.path){
295
+ resource=await stageFile(record.path)+(record.suffix||'');
177
296
  }
178
297
  data.request=inline+resource;
179
298
  modules.set(data.request,record);
@@ -206,6 +325,11 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
206
325
  });
207
326
  }},
208
327
  ]});
328
+ // Native loader modules live outside webpack's graph cache. Reload tracked
329
+ // implementations so editing a loader cannot retain its require() closure.
330
+ const clearLoader=file=>{const loaded=require.cache[file];delete require.cache[file];for(const child of loaded?.children||[])if(child.filename!==loaderPath)clearLoader(child.filename);};
331
+ for(const file of loaderFiles)clearLoader(file);
332
+ compiler.inputFileSystem.purge?.();
209
333
  const stats=await new Promise((resolve,reject)=>compiler.run((error,stats)=>error?reject(error):resolve(stats)));
210
334
  if(stats.hasErrors())throw new Error(stats.toString({all:false,errors:true,errorDetails:true}));
211
335
  for(const warning of stats.compilation.warnings){
@@ -218,10 +342,11 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
218
342
  for(const chunk of compilation.chunks)for(const file of chunk.files)chunkFiles.add(file);
219
343
  for(const [name,entry] of compilation.entrypoints){
220
344
  const file=[...entry.getEntrypointChunk().files].find(file=>file.endsWith(extension));
221
- if(file)entryFiles.set(file,entries[name]);
345
+ if(file)entryFiles.set(file,fromVirtual(entries[name]));
222
346
  const css=entry.getFiles().find(file=>file.endsWith('.css'));if(file&&css)styles.set(file,css);
223
347
  }
224
348
  for(const module of compilation.modules){
349
+ for(const loader of module.loaders||[])if(loader.loader!==loaderPath)loaderFiles.add(loader.loader);
225
350
  if(!module.resource)continue;
226
351
  const record=lookup(module.resource), imports=[];
227
352
  for(const connection of compilation.moduleGraph.getOutgoingConnections(module)){
@@ -234,6 +359,7 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
234
359
  safeAsset(asset.name);
235
360
  const contents=await readFile(path.join(actualOut,asset.name));
236
361
  const file=path.join(outdir,asset.name), key=slash(path.relative(state.root,file));
362
+ if(options.write!==false&&file!==path.join(actualOut,asset.name)){await mkdir(path.dirname(file),{recursive:true});await writeFile(file,contents);}
237
363
  outputs[key]={bytes:contents.length,imports:[],exports:[],inputs:inputSizes,
238
364
  ...(entryFiles.has(asset.name)?{entryPoint:slash(path.relative(state.root,entryFiles.get(asset.name)))}:{}),
239
365
  ...(styles.has(asset.name)?{cssBundle:slash(path.relative(state.root,path.join(outdir,styles.get(asset.name))))}:{})};
@@ -248,9 +374,23 @@ async function compileGraph(resolver, loads, options, {state,config,target}) {
248
374
  }
249
375
  const rank = file => entryFiles.has(slash(path.relative(outdir,file.path))) ? 0 : file.path.endsWith(extension) ? 1 : 2;
250
376
  outputFiles.sort((a,b)=>rank(a)-rank(b));
251
- return {errors:[],warnings:[],metafile:{inputs,outputs},...(options.write===false?{outputFiles}:{})};
377
+ if(reusable){
378
+ const currentOutputs=new Set(compilation.getAssets().map(asset=>asset.name));
379
+ for(const name of previousOutputs)if(!currentOutputs.has(name))await discardPrivateFile(path.join(actualOut,name),actualOut);
380
+ previousOutputs.clear();for(const name of currentOutputs)previousOutputs.add(name);
381
+ }
382
+ // This compatibility metafile does not yet encode webpack's runtime and
383
+ // async chunk-group edges. Consumers must not mistake empty import lists
384
+ // for a complete initial browser graph.
385
+ return {errors:[],warnings:[],metafile:{inputs,outputs,initialGraphComplete:false},...(options.write===false?{outputFiles}:{})};
252
386
  } finally {
253
- try {if(compiler)await new Promise((resolve,reject)=>compiler.close(error=>error?reject(error):resolve()));}
254
- finally {active.delete(bridgeId);await rm(temporary,{recursive:true,force:true});await rm(virtualDirectory,{recursive:true,force:true});}
387
+ active.delete(bridgeId);
388
+ for(const file of virtualFiles)if(!usedVirtualFiles.has(file)){virtualFiles.delete(file);await discardPrivateFile(file,home);}
255
389
  }
390
+ };
391
+ return {run,async dispose(){
392
+ if(disposed)return;disposed=true;
393
+ try{if(compiler)await new Promise((resolve,reject)=>compiler.close(error=>error?reject(error):resolve()));}
394
+ finally{active.delete(bridgeId);await rm(home,{recursive:true,force:true});compiler=resolver=state=config=options=modules=undefined;loads=[];virtualFiles.clear();loaderFiles.clear();previousOutputs.clear();}
395
+ }};
256
396
  }
@@ -0,0 +1,9 @@
1
+ export function validateWorkerMemory(value) {
2
+ if (value === undefined || value === false) return undefined;
3
+ if (!value || typeof value !== 'object' || Array.isArray(value) ||
4
+ Object.keys(value).some(key => key !== 'maxRssMb') ||
5
+ !Number.isSafeInteger(value.maxRssMb) || value.maxRssMb < 64 || value.maxRssMb > 65536) {
6
+ throw new TypeError('workerMemory must be false or { maxRssMb: 64..65536 }');
7
+ }
8
+ return { maxRssMb: value.maxRssMb };
9
+ }
@@ -0,0 +1,9 @@
1
+ export function validateWorkerPreload(value){
2
+ if(value===undefined||value===false)return undefined;
3
+ if(!value||typeof value!=='object'||Array.isArray(value))throw new TypeError('workerPreload must be false or an object');
4
+ for(const key of Object.keys(value))if(!['routes','maxRssMb','timeoutMs'].includes(key))throw new TypeError(`workerPreload.${key} is not supported`);
5
+ if(!Array.isArray(value.routes)||!value.routes.length||value.routes.length>16||value.routes.some(route=>typeof route!=='string'||!route.startsWith('/')||route.length>1024||/[?#\x00-\x1f]/.test(route))||new Set(value.routes).size!==value.routes.length)throw new TypeError('workerPreload.routes must contain 1 to 16 distinct route patterns');
6
+ const options={routes:value.routes,maxRssMb:value.maxRssMb??256,timeoutMs:value.timeoutMs??5000};
7
+ for(const [key,min,max] of [['maxRssMb',16,2048],['timeoutMs',100,30000]])if(!Number.isInteger(options[key])||options[key]<min||options[key]>max)throw new TypeError(`workerPreload.${key} must be between ${min} and ${max}`);
8
+ return options;
9
+ }