vinext 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +51 -30
  2. package/dist/build/css-url-assets.d.ts +3 -1
  3. package/dist/build/css-url-assets.js +18 -1
  4. package/dist/build/inject-pregenerated-paths.d.ts +7 -0
  5. package/dist/build/inject-pregenerated-paths.js +21 -0
  6. package/dist/build/layout-classification.js +1 -1
  7. package/dist/build/nitro-route-rules.js +1 -1
  8. package/dist/build/pages-client-assets-module.d.ts +11 -0
  9. package/dist/build/pages-client-assets-module.js +27 -0
  10. package/dist/build/prerender-server-entry.d.ts +1 -0
  11. package/dist/build/prerender-server-entry.js +49 -0
  12. package/dist/build/prerender-server-pool.d.ts +44 -0
  13. package/dist/build/prerender-server-pool.js +194 -0
  14. package/dist/build/prerender.d.ts +2 -1
  15. package/dist/build/prerender.js +98 -27
  16. package/dist/build/report.d.ts +13 -2
  17. package/dist/build/report.js +99 -2
  18. package/dist/build/run-prerender.d.ts +7 -0
  19. package/dist/build/run-prerender.js +12 -2
  20. package/dist/build/standalone.js +2 -0
  21. package/dist/cache/cache-adapters-virtual.js +3 -3
  22. package/dist/check.d.ts +4 -6
  23. package/dist/check.js +48 -11
  24. package/dist/cli-args.d.ts +1 -2
  25. package/dist/cli-args.js +1 -1
  26. package/dist/cli-dev-config.d.ts +12 -0
  27. package/dist/cli-dev-config.js +23 -0
  28. package/dist/cli.js +112 -96
  29. package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
  30. package/dist/{server → client}/dev-error-overlay-store.js +1 -1
  31. package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
  32. package/dist/{server → client}/dev-error-overlay.js +2 -2
  33. package/dist/client/navigation-runtime.d.ts +2 -0
  34. package/dist/client/navigation-runtime.js +3 -1
  35. package/dist/client/vinext-next-data.d.ts +4 -1
  36. package/dist/client/window-next.d.ts +2 -1
  37. package/dist/cloudflare/index.d.ts +2 -2
  38. package/dist/cloudflare/index.js +2 -2
  39. package/dist/config/config-matchers.js +45 -19
  40. package/dist/config/next-config.d.ts +41 -9
  41. package/dist/config/next-config.js +60 -3
  42. package/dist/config/prerender.d.ts +34 -0
  43. package/dist/config/prerender.js +55 -0
  44. package/dist/entries/app-browser-entry.js +5 -1
  45. package/dist/entries/app-rsc-entry.d.ts +5 -3
  46. package/dist/entries/app-rsc-entry.js +44 -10
  47. package/dist/entries/app-rsc-manifest.js +2 -0
  48. package/dist/entries/pages-client-entry.d.ts +3 -0
  49. package/dist/entries/pages-client-entry.js +28 -3
  50. package/dist/entries/pages-server-entry.js +23 -6
  51. package/dist/image/image-adapters-virtual.d.ts +59 -0
  52. package/dist/image/image-adapters-virtual.js +59 -0
  53. package/dist/index.d.ts +33 -0
  54. package/dist/index.js +482 -120
  55. package/dist/init-cloudflare.d.ts +43 -0
  56. package/dist/init-cloudflare.js +839 -0
  57. package/dist/init-platform.d.ts +40 -0
  58. package/dist/init-platform.js +195 -0
  59. package/dist/init.d.ts +16 -38
  60. package/dist/init.js +209 -99
  61. package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
  62. package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
  63. package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
  64. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
  65. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +9 -2
  66. package/dist/packages/cloudflare/src/deploy-config.js +150 -0
  67. package/dist/packages/cloudflare/src/deploy-help.js +55 -0
  68. package/dist/packages/cloudflare/src/deploy.js +272 -0
  69. package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
  70. package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +44 -20
  71. package/dist/plugins/ast-scope.d.ts +16 -0
  72. package/dist/plugins/ast-scope.js +62 -0
  73. package/dist/plugins/ast-utils.d.ts +27 -1
  74. package/dist/plugins/ast-utils.js +32 -1
  75. package/dist/plugins/css-module-imports.d.ts +14 -0
  76. package/dist/plugins/css-module-imports.js +59 -0
  77. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  78. package/dist/plugins/fonts.js +1 -1
  79. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  80. package/dist/plugins/ignore-dynamic-requests.js +530 -0
  81. package/dist/plugins/og-assets.js +2 -1
  82. package/dist/plugins/optimize-imports.js +1 -1
  83. package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
  84. package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
  85. package/dist/plugins/sass.d.ts +17 -1
  86. package/dist/plugins/sass.js +74 -1
  87. package/dist/plugins/styled-jsx.d.ts +16 -0
  88. package/dist/plugins/styled-jsx.js +149 -0
  89. package/dist/plugins/typeof-window.d.ts +1 -1
  90. package/dist/plugins/typeof-window.js +28 -56
  91. package/dist/routing/app-route-graph.d.ts +23 -2
  92. package/dist/routing/app-route-graph.js +138 -38
  93. package/dist/routing/file-matcher.d.ts +11 -1
  94. package/dist/routing/file-matcher.js +18 -3
  95. package/dist/routing/pages-router.js +2 -2
  96. package/dist/server/api-handler.js +9 -14
  97. package/dist/server/app-browser-action-result.d.ts +4 -1
  98. package/dist/server/app-browser-action-result.js +8 -1
  99. package/dist/server/app-browser-entry.js +198 -57
  100. package/dist/server/app-browser-interception-context.d.ts +1 -0
  101. package/dist/server/app-browser-interception-context.js +4 -2
  102. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  103. package/dist/server/app-browser-navigation-controller.js +60 -31
  104. package/dist/server/app-browser-server-action-client.d.ts +1 -1
  105. package/dist/server/app-browser-server-action-client.js +8 -7
  106. package/dist/server/app-browser-state.d.ts +12 -1
  107. package/dist/server/app-browser-state.js +25 -8
  108. package/dist/server/app-browser-visible-commit.d.ts +6 -1
  109. package/dist/server/app-browser-visible-commit.js +42 -24
  110. package/dist/server/app-elements-wire.d.ts +9 -2
  111. package/dist/server/app-elements-wire.js +8 -1
  112. package/dist/server/app-elements.d.ts +2 -2
  113. package/dist/server/app-elements.js +4 -3
  114. package/dist/server/app-fallback-renderer.d.ts +2 -1
  115. package/dist/server/app-fallback-renderer.js +10 -6
  116. package/dist/server/app-hydration-cache-publication.d.ts +11 -0
  117. package/dist/server/app-hydration-cache-publication.js +43 -0
  118. package/dist/server/app-middleware.d.ts +4 -3
  119. package/dist/server/app-middleware.js +8 -3
  120. package/dist/server/app-optimistic-routing.js +11 -4
  121. package/dist/server/app-page-boundary.js +2 -1
  122. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  123. package/dist/server/app-page-cache-finalizer.js +5 -4
  124. package/dist/server/app-page-cache-render.d.ts +1 -0
  125. package/dist/server/app-page-cache-render.js +8 -4
  126. package/dist/server/app-page-cache.d.ts +1 -0
  127. package/dist/server/app-page-cache.js +27 -7
  128. package/dist/server/app-page-dispatch.d.ts +1 -0
  129. package/dist/server/app-page-dispatch.js +26 -8
  130. package/dist/server/app-page-element-builder.d.ts +2 -1
  131. package/dist/server/app-page-element-builder.js +59 -4
  132. package/dist/server/app-page-execution.js +2 -1
  133. package/dist/server/app-page-probe.js +5 -0
  134. package/dist/server/app-page-render-identity.d.ts +2 -0
  135. package/dist/server/app-page-render-identity.js +3 -2
  136. package/dist/server/app-page-render.d.ts +2 -0
  137. package/dist/server/app-page-render.js +85 -16
  138. package/dist/server/app-page-request.d.ts +14 -0
  139. package/dist/server/app-page-request.js +69 -4
  140. package/dist/server/app-page-response.d.ts +7 -0
  141. package/dist/server/app-page-response.js +16 -4
  142. package/dist/server/app-page-route-wiring.d.ts +8 -1
  143. package/dist/server/app-page-route-wiring.js +32 -18
  144. package/dist/server/app-page-segment-state.d.ts +2 -1
  145. package/dist/server/app-page-segment-state.js +3 -1
  146. package/dist/server/app-page-stream.d.ts +7 -1
  147. package/dist/server/app-page-stream.js +3 -1
  148. package/dist/server/app-route-handler-execution.js +5 -1
  149. package/dist/server/app-route-handler-response.js +1 -0
  150. package/dist/server/app-route-handler-runtime.js +1 -1
  151. package/dist/server/app-route-tree-prefetch.d.ts +43 -0
  152. package/dist/server/app-route-tree-prefetch.js +187 -0
  153. package/dist/server/app-router-entry.js +9 -2
  154. package/dist/server/app-rsc-cache-busting.d.ts +2 -1
  155. package/dist/server/app-rsc-cache-busting.js +9 -4
  156. package/dist/server/app-rsc-handler.d.ts +7 -0
  157. package/dist/server/app-rsc-handler.js +148 -39
  158. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  159. package/dist/server/app-rsc-render-mode.js +4 -1
  160. package/dist/server/app-rsc-request-normalization.d.ts +6 -6
  161. package/dist/server/app-rsc-request-normalization.js +10 -8
  162. package/dist/server/app-segment-config.d.ts +11 -0
  163. package/dist/server/app-segment-config.js +62 -6
  164. package/dist/server/app-server-action-execution.d.ts +1 -0
  165. package/dist/server/app-server-action-execution.js +61 -30
  166. package/dist/server/app-ssr-entry.d.ts +3 -0
  167. package/dist/server/app-ssr-entry.js +13 -5
  168. package/dist/server/app-ssr-stream.d.ts +7 -3
  169. package/dist/server/app-ssr-stream.js +10 -6
  170. package/dist/server/app-visited-response-cache.d.ts +6 -0
  171. package/dist/server/app-visited-response-cache.js +3 -1
  172. package/dist/server/cache-control.d.ts +3 -1
  173. package/dist/server/cache-control.js +13 -1
  174. package/dist/server/client-trace-metadata.js +26 -0
  175. package/dist/server/default-global-not-found-module.d.ts +14 -0
  176. package/dist/server/default-global-not-found-module.js +14 -0
  177. package/dist/server/dev-route-files.js +4 -0
  178. package/dist/server/dev-server.d.ts +11 -1
  179. package/dist/server/dev-server.js +94 -35
  180. package/dist/server/dev-stack-sourcemap.d.ts +1 -1
  181. package/dist/server/dev-stack-sourcemap.js +1 -1
  182. package/dist/server/fetch-handler.d.ts +2 -0
  183. package/dist/server/fetch-handler.js +18 -0
  184. package/dist/server/headers.d.ts +7 -15
  185. package/dist/server/headers.js +11 -18
  186. package/dist/server/image-optimization.d.ts +51 -1
  187. package/dist/server/image-optimization.js +52 -2
  188. package/dist/server/implicit-tags.js +1 -1
  189. package/dist/server/isr-cache.d.ts +3 -2
  190. package/dist/server/isr-cache.js +9 -5
  191. package/dist/server/metadata-routes.d.ts +3 -1
  192. package/dist/server/metadata-routes.js +12 -2
  193. package/dist/server/middleware-runtime.js +6 -1
  194. package/dist/server/navigation-planner.d.ts +1 -0
  195. package/dist/server/navigation-planner.js +14 -3
  196. package/dist/server/pages-api-route.js +2 -2
  197. package/dist/server/pages-asset-tags.d.ts +4 -6
  198. package/dist/server/pages-asset-tags.js +12 -12
  199. package/dist/server/pages-client-assets.d.ts +12 -0
  200. package/dist/server/pages-client-assets.js +10 -0
  201. package/dist/server/pages-data-route.d.ts +4 -2
  202. package/dist/server/pages-data-route.js +18 -4
  203. package/dist/server/pages-dev-module-url.d.ts +2 -1
  204. package/dist/server/pages-dev-module-url.js +6 -3
  205. package/dist/server/pages-node-compat.d.ts +19 -1
  206. package/dist/server/pages-node-compat.js +81 -4
  207. package/dist/server/pages-page-data.d.ts +33 -1
  208. package/dist/server/pages-page-data.js +62 -33
  209. package/dist/server/pages-page-handler.d.ts +2 -1
  210. package/dist/server/pages-page-handler.js +38 -19
  211. package/dist/server/pages-page-response.d.ts +8 -0
  212. package/dist/server/pages-page-response.js +7 -3
  213. package/dist/server/pages-request-pipeline.d.ts +9 -5
  214. package/dist/server/pages-request-pipeline.js +55 -7
  215. package/dist/server/pages-router-entry.d.ts +30 -0
  216. package/dist/server/pages-router-entry.js +108 -0
  217. package/dist/server/prerender-manifest.d.ts +3 -1
  218. package/dist/server/prerender-route-params.js +1 -1
  219. package/dist/server/prod-server.d.ts +5 -3
  220. package/dist/server/prod-server.js +68 -39
  221. package/dist/server/request-pipeline.js +2 -1
  222. package/dist/server/seed-cache.js +4 -4
  223. package/dist/shims/app-router-scroll-state.d.ts +1 -0
  224. package/dist/shims/app-router-scroll-state.js +1 -0
  225. package/dist/shims/app-router-scroll.js +2 -1
  226. package/dist/shims/cache-handler.js +8 -1
  227. package/dist/shims/cache.js +22 -15
  228. package/dist/shims/cdn-cache.js +1 -1
  229. package/dist/shims/dynamic-preload-chunks.js +2 -1
  230. package/dist/shims/fetch-cache.d.ts +3 -1
  231. package/dist/shims/fetch-cache.js +77 -52
  232. package/dist/shims/form.d.ts +4 -2
  233. package/dist/shims/form.js +37 -43
  234. package/dist/shims/headers.d.ts +7 -1
  235. package/dist/shims/headers.js +17 -3
  236. package/dist/shims/image.js +75 -14
  237. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
  238. package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
  239. package/dist/shims/internal/app-route-detection.d.ts +2 -17
  240. package/dist/shims/internal/app-route-detection.js +4 -17
  241. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
  242. package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
  243. package/dist/shims/internal/hybrid-client-route-owner.d.ts +3 -15
  244. package/dist/shims/internal/hybrid-client-route-owner.js +40 -58
  245. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +10 -5
  246. package/dist/shims/internal/pages-data-fetch-dedup.js +84 -14
  247. package/dist/shims/internal/pages-data-target.d.ts +18 -12
  248. package/dist/shims/internal/pages-data-target.js +97 -13
  249. package/dist/shims/internal/pages-router-components.d.ts +7 -0
  250. package/dist/shims/internal/pages-router-components.js +13 -0
  251. package/dist/shims/layout-segment-context.d.ts +4 -1
  252. package/dist/shims/layout-segment-context.js +18 -4
  253. package/dist/shims/link.d.ts +1 -1
  254. package/dist/shims/link.js +224 -58
  255. package/dist/shims/navigation.d.ts +19 -3
  256. package/dist/shims/navigation.js +245 -47
  257. package/dist/shims/request-context.js +18 -0
  258. package/dist/shims/root-params.d.ts +15 -1
  259. package/dist/shims/root-params.js +21 -1
  260. package/dist/shims/router.d.ts +5 -7
  261. package/dist/shims/router.js +387 -102
  262. package/dist/shims/server.js +3 -2
  263. package/dist/shims/slot.js +3 -6
  264. package/dist/shims/unified-request-context.js +1 -0
  265. package/dist/typegen.js +1 -1
  266. package/dist/utils/client-runtime-metadata.d.ts +2 -18
  267. package/dist/utils/client-runtime-metadata.js +31 -22
  268. package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
  269. package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
  270. package/dist/utils/domain-locale.d.ts +6 -3
  271. package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
  272. package/dist/{server → utils}/middleware-request-headers.js +3 -3
  273. package/dist/utils/project.d.ts +48 -2
  274. package/dist/utils/project.js +225 -4
  275. package/dist/utils/protocol-headers.d.ts +23 -0
  276. package/dist/utils/protocol-headers.js +23 -0
  277. package/dist/utils/react-version.d.ts +4 -0
  278. package/dist/utils/react-version.js +44 -0
  279. package/dist/utils/vite-version.d.ts +2 -0
  280. package/dist/utils/vite-version.js +11 -1
  281. package/package.json +47 -2
  282. package/dist/deploy.d.ts +0 -197
  283. package/dist/deploy.js +0 -1040
  284. package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
  285. /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
