@qzsy/vinext 0.1.12 → 0.1.81
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 +19 -5
- package/dist/build/inject-pregenerated-paths.d.ts +4 -0
- package/dist/build/inject-pregenerated-paths.js +18 -0
- 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.d.ts +2 -1
- package/dist/build/prerender.js +11 -4
- package/dist/build/report.d.ts +2 -1
- package/dist/build/report.js +2 -1
- package/dist/build/run-prerender.d.ts +7 -0
- package/dist/build/run-prerender.js +9 -0
- package/dist/build/standalone.js +2 -0
- package/dist/check.d.ts +18 -0
- package/dist/check.js +77 -19
- package/dist/cli-dev-config.d.ts +12 -0
- package/dist/cli-dev-config.js +23 -0
- package/dist/cli.js +64 -28
- 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/cloudflare/deploy-config.d.ts +51 -0
- package/dist/cloudflare/deploy-config.js +153 -0
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/project.d.ts +41 -0
- package/dist/cloudflare/project.js +243 -0
- package/dist/cloudflare/tpr.js +1 -1
- package/dist/config/config-matchers.js +14 -10
- package/dist/config/next-config.d.ts +6 -3
- package/dist/config/next-config.js +47 -1
- package/dist/config/server-external-packages.d.ts +4 -0
- package/dist/config/server-external-packages.js +91 -0
- package/dist/deploy.d.ts +2 -122
- package/dist/deploy.js +20 -793
- package/dist/entries/app-rsc-entry.d.ts +2 -1
- package/dist/entries/app-rsc-entry.js +70 -12
- package/dist/entries/app-rsc-manifest.js +8 -0
- package/dist/entries/pages-client-entry.d.ts +1 -0
- package/dist/entries/pages-client-entry.js +2 -1
- package/dist/entries/pages-server-entry.js +6 -2
- package/dist/image/image-adapters-virtual.d.ts +59 -0
- package/dist/image/image-adapters-virtual.js +50 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +160 -160
- package/dist/init-cloudflare.d.ts +43 -0
- package/dist/init-cloudflare.js +1000 -0
- package/dist/init-platform.d.ts +38 -0
- package/dist/init-platform.js +150 -0
- package/dist/init.d.ts +14 -37
- package/dist/init.js +205 -95
- 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 +1 -1
- package/dist/plugins/ast-scope.d.ts +16 -0
- package/dist/plugins/ast-scope.js +62 -0
- package/dist/plugins/ast-utils.js +3 -0
- package/dist/plugins/css-module-imports.d.ts +14 -0
- package/dist/plugins/css-module-imports.js +59 -0
- package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
- package/dist/plugins/ignore-dynamic-requests.js +530 -0
- package/dist/plugins/middleware-server-only.d.ts +8 -6
- package/dist/plugins/middleware-server-only.js +8 -7
- package/dist/plugins/optimize-imports.js +1 -1
- 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 +13 -2
- package/dist/routing/app-route-graph.js +116 -32
- package/dist/routing/app-router.d.ts +5 -0
- package/dist/routing/app-router.js +5 -0
- package/dist/routing/file-matcher.d.ts +8 -0
- package/dist/routing/file-matcher.js +10 -1
- package/dist/routing/pages-router.js +2 -2
- package/dist/server/app-browser-action-result.d.ts +2 -1
- package/dist/server/app-browser-action-result.js +5 -1
- package/dist/server/app-browser-entry.js +17 -12
- package/dist/server/app-browser-history-controller.d.ts +2 -1
- package/dist/server/app-browser-history-controller.js +6 -2
- 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.js +1 -0
- package/dist/server/app-browser-server-action-client.js +2 -3
- package/dist/server/app-browser-state.d.ts +1 -0
- package/dist/server/app-browser-state.js +3 -2
- package/dist/server/app-fallback-renderer.d.ts +3 -2
- package/dist/server/app-fallback-renderer.js +12 -7
- package/dist/server/app-middleware.d.ts +2 -3
- package/dist/server/app-middleware.js +3 -2
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +12 -3
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +10 -3
- 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 +4 -1
- package/dist/server/app-page-dispatch.d.ts +11 -3
- package/dist/server/app-page-dispatch.js +55 -15
- package/dist/server/app-page-element-builder.d.ts +5 -1
- package/dist/server/app-page-element-builder.js +57 -20
- package/dist/server/app-page-head.d.ts +12 -0
- package/dist/server/app-page-head.js +42 -19
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +8 -1
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +6 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render.d.ts +4 -1
- package/dist/server/app-page-render.js +8 -3
- package/dist/server/app-page-request.d.ts +22 -1
- package/dist/server/app-page-request.js +89 -13
- package/dist/server/app-page-route-wiring.d.ts +6 -1
- package/dist/server/app-page-route-wiring.js +31 -15
- package/dist/server/app-page-search-params-observation.d.ts +4 -2
- package/dist/server/app-page-search-params-observation.js +11 -7
- package/dist/server/app-page-segment-state.js +2 -0
- package/dist/server/app-route-handler-dispatch.js +1 -0
- package/dist/server/app-route-handler-execution.js +7 -2
- 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-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-router-entry.d.ts +12 -0
- package/dist/server/app-router-entry.js +22 -8
- package/dist/server/app-router-image-optimization.d.ts +37 -0
- package/dist/server/app-router-image-optimization.js +40 -0
- package/dist/server/app-rsc-errors.js +7 -1
- package/dist/server/app-rsc-handler.js +27 -14
- package/dist/server/app-rsc-route-matching.d.ts +7 -0
- package/dist/server/app-rsc-route-matching.js +36 -3
- package/dist/server/app-segment-config.d.ts +12 -0
- package/dist/server/app-segment-config.js +91 -5
- package/dist/server/app-server-action-execution.d.ts +5 -0
- package/dist/server/app-server-action-execution.js +106 -33
- package/dist/server/app-ssr-entry.js +12 -1
- package/dist/server/app-static-generation.d.ts +1 -0
- package/dist/server/app-static-generation.js +1 -0
- 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-response-headers.d.ts +19 -0
- package/dist/server/dev-response-headers.js +78 -0
- package/dist/server/dev-server.js +8 -15
- package/dist/server/dev-stack-sourcemap.d.ts +1 -1
- package/dist/server/dev-stack-sourcemap.js +1 -1
- package/dist/server/headers.d.ts +7 -15
- package/dist/server/headers.js +6 -15
- package/dist/server/image-optimization.d.ts +51 -1
- package/dist/server/image-optimization.js +52 -2
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/isr-cache.js +2 -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-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-page-data.d.ts +23 -1
- package/dist/server/pages-page-data.js +43 -24
- package/dist/server/pages-page-handler.d.ts +2 -1
- package/dist/server/pages-page-handler.js +10 -4
- package/dist/server/pages-request-pipeline.d.ts +2 -0
- package/dist/server/pages-request-pipeline.js +25 -1
- 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 +1 -1
- package/dist/server/prod-server.js +47 -25
- package/dist/server/request-log.d.ts +5 -14
- package/dist/server/request-log.js +7 -1
- package/dist/server/request-pipeline.js +1 -0
- package/dist/server/seed-cache.js +4 -4
- package/dist/server/server-action-logger.d.ts +39 -0
- package/dist/server/server-action-logger.js +104 -0
- package/dist/server/worker-utils.d.ts +2 -1
- package/dist/server/worker-utils.js +7 -1
- 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.js +19 -15
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/dynamic-preload-chunks.js +2 -1
- package/dist/shims/error-boundary.d.ts +19 -1
- package/dist/shims/error-boundary.js +11 -1
- package/dist/shims/form.d.ts +3 -1
- package/dist/shims/form.js +37 -43
- package/dist/shims/headers.d.ts +9 -1
- package/dist/shims/headers.js +31 -6
- package/dist/shims/image-optimization-url.d.ts +4 -0
- package/dist/shims/image-optimization-url.js +33 -1
- package/dist/shims/image.js +46 -13
- 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 +2 -5
- package/dist/shims/internal/hybrid-client-route-owner.js +9 -60
- package/dist/shims/internal/pages-router-components.d.ts +7 -0
- package/dist/shims/internal/pages-router-components.js +13 -0
- package/dist/shims/link.js +23 -16
- package/dist/shims/metadata.d.ts +3 -2
- package/dist/shims/metadata.js +8 -4
- package/dist/shims/navigation.js +4 -2
- package/dist/shims/root-params.d.ts +15 -1
- package/dist/shims/root-params.js +21 -1
- package/dist/shims/router.d.ts +2 -5
- package/dist/shims/router.js +41 -22
- package/dist/shims/server.js +3 -2
- package/dist/typegen.js +6 -5
- 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 +2 -2
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +1 -1
- package/dist/utils/project.d.ts +9 -1
- package/dist/utils/project.js +21 -4
- package/dist/utils/protocol-headers.d.ts +17 -0
- package/dist/utils/protocol-headers.js +17 -0
- package/dist/utils/react-version.d.ts +4 -0
- package/dist/utils/react-version.js +44 -0
- package/package.json +29 -23
- 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,1000 @@
|
|
|
1
|
+
import { detectProject } from "./cloudflare/project.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseSync } from "vite";
|
|
5
|
+
import MagicString from "magic-string";
|
|
6
|
+
//#region src/init-cloudflare.ts
|
|
7
|
+
const DEFAULT_CLOUDFLARE_INIT_OPTIONS = {
|
|
8
|
+
dataCache: "kv",
|
|
9
|
+
cdnCache: "data-cache",
|
|
10
|
+
imageOptimization: "cloudflare-images"
|
|
11
|
+
};
|
|
12
|
+
function validateCloudflarePlatformSetup(context, cloudflare) {
|
|
13
|
+
const tomlPath = path.join(context.root, "wrangler.toml");
|
|
14
|
+
if (fs.existsSync(tomlPath)) throw new Error("wrangler.toml is not supported by vinext init. Convert it to wrangler.jsonc and rerun.");
|
|
15
|
+
const projectInfo = detectProject(context.root);
|
|
16
|
+
const wranglerPath = ["wrangler.jsonc", "wrangler.json"].map((fileName) => path.join(context.root, fileName)).find((candidate) => fs.existsSync(candidate));
|
|
17
|
+
const wranglerCode = wranglerPath ? fs.readFileSync(wranglerPath, "utf-8") : void 0;
|
|
18
|
+
const updatedWranglerCode = wranglerCode ? updateWranglerConfigForCloudflare(wranglerCode, cloudflare) : void 0;
|
|
19
|
+
const imagesBinding = updatedWranglerCode ? getWranglerImagesBinding(updatedWranglerCode) : "IMAGES";
|
|
20
|
+
if (context.existingViteConfigPath) updateViteConfigForCloudflare(context.existingViteConfigPath, fs.readFileSync(context.existingViteConfigPath, "utf-8"), {
|
|
21
|
+
isAppRouter: context.isAppRouter,
|
|
22
|
+
nativeModulesToStub: projectInfo.nativeModulesToStub,
|
|
23
|
+
cache: cloudflare,
|
|
24
|
+
imagesBinding
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function setupCloudflarePlatform(context, cloudflare) {
|
|
28
|
+
const projectInfo = detectProject(context.root);
|
|
29
|
+
const wranglerPath = ["wrangler.jsonc", "wrangler.json"].map((fileName) => path.join(context.root, fileName)).find((candidate) => fs.existsSync(candidate));
|
|
30
|
+
const wranglerCode = wranglerPath ? fs.readFileSync(wranglerPath, "utf-8") : void 0;
|
|
31
|
+
const imagesBinding = wranglerCode ? getWranglerImagesBinding(wranglerCode) : "IMAGES";
|
|
32
|
+
let generatedViteConfig = false;
|
|
33
|
+
let skippedViteConfig = false;
|
|
34
|
+
if (context.existingViteConfigPath) {
|
|
35
|
+
const currentConfig = fs.readFileSync(context.existingViteConfigPath, "utf-8");
|
|
36
|
+
const updatedConfig = updateViteConfigForCloudflare(context.existingViteConfigPath, currentConfig, {
|
|
37
|
+
isAppRouter: context.isAppRouter,
|
|
38
|
+
nativeModulesToStub: projectInfo.nativeModulesToStub,
|
|
39
|
+
cache: cloudflare,
|
|
40
|
+
imagesBinding
|
|
41
|
+
});
|
|
42
|
+
if (updatedConfig !== currentConfig) {
|
|
43
|
+
fs.writeFileSync(context.existingViteConfigPath, updatedConfig, "utf-8");
|
|
44
|
+
generatedViteConfig = true;
|
|
45
|
+
} else skippedViteConfig = true;
|
|
46
|
+
} else {
|
|
47
|
+
const configContent = context.isAppRouter ? generateAppRouterViteConfig(projectInfo, cloudflare, imagesBinding) : generatePagesRouterViteConfig(projectInfo, cloudflare, imagesBinding);
|
|
48
|
+
fs.writeFileSync(path.join(context.root, "vite.config.ts"), configContent, "utf-8");
|
|
49
|
+
generatedViteConfig = true;
|
|
50
|
+
}
|
|
51
|
+
const generatedPlatformFiles = [];
|
|
52
|
+
if (!wranglerPath) {
|
|
53
|
+
fs.writeFileSync(path.join(context.root, "wrangler.jsonc"), generateWranglerConfig(projectInfo, cloudflare, context.today), "utf-8");
|
|
54
|
+
generatedPlatformFiles.push("wrangler.jsonc");
|
|
55
|
+
} else if (wranglerCode) {
|
|
56
|
+
const updatedConfig = updateWranglerConfigForCloudflare(wranglerCode, cloudflare);
|
|
57
|
+
if (updatedConfig !== wranglerCode) {
|
|
58
|
+
fs.writeFileSync(wranglerPath, updatedConfig, "utf-8");
|
|
59
|
+
generatedPlatformFiles.push(path.basename(wranglerPath));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!context.isAppRouter && !projectInfo.hasWorkerEntry) {
|
|
63
|
+
fs.mkdirSync(path.join(context.root, "worker"), { recursive: true });
|
|
64
|
+
fs.writeFileSync(path.join(context.root, "worker", "index.ts"), generatePagesRouterWorkerEntry(), "utf-8");
|
|
65
|
+
generatedPlatformFiles.push("worker/index.ts");
|
|
66
|
+
}
|
|
67
|
+
const finalWranglerPath = wranglerPath ?? path.join(context.root, "wrangler.jsonc");
|
|
68
|
+
const finalWranglerFileName = path.basename(finalWranglerPath);
|
|
69
|
+
const kvBinding = JSON.parse(stripJsonComments(fs.readFileSync(finalWranglerPath, "utf-8"))).kv_namespaces?.find((namespace) => namespace.binding === "VINEXT_KV_CACHE");
|
|
70
|
+
const needsKvNamespaceId = cloudflare.dataCache === "kv" && (!kvBinding || typeof kvBinding.id !== "string" || kvBinding.id.length === 0 || kvBinding.id === "<your-kv-namespace-id>");
|
|
71
|
+
return {
|
|
72
|
+
generatedViteConfig,
|
|
73
|
+
skippedViteConfig,
|
|
74
|
+
generatedPlatformFiles,
|
|
75
|
+
nextSteps: needsKvNamespaceId ? [
|
|
76
|
+
"Cloudflare setup is incomplete until you finish KV configuration:",
|
|
77
|
+
"1. Create the KV namespace:",
|
|
78
|
+
" npx wrangler kv namespace create VINEXT_KV_CACHE",
|
|
79
|
+
`2. Copy the returned namespace ID into the VINEXT_KV_CACHE entry in ${finalWranglerFileName}:`,
|
|
80
|
+
" Set its \"id\" value, replacing \"<your-kv-namespace-id>\" if present."
|
|
81
|
+
] : []
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function generateWranglerConfig(info, options = DEFAULT_CLOUDFLARE_INIT_OPTIONS, today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0]) {
|
|
85
|
+
const workerEntry = (fs.existsSync(path.join(info.root, "worker", "index.ts")) ? "./worker/index.ts" : fs.existsSync(path.join(info.root, "worker", "index.js")) ? "./worker/index.js" : void 0) ?? (info.isAppRouter ? "vinext/server/app-router-entry" : "./worker/index.ts");
|
|
86
|
+
const config = {
|
|
87
|
+
$schema: "node_modules/wrangler/config-schema.json",
|
|
88
|
+
name: info.projectName,
|
|
89
|
+
compatibility_date: today,
|
|
90
|
+
compatibility_flags: ["nodejs_compat"],
|
|
91
|
+
main: workerEntry,
|
|
92
|
+
assets: {
|
|
93
|
+
directory: "dist/client",
|
|
94
|
+
not_found_handling: "none",
|
|
95
|
+
binding: "ASSETS"
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (options.cdnCache === "workers-cache") config.cache = { enabled: true };
|
|
99
|
+
if (options.imageOptimization === "cloudflare-images") config.images = { binding: "IMAGES" };
|
|
100
|
+
if (options.dataCache === "kv") config.kv_namespaces = [{
|
|
101
|
+
binding: "VINEXT_KV_CACHE",
|
|
102
|
+
id: "<your-kv-namespace-id>"
|
|
103
|
+
}];
|
|
104
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
105
|
+
}
|
|
106
|
+
function stripJsonComments(code) {
|
|
107
|
+
let output = "";
|
|
108
|
+
let inString = false;
|
|
109
|
+
let escaped = false;
|
|
110
|
+
for (let index = 0; index < code.length; index++) {
|
|
111
|
+
const char = code[index];
|
|
112
|
+
const next = code[index + 1];
|
|
113
|
+
if (inString) {
|
|
114
|
+
output += char;
|
|
115
|
+
if (escaped) escaped = false;
|
|
116
|
+
else if (char === "\\") escaped = true;
|
|
117
|
+
else if (char === "\"") inString = false;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (char === "\"") {
|
|
121
|
+
inString = true;
|
|
122
|
+
output += char;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (char === "/" && next === "/") {
|
|
126
|
+
while (index < code.length && code[index] !== "\n") index++;
|
|
127
|
+
output += "\n";
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (char === "/" && next === "*") {
|
|
131
|
+
index += 2;
|
|
132
|
+
while (index < code.length && !(code[index] === "*" && code[index + 1] === "/")) {
|
|
133
|
+
output += code[index] === "\n" ? "\n" : " ";
|
|
134
|
+
index++;
|
|
135
|
+
}
|
|
136
|
+
index++;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
output += char;
|
|
140
|
+
}
|
|
141
|
+
return output.replace(/,\s*([}\]])/g, "$1");
|
|
142
|
+
}
|
|
143
|
+
function findTopLevelJsonProperty(code, name) {
|
|
144
|
+
let depth = 0;
|
|
145
|
+
let inString = false;
|
|
146
|
+
let escaped = false;
|
|
147
|
+
let lineComment = false;
|
|
148
|
+
let blockComment = false;
|
|
149
|
+
for (let index = 0; index < code.length; index++) {
|
|
150
|
+
const char = code[index];
|
|
151
|
+
const next = code[index + 1];
|
|
152
|
+
if (lineComment) {
|
|
153
|
+
if (char === "\n") lineComment = false;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (blockComment) {
|
|
157
|
+
if (char === "*" && next === "/") {
|
|
158
|
+
blockComment = false;
|
|
159
|
+
index++;
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (inString) {
|
|
164
|
+
if (escaped) escaped = false;
|
|
165
|
+
else if (char === "\\") escaped = true;
|
|
166
|
+
else if (char === "\"") inString = false;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (char === "/" && next === "/") {
|
|
170
|
+
lineComment = true;
|
|
171
|
+
index++;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (char === "/" && next === "*") {
|
|
175
|
+
blockComment = true;
|
|
176
|
+
index++;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (char === "\"") {
|
|
180
|
+
inString = true;
|
|
181
|
+
let value = "";
|
|
182
|
+
index++;
|
|
183
|
+
for (; index < code.length; index++) {
|
|
184
|
+
const stringChar = code[index];
|
|
185
|
+
if (stringChar === "\\") value += stringChar + (code[++index] ?? "");
|
|
186
|
+
else if (stringChar === "\"") {
|
|
187
|
+
inString = false;
|
|
188
|
+
break;
|
|
189
|
+
} else value += stringChar;
|
|
190
|
+
}
|
|
191
|
+
if (depth !== 1 || value !== name) continue;
|
|
192
|
+
let cursor = index + 1;
|
|
193
|
+
while (/\s/.test(code[cursor] ?? "")) cursor++;
|
|
194
|
+
if (code[cursor] !== ":") continue;
|
|
195
|
+
cursor++;
|
|
196
|
+
while (/\s/.test(code[cursor] ?? "")) cursor++;
|
|
197
|
+
const valueStart = cursor;
|
|
198
|
+
let valueDepth = 0;
|
|
199
|
+
let valueString = false;
|
|
200
|
+
let valueEscaped = false;
|
|
201
|
+
let valueLineComment = false;
|
|
202
|
+
let valueBlockComment = false;
|
|
203
|
+
for (; cursor < code.length; cursor++) {
|
|
204
|
+
const valueChar = code[cursor];
|
|
205
|
+
const valueNext = code[cursor + 1];
|
|
206
|
+
if (valueLineComment) {
|
|
207
|
+
if (valueChar === "\n") valueLineComment = false;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (valueBlockComment) {
|
|
211
|
+
if (valueChar === "*" && valueNext === "/") {
|
|
212
|
+
valueBlockComment = false;
|
|
213
|
+
cursor++;
|
|
214
|
+
}
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (valueString) {
|
|
218
|
+
if (valueEscaped) valueEscaped = false;
|
|
219
|
+
else if (valueChar === "\\") valueEscaped = true;
|
|
220
|
+
else if (valueChar === "\"") valueString = false;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (valueChar === "/" && valueNext === "/") {
|
|
224
|
+
valueLineComment = true;
|
|
225
|
+
cursor++;
|
|
226
|
+
} else if (valueChar === "/" && valueNext === "*") {
|
|
227
|
+
valueBlockComment = true;
|
|
228
|
+
cursor++;
|
|
229
|
+
} else if (valueChar === "\"") valueString = true;
|
|
230
|
+
else if (valueChar === "{" || valueChar === "[") valueDepth++;
|
|
231
|
+
else if (valueChar === "}" || valueChar === "]") {
|
|
232
|
+
if (valueDepth === 0) return {
|
|
233
|
+
valueStart,
|
|
234
|
+
valueEnd: cursor
|
|
235
|
+
};
|
|
236
|
+
valueDepth--;
|
|
237
|
+
if (valueDepth === 0) return {
|
|
238
|
+
valueStart,
|
|
239
|
+
valueEnd: cursor + 1
|
|
240
|
+
};
|
|
241
|
+
} else if (valueChar === "," && valueDepth === 0) return {
|
|
242
|
+
valueStart,
|
|
243
|
+
valueEnd: cursor
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
valueStart,
|
|
248
|
+
valueEnd: cursor
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (char === "{") depth++;
|
|
252
|
+
else if (char === "}") depth--;
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
function appendTopLevelJsonProperty(code, property) {
|
|
257
|
+
const closing = code.lastIndexOf("}");
|
|
258
|
+
if (closing < 0) throw new Error("Could not find the root object in Wrangler config.");
|
|
259
|
+
const before = code.slice(0, closing);
|
|
260
|
+
const structuralBefore = stripJsonComments(before);
|
|
261
|
+
return `${before}${!/,\s*$/.test(structuralBefore) && !/{\s*$/.test(structuralBefore) ? "," : ""}\n${property}\n${code.slice(closing)}`;
|
|
262
|
+
}
|
|
263
|
+
function updateWranglerConfigForCloudflare(code, options) {
|
|
264
|
+
try {
|
|
265
|
+
JSON.parse(stripJsonComments(code));
|
|
266
|
+
} catch (cause) {
|
|
267
|
+
throw new Error("Could not parse the existing Wrangler JSON/JSONC config.", { cause });
|
|
268
|
+
}
|
|
269
|
+
let output = code;
|
|
270
|
+
if (options.cdnCache === "workers-cache") {
|
|
271
|
+
const cacheProperty = findTopLevelJsonProperty(output, "cache");
|
|
272
|
+
if (!cacheProperty) output = appendTopLevelJsonProperty(output, " \"cache\": { \"enabled\": true }");
|
|
273
|
+
else {
|
|
274
|
+
const cache = JSON.parse(stripJsonComments(output.slice(cacheProperty.valueStart, cacheProperty.valueEnd)));
|
|
275
|
+
if (!cache || cache.enabled !== true) {
|
|
276
|
+
const updatedCache = JSON.stringify({
|
|
277
|
+
...cache,
|
|
278
|
+
enabled: true
|
|
279
|
+
});
|
|
280
|
+
output = `${output.slice(0, cacheProperty.valueStart)}${updatedCache}${output.slice(cacheProperty.valueEnd)}`;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (options.imageOptimization === "cloudflare-images") {
|
|
285
|
+
const imagesProperty = findTopLevelJsonProperty(output, "images");
|
|
286
|
+
if (!imagesProperty) output = appendTopLevelJsonProperty(output, " \"images\": { \"binding\": \"IMAGES\" }");
|
|
287
|
+
else {
|
|
288
|
+
const images = JSON.parse(stripJsonComments(output.slice(imagesProperty.valueStart, imagesProperty.valueEnd)));
|
|
289
|
+
if (!images || typeof images.binding !== "string" || images.binding.length === 0) output = `${output.slice(0, imagesProperty.valueStart)}{ "binding": "IMAGES" }${output.slice(imagesProperty.valueEnd)}`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (options.dataCache === "kv") {
|
|
293
|
+
const kvProperty = findTopLevelJsonProperty(output, "kv_namespaces");
|
|
294
|
+
if (!kvProperty) output = appendTopLevelJsonProperty(output, " \"kv_namespaces\": [{ \"binding\": \"VINEXT_KV_CACHE\", \"id\": \"<your-kv-namespace-id>\" }]");
|
|
295
|
+
else {
|
|
296
|
+
const rawValue = output.slice(kvProperty.valueStart, kvProperty.valueEnd);
|
|
297
|
+
if (!JSON.parse(stripJsonComments(rawValue)).some((namespace) => namespace.binding === "VINEXT_KV_CACHE")) {
|
|
298
|
+
const closing = kvProperty.valueEnd - 1;
|
|
299
|
+
const content = output.slice(kvProperty.valueStart + 1, closing);
|
|
300
|
+
const separator = content.trim() ? `${/,\s*$/.test(content) ? "" : ","}\n ` : "";
|
|
301
|
+
output = `${output.slice(0, closing)}${separator}{ "binding": "VINEXT_KV_CACHE", "id": "<your-kv-namespace-id>" }${output.slice(closing)}`;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return output;
|
|
306
|
+
}
|
|
307
|
+
function getWranglerImagesBinding(code) {
|
|
308
|
+
const property = findTopLevelJsonProperty(code, "images");
|
|
309
|
+
if (!property) return "IMAGES";
|
|
310
|
+
const images = JSON.parse(stripJsonComments(code.slice(property.valueStart, property.valueEnd)));
|
|
311
|
+
return images && typeof images.binding === "string" && images.binding.length > 0 ? images.binding : "IMAGES";
|
|
312
|
+
}
|
|
313
|
+
/** Generate worker/index.ts for Pages Router */
|
|
314
|
+
function generatePagesRouterWorkerEntry() {
|
|
315
|
+
return `/**
|
|
316
|
+
* Cloudflare Worker entry point -- auto-generated by vinext.
|
|
317
|
+
* Edit freely or delete to regenerate with vinext init.
|
|
318
|
+
*/
|
|
319
|
+
import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "vinext/server/pages-request-pipeline";
|
|
320
|
+
import type { PagesPipelineDeps } from "vinext/server/pages-request-pipeline";
|
|
321
|
+
import { handleConfiguredImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath } from "vinext/server/image-optimization";
|
|
322
|
+
import type { ImageConfig } from "vinext/server/image-optimization";
|
|
323
|
+
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, isOpenRedirectShaped } from "vinext/server/request-pipeline";
|
|
324
|
+
import { notFoundStaticAssetResponse } from "vinext/server/http-error-responses";
|
|
325
|
+
import { finalizeMissingStaticAssetResponse } from "vinext/server/worker-utils";
|
|
326
|
+
import { assetPrefixPathname, isNextStaticPath } from "vinext/utils/asset-prefix";
|
|
327
|
+
import { hasBasePath, stripBasePath } from "vinext/utils/base-path";
|
|
328
|
+
|
|
329
|
+
// @ts-expect-error -- virtual module resolved by vinext at build time
|
|
330
|
+
import { renderPage, handleApiRoute, runMiddleware, normalizeDataRequest, vinextConfig, matchPageRoute, hasMiddleware } from "virtual:vinext-server-entry";
|
|
331
|
+
// @ts-expect-error -- virtual module resolved by vinext at build time
|
|
332
|
+
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
333
|
+
// @ts-expect-error -- virtual module resolved by vinext at build time
|
|
334
|
+
import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
|
|
335
|
+
|
|
336
|
+
interface Env {
|
|
337
|
+
ASSETS: Fetcher;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
interface ExecutionContext {
|
|
341
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
342
|
+
passThroughOnException(): void;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Extract config values (embedded at build time in the server entry)
|
|
346
|
+
const basePath: string = vinextConfig?.basePath ?? "";
|
|
347
|
+
const assetPathPrefix: string = assetPrefixPathname(vinextConfig?.assetPrefix ?? "");
|
|
348
|
+
const trailingSlash: boolean = vinextConfig?.trailingSlash ?? false;
|
|
349
|
+
const i18nConfig = vinextConfig?.i18n ?? null;
|
|
350
|
+
const configRedirects = vinextConfig?.redirects ?? [];
|
|
351
|
+
const configRewrites = vinextConfig?.rewrites ?? { beforeFiles: [], afterFiles: [], fallback: [] };
|
|
352
|
+
const configHeaders = vinextConfig?.headers ?? [];
|
|
353
|
+
const imageConfig: ImageConfig | undefined = vinextConfig?.images ? {
|
|
354
|
+
qualities: vinextConfig.images.qualities,
|
|
355
|
+
dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
|
|
356
|
+
dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
|
|
357
|
+
contentDispositionType: vinextConfig.images.contentDispositionType,
|
|
358
|
+
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy,
|
|
359
|
+
} : undefined;
|
|
360
|
+
|
|
361
|
+
export default {
|
|
362
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
363
|
+
// Pass the Worker \`env\` so binding-backed adapters (e.g. KV) resolve.
|
|
364
|
+
registerConfiguredCacheAdapters(env);
|
|
365
|
+
registerConfiguredImageOptimizer(env);
|
|
366
|
+
try {
|
|
367
|
+
const url = new URL(request.url);
|
|
368
|
+
let pathname = url.pathname;
|
|
369
|
+
|
|
370
|
+
// Block protocol-relative URL open redirects in all shapes:
|
|
371
|
+
// literal //evil.com, /\\\\evil.com
|
|
372
|
+
// encoded /%5Cevil.com, /%2F/evil.com
|
|
373
|
+
// Browsers normalize backslash to forward slash, and they percent-decode
|
|
374
|
+
// Location headers, so an encoded backslash in a downstream 308 redirect
|
|
375
|
+
// would also navigate to the attacker's origin.
|
|
376
|
+
if (isOpenRedirectShaped(pathname)) {
|
|
377
|
+
return new Response("This page could not be found", { status: 404 });
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Valid assets are served by Cloudflare's ASSETS binding before the
|
|
381
|
+
// worker runs. Missing asset-shaped requests still need to reach
|
|
382
|
+
// middleware so it can rewrite or respond; a final 404 is converted
|
|
383
|
+
// back to Next.js's canonical plain-text static-file response below.
|
|
384
|
+
const missingBuildAsset = isNextStaticPath(pathname, basePath, assetPathPrefix);
|
|
385
|
+
|
|
386
|
+
// Strip internal headers from inbound requests so they cannot be
|
|
387
|
+
// forged to influence routing or impersonate internal state.
|
|
388
|
+
// Request.headers is immutable in Workers, so build a clean copy.
|
|
389
|
+
{
|
|
390
|
+
const filteredHeaders = filterInternalHeaders(request.headers);
|
|
391
|
+
request = cloneRequestWithHeaders(request, filteredHeaders);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ── 1. Strip basePath ─────────────────────────────────────────
|
|
395
|
+
// Track basePath presence on the original request so the matcher
|
|
396
|
+
// gating below can distinguish requests inside basePath (default
|
|
397
|
+
// rules apply) from requests outside it (only opt-out rules apply).
|
|
398
|
+
const hadBasePath = !basePath || hasBasePath(pathname, basePath);
|
|
399
|
+
{
|
|
400
|
+
const stripped = stripBasePath(pathname, basePath);
|
|
401
|
+
if (stripped !== pathname) {
|
|
402
|
+
const strippedUrl = new URL(request.url);
|
|
403
|
+
strippedUrl.pathname = stripped;
|
|
404
|
+
request = cloneRequestWithUrl(request, strippedUrl.toString());
|
|
405
|
+
pathname = stripped;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const dataNorm = normalizeDataRequest(request);
|
|
410
|
+
if (dataNorm.notFoundResponse) return dataNorm.notFoundResponse;
|
|
411
|
+
const isDataReq = dataNorm.isDataReq;
|
|
412
|
+
if (isDataReq) {
|
|
413
|
+
request = dataNorm.request;
|
|
414
|
+
pathname = dataNorm.normalizedPathname;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ── Image optimization via Cloudflare Images binding ──────────
|
|
418
|
+
// Checked after basePath stripping so /<basePath>/_next/image works.
|
|
419
|
+
if (isImageOptimizationPath(pathname)) {
|
|
420
|
+
const allowedWidths = [
|
|
421
|
+
...(vinextConfig?.images?.deviceSizes ?? DEFAULT_DEVICE_SIZES),
|
|
422
|
+
...(vinextConfig?.images?.imageSizes ?? DEFAULT_IMAGE_SIZES),
|
|
423
|
+
];
|
|
424
|
+
return handleConfiguredImageOptimization(
|
|
425
|
+
request,
|
|
426
|
+
(assetPath) => env.ASSETS.fetch(new Request(new URL(assetPath, request.url))),
|
|
427
|
+
allowedWidths,
|
|
428
|
+
imageConfig,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Delegate the canonical 9-step Next.js pipeline to the shared owner.
|
|
433
|
+
// The worker adapter is responsible for: open-redirect guard, _next/static
|
|
434
|
+
// 404 short-circuit, header filtering, basePath stripping, and image
|
|
435
|
+
// optimization. runPagesRequest receives a clean, basePath-stripped request.
|
|
436
|
+
const deps: PagesPipelineDeps = {
|
|
437
|
+
basePath,
|
|
438
|
+
trailingSlash,
|
|
439
|
+
i18nConfig,
|
|
440
|
+
configRedirects,
|
|
441
|
+
configRewrites,
|
|
442
|
+
configHeaders,
|
|
443
|
+
hadBasePath,
|
|
444
|
+
isDataReq,
|
|
445
|
+
isDataRequest: isDataReq,
|
|
446
|
+
hasMiddleware,
|
|
447
|
+
ctx,
|
|
448
|
+
matchPageRoute: typeof matchPageRoute === "function" ? matchPageRoute : null,
|
|
449
|
+
// Pass the original (pre-basePath-stripping) URL to middleware so that
|
|
450
|
+
// request.nextUrl.basePath reflects whether the URL actually had the
|
|
451
|
+
// basePath prefix. Matches Next.js behavior and the prod-server.ts
|
|
452
|
+
// equivalent (shared via wrapMiddlewareWithBasePath).
|
|
453
|
+
runMiddleware:
|
|
454
|
+
typeof runMiddleware === "function"
|
|
455
|
+
? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath)
|
|
456
|
+
: null,
|
|
457
|
+
renderPage: typeof renderPage === "function"
|
|
458
|
+
? (req, resolvedUrl, options, stagedHeaders) =>
|
|
459
|
+
renderPage(req, resolvedUrl, null, ctx, stagedHeaders, options)
|
|
460
|
+
: null,
|
|
461
|
+
handleApi: typeof handleApiRoute === "function"
|
|
462
|
+
? (req, apiUrl) => handleApiRoute(req, apiUrl, ctx)
|
|
463
|
+
: null,
|
|
464
|
+
serveFilesystemRoute: async (requestPathname, _stagedHeaders, phase) => {
|
|
465
|
+
return fetchWorkerFilesystemRoute(
|
|
466
|
+
request,
|
|
467
|
+
requestPathname,
|
|
468
|
+
phase,
|
|
469
|
+
(assetRequest) => env.ASSETS.fetch(assetRequest),
|
|
470
|
+
);
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const result = await runPagesRequest(request, deps);
|
|
475
|
+
if (result.type === "response") {
|
|
476
|
+
return finalizeMissingStaticAssetResponse(result.response, missingBuildAsset);
|
|
477
|
+
}
|
|
478
|
+
// Should not reach here for prod/worker (all callbacks supplied).
|
|
479
|
+
return missingBuildAsset
|
|
480
|
+
? notFoundStaticAssetResponse()
|
|
481
|
+
: new Response("This page could not be found", { status: 404 });
|
|
482
|
+
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error("[vinext] Worker error:", error);
|
|
485
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
`;
|
|
491
|
+
}
|
|
492
|
+
function cacheImports(options) {
|
|
493
|
+
const imports = [];
|
|
494
|
+
if (options.dataCache === "kv") imports.push("import { kvDataAdapter } from \"@vinext/cloudflare/cache/kv-data-adapter\";");
|
|
495
|
+
if (options.cdnCache === "workers-cache") imports.push("import { cdnAdapter } from \"@vinext/cloudflare/cache/cdn-adapter\";");
|
|
496
|
+
if (options.imageOptimization === "cloudflare-images") imports.push("import { imagesOptimizer } from \"@vinext/cloudflare/images/images-optimizer\";");
|
|
497
|
+
return imports;
|
|
498
|
+
}
|
|
499
|
+
function vinextExpression(options, binding = "vinext", imageBinding = "imagesOptimizer", imagesBinding = "IMAGES") {
|
|
500
|
+
const cacheEntries = [];
|
|
501
|
+
if (options.dataCache === "kv") cacheEntries.push("data: kvDataAdapter()");
|
|
502
|
+
if (options.cdnCache === "workers-cache") cacheEntries.push("cdn: cdnAdapter()");
|
|
503
|
+
const optionEntries = [];
|
|
504
|
+
if (cacheEntries.length > 0) optionEntries.push(`cache: { ${cacheEntries.join(", ")} }`);
|
|
505
|
+
if (options.imageOptimization === "cloudflare-images") {
|
|
506
|
+
const adapterOptions = imagesBinding === "IMAGES" ? "" : `{ binding: ${JSON.stringify(imagesBinding)} }`;
|
|
507
|
+
optionEntries.push(`images: { optimizer: ${imageBinding}(${adapterOptions}) }`);
|
|
508
|
+
}
|
|
509
|
+
return optionEntries.length === 0 ? `${binding}()` : `${binding}({\n ${optionEntries.join(",\n ")},\n})`;
|
|
510
|
+
}
|
|
511
|
+
/** Generate vite.config.ts for App Router */
|
|
512
|
+
function generateAppRouterViteConfig(info, options = DEFAULT_CLOUDFLARE_INIT_OPTIONS, imagesBinding = "IMAGES") {
|
|
513
|
+
const imports = [
|
|
514
|
+
`import { defineConfig } from "vite";`,
|
|
515
|
+
`import vinext from "vinext";`,
|
|
516
|
+
`import { cloudflare } from "@cloudflare/vite-plugin";`,
|
|
517
|
+
...cacheImports(options)
|
|
518
|
+
];
|
|
519
|
+
if (info?.nativeModulesToStub && info.nativeModulesToStub.length > 0) imports.push(`import path from "node:path";`);
|
|
520
|
+
const plugins = [];
|
|
521
|
+
if (info?.hasMDX) plugins.push(` // vinext auto-injects @mdx-js/rollup with plugins from next.config`);
|
|
522
|
+
plugins.push(` ${vinextExpression(options, "vinext", "imagesOptimizer", imagesBinding).replace(/\n/g, "\n ")},`);
|
|
523
|
+
plugins.push(` cloudflare({
|
|
524
|
+
viteEnvironment: {
|
|
525
|
+
name: "rsc",
|
|
526
|
+
childEnvironments: ["ssr"],
|
|
527
|
+
},
|
|
528
|
+
}),`);
|
|
529
|
+
let resolveBlock = "";
|
|
530
|
+
const aliases = [];
|
|
531
|
+
if (info?.nativeModulesToStub && info.nativeModulesToStub.length > 0) for (const mod of info.nativeModulesToStub) aliases.push(` "${mod}": path.resolve(__dirname, "empty-stub.js"),`);
|
|
532
|
+
if (aliases.length > 0) resolveBlock = `\n resolve: {\n alias: {\n${aliases.join("\n")}\n },\n },`;
|
|
533
|
+
return `${imports.join("\n")}
|
|
534
|
+
|
|
535
|
+
export default defineConfig({
|
|
536
|
+
plugins: [
|
|
537
|
+
${plugins.join("\n")}
|
|
538
|
+
],${resolveBlock}
|
|
539
|
+
});
|
|
540
|
+
`;
|
|
541
|
+
}
|
|
542
|
+
/** Generate vite.config.ts for Pages Router */
|
|
543
|
+
function generatePagesRouterViteConfig(info, options = DEFAULT_CLOUDFLARE_INIT_OPTIONS, imagesBinding = "IMAGES") {
|
|
544
|
+
const imports = [
|
|
545
|
+
`import { defineConfig } from "vite";`,
|
|
546
|
+
`import vinext from "vinext";`,
|
|
547
|
+
`import { cloudflare } from "@cloudflare/vite-plugin";`,
|
|
548
|
+
...cacheImports(options)
|
|
549
|
+
];
|
|
550
|
+
if (info?.nativeModulesToStub && info.nativeModulesToStub.length > 0) imports.push(`import path from "node:path";`);
|
|
551
|
+
let resolveBlock = "";
|
|
552
|
+
const aliases = [];
|
|
553
|
+
if (info?.nativeModulesToStub && info.nativeModulesToStub.length > 0) for (const mod of info.nativeModulesToStub) aliases.push(` "${mod}": path.resolve(__dirname, "empty-stub.js"),`);
|
|
554
|
+
if (aliases.length > 0) resolveBlock = `\n resolve: {\n alias: {\n${aliases.join("\n")}\n },\n },`;
|
|
555
|
+
return `${imports.join("\n")}
|
|
556
|
+
|
|
557
|
+
export default defineConfig({
|
|
558
|
+
plugins: [
|
|
559
|
+
${vinextExpression(options, "vinext", "imagesOptimizer", imagesBinding).replace(/\n/g, "\n ")},
|
|
560
|
+
cloudflare(),
|
|
561
|
+
],${resolveBlock}
|
|
562
|
+
});
|
|
563
|
+
`;
|
|
564
|
+
}
|
|
565
|
+
function parseViteConfig(filePath, code) {
|
|
566
|
+
const extension = path.extname(filePath).slice(1);
|
|
567
|
+
const lang = extension === "ts" || extension === "mts" || extension === "cts" ? "ts" : "js";
|
|
568
|
+
const parsed = parseSync(path.basename(filePath), code, {
|
|
569
|
+
astType: "ts",
|
|
570
|
+
lang,
|
|
571
|
+
sourceType: "module"
|
|
572
|
+
});
|
|
573
|
+
const error = parsed.errors.find((diagnostic) => diagnostic.severity === "Error");
|
|
574
|
+
if (error) throw new Error(`Could not parse ${path.basename(filePath)}: ${error.message}`);
|
|
575
|
+
return parsed.program;
|
|
576
|
+
}
|
|
577
|
+
function propertyName(property) {
|
|
578
|
+
if (property.computed) return void 0;
|
|
579
|
+
if (property.key.type === "Identifier") return property.key.name;
|
|
580
|
+
if (property.key.type === "Literal" && typeof property.key.value === "string") return property.key.value;
|
|
581
|
+
}
|
|
582
|
+
function findProperty(object, name) {
|
|
583
|
+
return object.properties.find((property) => property.type === "Property" && propertyName(property) === name);
|
|
584
|
+
}
|
|
585
|
+
function unwrapObject(expression) {
|
|
586
|
+
if (expression.type === "ObjectExpression") return expression;
|
|
587
|
+
if (expression.type === "ParenthesizedExpression") return unwrapObject(expression.expression);
|
|
588
|
+
}
|
|
589
|
+
function findVariableObject(program, name) {
|
|
590
|
+
for (const statement of program.body) {
|
|
591
|
+
if (statement.type !== "VariableDeclaration") continue;
|
|
592
|
+
for (const declaration of statement.declarations) {
|
|
593
|
+
if (declaration.id.type !== "Identifier" || declaration.id.name !== name || !declaration.init) continue;
|
|
594
|
+
return unwrapObject(declaration.init);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function findConfigObject(program) {
|
|
599
|
+
const defaultExport = program.body.find((statement) => statement.type === "ExportDefaultDeclaration");
|
|
600
|
+
if (!defaultExport) {
|
|
601
|
+
for (const statement of program.body) {
|
|
602
|
+
if (statement.type !== "ExpressionStatement") continue;
|
|
603
|
+
const expression = statement.expression;
|
|
604
|
+
if (expression.type !== "AssignmentExpression" || expression.left.type !== "MemberExpression" || expression.left.object.type !== "Identifier" || expression.left.object.name !== "module" || expression.left.property.type !== "Identifier" || expression.left.property.name !== "exports") continue;
|
|
605
|
+
const direct = unwrapObject(expression.right);
|
|
606
|
+
if (direct) return direct;
|
|
607
|
+
if (expression.right.type === "CallExpression" && expression.right.arguments.length > 0) {
|
|
608
|
+
const firstArgument = expression.right.arguments[0];
|
|
609
|
+
if (firstArgument.type !== "SpreadElement") return unwrapObject(firstArgument);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (defaultExport.declaration.type === "FunctionDeclaration") return void 0;
|
|
615
|
+
const declaration = defaultExport.declaration;
|
|
616
|
+
if (declaration.type === "ClassDeclaration" || declaration.type === "TSInterfaceDeclaration") return;
|
|
617
|
+
const direct = unwrapObject(declaration);
|
|
618
|
+
if (direct) return direct;
|
|
619
|
+
if (declaration.type === "Identifier") return findVariableObject(program, declaration.name);
|
|
620
|
+
if (declaration.type !== "CallExpression" || declaration.arguments.length === 0) return void 0;
|
|
621
|
+
const firstArgument = declaration.arguments[0];
|
|
622
|
+
if (firstArgument.type === "SpreadElement") return void 0;
|
|
623
|
+
const argumentObject = unwrapObject(firstArgument);
|
|
624
|
+
if (argumentObject) return argumentObject;
|
|
625
|
+
if (firstArgument.type !== "ArrowFunctionExpression" && firstArgument.type !== "FunctionExpression") return;
|
|
626
|
+
if (!firstArgument.body) return void 0;
|
|
627
|
+
if (firstArgument.body.type !== "BlockStatement") return unwrapObject(firstArgument.body);
|
|
628
|
+
const returnStatement = firstArgument.body.body.find((statement) => statement.type === "ReturnStatement");
|
|
629
|
+
return returnStatement?.argument ? unwrapObject(returnStatement.argument) : void 0;
|
|
630
|
+
}
|
|
631
|
+
function importInsertionOffset(program) {
|
|
632
|
+
let offset = 0;
|
|
633
|
+
for (const statement of program.body) {
|
|
634
|
+
if (statement.type !== "ImportDeclaration") break;
|
|
635
|
+
offset = statement.end;
|
|
636
|
+
}
|
|
637
|
+
return offset;
|
|
638
|
+
}
|
|
639
|
+
function collectPatternBindings(pattern, bindings) {
|
|
640
|
+
if (pattern.type === "Identifier") {
|
|
641
|
+
bindings.add(pattern.name);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (pattern.type === "RestElement") {
|
|
645
|
+
collectPatternBindings(pattern.argument, bindings);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (pattern.type === "AssignmentPattern") {
|
|
649
|
+
collectPatternBindings(pattern.left, bindings);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
if (pattern.type === "ArrayPattern") {
|
|
653
|
+
for (const element of pattern.elements) if (element) collectPatternBindings(element, bindings);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (pattern.type !== "ObjectPattern") return;
|
|
657
|
+
for (const property of pattern.properties) if (property.type === "RestElement") collectPatternBindings(property.argument, bindings);
|
|
658
|
+
else collectPatternBindings(property.value, bindings);
|
|
659
|
+
}
|
|
660
|
+
function collectTopLevelBindings(program) {
|
|
661
|
+
const bindings = /* @__PURE__ */ new Set();
|
|
662
|
+
for (const statement of program.body) {
|
|
663
|
+
if (statement.type === "ImportDeclaration") {
|
|
664
|
+
for (const specifier of statement.specifiers) bindings.add(specifier.local.name);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
668
|
+
if (!declaration) continue;
|
|
669
|
+
if (declaration.type === "VariableDeclaration") for (const declarator of declaration.declarations) collectPatternBindings(declarator.id, bindings);
|
|
670
|
+
else if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id) bindings.add(declaration.id.name);
|
|
671
|
+
else if (declaration.type === "TSEnumDeclaration") bindings.add(declaration.id.name);
|
|
672
|
+
else if (declaration.type === "TSModuleDeclaration" && declaration.id.type === "Identifier") bindings.add(declaration.id.name);
|
|
673
|
+
}
|
|
674
|
+
return bindings;
|
|
675
|
+
}
|
|
676
|
+
function allocateBinding(bindings, preferred) {
|
|
677
|
+
if (!bindings.has(preferred)) {
|
|
678
|
+
bindings.add(preferred);
|
|
679
|
+
return preferred;
|
|
680
|
+
}
|
|
681
|
+
let suffix = 2;
|
|
682
|
+
while (bindings.has(`${preferred}${suffix}`)) suffix++;
|
|
683
|
+
const binding = `${preferred}${suffix}`;
|
|
684
|
+
bindings.add(binding);
|
|
685
|
+
return binding;
|
|
686
|
+
}
|
|
687
|
+
function findImportedBinding(program, source, imported) {
|
|
688
|
+
for (const statement of program.body) {
|
|
689
|
+
if (statement.type !== "ImportDeclaration" || statement.source.value !== source) continue;
|
|
690
|
+
for (const specifier of statement.specifiers) if (specifier.type === "ImportSpecifier" && specifier.imported.type === "Identifier" && specifier.imported.name === imported) return specifier.local.name;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function ensureNamedImport(program, output, source, imported, binding) {
|
|
694
|
+
const existing = findImportedBinding(program, source, imported);
|
|
695
|
+
if (existing) return existing;
|
|
696
|
+
const declaration = program.body.find((statement) => statement.type === "ImportDeclaration" && statement.source.value === source);
|
|
697
|
+
if (declaration) {
|
|
698
|
+
const named = declaration.specifiers.filter((specifier) => specifier.type === "ImportSpecifier");
|
|
699
|
+
if (named.length > 0) {
|
|
700
|
+
const specifier = binding === imported ? imported : `${imported} as ${binding}`;
|
|
701
|
+
output.appendLeft(named[named.length - 1].end, `, ${specifier}`);
|
|
702
|
+
return binding;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const offset = importInsertionOffset(program);
|
|
706
|
+
const sourceText = `import { ${binding === imported ? imported : `${imported} as ${binding}`} } from ${JSON.stringify(source)};`;
|
|
707
|
+
output.appendLeft(offset, offset === 0 ? `${sourceText}\n` : `\n${sourceText}`);
|
|
708
|
+
return binding;
|
|
709
|
+
}
|
|
710
|
+
function ensureDefaultImport(program, output, source, binding) {
|
|
711
|
+
const existing = program.body.find((statement) => statement.type === "ImportDeclaration" && statement.source.value === source)?.specifiers.find((specifier) => specifier.type === "ImportDefaultSpecifier");
|
|
712
|
+
if (existing) return existing.local.name;
|
|
713
|
+
const offset = importInsertionOffset(program);
|
|
714
|
+
const sourceText = `import ${binding} from ${JSON.stringify(source)};`;
|
|
715
|
+
output.appendLeft(offset, offset === 0 ? `${sourceText}\n` : `\n${sourceText}`);
|
|
716
|
+
return binding;
|
|
717
|
+
}
|
|
718
|
+
function findRequiredBinding(program, source, imported) {
|
|
719
|
+
for (const statement of program.body) {
|
|
720
|
+
if (statement.type !== "VariableDeclaration") continue;
|
|
721
|
+
for (const declaration of statement.declarations) {
|
|
722
|
+
if (!declaration.init || declaration.init.type !== "CallExpression" || declaration.init.callee.type !== "Identifier" || declaration.init.callee.name !== "require" || declaration.init.arguments[0]?.type !== "Literal" || declaration.init.arguments[0].value !== source) continue;
|
|
723
|
+
if (imported === "default" && declaration.id.type === "Identifier") return declaration.id.name;
|
|
724
|
+
if (declaration.id.type !== "ObjectPattern") continue;
|
|
725
|
+
for (const property of declaration.id.properties) if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === imported && property.value.type === "Identifier") return property.value.name;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function requireInsertionOffset(program) {
|
|
730
|
+
let offset = 0;
|
|
731
|
+
for (const statement of program.body) {
|
|
732
|
+
if (statement.type !== "VariableDeclaration") break;
|
|
733
|
+
offset = statement.end;
|
|
734
|
+
}
|
|
735
|
+
return offset;
|
|
736
|
+
}
|
|
737
|
+
function ensureNamedRequire(program, output, source, imported, binding) {
|
|
738
|
+
const existing = findRequiredBinding(program, source, imported);
|
|
739
|
+
if (existing) return existing;
|
|
740
|
+
const offset = requireInsertionOffset(program);
|
|
741
|
+
const sourceText = `const { ${binding === imported ? imported : `${imported}: ${binding}`} } = require(${JSON.stringify(source)});`;
|
|
742
|
+
output.appendLeft(offset, offset === 0 ? `${sourceText}\n` : `\n${sourceText}`);
|
|
743
|
+
return binding;
|
|
744
|
+
}
|
|
745
|
+
function ensureDefaultRequire(program, output, source, binding) {
|
|
746
|
+
const existing = findRequiredBinding(program, source, "default");
|
|
747
|
+
if (existing) return existing;
|
|
748
|
+
const offset = requireInsertionOffset(program);
|
|
749
|
+
const sourceText = `const ${binding} = require(${JSON.stringify(source)});`;
|
|
750
|
+
output.appendLeft(offset, offset === 0 ? `${sourceText}\n` : `\n${sourceText}`);
|
|
751
|
+
return binding;
|
|
752
|
+
}
|
|
753
|
+
function insertObjectProperty(output, object, source, code) {
|
|
754
|
+
const offset = object.end - 1;
|
|
755
|
+
const hasProperties = object.properties.length > 0;
|
|
756
|
+
const hasTrailingComma = /,\s*$/.test(code.slice(object.start + 1, offset));
|
|
757
|
+
output.appendLeft(offset, `${hasProperties && !hasTrailingComma ? "," : ""}\n${source}\n`);
|
|
758
|
+
}
|
|
759
|
+
function endsWithCommaIgnoringWhitespaceAndComments(code) {
|
|
760
|
+
let index = 0;
|
|
761
|
+
let lastToken = "";
|
|
762
|
+
while (index < code.length) {
|
|
763
|
+
const char = code[index];
|
|
764
|
+
const next = code[index + 1];
|
|
765
|
+
if (/\s/.test(char)) {
|
|
766
|
+
index++;
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
if (char === "/" && next === "/") {
|
|
770
|
+
index += 2;
|
|
771
|
+
while (index < code.length && code[index] !== "\n") index++;
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
if (char === "/" && next === "*") {
|
|
775
|
+
index += 2;
|
|
776
|
+
while (index < code.length && !(code[index] === "*" && code[index + 1] === "/")) index++;
|
|
777
|
+
index += 2;
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
lastToken = char;
|
|
781
|
+
index++;
|
|
782
|
+
}
|
|
783
|
+
return lastToken === ",";
|
|
784
|
+
}
|
|
785
|
+
function cloudflarePluginExpression(isAppRouter, binding) {
|
|
786
|
+
return isAppRouter ? `${binding}({\n viteEnvironment: {\n name: "rsc",\n childEnvironments: ["ssr"],\n },\n})` : `${binding}()`;
|
|
787
|
+
}
|
|
788
|
+
function findPluginCall(config, binding) {
|
|
789
|
+
const plugins = findProperty(config, "plugins");
|
|
790
|
+
if (!plugins || plugins.value.type !== "ArrayExpression") return void 0;
|
|
791
|
+
return plugins.value.elements.find((element) => element?.type === "CallExpression" && element.callee.type === "Identifier" && element.callee.name === binding);
|
|
792
|
+
}
|
|
793
|
+
function hasVinextCacheSlot(call, name) {
|
|
794
|
+
const firstArgument = call?.arguments[0];
|
|
795
|
+
if (!firstArgument || firstArgument.type === "SpreadElement" || firstArgument.type !== "ObjectExpression") return false;
|
|
796
|
+
const cache = findProperty(firstArgument, "cache");
|
|
797
|
+
return cache?.value.type === "ObjectExpression" && Boolean(findProperty(cache.value, name));
|
|
798
|
+
}
|
|
799
|
+
function getVinextImageOptimizer(call) {
|
|
800
|
+
const firstArgument = call?.arguments[0];
|
|
801
|
+
if (!firstArgument || firstArgument.type === "SpreadElement" || firstArgument.type !== "ObjectExpression") return;
|
|
802
|
+
const images = findProperty(firstArgument, "images");
|
|
803
|
+
if (images?.value.type !== "ObjectExpression") return void 0;
|
|
804
|
+
return findProperty(images.value, "optimizer");
|
|
805
|
+
}
|
|
806
|
+
function isUsableImageOptimizer(property) {
|
|
807
|
+
if (!property) return false;
|
|
808
|
+
const value = property.value;
|
|
809
|
+
return !(value.type === "Identifier" && value.name === "undefined" || value.type === "Literal" && value.value === null);
|
|
810
|
+
}
|
|
811
|
+
function isImagesOptimizerCall(property, binding) {
|
|
812
|
+
return Boolean(property && binding && property.value.type === "CallExpression" && property.value.callee.type === "Identifier" && property.value.callee.name === binding);
|
|
813
|
+
}
|
|
814
|
+
function ensureVinextCache(output, config, vinextBinding, additions, code) {
|
|
815
|
+
if (additions.length === 0) return;
|
|
816
|
+
const call = findPluginCall(config, vinextBinding);
|
|
817
|
+
if (!call) return;
|
|
818
|
+
if (call.arguments.length === 0) {
|
|
819
|
+
output.appendLeft(call.end - 1, `{ cache: { ${additions.map(({ name, expression }) => `${name}: ${expression}`).join(", ")} } }`);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const firstArgument = call.arguments[0];
|
|
823
|
+
if (firstArgument.type === "SpreadElement" || firstArgument.type !== "ObjectExpression") throw new Error("The vinext() plugin options must be a static object for vinext init to add cache handlers.");
|
|
824
|
+
const optionsObject = firstArgument;
|
|
825
|
+
const cache = findProperty(optionsObject, "cache");
|
|
826
|
+
if (!cache) {
|
|
827
|
+
insertObjectProperty(output, optionsObject, ` cache: {\n${additions.map(({ name, expression }) => ` ${name}: ${expression},`).join("\n")}\n },`, code);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
if (cache.value.type !== "ObjectExpression") throw new Error("The vinext() cache option must be a static object for vinext init to add cache handlers.");
|
|
831
|
+
const cacheObject = cache.value;
|
|
832
|
+
const missing = additions.filter(({ name }) => !findProperty(cacheObject, name));
|
|
833
|
+
if (missing.length > 0) insertObjectProperty(output, cacheObject, missing.map(({ name, expression }) => ` ${name}: ${expression},`).join("\n"), code);
|
|
834
|
+
}
|
|
835
|
+
function ensureVinextImageOptimizer(output, config, vinextBinding, expression, code) {
|
|
836
|
+
if (!expression) return;
|
|
837
|
+
const call = findPluginCall(config, vinextBinding);
|
|
838
|
+
if (!call) return;
|
|
839
|
+
if (call.arguments.length === 0) {
|
|
840
|
+
output.appendLeft(call.end - 1, `{ images: { optimizer: ${expression} } }`);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const firstArgument = call.arguments[0];
|
|
844
|
+
if (firstArgument.type === "SpreadElement" || firstArgument.type !== "ObjectExpression") throw new Error("The vinext() plugin options must be a static object for vinext init to configure image optimization.");
|
|
845
|
+
const optionsObject = firstArgument;
|
|
846
|
+
const images = findProperty(optionsObject, "images");
|
|
847
|
+
if (!images) {
|
|
848
|
+
insertObjectProperty(output, optionsObject, ` images: { optimizer: ${expression} },`, code);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (images.value.type !== "ObjectExpression") throw new Error("The vinext() images option must be a static object for vinext init to add an image optimizer.");
|
|
852
|
+
const imagesObject = images.value;
|
|
853
|
+
const optimizer = findProperty(imagesObject, "optimizer");
|
|
854
|
+
if (!optimizer) insertObjectProperty(output, imagesObject, ` optimizer: ${expression},`, code);
|
|
855
|
+
else output.overwrite(optimizer.value.start, optimizer.value.end, expression);
|
|
856
|
+
}
|
|
857
|
+
function indentBlock(source, indent) {
|
|
858
|
+
return source.split("\n").map((line) => `${indent}${line}`).join("\n");
|
|
859
|
+
}
|
|
860
|
+
function ensurePlugins(output, config, additions, code) {
|
|
861
|
+
const plugins = findProperty(config, "plugins");
|
|
862
|
+
if (!plugins) {
|
|
863
|
+
insertObjectProperty(output, config, ` plugins: [\n${additions.map(({ expression }) => indentBlock(expression, " ")).join(",\n")},\n ],`, code);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (plugins.value.type !== "ArrayExpression") throw new Error("The Vite config's plugins option must be an array for vinext init to update it.");
|
|
867
|
+
const array = plugins.value;
|
|
868
|
+
const propertyIndent = code.slice(0, plugins.start).split("\n").at(-1)?.match(/^\s*/)?.[0] ?? "";
|
|
869
|
+
const elementIndent = `${propertyIndent} `;
|
|
870
|
+
const missingExpressions = [];
|
|
871
|
+
for (const addition of additions) if (!array.elements.some((element) => element?.type === "CallExpression" && element.callee.type === "Identifier" && element.callee.name === addition.binding)) missingExpressions.push(addition.expression);
|
|
872
|
+
if (missingExpressions.length === 0) return;
|
|
873
|
+
const closingOffset = array.end - 1;
|
|
874
|
+
const hasExistingElements = array.elements.some(Boolean);
|
|
875
|
+
let finalElement = null;
|
|
876
|
+
for (let index = array.elements.length - 1; index >= 0; index--) if (array.elements[index] !== null) {
|
|
877
|
+
finalElement = array.elements[index];
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
const hasTrailingComma = endsWithCommaIgnoringWhitespaceAndComments(code.slice(finalElement?.end ?? array.start + 1, closingOffset));
|
|
881
|
+
const prefix = hasExistingElements && !hasTrailingComma ? "," : "";
|
|
882
|
+
if (!code.slice(array.start, array.end).includes("\n") && hasExistingElements) {
|
|
883
|
+
output.appendLeft(array.start + 1, `\n${elementIndent}`);
|
|
884
|
+
let previousElement = null;
|
|
885
|
+
for (const element of array.elements) {
|
|
886
|
+
if (!element) continue;
|
|
887
|
+
if (previousElement) {
|
|
888
|
+
const gap = code.slice(previousElement.end, element.start);
|
|
889
|
+
const commaIndex = gap.indexOf(",");
|
|
890
|
+
if (commaIndex >= 0) {
|
|
891
|
+
const trivia = gap.slice(commaIndex + 1).trim();
|
|
892
|
+
output.overwrite(previousElement.end, element.start, trivia ? `,\n${elementIndent}${trivia}\n${elementIndent}` : `,\n${elementIndent}`);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
previousElement = element;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
output.appendLeft(closingOffset, `${prefix}\n${missingExpressions.map((expression) => indentBlock(expression, elementIndent)).join(",\n")},\n${propertyIndent}`);
|
|
899
|
+
}
|
|
900
|
+
function ensureNativeAliases(output, config, modules, pathBinding, code) {
|
|
901
|
+
if (modules.length === 0) return;
|
|
902
|
+
const resolve = findProperty(config, "resolve");
|
|
903
|
+
if (resolve && resolve.value.type !== "ObjectExpression") throw new Error("The Vite config's resolve option must be an object for vinext init to update it.");
|
|
904
|
+
const resolveObject = resolve?.value;
|
|
905
|
+
const alias = resolveObject ? findProperty(resolveObject, "alias") : void 0;
|
|
906
|
+
if (alias && alias.value.type !== "ObjectExpression") throw new Error("The Vite config's resolve.alias option must be an object for vinext init to update it.");
|
|
907
|
+
const aliasLines = modules.map((moduleName) => ` ${JSON.stringify(moduleName)}: ${pathBinding}.resolve(__dirname, "empty-stub.js"),`);
|
|
908
|
+
if (!resolveObject) {
|
|
909
|
+
insertObjectProperty(output, config, ` resolve: {\n alias: {\n${aliasLines.join("\n")}\n },\n },`, code);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
if (!alias) {
|
|
913
|
+
insertObjectProperty(output, resolveObject, ` alias: {\n${aliasLines.join("\n")}\n },`, code);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const aliasObject = alias.value;
|
|
917
|
+
const existingAliases = new Set(aliasObject.properties.flatMap((property) => property.type === "Property" && propertyName(property) ? [propertyName(property)] : []));
|
|
918
|
+
const missingLines = aliasLines.filter((_, index) => !existingAliases.has(modules[index]));
|
|
919
|
+
if (missingLines.length > 0) insertObjectProperty(output, aliasObject, missingLines.join("\n"), code);
|
|
920
|
+
}
|
|
921
|
+
function updateViteConfigForCloudflare(filePath, code, options) {
|
|
922
|
+
const program = parseViteConfig(filePath, code);
|
|
923
|
+
const cacheOptions = options.cache ?? {
|
|
924
|
+
dataCache: "none",
|
|
925
|
+
cdnCache: "data-cache",
|
|
926
|
+
imageOptimization: "cloudflare-images"
|
|
927
|
+
};
|
|
928
|
+
const config = findConfigObject(program);
|
|
929
|
+
if (!config) throw new Error(`Could not find a static Vite config object in ${path.basename(filePath)}. Use an object export or defineConfig({...}) so vinext init can update it.`);
|
|
930
|
+
const output = new MagicString(code);
|
|
931
|
+
const commonJs = usesCommonJsViteConfig(filePath, code);
|
|
932
|
+
const bindings = collectTopLevelBindings(program);
|
|
933
|
+
const vinextLocal = (commonJs ? findRequiredBinding(program, "vinext", "default") : program.body.filter((statement) => statement.type === "ImportDeclaration").find((statement) => statement.source.value === "vinext")?.specifiers.find((specifier) => specifier.type === "ImportDefaultSpecifier")?.local.name) ?? allocateBinding(bindings, "vinext");
|
|
934
|
+
const vinextBinding = commonJs ? ensureDefaultRequire(program, output, "vinext", vinextLocal) : ensureDefaultImport(program, output, "vinext", vinextLocal);
|
|
935
|
+
const existingVinextCall = findPluginCall(config, vinextBinding);
|
|
936
|
+
const existingImageOptimizer = getVinextImageOptimizer(existingVinextCall);
|
|
937
|
+
const configureCaches = options.cache !== void 0;
|
|
938
|
+
const cacheAdditions = [];
|
|
939
|
+
if (cacheOptions.dataCache === "kv" && !hasVinextCacheSlot(existingVinextCall, "data")) {
|
|
940
|
+
const local = (commonJs ? findRequiredBinding(program, "@vinext/cloudflare/cache/kv-data-adapter", "kvDataAdapter") : findImportedBinding(program, "@vinext/cloudflare/cache/kv-data-adapter", "kvDataAdapter")) ?? allocateBinding(bindings, "kvDataAdapter");
|
|
941
|
+
const binding = commonJs ? ensureNamedRequire(program, output, "@vinext/cloudflare/cache/kv-data-adapter", "kvDataAdapter", local) : ensureNamedImport(program, output, "@vinext/cloudflare/cache/kv-data-adapter", "kvDataAdapter", local);
|
|
942
|
+
cacheAdditions.push({
|
|
943
|
+
name: "data",
|
|
944
|
+
expression: `${binding}()`
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
if (configureCaches && cacheOptions.cdnCache === "workers-cache" && !hasVinextCacheSlot(existingVinextCall, "cdn")) {
|
|
948
|
+
const imported = "cdnAdapter";
|
|
949
|
+
const source = "@vinext/cloudflare/cache/cdn-adapter";
|
|
950
|
+
const local = (commonJs ? findRequiredBinding(program, source, imported) : findImportedBinding(program, source, imported)) ?? allocateBinding(bindings, imported);
|
|
951
|
+
const binding = commonJs ? ensureNamedRequire(program, output, source, imported, local) : ensureNamedImport(program, output, source, imported, local);
|
|
952
|
+
cacheAdditions.push({
|
|
953
|
+
name: "cdn",
|
|
954
|
+
expression: `${binding}()`
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
let imageOptimizerExpression;
|
|
958
|
+
if (cacheOptions.imageOptimization === "cloudflare-images") {
|
|
959
|
+
const source = "@vinext/cloudflare/images/images-optimizer";
|
|
960
|
+
const imported = "imagesOptimizer";
|
|
961
|
+
const existing = commonJs ? findRequiredBinding(program, source, imported) : findImportedBinding(program, source, imported);
|
|
962
|
+
if (!isUsableImageOptimizer(existingImageOptimizer) || isImagesOptimizerCall(existingImageOptimizer, existing)) {
|
|
963
|
+
const local = existing ?? allocateBinding(bindings, imported);
|
|
964
|
+
imageOptimizerExpression = `${commonJs ? ensureNamedRequire(program, output, source, imported, local) : ensureNamedImport(program, output, source, imported, local)}(${options.imagesBinding && options.imagesBinding !== "IMAGES" ? `{ binding: ${JSON.stringify(options.imagesBinding)} }` : ""})`;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
const cloudflareLocal = (commonJs ? findRequiredBinding(program, "@cloudflare/vite-plugin", "cloudflare") : findImportedBinding(program, "@cloudflare/vite-plugin", "cloudflare")) ?? allocateBinding(bindings, "cloudflare");
|
|
968
|
+
const cloudflareBinding = commonJs ? ensureNamedRequire(program, output, "@cloudflare/vite-plugin", "cloudflare", cloudflareLocal) : ensureNamedImport(program, output, "@cloudflare/vite-plugin", "cloudflare", cloudflareLocal);
|
|
969
|
+
ensurePlugins(output, config, [{
|
|
970
|
+
expression: existingVinextCall ? `${vinextBinding}()` : options.cache ? vinextExpression(cacheOptions, vinextBinding, imageOptimizerExpression?.slice(0, imageOptimizerExpression.indexOf("(")) || "imagesOptimizer", options.imagesBinding) : `${vinextBinding}()`,
|
|
971
|
+
binding: vinextBinding
|
|
972
|
+
}, {
|
|
973
|
+
expression: cloudflarePluginExpression(options.isAppRouter, cloudflareBinding),
|
|
974
|
+
binding: cloudflareBinding
|
|
975
|
+
}], code);
|
|
976
|
+
if (existingVinextCall) if (existingVinextCall.arguments.length === 0 && (cacheAdditions.length > 0 || imageOptimizerExpression)) {
|
|
977
|
+
const properties = [];
|
|
978
|
+
if (cacheAdditions.length > 0) properties.push(`cache: { ${cacheAdditions.map(({ name, expression }) => `${name}: ${expression}`).join(", ")} }`);
|
|
979
|
+
if (imageOptimizerExpression) properties.push(`images: { optimizer: ${imageOptimizerExpression} }`);
|
|
980
|
+
const plugins = findProperty(config, "plugins");
|
|
981
|
+
const closingIndent = `${plugins ? code.slice(0, plugins.start).split("\n").at(-1)?.match(/^\s*/)?.[0] ?? "" : ""} `;
|
|
982
|
+
const propertyEntryIndent = `${closingIndent} `;
|
|
983
|
+
output.appendLeft(existingVinextCall.end - 1, `{\n${propertyEntryIndent}${properties.join(`,\n${propertyEntryIndent}`)},\n${closingIndent}}`);
|
|
984
|
+
} else {
|
|
985
|
+
ensureVinextCache(output, config, vinextBinding, cacheAdditions, code);
|
|
986
|
+
ensureVinextImageOptimizer(output, config, vinextBinding, imageOptimizerExpression, code);
|
|
987
|
+
}
|
|
988
|
+
if (options.nativeModulesToStub.length > 0) {
|
|
989
|
+
const pathLocal = (commonJs ? findRequiredBinding(program, "node:path", "default") : program.body.filter((statement) => statement.type === "ImportDeclaration").find((statement) => statement.source.value === "node:path")?.specifiers.find((specifier) => specifier.type === "ImportDefaultSpecifier")?.local.name) ?? allocateBinding(bindings, "path");
|
|
990
|
+
const pathBinding = commonJs ? ensureDefaultRequire(program, output, "node:path", pathLocal) : ensureDefaultImport(program, output, "node:path", pathLocal);
|
|
991
|
+
ensureNativeAliases(output, config, options.nativeModulesToStub, pathBinding, code);
|
|
992
|
+
}
|
|
993
|
+
return output.toString();
|
|
994
|
+
}
|
|
995
|
+
function usesCommonJsViteConfig(filePath, code) {
|
|
996
|
+
if (/\.(?:cjs|cts)$/.test(filePath)) return true;
|
|
997
|
+
return parseViteConfig(filePath, code).body.some((statement) => statement.type === "ExpressionStatement" && statement.expression.type === "AssignmentExpression" && statement.expression.left.type === "MemberExpression" && statement.expression.left.object.type === "Identifier" && statement.expression.left.object.name === "module" && statement.expression.left.property.type === "Identifier" && statement.expression.left.property.name === "exports");
|
|
998
|
+
}
|
|
999
|
+
//#endregion
|
|
1000
|
+
export { generateAppRouterViteConfig, generatePagesRouterViteConfig, generatePagesRouterWorkerEntry, generateWranglerConfig, getWranglerImagesBinding, setupCloudflarePlatform, updateViteConfigForCloudflare, updateWranglerConfigForCloudflare, usesCommonJsViteConfig, validateCloudflarePlatformSetup };
|