@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,4 +1,10 @@
1
+ import {fileURLToPath} from 'node:url';
2
+ import {dirname} from 'node:path';
3
+ import {readFile} from 'node:fs/promises';
4
+ import {analyzeSource} from './source-analysis.mjs';
5
+ import {cachedTransform} from './transform-cache.mjs';
1
6
  const adapters = new Map([
7
+ ['cache/redis','redis-cache'],
2
8
  ['dist/shared/lib/router-context.shared-runtime','next-router-context'],
3
9
  ['dist/shared/lib/router-context','next-router-context'],
4
10
  ['dist/shared/lib/app-router-context.shared-runtime','next-app-router-context'],
@@ -15,3 +21,47 @@ export function frameworkImportName(specifier) {
15
21
  const name=specifier.replace(frameworkSubpathPattern, '').replace(/\.js$/,'');
16
22
  return adapters.get(name) || name;
17
23
  }
24
+
25
+ // Application graphs must not traverse the programmatic build API's native tools.
26
+ // Outside a compiled application, the package root retains its full Node API.
27
+ export function applicationEntry(builder,readSource=(file,override)=>override??readFile(file,'utf8'),{transformTypedHelpers=false}={}){
28
+ // Server/client module boundaries in .mts/.cts are not supported. Never
29
+ // silently bundle a server implementation or misclassify a client boundary.
30
+ builder.onLoad({filter:/\.[cm]ts$/i,namespace:'file'},async args=>{
31
+ // Loader bridges dispatch framework callbacks independently of their filters.
32
+ if(!/\.[cm]ts$/i.test(args.path))return;
33
+ const source=await readSource(args.path,args.prnextSource);
34
+ const original=args.prnextSource===undefined?source:await readSource(args.path);
35
+ let nativeDecorators=false;
36
+ // A loader may remove a directive as well as introduce one. Both versions
37
+ // must be safe before its output can become an ordinary browser module.
38
+ for(const candidate of new Set([original,source])){
39
+ const unsupported=cachedTransform('unsupported-framework-directives',candidate,[args.path,'v4-native-decorators'],()=>{
40
+ const analysis=analyzeSource(candidate,args.path);
41
+ if(analysis.frameworkBoundary)return 'yes';
42
+ const pending=[analysis.program];let decorators=false;
43
+ while(pending.length){
44
+ const node=pending.pop();
45
+ if(node.type==='ExpressionStatement'&&node.directive==='use server')return 'yes';
46
+ if(node.type==='Decorator'||node.decorators?.length)decorators=true;
47
+ for(const value of Object.values(node)){
48
+ if(Array.isArray(value)){for(const item of value)if(item?.type)pending.push(item);}
49
+ else if(value&&typeof value==='object'&&value.type)pending.push(value);
50
+ }
51
+ }
52
+ return decorators?'decorated':'no';
53
+ });
54
+ if(unsupported==='yes')throw new Error(`React server/client directives are not supported in ${args.path}. Rename this file to .ts or .tsx.`);
55
+ nativeDecorators ||= unsupported==='decorated';
56
+ }
57
+ // Compiler-enabled render graphs can transform ordinary typed helpers after
58
+ // this boundary check. Other consumers keep the historical esbuild path.
59
+ // Decorated typed helpers already have a native esbuild implementation.
60
+ // Keep that path instead of widening the syntax accepted by every Babel
61
+ // transform, while their importing React components can still be compiled.
62
+ if(transformTypedHelpers && !nativeDecorators)return;
63
+ return {contents:source,loader:args.prnextSource===undefined?'ts':'tsx',resolveDir:dirname(args.path)};
64
+ });
65
+ builder.onResolve({filter:/^(?:prnext|@thomas\.f\/prnext)(?:\/route(?:\.js)?)?$/},()=>
66
+ ({path:fileURLToPath(new URL('../runtime/typed-route.mjs',import.meta.url))}));
67
+ }
@@ -0,0 +1,53 @@
1
+ import {assertHtmlOnlyDocument} from '../runtime/html-only.mjs';
2
+ const MAX_PATTERNS = 128;
3
+
4
+ export function validateHtmlOnly(value) {
5
+ if (value === undefined) return [];
6
+ if (!Array.isArray(value) || value.length > MAX_PATTERNS) throw new TypeError('htmlOnly must be an array of at most 128 App route patterns.');
7
+ for (const pattern of value) {
8
+ if (typeof pattern !== 'string' || pattern.length > 512 || !pattern.startsWith('/') || pattern.startsWith('//') ||
9
+ /[\s\\?#\u0000-\u001f\u007f]/.test(pattern) || /%(?![\da-f]{2})/i.test(pattern)) throw new TypeError('htmlOnly entries must be clean absolute route patterns without queries, fragments, whitespace or backslashes.');
10
+ const pathname = pattern.endsWith('/**') ? pattern.slice(0, -3) || '/' : pattern;
11
+ if (pathname.includes('*') || (pathname !== '/' && pathname.endsWith('/')) || pathname.slice(1).includes('//')) throw new TypeError('htmlOnly supports exact registered route patterns and a final /** subtree suffix only.');
12
+ for (const segment of pathname.split('/').slice(1)) {
13
+ let decoded;
14
+ try { decoded = decodeURIComponent(segment); } catch { throw new TypeError('htmlOnly patterns must use valid URL encoding.'); }
15
+ if (decoded === '.' || decoded === '..' || /[\\/\u0000-\u001f\u007f]/.test(decoded)) throw new TypeError('htmlOnly patterns cannot contain dot segments or encoded path separators.');
16
+ }
17
+ }
18
+ return [...new Set(value)];
19
+ }
20
+
21
+ export function matchesHtmlOnly(pattern, routePattern) {
22
+ if (!pattern.endsWith('/**')) return pattern === routePattern;
23
+ const prefix = pattern.slice(0, -3);
24
+ return !prefix || routePattern === prefix || routePattern.startsWith(prefix + '/');
25
+ }
26
+
27
+ export function selectHtmlOnlyRoutes(routes, patterns, { cacheComponents = false } = {}) {
28
+ const selected = new Set();
29
+ for (const pattern of validateHtmlOnly(patterns)) {
30
+ const matches = routes.filter(route => !route.internal && matchesHtmlOnly(pattern, route.pattern));
31
+ if (!matches.length) throw new Error(`htmlOnly pattern ${JSON.stringify(pattern)} does not match a registered route.`);
32
+ for (const route of matches) {
33
+ if (route.router !== 'app' || route.kind !== 'page') throw new Error(`htmlOnly route ${route.pattern} must be an App Router page; Pages Router and Route Handlers are not supported.`);
34
+ const config = route.cacheConfig || {};
35
+ if (cacheComponents || config.runtime === 'edge' || config.dynamic === 'force-dynamic' || config.forceNoStore || (config.revalidate !== undefined && config.revalidate !== false)) {
36
+ throw new Error(`htmlOnly route ${route.pattern} must be fully prerendered without Edge, Cache Components/PPR, dynamic rendering or ISR.`);
37
+ }
38
+ if (route.parallel || route.interception) throw new Error(`htmlOnly route ${route.pattern} does not support parallel or intercepted routing.`);
39
+ if (route.pattern.includes('[') && config.dynamicParams !== false) throw new Error(`htmlOnly route ${route.pattern} requires dynamicParams: false and complete generateStaticParams results.`);
40
+ selected.add(route.id);
41
+ }
42
+ }
43
+ return selected;
44
+ }
45
+
46
+ export function validateHtmlOnlyPrerender(route, rendered) {
47
+ if (!route.htmlOnly) return;
48
+ if (rendered.dynamic || rendered.partial || rendered.revalidate !== false || (rendered.status ?? 200) !== 200) {
49
+ throw new Error(`htmlOnly route ${route.pattern} must produce a complete successful static HTML document without dynamic data, partial rendering, redirects or ISR.`);
50
+ }
51
+ const html = Buffer.isBuffer(rendered.body) ? rendered.body.toString('utf8') : rendered.body;
52
+ assertHtmlOnlyDocument(html, route.pattern);
53
+ }
@@ -0,0 +1,67 @@
1
+ import path from 'node:path';
2
+ import { builtinModules } from 'node:module';
3
+ import { analyzeSource } from './source-analysis.mjs';
4
+ import { frameworkImportName, frameworkImportPattern } from './framework-imports.mjs';
5
+
6
+ const builtins = new Set([...builtinModules, ...builtinModules.map(name => 'node:' + name)]);
7
+ const interactive = new Set(['link','image','head','script','dynamic','router','compat/router','next-router-context','next-app-router-context']);
8
+ const namespace = /^prnext-(?:css-mapping|compiled-css|app-empty|edge-empty):/;
9
+
10
+ export function createHtmlOnlyGraph(root) {
11
+ const graph = new Map(), issues = new Map();
12
+ const file = value => path.resolve(root, value.replace(namespace, ''));
13
+ function issue(filename, reason) {
14
+ const key = file(filename);
15
+ if (!issues.has(key)) issues.set(key, reason);
16
+ }
17
+ return {
18
+ issue,
19
+ alias(source, target) {
20
+ if (file(source) !== file(target)) graph.set(file(source), [{ path:file(target) }]);
21
+ },
22
+ source(filename, source) {
23
+ const { program, directives, computedRequire, imports } = analyzeSource(source, filename);
24
+ if (directives.includes('use client')) issue(filename, 'a Client Component (use client)');
25
+ if (computedRequire) issue(filename, 'a computed require() whose dependencies cannot be validated');
26
+ for (const dependency of imports) if (frameworkImportPattern.test(dependency.path) && interactive.has(frameworkImportName(dependency.path))) issue(filename, `interactive framework import ${dependency.path}; use ordinary HTML elements instead`);
27
+ const pending = [program];
28
+ while (pending.length) {
29
+ const node = pending.pop();
30
+ if (node.directive === 'use server') issue(filename, 'a Server Action (use server)');
31
+ if (node.type === 'ImportExpression' && typeof node.source?.value !== 'string') issue(filename, 'a computed import() whose dependencies cannot be validated');
32
+ if (node.type === 'JSXOpeningElement' && node.name?.type === 'JSXIdentifier' && ['script','iframe','object','embed'].includes(node.name.name.toLowerCase())) issue(filename, `a <${node.name.name}> element`);
33
+ if (node.type === 'JSXAttribute' && node.name?.type === 'JSXIdentifier' && /^on[A-Z]/.test(node.name.name)) issue(filename, `interactive event property ${node.name.name}`);
34
+ for (const value of Object.values(node)) {
35
+ if (Array.isArray(value)) { for (const child of value) if (child?.type) pending.push(child); }
36
+ else if (value && typeof value === 'object' && value.type) pending.push(value);
37
+ }
38
+ }
39
+ },
40
+ record(metafile) {
41
+ for (const [name, input] of Object.entries(metafile?.inputs || {})) {
42
+ const key=file(name), dependencies=graph.get(key) || [];
43
+ for (const dependency of input.imports || []) {
44
+ if (!dependency.external) dependencies.push({path:file(dependency.path)});
45
+ else if (!builtins.has(dependency.path) && !/^(?:react|react-dom|react-server-dom-webpack)(?:\/|$)/.test(dependency.path) && !/^\.\.\/compat\/(?:navigation|headers|cache|server|font-local|font-google|og)\.cjs$/.test(dependency.path)) {
46
+ dependencies.push({external:dependency.path});
47
+ }
48
+ }
49
+ graph.set(key,dependencies);
50
+ }
51
+ },
52
+ validate(route) {
53
+ const roots = new Set([route.file, ...Object.values(route.segments || {}).flatMap(segment => Object.values(segment).filter(value => typeof value === 'string' && /\.[cm]?[jt]sx?$/.test(value))), ...(route.metadataFiles || []).map(item => item.file)].filter(Boolean));
54
+ const pending=[...roots].map(file), seen=new Set();
55
+ while (pending.length) {
56
+ const current=pending.pop();
57
+ if (seen.has(current)) continue;
58
+ seen.add(current);
59
+ if (issues.has(current)) throw new Error(`htmlOnly route ${route.pattern} reaches ${issues.get(current)} in ${path.relative(root,current)}.`);
60
+ for (const dependency of graph.get(current) || []) {
61
+ if (dependency.external) throw new Error(`htmlOnly route ${route.pattern} reaches unverified external dependency ${dependency.external} from ${path.relative(root,current)}. Bundle a static server dependency or remove it from this HTML-only route.`);
62
+ pending.push(dependency.path);
63
+ }
64
+ }
65
+ },
66
+ };
67
+ }
@@ -3,13 +3,17 @@ import { readFile, writeFile, mkdir, realpath } from 'node:fs/promises';
3
3
  import { createHash } from 'node:crypto';
4
4
  import path from 'node:path';
5
5
  import {clearDirectoryCache} from './directory-cache.mjs';
6
+ import {closeWebpackCompiler,webpackCompilerStats} from './webpack-graph.mjs';
6
7
  const contexts = new Map();
7
8
  const LIMIT = 8;
8
9
  let created = 0, reused = 0;
9
- export const incrementalCompilerStats = () => ({contexts:contexts.size, created, reused});
10
- export async function closeIncrementalCompiler() {
10
+ export const incrementalCompilerStats = () => ({contexts:contexts.size, created, reused,webpack:webpackCompilerStats()});
11
+ export async function closeIncrementalCompiler(root) {
11
12
  clearDirectoryCache();
12
- const active=[...contexts.values()];contexts.clear();await Promise.all(active.map(slot=>slot.context.dispose()));
13
+ const active=[];for(const [key,slot] of contexts)if(root===undefined||slot.root===root){contexts.delete(key);active.push(slot);}
14
+ const results=await Promise.allSettled([...active.map(slot=>slot.context.dispose()),closeWebpackCompiler(root)]);
15
+ const errors=results.filter(result=>result.status==='rejected').map(result=>result.reason);
16
+ if(errors.length)throw new AggregateError(errors,'Unable to dispose incremental compiler sessions');
13
17
  }
14
18
  /** Retain esbuild's parsed module graphs while rebinding per-build framework plugins. */
15
19
  export async function incrementalBuild(options, {root,stage,group='project',run=callback=>callback()}) {
@@ -40,7 +44,7 @@ export async function incrementalBuild(options, {root,stage,group='project',run=
40
44
  if(slot){contexts.delete(key);reused++;}
41
45
  else {
42
46
  while([...contexts.values()].filter(slot=>slot.group===group).length>=(group==='auxiliary'?2:LIMIT)){const [oldest,previous]=[...contexts].find(([,slot])=>slot.group===group);contexts.delete(oldest);await previous.context.dispose();}
43
- created++;slot={group};
47
+ created++;slot={group,root};
44
48
  slot.current={hooks,run,toVirtual,fromVirtual,virtualResult,stage};
45
49
  slot.context=await context({...normalized,plugins:[{name:'prnext-incremental-delegates',setup(builder){
46
50
  slot.builder=builder;