@@ -0,0 +1,272 @@
1
+ import { detectProject, findInNodeModules, formatMissingCloudflarePluginError, getMissingDeps } from "../../../utils/project.js";
2
+ import { loadNextConfig, resolveNextConfig } from "../../../config/next-config.js";
3
+ import { formatVinextPrerenderLabel, loadVinextPrerenderConfigFromViteConfig, resolveVinextPrerenderDecision } from "../../../config/prerender.js";
4
+ import { runPrerender } from "../../../build/run-prerender.js";
5
+ import { loadDotenv } from "../../../config/dotenv.js";
6
+ import { runTPR } from "./tpr.js";
7
+ import { formatImageOptimizationHint, formatMissingCacheAdapterError, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler } from "./deploy-config.js";
8
+ import { createRequire } from "node:module";
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ import { parseArgs } from "node:util";
13
+ import { execFileSync } from "node:child_process";
14
+ //#region ../cloudflare/src/deploy.ts
15
+ /**
16
+ * vinext-cloudflare deploy — one-command Cloudflare Workers deployment.
17
+ *
18
+ * Takes any Next.js app and deploys it to Cloudflare Workers:
19
+ *
20
+ * 1. Validates the project was prepared by `vinext init --platform=cloudflare`
21
+ * 2. Runs the Vite build
22
+ * 3. Deploys to Cloudflare Workers via Wrangler
23
+ */
24
+ function parsePositiveIntegerArg(raw, flag) {
25
+ if (raw === "") throw new Error(`${flag} requires a value, but none was provided.`);
26
+ const parsed = Number(raw);
27
+ if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`${flag} expects a positive integer, but got "${raw}".`);
28
+ return parsed;
29
+ }
30
+ /** Deploy command flag definitions for util.parseArgs. */
31
+ const deployArgOptions = {
32
+ help: {
33
+ type: "boolean",
34
+ short: "h",
35
+ default: false
36
+ },
37
+ preview: {
38
+ type: "boolean",
39
+ default: false
40
+ },
41
+ env: { type: "string" },
42
+ name: { type: "string" },
43
+ "skip-build": {
44
+ type: "boolean",
45
+ default: false
46
+ },
47
+ "dry-run": {
48
+ type: "boolean",
49
+ default: false
50
+ },
51
+ "prerender-all": {
52
+ type: "boolean",
53
+ default: false
54
+ },
55
+ "prerender-concurrency": { type: "string" },
56
+ "experimental-tpr": {
57
+ type: "boolean",
58
+ default: false
59
+ },
60
+ "tpr-coverage": { type: "string" },
61
+ "tpr-limit": { type: "string" },
62
+ "tpr-window": { type: "string" }
63
+ };
64
+ function parseDeployArgs(args) {
65
+ const { values } = parseArgs({
66
+ args,
67
+ options: deployArgOptions,
68
+ strict: true
69
+ });
70
+ function parseIntArg(name, raw) {
71
+ if (!raw) return void 0;
72
+ const n = parseInt(raw, 10);
73
+ if (isNaN(n)) {
74
+ console.error(` --${name} must be a number (got: ${raw})`);
75
+ process.exit(1);
76
+ }
77
+ return n;
78
+ }
79
+ return {
80
+ help: values.help,
81
+ preview: values.preview,
82
+ env: values.env?.trim() || void 0,
83
+ name: values.name?.trim() || void 0,
84
+ skipBuild: values["skip-build"],
85
+ dryRun: values["dry-run"],
86
+ prerenderAll: values["prerender-all"],
87
+ prerenderConcurrency: values["prerender-concurrency"] === void 0 ? void 0 : parsePositiveIntegerArg(values["prerender-concurrency"], "--prerender-concurrency"),
88
+ experimentalTPR: values["experimental-tpr"],
89
+ tprCoverage: parseIntArg("tpr-coverage", values["tpr-coverage"]),
90
+ tprLimit: parseIntArg("tpr-limit", values["tpr-limit"]),
91
+ tprWindow: parseIntArg("tpr-window", values["tpr-window"])
92
+ };
93
+ }
94
+ /**
95
+ * Run a function with `process.env.CLOUDFLARE_ENV` set to the given value,
96
+ * restoring the previous state (whether set or absent) after the function
97
+ * resolves or throws.
98
+ *
99
+ * The `@cloudflare/vite-plugin` reads `CLOUDFLARE_ENV` from `process.env` to
100
+ * drive the multi-environment merge applied to the emitted `wrangler.json`.
101
+ * Without this propagation the `--env <name>` CLI flag is silently ignored at
102
+ * build time and the top-level config is emitted regardless. See issue #1210.
103
+ *
104
+ * Passing `undefined` is a no-op; the callback runs with `process.env` untouched.
105
+ */
106
+ async function withCloudflareEnv(env, fn) {
107
+ if (env === void 0 || env === "") return fn();
108
+ const hadPrev = "CLOUDFLARE_ENV" in process.env;
109
+ const prev = process.env.CLOUDFLARE_ENV;
110
+ process.env.CLOUDFLARE_ENV = env;
111
+ try {
112
+ return await fn();
113
+ } finally {
114
+ if (hadPrev) process.env.CLOUDFLARE_ENV = prev;
115
+ else delete process.env.CLOUDFLARE_ENV;
116
+ }
117
+ }
118
+ async function loadProjectViteApi(root) {
119
+ let vitePath;
120
+ try {
121
+ vitePath = createRequire(path.join(root, "package.json")).resolve("vite");
122
+ } catch {
123
+ vitePath = "vite";
124
+ }
125
+ return await (vitePath === "vite" ? import(vitePath) : import(pathToFileURL(vitePath).href));
126
+ }
127
+ async function runBuild(info, env) {
128
+ console.log("\n Building for Cloudflare Workers...\n");
129
+ const { createBuilder } = await loadProjectViteApi(info.root);
130
+ await withCloudflareEnv(env, async () => {
131
+ await (await createBuilder({ root: info.root })).buildApp();
132
+ });
133
+ }
134
+ function validateWranglerEnvName(env) {
135
+ if (env.includes("\0")) throw new Error("Wrangler environment names cannot contain null bytes.");
136
+ return env;
137
+ }
138
+ function buildWranglerDeployArgs(options) {
139
+ const args = ["deploy"];
140
+ const env = options.env || (options.preview ? "preview" : void 0);
141
+ if (env) args.push("--env", validateWranglerEnvName(env));
142
+ return {
143
+ args,
144
+ env
145
+ };
146
+ }
147
+ /**
148
+ * Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
149
+ *
150
+ * Invoking the JavaScript file through `process.execPath` avoids the `.cmd`
151
+ * shim and command shell that package managers create on Windows.
152
+ */
153
+ function resolveWranglerBin(root, resolvePackageJson = (projectRoot) => {
154
+ try {
155
+ return createRequire(path.join(projectRoot, "package.json")).resolve("wrangler/package.json");
156
+ } catch {
157
+ return findInNodeModules(projectRoot, "wrangler/package.json");
158
+ }
159
+ }) {
160
+ const packageJsonPath = resolvePackageJson(root);
161
+ if (packageJsonPath) {
162
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
163
+ const bin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.wrangler;
164
+ if (bin) return path.resolve(path.dirname(packageJsonPath), bin);
165
+ }
166
+ return path.join(root, "node_modules", "wrangler", "bin", "wrangler.js");
167
+ }
168
+ function buildNodeCliInvocation(scriptPath, args, nodeExecutable = process.execPath) {
169
+ return {
170
+ file: nodeExecutable,
171
+ args: [scriptPath, ...args]
172
+ };
173
+ }
174
+ function buildWranglerInvocation(root, options, nodeExecutable = process.execPath) {
175
+ const wranglerBin = resolveWranglerBin(root);
176
+ const { args, env } = buildWranglerDeployArgs(options);
177
+ return {
178
+ ...buildNodeCliInvocation(wranglerBin, args, nodeExecutable),
179
+ env
180
+ };
181
+ }
182
+ function runWranglerDeploy(root, options, execute = execFileSync) {
183
+ const execOpts = {
184
+ cwd: root,
185
+ stdio: "pipe",
186
+ encoding: "utf-8",
187
+ shell: false
188
+ };
189
+ const { file, args, env } = buildWranglerInvocation(root, options);
190
+ if (env) console.log(`\n Deploying to env: ${env}...`);
191
+ else console.log("\n Deploying to production...");
192
+ const output = execute(file, args, execOpts);
193
+ const urlMatch = output.match(/https:\/\/[^\s]+\.workers\.dev[^\s]*/);
194
+ const deployedUrl = urlMatch ? urlMatch[0] : null;
195
+ if (output.trim()) for (const line of output.trim().split("\n")) console.log(` ${line}`);
196
+ return deployedUrl ?? "(URL not detected in wrangler output)";
197
+ }
198
+ async function deploy(options) {
199
+ const deployEnv = validateWranglerEnvName(options.env || (options.preview ? "preview" : "production"));
200
+ const root = path.resolve(options.root);
201
+ loadDotenv({
202
+ root,
203
+ mode: "production"
204
+ });
205
+ console.log("\n vinext-cloudflare deploy\n");
206
+ const info = detectProject(root);
207
+ if (!info.isAppRouter && !info.isPagesRouter) {
208
+ console.error(" Error: No app/ or pages/ directory found.");
209
+ console.error(" vinext-cloudflare deploy requires a Next.js project with an app/ or pages/ directory");
210
+ console.error(" (also checks src/app/ and src/pages/).\n");
211
+ process.exit(1);
212
+ }
213
+ if (options.name) info.projectName = options.name;
214
+ console.log(` Project: ${info.projectName}`);
215
+ console.log(` Router: ${info.isAppRouter ? "App Router" : "Pages Router"}`);
216
+ console.log(` ISR: ${info.hasISR ? "detected" : "none"}`);
217
+ const missingScaffolding = [!info.hasViteConfig && "Vite config", !info.hasWranglerConfig && "Wrangler config"].filter((value) => Boolean(value));
218
+ if (missingScaffolding.length > 0) throw new Error(`Missing Cloudflare deployment setup: ${missingScaffolding.join(", ")}. Run \`vinext init --platform=cloudflare\` first.`);
219
+ const missingDeps = getMissingDeps(info);
220
+ if (missingDeps.length > 0) throw new Error(`Missing deployment dependencies: ${missingDeps.map((dependency) => dependency.name).join(", ")}. Run \`vinext init --platform=cloudflare\` first.`);
221
+ if (info.hasViteConfig && !viteConfigHasCloudflarePlugin(root)) throw new Error(formatMissingCloudflarePluginError({ isAppRouter: info.isAppRouter }));
222
+ if (info.hasISR && !viteConfigHasCacheAdapter(root) && !workerEntryHasCacheHandler(root)) throw new Error(formatMissingCacheAdapterError({}));
223
+ if (!viteConfigHasImageAdapter(root)) {
224
+ console.log();
225
+ console.log(formatImageOptimizationHint());
226
+ }
227
+ if (options.dryRun) {
228
+ console.log("\n Dry run complete. No build or deploy performed.\n");
229
+ return;
230
+ }
231
+ const buildEnv = deployEnv === "production" && !options.env ? void 0 : deployEnv;
232
+ if (!options.skipBuild) await runBuild(info, buildEnv);
233
+ else console.log("\n Skipping build (--skip-build)");
234
+ {
235
+ const nextConfig = await resolveNextConfig(await loadNextConfig(info.root), info.root);
236
+ const vinextPrerenderConfig = !options.prerenderAll && nextConfig.output !== "export" ? await withCloudflareEnv(buildEnv, async () => {
237
+ return loadVinextPrerenderConfigFromViteConfig(await loadProjectViteApi(info.root), info.root);
238
+ }) : null;
239
+ const prerenderDecision = resolveVinextPrerenderDecision({
240
+ prerenderAllFlag: options.prerenderAll,
241
+ vinextPrerenderConfig,
242
+ nextOutput: nextConfig.output
243
+ });
244
+ if (prerenderDecision) {
245
+ console.log(`\n ${formatVinextPrerenderLabel(prerenderDecision)}`);
246
+ if (nextConfig.enablePrerenderSourceMaps) {
247
+ process.setSourceMapsEnabled(true);
248
+ Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 50);
249
+ }
250
+ await runPrerender({
251
+ root: info.root,
252
+ concurrency: options.prerenderConcurrency
253
+ });
254
+ }
255
+ }
256
+ if (options.experimentalTPR) {
257
+ console.log();
258
+ const tprResult = await runTPR({
259
+ root,
260
+ coverage: Math.max(1, Math.min(100, options.tprCoverage ?? 90)),
261
+ limit: Math.max(1, options.tprLimit ?? 1e3),
262
+ window: Math.max(1, options.tprWindow ?? 24)
263
+ });
264
+ if (tprResult.skipped) console.log(` TPR: Skipped (${tprResult.skipped})`);
265
+ }
266
+ const url = runWranglerDeploy(root, { env: deployEnv === "production" && !options.env ? void 0 : deployEnv });
267
+ console.log("\n ─────────────────────────────────────────");
268
+ console.log(` Deployed to: ${url}`);
269
+ console.log(" ─────────────────────────────────────────\n");
270
+ }
271
+ //#endregion
272
+ export { deploy, parseDeployArgs };
@@ -1,4 +1,4 @@
1
- //#region src/cloudflare/tpr.d.ts
1
+ //#region ../cloudflare/src/tpr.d.ts
2
2
  /**
3
3
  * TPR: Traffic-aware Pre-Rendering
4
4
  *
@@ -33,50 +33,13 @@ type TPRResult = {
33
33
  durationMs: number; /** If TPR was skipped, the reason. */
