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.
- package/README.md +51 -30
- package/dist/build/css-url-assets.d.ts +3 -1
- package/dist/build/css-url-assets.js +18 -1
- package/dist/build/inject-pregenerated-paths.d.ts +7 -0
- package/dist/build/inject-pregenerated-paths.js +21 -0
- package/dist/build/layout-classification.js +1 -1
- package/dist/build/nitro-route-rules.js +1 -1
- package/dist/build/pages-client-assets-module.d.ts +11 -0
- package/dist/build/pages-client-assets-module.js +27 -0
- package/dist/build/prerender-server-entry.d.ts +1 -0
- package/dist/build/prerender-server-entry.js +49 -0
- package/dist/build/prerender-server-pool.d.ts +44 -0
- package/dist/build/prerender-server-pool.js +194 -0
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +98 -27
- package/dist/build/report.d.ts +13 -2
- package/dist/build/report.js +99 -2
- package/dist/build/run-prerender.d.ts +7 -0
- package/dist/build/run-prerender.js +12 -2
- package/dist/build/standalone.js +2 -0
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.d.ts +4 -6
- package/dist/check.js +48 -11
- package/dist/cli-args.d.ts +1 -2
- package/dist/cli-args.js +1 -1
- package/dist/cli-dev-config.d.ts +12 -0
- package/dist/cli-dev-config.js +23 -0
- package/dist/cli.js +112 -96
- package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay-store.js +1 -1
- package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay.js +2 -2
- package/dist/client/navigation-runtime.d.ts +2 -0
- package/dist/client/navigation-runtime.js +3 -1
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/config/config-matchers.js +45 -19
- package/dist/config/next-config.d.ts +41 -9
- package/dist/config/next-config.js +60 -3
- package/dist/config/prerender.d.ts +34 -0
- package/dist/config/prerender.js +55 -0
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +5 -3
- package/dist/entries/app-rsc-entry.js +44 -10
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/pages-client-entry.d.ts +3 -0
- package/dist/entries/pages-client-entry.js +28 -3
- package/dist/entries/pages-server-entry.js +23 -6
- package/dist/image/image-adapters-virtual.d.ts +59 -0
- package/dist/image/image-adapters-virtual.js +59 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +482 -120
- package/dist/init-cloudflare.d.ts +43 -0
- package/dist/init-cloudflare.js +839 -0
- package/dist/init-platform.d.ts +40 -0
- package/dist/init-platform.js +195 -0
- package/dist/init.d.ts +16 -38
- package/dist/init.js +209 -99
- package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
- package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
- package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +9 -2
- package/dist/packages/cloudflare/src/deploy-config.js +150 -0
- package/dist/packages/cloudflare/src/deploy-help.js +55 -0
- package/dist/packages/cloudflare/src/deploy.js +272 -0
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +44 -20
- package/dist/plugins/ast-scope.d.ts +16 -0
- package/dist/plugins/ast-scope.js +62 -0
- package/dist/plugins/ast-utils.d.ts +27 -1
- package/dist/plugins/ast-utils.js +32 -1
- package/dist/plugins/css-module-imports.d.ts +14 -0
- package/dist/plugins/css-module-imports.js +59 -0
- package/dist/plugins/extensionless-dynamic-import.js +2 -2
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
- package/dist/plugins/ignore-dynamic-requests.js +530 -0
- package/dist/plugins/og-assets.js +2 -1
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
- package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
- package/dist/plugins/sass.d.ts +17 -1
- package/dist/plugins/sass.js +74 -1
- package/dist/plugins/styled-jsx.d.ts +16 -0
- package/dist/plugins/styled-jsx.js +149 -0
- package/dist/plugins/typeof-window.d.ts +1 -1
- package/dist/plugins/typeof-window.js +28 -56
- package/dist/routing/app-route-graph.d.ts +23 -2
- package/dist/routing/app-route-graph.js +138 -38
- package/dist/routing/file-matcher.d.ts +11 -1
- package/dist/routing/file-matcher.js +18 -3
- package/dist/routing/pages-router.js +2 -2
- package/dist/server/api-handler.js +9 -14
- package/dist/server/app-browser-action-result.d.ts +4 -1
- package/dist/server/app-browser-action-result.js +8 -1
- package/dist/server/app-browser-entry.js +198 -57
- package/dist/server/app-browser-interception-context.d.ts +1 -0
- package/dist/server/app-browser-interception-context.js +4 -2
- package/dist/server/app-browser-navigation-controller.d.ts +2 -0
- package/dist/server/app-browser-navigation-controller.js +60 -31
- package/dist/server/app-browser-server-action-client.d.ts +1 -1
- package/dist/server/app-browser-server-action-client.js +8 -7
- package/dist/server/app-browser-state.d.ts +12 -1
- package/dist/server/app-browser-state.js +25 -8
- package/dist/server/app-browser-visible-commit.d.ts +6 -1
- package/dist/server/app-browser-visible-commit.js +42 -24
- package/dist/server/app-elements-wire.d.ts +9 -2
- package/dist/server/app-elements-wire.js +8 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +4 -3
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +10 -6
- package/dist/server/app-hydration-cache-publication.d.ts +11 -0
- package/dist/server/app-hydration-cache-publication.js +43 -0
- package/dist/server/app-middleware.d.ts +4 -3
- package/dist/server/app-middleware.js +8 -3
- package/dist/server/app-optimistic-routing.js +11 -4
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +5 -4
- package/dist/server/app-page-cache-render.d.ts +1 -0
- package/dist/server/app-page-cache-render.js +8 -4
- package/dist/server/app-page-cache.d.ts +1 -0
- package/dist/server/app-page-cache.js +27 -7
- package/dist/server/app-page-dispatch.d.ts +1 -0
- package/dist/server/app-page-dispatch.js +26 -8
- package/dist/server/app-page-element-builder.d.ts +2 -1
- package/dist/server/app-page-element-builder.js +59 -4
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-probe.js +5 -0
- package/dist/server/app-page-render-identity.d.ts +2 -0
- package/dist/server/app-page-render-identity.js +3 -2
- package/dist/server/app-page-render.d.ts +2 -0
- package/dist/server/app-page-render.js +85 -16
- package/dist/server/app-page-request.d.ts +14 -0
- package/dist/server/app-page-request.js +69 -4
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-route-wiring.d.ts +8 -1
- package/dist/server/app-page-route-wiring.js +32 -18
- package/dist/server/app-page-segment-state.d.ts +2 -1
- package/dist/server/app-page-segment-state.js +3 -1
- package/dist/server/app-page-stream.d.ts +7 -1
- package/dist/server/app-page-stream.js +3 -1
- package/dist/server/app-route-handler-execution.js +5 -1
- package/dist/server/app-route-handler-response.js +1 -0
- package/dist/server/app-route-handler-runtime.js +1 -1
- package/dist/server/app-route-tree-prefetch.d.ts +43 -0
- package/dist/server/app-route-tree-prefetch.js +187 -0
- package/dist/server/app-router-entry.js +9 -2
- package/dist/server/app-rsc-cache-busting.d.ts +2 -1
- package/dist/server/app-rsc-cache-busting.js +9 -4
- package/dist/server/app-rsc-handler.d.ts +7 -0
- package/dist/server/app-rsc-handler.js +148 -39
- package/dist/server/app-rsc-render-mode.d.ts +3 -2
- package/dist/server/app-rsc-render-mode.js +4 -1
- package/dist/server/app-rsc-request-normalization.d.ts +6 -6
- package/dist/server/app-rsc-request-normalization.js +10 -8
- package/dist/server/app-segment-config.d.ts +11 -0
- package/dist/server/app-segment-config.js +62 -6
- package/dist/server/app-server-action-execution.d.ts +1 -0
- package/dist/server/app-server-action-execution.js +61 -30
- package/dist/server/app-ssr-entry.d.ts +3 -0
- package/dist/server/app-ssr-entry.js +13 -5
- package/dist/server/app-ssr-stream.d.ts +7 -3
- package/dist/server/app-ssr-stream.js +10 -6
- package/dist/server/app-visited-response-cache.d.ts +6 -0
- package/dist/server/app-visited-response-cache.js +3 -1
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/client-trace-metadata.js +26 -0
- package/dist/server/default-global-not-found-module.d.ts +14 -0
- package/dist/server/default-global-not-found-module.js +14 -0
- package/dist/server/dev-route-files.js +4 -0
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +94 -35
- package/dist/server/dev-stack-sourcemap.d.ts +1 -1
- package/dist/server/dev-stack-sourcemap.js +1 -1
- package/dist/server/fetch-handler.d.ts +2 -0
- package/dist/server/fetch-handler.js +18 -0
- package/dist/server/headers.d.ts +7 -15
- package/dist/server/headers.js +11 -18
- package/dist/server/image-optimization.d.ts +51 -1
- package/dist/server/image-optimization.js +52 -2
- package/dist/server/implicit-tags.js +1 -1
- package/dist/server/isr-cache.d.ts +3 -2
- package/dist/server/isr-cache.js +9 -5
- package/dist/server/metadata-routes.d.ts +3 -1
- package/dist/server/metadata-routes.js +12 -2
- package/dist/server/middleware-runtime.js +6 -1
- package/dist/server/navigation-planner.d.ts +1 -0
- package/dist/server/navigation-planner.js +14 -3
- package/dist/server/pages-api-route.js +2 -2
- package/dist/server/pages-asset-tags.d.ts +4 -6
- package/dist/server/pages-asset-tags.js +12 -12
- package/dist/server/pages-client-assets.d.ts +12 -0
- package/dist/server/pages-client-assets.js +10 -0
- package/dist/server/pages-data-route.d.ts +4 -2
- package/dist/server/pages-data-route.js +18 -4
- package/dist/server/pages-dev-module-url.d.ts +2 -1
- package/dist/server/pages-dev-module-url.js +6 -3
- package/dist/server/pages-node-compat.d.ts +19 -1
- package/dist/server/pages-node-compat.js +81 -4
- package/dist/server/pages-page-data.d.ts +33 -1
- package/dist/server/pages-page-data.js +62 -33
- package/dist/server/pages-page-handler.d.ts +2 -1
- package/dist/server/pages-page-handler.js +38 -19
- package/dist/server/pages-page-response.d.ts +8 -0
- package/dist/server/pages-page-response.js +7 -3
- package/dist/server/pages-request-pipeline.d.ts +9 -5
- package/dist/server/pages-request-pipeline.js +55 -7
- package/dist/server/pages-router-entry.d.ts +30 -0
- package/dist/server/pages-router-entry.js +108 -0
- package/dist/server/prerender-manifest.d.ts +3 -1
- package/dist/server/prerender-route-params.js +1 -1
- package/dist/server/prod-server.d.ts +5 -3
- package/dist/server/prod-server.js +68 -39
- package/dist/server/request-pipeline.js +2 -1
- package/dist/server/seed-cache.js +4 -4
- package/dist/shims/app-router-scroll-state.d.ts +1 -0
- package/dist/shims/app-router-scroll-state.js +1 -0
- package/dist/shims/app-router-scroll.js +2 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.js +22 -15
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/dynamic-preload-chunks.js +2 -1
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/form.d.ts +4 -2
- package/dist/shims/form.js +37 -43
- package/dist/shims/headers.d.ts +7 -1
- package/dist/shims/headers.js +17 -3
- package/dist/shims/image.js +75 -14
- package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
- package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
- package/dist/shims/internal/app-route-detection.d.ts +2 -17
- package/dist/shims/internal/app-route-detection.js +4 -17
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +3 -15
- package/dist/shims/internal/hybrid-client-route-owner.js +40 -58
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +10 -5
- package/dist/shims/internal/pages-data-fetch-dedup.js +84 -14
- package/dist/shims/internal/pages-data-target.d.ts +18 -12
- package/dist/shims/internal/pages-data-target.js +97 -13
- package/dist/shims/internal/pages-router-components.d.ts +7 -0
- package/dist/shims/internal/pages-router-components.js +13 -0
- package/dist/shims/layout-segment-context.d.ts +4 -1
- package/dist/shims/layout-segment-context.js +18 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +224 -58
- package/dist/shims/navigation.d.ts +19 -3
- package/dist/shims/navigation.js +245 -47
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/root-params.d.ts +15 -1
- package/dist/shims/root-params.js +21 -1
- package/dist/shims/router.d.ts +5 -7
- package/dist/shims/router.js +387 -102
- package/dist/shims/server.js +3 -2
- package/dist/shims/slot.js +3 -6
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/client-runtime-metadata.d.ts +2 -18
- package/dist/utils/client-runtime-metadata.js +31 -22
- package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
- package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
- package/dist/utils/domain-locale.d.ts +6 -3
- package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
- package/dist/{server → utils}/middleware-request-headers.js +3 -3
- package/dist/utils/project.d.ts +48 -2
- package/dist/utils/project.js +225 -4
- package/dist/utils/protocol-headers.d.ts +23 -0
- package/dist/utils/protocol-headers.js +23 -0
- package/dist/utils/react-version.d.ts +4 -0
- package/dist/utils/react-version.js +44 -0
- package/dist/utils/vite-version.d.ts +2 -0
- package/dist/utils/vite-version.js +11 -1
- package/package.json +47 -2
- package/dist/deploy.d.ts +0 -197
- package/dist/deploy.js +0 -1040
- package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
- /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/
|
|
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
|
-
*
|
|
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,
|
|
45
|
+
export { TPROptions, TPRResult, runTPR };
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { VINEXT_REVALIDATE_HEADER } from "
|
|
2
|
-
import { isrCacheKey } from "
|
|
3
|
-
import { buildAppPageCacheTags } from "
|
|
4
|
-
import { ENTRY_PREFIX } from "./
|
|
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/
|
|
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
|
|
364
|
-
*
|
|
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 =
|
|
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(
|
|
373
|
-
`.then(m => m.startProdServer({ port: ${port}, host: "127.0.0.1", outDir:
|
|
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
|
-
|
|
428
|
-
const revalidateSeconds =
|
|
429
|
-
|
|
430
|
-
const
|
|
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
|
-
*
|
|
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 {
|
|
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 };
|