34
34
  skipped?: string;
35
35
  };
36
- type PrerenderResult = {
37
- html: string;
38
- status: number;
39
- headers: Record<string, string>;
40
- };
41
- type WranglerConfig = {
42
- accountId?: string;
43
- kvNamespaceId?: string;
44
- customDomain?: string;
45
- };
46
- /**
47
- * Parse wrangler config (JSONC or TOML) to extract the fields TPR needs:
48
- * account_id, VINEXT_KV_CACHE KV namespace ID, and custom domain.
49
- */
50
- declare function parseWranglerConfig(root: string): WranglerConfig | null;
51
- /**
52
- * Generate zone lookup candidates from shortest (2-part) to longest.
53
- * Tries the most common case first (e.g., "example.com") and progressively
54
- * adds labels for multi-part TLDs (e.g., "co.uk" → "example.co.uk").
55
- *
56
- * "shop.example.com" → ["example.com", "shop.example.com"]
57
- * "shop.example.co.uk" → ["co.uk", "example.co.uk", "shop.example.co.uk"]
58
- * "example.com" → ["example.com"]
59
- */
60
- declare function domainCandidates(domain: string): string[];
61
- /**
62
- * Build KV bulk API pairs from pre-rendered entries.
63
- *
64
- * Key format matches the runtime KVCacheHandler exactly:
65
- * ENTRY_PREFIX + isrCacheKey("app", pathname, buildId) + ":html"
66
- * → "cache:app:<buildId>:<pathname>:html"
67
- */
68
- declare function buildTprKVPairs(entries: Map<string, PrerenderResult>, buildId: string | undefined, defaultRevalidateSeconds: number): Array<{
69
- key: string;
70
- value: string;
71
- expiration_ttl: number;
72
- }>;
73
36
  /**
74
37
  * Run the TPR pipeline: query traffic, select routes, pre-render, upload.
75
38
  *
76
- * Designed to be called between the build step and wrangler deploy in
77
- * the `vinext deploy` pipeline. Gracefully skips (never errors) when
39
+ * Designed to be called between the build step and wrangler deploy in the
40
+ * `vinext-cloudflare deploy` pipeline. Gracefully skips (never errors) when
78
41
  * the prerequisites aren't met.
79
42
  */
80
43
  declare function runTPR(options: TPROptions): Promise<TPRResult>;
81
44
  //#endregion
82
- export { TPROptions, TPRResult, buildTprKVPairs, domainCandidates, parseWranglerConfig, runTPR };
45
+ export { TPROptions, TPRResult, runTPR };
@@ -1,11 +1,13 @@
1
- import { VINEXT_REVALIDATE_HEADER } from "../server/headers.js";
2
- import { isrCacheKey } from "../server/isr-cache.js";
3
- import { buildAppPageCacheTags } from "../server/app-page-cache.js";
4
- import { ENTRY_PREFIX } from "./src/cache/kv-data-adapter.runtime.js";
1
+ import { VINEXT_REVALIDATE_HEADER } from "../../../server/headers.js";
2
+ import { isrCacheKey } from "../../../server/isr-cache.js";
3
+ import { buildAppPageCacheTags } from "../../../server/app-page-cache.js";
4
+ import { ENTRY_PREFIX } from "./cache/kv-data-adapter.runtime.js";
5
+ import { createRequire } from "node:module";
5
6
  import fs from "node:fs";
6
7
  import path from "node:path";
8
+ import { pathToFileURL } from "node:url";
7
9
  import { spawn } from "node:child_process";
8
- //#region src/cloudflare/tpr.ts
10
+ //#region ../cloudflare/src/tpr.ts
9
11
  /**
10
12
  * TPR: Traffic-aware Pre-Rendering
11
13
  *
@@ -304,6 +306,25 @@ const PRERENDER_PORT = 19384;
304
306
  const SERVER_STARTUP_TIMEOUT = 3e4;
305
307
  /** Max concurrent fetch requests during pre-rendering. */
306
308
  const FETCH_CONCURRENCY = 10;
309
+ const NON_CACHEABLE_CACHE_CONTROL_RE = /\b(?:no-store|no-cache|private)\b/i;
310
+ function getTprHeader(headers, name) {
311
+ const normalizedName = name.toLowerCase();
312
+ for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === normalizedName) return value;
313
+ }
314
+ function hasNonCacheableCacheControl(headers) {
315
+ const cacheControl = getTprHeader(headers, "cache-control");
316
+ return cacheControl ? NON_CACHEABLE_CACHE_CONTROL_RE.test(cacheControl) : false;
317
+ }
318
+ function readTprRevalidateHeader(headers) {
319
+ const revalidateHeader = getTprHeader(headers, VINEXT_REVALIDATE_HEADER);
320
+ if (revalidateHeader === void 0) return void 0;
321
+ return Number(revalidateHeader);
322
+ }
323
+ function isTprCacheable(headers) {
324
+ if (hasNonCacheableCacheControl(headers)) return false;
325
+ const revalidate = readTprRevalidateHeader(headers);
326
+ return revalidate === void 0 || Number.isFinite(revalidate) && revalidate > 0;
327
+ }
307
328
  /**
308
329
  * Start a local production server, fetch each route to produce HTML,
309
330
  * and return the results. Pages that fail to render are skipped.
@@ -331,11 +352,12 @@ async function prerenderRoutes(routes, root, hostDomain) {
331
352
  redirect: "manual"
332
353
  });
333
354
  if (response.status < 400) {
334
- const html = await response.text();
335
355
  const headers = {};
336
356
  response.headers.forEach((value, key) => {
337
357
  if (key === "content-type" || key === "cache-control" || key === "x-vinext-revalidate" || key === "location") headers[key] = value;
338
358
  });
359
+ if (!isTprCacheable(headers)) return;
360
+ const html = await response.text();
339
361
  results.set(routePath, {
340
362
  html,
341
363
  status: response.status,
@@ -360,17 +382,15 @@ async function prerenderRoutes(routes, root, hostDomain) {
360
382
  }
361
383
  /**
362
384
  * Spawn a subprocess running the vinext production server.
363
- * Uses the same Node.js binary and resolves prod-server.js relative
364
- * to the current module (works whether vinext is installed or linked).
385
+ * Uses the same Node.js binary and resolves vinext from the project root so
386
+ * linked/package-manager-managed installs use the app's vinext peer.
365
387
  */
366
388
  function startLocalServer(root, port) {
367
- const prodServerPath = path.resolve(import.meta.dirname, "..", "server", "prod-server.js");
389
+ const prodServerPath = resolveVinextProdServerPath(root);
368
390
  const outDir = path.join(root, "dist");
369
- const escapedProdServer = prodServerPath.replace(/\\/g, "\\\\");
370
- const escapedOutDir = outDir.replace(/\\/g, "\\\\");
371
391
  const script = [
372
- `import("file://${escapedProdServer}")`,
373
- `.then(m => m.startProdServer({ port: ${port}, host: "127.0.0.1", outDir: "${escapedOutDir}" }))`,
392
+ `import(${JSON.stringify(pathToFileURL(prodServerPath).href)})`,
393
+ `.then(m => m.startProdServer({ port: ${port}, host: "127.0.0.1", outDir: ${JSON.stringify(outDir)} }))`,
374
394
  `.catch(e => { console.error("[vinext-tpr] Server failed to start:", e); process.exit(1); });`
375
395
  ].join("");
376
396
  const proc = spawn(process.execPath, [
@@ -391,6 +411,9 @@ function startLocalServer(root, port) {
391
411
  });
392
412
  return proc;
393
413
  }
414
+ function resolveVinextProdServerPath(root) {
415
+ return createRequire(path.join(root, "package.json")).resolve("vinext/server/prod-server");
416
+ }
394
417
  /** Poll the local server until it responds or the timeout is reached. */
395
418
  async function waitForServer(port, timeoutMs) {
396
419
  const start = Date.now();
@@ -424,10 +447,11 @@ function buildTprKVPairs(entries, buildId, defaultRevalidateSeconds) {
424
447
  const now = Date.now();
425
448
  const pairs = [];
426
449
  for (const [routePath, result] of entries) {
427
- const revalidateHeader = result.headers[VINEXT_REVALIDATE_HEADER];
428
- const revalidateSeconds = revalidateHeader && !isNaN(Number(revalidateHeader)) ? Number(revalidateHeader) : defaultRevalidateSeconds;
429
- const revalidateAt = revalidateSeconds > 0 ? now + revalidateSeconds * 1e3 : null;
430
- const kvTtl = revalidateSeconds > 0 ? MAX_KV_TTL_SECONDS : 24 * 3600;
450
+ if (!isTprCacheable(result.headers)) continue;
451
+ const revalidateSeconds = readTprRevalidateHeader(result.headers) ?? defaultRevalidateSeconds;
452
+ if (!Number.isFinite(revalidateSeconds) || revalidateSeconds <= 0) continue;
453
+ const revalidateAt = now + revalidateSeconds * 1e3;
454
+ const kvTtl = MAX_KV_TTL_SECONDS;
431
455
  const tags = buildAppPageCacheTags(routePath, []);
432
456
  const entry = {
433
457
  value: {
@@ -477,8 +501,8 @@ const DEFAULT_REVALIDATE_SECONDS = 3600;
477
501
  /**
478
502
  * Run the TPR pipeline: query traffic, select routes, pre-render, upload.
479
503
  *
480
- * Designed to be called between the build step and wrangler deploy in
481
- * the `vinext deploy` pipeline. Gracefully skips (never errors) when
504
+ * Designed to be called between the build step and wrangler deploy in the
505
+ * `vinext-cloudflare deploy` pipeline. Gracefully skips (never errors) when
482
506
  * the prerequisites aren't met.
483
507
  */
484
508
  async function runTPR(options) {
@@ -555,4 +579,4 @@ async function runTPR(options) {
555
579
  };
556
580
  }
557
581
  //#endregion
558
- export { buildTprKVPairs, domainCandidates, parseWranglerConfig, runTPR };
582
+ export { runTPR };
@@ -0,0 +1,16 @@
1
+ import { AstRecord } from "./ast-utils.js";
2
+
3
+ //#region src/plugins/ast-scope.d.ts
4
+ type AstScope = {
5
+ parent: AstScope | null;
6
+ bindings: Set<string>;
7
+ };
8
+ declare function createAstScope<T extends AstScope>(parent: T | null): AstScope;
9
+ declare function hasAstBinding(scope: AstScope, name: string): boolean;
10
+ declare function isFunctionNode(node: AstRecord): boolean;
11
+ declare function collectDirectScopeBindings(node: AstRecord, scope: AstScope, onVariableDeclarator?: (declaration: AstRecord, declarator: AstRecord) => void): void;
12
+ declare function collectLoopScopeBindings(node: AstRecord, scope: AstScope, onVariableDeclarator?: (declaration: AstRecord, declarator: AstRecord) => void): void;
13
+ declare function collectSwitchScopeBindings(node: AstRecord, scope: AstScope, onVariableDeclarator?: (declaration: AstRecord, declarator: AstRecord) => void): void;
14
+ declare function collectVarScopeBindings(node: AstRecord, scope: AstScope, root?: boolean): void;
15
+ //#endregion
16
+ export { AstScope, collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, createAstScope, hasAstBinding, isFunctionNode };
@@ -0,0 +1,62 @@
1
+ import { collectBindingNames, forEachAstChild, isAstRecord, nodeArray } from "./ast-utils.js";
2
+ //#region src/plugins/ast-scope.ts
3
+ function createAstScope(parent) {
4
+ return {
5
+ parent,
6
+ bindings: /* @__PURE__ */ new Set()
7
+ };
8
+ }
9
+ function hasAstBinding(scope, name) {
10
+ for (let current = scope; current; current = current.parent) if (current.bindings.has(name)) return true;
11
+ return false;
12
+ }
13
+ function isFunctionNode(node) {
14
+ return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
15
+ }
16
+ function collectDirectScopeBindings(node, scope, onVariableDeclarator) {
17
+ for (const statementValue of nodeArray(node.body)) {
18
+ const statement = isAstRecord(statementValue) ? statementValue : null;
19
+ if (!statement) continue;
20
+ const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? isAstRecord(statement.declaration) ? statement.declaration : null : statement;
21
+ if (!declaration) continue;
22
+ if (declaration.type === "ImportDeclaration") {
23
+ if (declaration.importKind === "type") continue;
24
+ for (const specifier of nodeArray(declaration.specifiers)) if (isAstRecord(specifier) && specifier.importKind !== "type") collectBindingNames(specifier.local, scope.bindings);
25
+ } else if (declaration.type === "TSImportEqualsDeclaration" && declaration.importKind !== "type") collectBindingNames(declaration.id, scope.bindings);
26
+ else if (declaration.type === "VariableDeclaration" && declaration.declare !== true) for (const declarator of nodeArray(declaration.declarations)) {
27
+ if (!isAstRecord(declarator)) continue;
28
+ collectBindingNames(declarator.id, scope.bindings);
29
+ onVariableDeclarator?.(declaration, declarator);
30
+ }
31
+ else if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.declare !== true) collectBindingNames(declaration.id, scope.bindings);
32
+ else if ((declaration.type === "TSEnumDeclaration" || declaration.type === "TSModuleDeclaration") && declaration.declare !== true) collectBindingNames(declaration.id, scope.bindings);
33
+ }
34
+ }
35
+ function collectLoopScopeBindings(node, scope, onVariableDeclarator) {
36
+ const declarationValue = node.type === "ForStatement" ? node.init : node.left;
37
+ if (!isAstRecord(declarationValue)) return;
38
+ if (declarationValue.type !== "VariableDeclaration" || declarationValue.declare === true) return;
39
+ for (const declarator of nodeArray(declarationValue.declarations)) {
40
+ if (!isAstRecord(declarator)) continue;
41
+ collectBindingNames(declarator.id, scope.bindings);
42
+ onVariableDeclarator?.(declarationValue, declarator);
43
+ }
44
+ }
45
+ function collectSwitchScopeBindings(node, scope, onVariableDeclarator) {
46
+ for (const caseValue of nodeArray(node.cases)) {
47
+ if (!isAstRecord(caseValue)) continue;
48
+ collectDirectScopeBindings({
49
+ type: "BlockStatement",
50
+ body: nodeArray(caseValue.consequent)
51
+ }, scope, onVariableDeclarator);
52
+ }
53
+ }
54
+ function collectVarScopeBindings(node, scope, root = true) {
55
+ if (!root && (isFunctionNode(node) || node.type === "StaticBlock" || node.type === "TSModuleBlock")) return;
56
+ if (node.type === "VariableDeclaration" && node.kind === "var" && node.declare !== true) {
57
+ for (const declarator of nodeArray(node.declarations)) if (isAstRecord(declarator)) collectBindingNames(declarator.id, scope.bindings);
58
+ }
59
+ forEachAstChild(node, (child) => collectVarScopeBindings(child, scope, false));
60
+ }
61
+ //#endregion
62
+ export { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, createAstScope, hasAstBinding, isFunctionNode };
@@ -9,6 +9,32 @@ type AstRange = AstRecord & {
9
9
  start: number;
10
10
  end: number;
11
11
  };
12
+ /**
13
+ * Cheap pre-parse gate for plugins that only transform *dynamic* `import(...)`.
14
+ *
15
+ * Static imports — `import x from "..."`, `import { ... } from "..."`,
16
+ * `import "..."` — never place a `(` (nor a comment leading to one) immediately
17
+ * after the `import` keyword. Plugins that act only on dynamic `import(...)` use
18
+ * this to skip `parseAst` for the overwhelming majority of modules in a large
19
+ * app: at ~5k routes, where almost every module is a static-import-only page,
20
+ * it removes most of the build's AST-parse/GC cost. This is a deliberate,
21
+ * measured performance filter — keep it a regex, never a parse.
22
+ *
23
+ * It intentionally errs toward over-matching: a false positive costs one
24
+ * redundant parse, whereas a false negative would silently skip a real dynamic
25
+ * import (a correctness bug). `\s*[(/]` therefore tolerates whitespace and
26
+ * block/line comments between the `import` keyword and its parenthesis.
27
+ *
28
+ * Usable directly as a Rolldown `transform.filter.code` regex, or via
29
+ * {@link mayContainDynamicImport} for an in-handler prescan.
30
+ */
31
+ declare const DYNAMIC_IMPORT_PRESCAN: RegExp;
32
+ /**
33
+ * Whether `code` might contain a dynamic `import(...)` call. See
34
+ * {@link DYNAMIC_IMPORT_PRESCAN} — a cheap, deliberately over-inclusive regex
35
+ * gate used to avoid parsing static-import-only modules.
36
+ */
37
+ declare function mayContainDynamicImport(code: string): boolean;
12
38
  declare function isAstRecord(value: unknown): value is AstRecord;
13
39
  declare function nodeArray(value: unknown): unknown[];
14
40
  declare function hasRange(node: AstRecord | null): node is AstRange;
@@ -17,4 +43,4 @@ declare function getAstName(value: unknown): string | null;
17
43
  declare function forEachAstChild(node: AstRecord, callback: (child: AstRecord) => void): void;
18
44
  declare function collectBindingNames(pattern: unknown, target: Set<string>): void;
19
45
  //#endregion
20
- export { AstRange, AstRecord, collectBindingNames, forEachAstChild, getAstName, hasRange, isAstRecord, isIdentifierNamed, nodeArray };
46
+ export { AstRange, AstRecord, DYNAMIC_IMPORT_PRESCAN, collectBindingNames, forEachAstChild, getAstName, hasRange, isAstRecord, isIdentifierNamed, mayContainDynamicImport, nodeArray };
@@ -1,4 +1,32 @@
1
1
  //#region src/plugins/ast-utils.ts
2
+ /**
3
+ * Cheap pre-parse gate for plugins that only transform *dynamic* `import(...)`.
4
+ *
5
+ * Static imports — `import x from "..."`, `import { ... } from "..."`,
6
+ * `import "..."` — never place a `(` (nor a comment leading to one) immediately
7
+ * after the `import` keyword. Plugins that act only on dynamic `import(...)` use
8
+ * this to skip `parseAst` for the overwhelming majority of modules in a large
9
+ * app: at ~5k routes, where almost every module is a static-import-only page,
10
+ * it removes most of the build's AST-parse/GC cost. This is a deliberate,
11
+ * measured performance filter — keep it a regex, never a parse.
12
+ *
13
+ * It intentionally errs toward over-matching: a false positive costs one
14
+ * redundant parse, whereas a false negative would silently skip a real dynamic
15
+ * import (a correctness bug). `\s*[(/]` therefore tolerates whitespace and
16
+ * block/line comments between the `import` keyword and its parenthesis.
17
+ *
18
+ * Usable directly as a Rolldown `transform.filter.code` regex, or via
19
+ * {@link mayContainDynamicImport} for an in-handler prescan.
20
+ */
21
+ const DYNAMIC_IMPORT_PRESCAN = /\bimport\s*[(/]/;
22
+ /**
23
+ * Whether `code` might contain a dynamic `import(...)` call. See
24
+ * {@link DYNAMIC_IMPORT_PRESCAN} — a cheap, deliberately over-inclusive regex
25
+ * gate used to avoid parsing static-import-only modules.
26
+ */
27
+ function mayContainDynamicImport(code) {
28
+ return DYNAMIC_IMPORT_PRESCAN.test(code);
29
+ }
2
30
  const SKIP_CHILD_KEYS = new Set([
3
31
  "type",
4
32
  "parent",
@@ -59,6 +87,9 @@ function collectBindingNames(pattern, target) {
59
87
  case "AssignmentPattern":
60
88
  collectBindingNames(node.left, target);
61
89
  return;
90
+ case "TSParameterProperty":
91
+ collectBindingNames(node.parameter, target);
92
+ return;
62
93
  case "ArrayPattern":
63
94
  for (const element of nodeArray(node.elements)) collectBindingNames(element, target);
64
95
  return;
@@ -75,4 +106,4 @@ function collectBindingNames(pattern, target) {
75
106
  }
76
107
  }
77
108
  //#endregion
78
- export { collectBindingNames, forEachAstChild, getAstName, hasRange, isAstRecord, isIdentifierNamed, nodeArray };
109
+ export { DYNAMIC_IMPORT_PRESCAN, collectBindingNames, forEachAstChild, getAstName, hasRange, isAstRecord, isIdentifierNamed, mayContainDynamicImport, nodeArray };
@@ -0,0 +1,14 @@
1
+ import { Plugin } from "vite";
2
+ import MagicString from "magic-string";
3
+
4
+ //#region src/plugins/css-module-imports.d.ts
5
+ type ScriptLanguage = "js" | "jsx" | "ts" | "tsx";
6
+ declare function rewriteCssModuleNamespaceImports(code: string, lang?: ScriptLanguage): {
7
+ code: string;
8
+ map: ReturnType<MagicString["generateMap"]>;
9
+ } | null;
10
+ declare function createCssModuleImportCompatibilityPlugin(options?: {
11
+ compiledMdx?: boolean;
12
+ }): Plugin;
13
+ //#endregion
14
+ export { createCssModuleImportCompatibilityPlugin, rewriteCssModuleNamespaceImports };