vinext 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +1 -1
  2. package/dist/build/output-root-externals.d.ts +21 -0
  3. package/dist/build/output-root-externals.js +40 -0
  4. package/dist/build/prerender-paths.js +1 -1
  5. package/dist/build/prerender.js +1 -1
  6. package/dist/check.js +5 -6
  7. package/dist/cli-dev-config.js +3 -3
  8. package/dist/cli.js +1 -1
  9. package/dist/config/config-matchers.js +12 -7
  10. package/dist/index.js +113 -94
  11. package/dist/init.d.ts +1 -1
  12. package/dist/init.js +86 -6
  13. package/dist/plugins/ast-utils.d.ts +53 -2
  14. package/dist/plugins/ast-utils.js +90 -2
  15. package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
  16. package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
  17. package/dist/plugins/commonjs-esm-facade.js +2 -2
  18. package/dist/plugins/css-module-imports.js +13 -20
  19. package/dist/plugins/dynamic-preload-metadata.js +2 -3
  20. package/dist/plugins/extensionless-dynamic-import.js +2 -3
  21. package/dist/plugins/fonts.d.ts +1 -1
  22. package/dist/plugins/fonts.js +3 -3
  23. package/dist/plugins/ignore-dynamic-requests.js +2 -3
  24. package/dist/plugins/import-meta-url.js +3 -4
  25. package/dist/plugins/instrumentation-client.js +3 -2
  26. package/dist/plugins/middleware-export-validation.js +2 -3
  27. package/dist/plugins/og-harfbuzz.js +2 -3
  28. package/dist/plugins/optimize-imports.js +3 -4
  29. package/dist/plugins/pages-node-externals.js +2 -3
  30. package/dist/plugins/remove-console.js +2 -18
  31. package/dist/plugins/require-condition-resolution.js +3 -3
  32. package/dist/plugins/require-context.js +2 -3
  33. package/dist/plugins/strip-server-exports.js +3 -4
  34. package/dist/plugins/styled-jsx.js +2 -3
  35. package/dist/plugins/use-cache-callable.d.ts +21 -2
  36. package/dist/plugins/use-cache-callable.js +76 -33
  37. package/dist/plugins/worker-image-imports.js +30 -33
  38. package/dist/plugins/worker-use-server.js +38 -14
  39. package/dist/server/api-handler.js +1 -1
  40. package/dist/server/app-action-forwarding.js +2 -2
  41. package/dist/server/app-browser-entry.js +3 -3
  42. package/dist/server/app-browser-interception-context.js +1 -1
  43. package/dist/server/app-browser-prefetch-response.js +2 -2
  44. package/dist/server/app-browser-state.js +1 -1
  45. package/dist/server/app-browser-supplemental-refresh.js +1 -1
  46. package/dist/server/app-optimistic-routing.js +1 -1
  47. package/dist/server/app-page-dispatch.js +1 -1
  48. package/dist/server/app-page-render.js +1 -1
  49. package/dist/server/app-pages-bridge.js +1 -1
  50. package/dist/server/app-prerender-endpoints.js +1 -1
  51. package/dist/server/app-render-abort-error.d.ts +19 -0
  52. package/dist/server/app-render-abort-error.js +31 -1
  53. package/dist/server/app-request-stage-independent-entry.js +5 -5
  54. package/dist/server/app-response-stage-entry.js +2 -2
  55. package/dist/server/app-route-handler-runtime.js +1 -1
  56. package/dist/server/app-router-entry.js +5 -5
  57. package/dist/server/app-rsc-cache-busting.d.ts +16 -0
  58. package/dist/server/app-rsc-cache-busting.js +29 -12
  59. package/dist/server/app-rsc-handler.js +5 -5
  60. package/dist/server/app-rsc-request-normalization.js +3 -3
  61. package/dist/server/app-rsc-response-stage.js +2 -2
  62. package/dist/server/app-server-action-execution.js +1 -1
  63. package/dist/server/app-ssr-entry.js +5 -4
  64. package/dist/server/app-visited-response-cache.js +2 -2
  65. package/dist/server/config-headers.js +1 -1
  66. package/dist/server/defer-until-stream-consumed.d.ts +2 -1
  67. package/dist/server/defer-until-stream-consumed.js +3 -1
  68. package/dist/server/dev-server.js +1 -1
  69. package/dist/server/image-optimization.js +1 -0
  70. package/dist/server/metadata-route-response.js +2 -2
  71. package/dist/server/middleware-path-to-regexp.js +1 -1
  72. package/dist/server/middleware-runtime.js +5 -5
  73. package/dist/server/navigation-planner.js +1 -1
  74. package/dist/server/open-redirect.d.ts +11 -4
  75. package/dist/server/open-redirect.js +11 -4
  76. package/dist/server/pages-api-route.js +2 -2
  77. package/dist/server/pages-data-route.js +2 -1
  78. package/dist/server/pages-page-data.js +1 -1
  79. package/dist/server/pages-page-handler.js +1 -1
  80. package/dist/server/pages-page-response.js +1 -1
  81. package/dist/server/pages-request-pipeline.js +5 -5
  82. package/dist/server/pages-request-stage-entry.js +4 -4
  83. package/dist/server/pages-revalidate.js +2 -2
  84. package/dist/server/pages-router-entry.js +1 -1
  85. package/dist/server/prod-server.js +5 -5
  86. package/dist/server/rsc-stream-hints.d.ts +1 -1
  87. package/dist/server/rsc-stream-hints.js +21 -3
  88. package/dist/server/worker-image-optimization.js +19 -6
  89. package/dist/shims/head.js +1 -1
  90. package/dist/shims/headers.js +2 -2
  91. package/dist/shims/internal/hybrid-client-route-owner-direct.js +1 -1
  92. package/dist/shims/internal/hybrid-client-route-owner.js +1 -0
  93. package/dist/shims/link-prefetch.js +5 -4
  94. package/dist/shims/link.js +2 -2
  95. package/dist/shims/navigation.js +4 -5
  96. package/dist/shims/router.js +9 -12
  97. package/dist/shims/server.d.ts +11 -4
  98. package/dist/shims/server.js +41 -5
  99. package/dist/shims/url-utils.d.ts +7 -0
  100. package/dist/shims/url-utils.js +12 -3
  101. package/dist/utils/domain-locale.js +1 -1
  102. package/dist/utils/plugin-rsc-version.d.ts +2 -0
  103. package/dist/utils/plugin-rsc-version.js +28 -2
  104. package/dist/utils/react-version.d.ts +7 -0
  105. package/dist/utils/react-version.js +39 -16
  106. package/package.json +6 -6
  107. package/dist/build/nitro-route-rules.d.ts +0 -47
  108. package/dist/build/nitro-route-rules.js +0 -79
  109. package/dist/plugins/cache-flight-codec.d.ts +0 -19
  110. package/dist/plugins/cache-flight-codec.js +0 -68
@@ -1,12 +1,11 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { DYNAMIC_IMPORT_PRESCAN, SCRIPT_MODULE_ID_RE, collectBindingNames, forEachAstChild, isIdentifierNamed, mayContainDynamicImport, scriptParserLanguage, stringLiteralValue, unwrapExpression } from "./ast-utils.js";
2
+ import { DYNAMIC_IMPORT_PRESCAN, SCRIPT_MODULE_ID_RE, collectBindingNames, forEachAstChild, isIdentifierNamed, mayContainDynamicImport, parseProgram, scriptParserLanguage, stringLiteralValue, unwrapExpression } from "./ast-utils.js";
3
3
  import { magicStringTransformResult, omitUnusedBuildSourcemap } from "./transform-result.js";
4
4
  import { stripViteModuleQuery } from "../utils/path.js";
5
5
  import { isForeignNodeModule } from "../utils/package-name.js";
6
6
  import { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, hasAstBinding, isFunctionNode } from "./ast-scope.js";
7
7
  import { createTransformCache } from "./transform-cache.js";
8
8
  import { fileURLToPath } from "node:url";
9
- import { parseAst } from "vite";
10
9
  import MagicString from "magic-string";
11
10
  //#region src/plugins/ignore-dynamic-requests.ts
12
11
  const DYNAMIC_REQUEST_ERROR = "Cannot find module as expression is too dynamic";
@@ -411,7 +410,7 @@ function transformVeryDynamicRequests(code, id) {
411
410
  const lang = scriptParserLanguage(id) ?? "js";
412
411
  let ast;
413
412
  try {
414
- ast = parseAst(code, { lang });
413
+ ast = parseProgram(code, { lang });
415
414
  } catch {
416
415
  return null;
417
416
  }
@@ -1,11 +1,10 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { SCRIPT_MODULE_ID_RE, collectBindingNames, forEachAstChild, isIdentifierNamed, scriptParserLanguage } from "./ast-utils.js";
2
+ import { SCRIPT_MODULE_ID_RE, collectBindingNames, forEachAstChild, isIdentifierNamed, parseProgram, scriptParserLanguage } from "./ast-utils.js";
3
3
  import { magicStringTransformResult, omitUnusedBuildSourcemap } from "./transform-result.js";
4
4
  import { canonicalizeFilePath, isPathInsideOrEqual, stripViteModuleQuery } from "../utils/path.js";
5
5
  import { VIRTUAL_MODULE_ID_RE } from "../utils/virtual-module.js";
6
6
  import fs from "node:fs";
7
7
  import { fileURLToPath, pathToFileURL } from "node:url";
8
- import { parseAst } from "vite";
9
8
  import { randomUUID } from "node:crypto";
10
9
  import MagicString from "magic-string";
11
10
  //#region src/plugins/import-meta-url.ts
@@ -254,14 +253,14 @@ function finalizeEmittedModuleIdentity(code, emittedIdentitySentinels, fileName)
254
253
  function rewriteModuleIdentity(code, options) {
255
254
  let ast;
256
255
  try {
257
- ast = parseAst(code, {
256
+ ast = parseProgram(code, {
258
257
  lang: scriptParserLanguage(options.id) ?? "jsx",
259
258
  sourceType: options.cjsGlobalInitializers ? "commonjs" : void 0
260
259
  });
261
260
  } catch {
262
261
  if (!options.cjsGlobalInitializers) return null;
263
262
  try {
264
- ast = parseAst(code, { lang: scriptParserLanguage(options.id) ?? "jsx" });
263
+ ast = parseProgram(code, { lang: scriptParserLanguage(options.id) ?? "jsx" });
265
264
  } catch {
266
265
  return null;
267
266
  }
@@ -1,10 +1,11 @@
1
+ import { parseProgram } from "./ast-utils.js";
1
2
  import { magicStringTransformResult } from "./transform-result.js";
2
3
  import { stripViteModuleQuery } from "../utils/path.js";
3
- import { normalizePath, parseAst } from "vite";
4
+ import { normalizePath } from "vite";
4
5
  import MagicString from "magic-string";
5
6
  //#region src/plugins/instrumentation-client.ts
6
7
  function insertionPositionAfterDirectives(code) {
7
- const ast = parseAst(code);
8
+ const ast = parseProgram(code);
8
9
  let insertPos = 0;
9
10
  for (const node of ast.body) {
10
11
  if (node.type !== "ExpressionStatement" || node.directive === void 0) break;
@@ -1,9 +1,8 @@
1
- import { getAstName, scriptParserLanguage } from "./ast-utils.js";
1
+ import { getAstName, parseProgram, scriptParserLanguage } from "./ast-utils.js";
2
2
  import { createMiddlewareMissingExportError } from "../server/middleware-runtime.js";
3
- import { parseAst } from "vite";
4
3
  //#region src/plugins/middleware-export-validation.ts
5
4
  function hasValidMiddlewareModuleExport(source, id, isProxy) {
6
- const ast = parseAst(source, { lang: scriptParserLanguage(id) ?? "jsx" });
5
+ const ast = parseProgram(source, { lang: scriptParserLanguage(id) ?? "jsx" });
7
6
  const expectedExport = isProxy ? "proxy" : "middleware";
8
7
  for (const statement of ast.body) {
9
8
  if (statement.type === "ExportDefaultDeclaration") return true;
@@ -1,11 +1,10 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { forEachAstChild, getAstName, staticStringValue } from "./ast-utils.js";
2
+ import { forEachAstChild, getAstName, parseProgram, staticStringValue } from "./ast-utils.js";
3
3
  import { magicStringTransformResult, omitUnusedBuildSourcemap } from "./transform-result.js";
4
4
  import { isFunctionNode } from "./ast-scope.js";
5
5
  import { createTransformCache } from "./transform-cache.js";
6
6
  import { createRequire } from "node:module";
7
7
  import fs from "node:fs";
8
- import { parseAst } from "vite";
9
8
  import MagicString from "magic-string";
10
9
  //#region src/plugins/og-harfbuzz.ts
11
10
  /**
@@ -220,7 +219,7 @@ function patchHarfbuzzBundle(code, id, isBuild, callbackModuleDir, error) {
220
219
  const ogEntry = toSlash(id.split("?", 1)[0]);
221
220
  const isWorkerd = ogEntry.endsWith("/index.edge.js");
222
221
  const fail = (reason) => error(`[vinext] Unsupported @vercel/og HarfBuzz bundle in ${ogEntry}: ${reason}. vinext must be updated to support this @vercel/og version.`);
223
- const sites = findHarfbuzzPatchSites(parseAst(code, { lang: "js" }));
222
+ const sites = findHarfbuzzPatchSites(parseProgram(code, { lang: "js" }));
224
223
  if (typeof sites === "string") return fail(sites);
225
224
  const output = new MagicString(code);
226
225
  for (const flag of sites.environmentFlags) output.overwrite(flag.start, flag.end, "false");
@@ -1,11 +1,10 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { escapeRegExp } from "../utils/regex.js";
3
- import { collectBindingNames, getAstName } from "./ast-utils.js";
3
+ import { collectBindingNames, getAstName, parseProgram } from "./ast-utils.js";
4
4
  import { magicStringTransformResult } from "./transform-result.js";
5
5
  import { VIRTUAL_MODULE_ID_RE } from "../utils/virtual-module.js";
6
6
  import { createRequire } from "node:module";
7
7
  import fsp from "node:fs/promises";
8
- import { parseAst } from "vite";
9
8
  import MagicString from "magic-string";
10
9
  //#region src/plugins/optimize-imports.ts
11
10
  /**
@@ -234,7 +233,7 @@ async function buildExportMapFromFile(filePath, readFile, cache, visited, initia
234
233
  if (!content) return /* @__PURE__ */ new Map();
235
234
  let ast;
236
235
  try {
237
- ast = parseAst(content);
236
+ ast = parseProgram(content);
238
237
  } catch {
239
238
  return /* @__PURE__ */ new Map();
240
239
  }
@@ -456,7 +455,7 @@ function createOptimizeImportsPlugin(getNextConfig, getRoot) {
456
455
  if (!hasOptimizedImportSource(code)) return null;
457
456
  let ast;
458
457
  try {
459
- ast = parseAst(code);
458
+ ast = parseProgram(code);
460
459
  } catch {
461
460
  return null;
462
461
  }
@@ -1,10 +1,9 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { SCRIPT_MODULE_ID_RE, mayContainDynamicImport, scriptParserLanguage, staticStringValue, walkAst } from "./ast-utils.js";
2
+ import { SCRIPT_MODULE_ID_RE, mayContainDynamicImport, parseProgram, scriptParserLanguage, staticStringValue, walkAst } from "./ast-utils.js";
3
3
  import { canonicalizeFilePath, isPathInsideOrEqual, stripViteModuleQuery } from "../utils/path.js";
4
4
  import { isServerEnvironment } from "./environment.js";
5
5
  import { packageNameFromSpecifier } from "../utils/package-name.js";
6
6
  import fs from "node:fs";
7
- import { parseAst } from "vite";
8
7
  //#region src/plugins/pages-node-externals.ts
9
8
  const FRAMEWORK_PACKAGES = /* @__PURE__ */ new Set([
10
9
  "@vitejs/plugin-react",
@@ -47,7 +46,7 @@ function matchesAlias(id, aliases) {
47
46
  function moduleDependencySpecifiers(code, id) {
48
47
  let ast;
49
48
  try {
50
- ast = parseAst(code, { lang: scriptParserLanguage(id) ?? "jsx" });
49
+ ast = parseProgram(code, { lang: scriptParserLanguage(id) ?? "jsx" });
51
50
  } catch {
52
51
  return [];
53
52
  }
@@ -1,23 +1,7 @@
1
+ import { parseProgram } from "./ast-utils.js";
1
2
  import { magicStringTransformResult } from "./transform-result.js";
2
- import { parseAst } from "vite";
3
3
  import MagicString from "magic-string";
4
4
  //#region src/plugins/remove-console.ts
5
- /**
6
- * Strip `console.*` calls from client bundle code.
7
- *
8
- * Mirrors Next.js's SWC `remove_console` transform:
9
- * - Strips all `console.<method>()` calls regardless of context (top-level,
10
- * JSX expressions, function arguments, return values, ternary branches)
11
- * - Replaces removed calls with `void 0` to keep the AST valid in every
12
- * position a CallExpression can appear
13
- * - Respects `exclude: ["error"]` to preserve certain methods (case-insensitive)
14
- * - Preserves calls when `console` is shadowed (local variable, function
15
- * parameter, or destructured binding)
16
- * - Preserves computed property access `console[prop]()`
17
- *
18
- * Uses Vite's `parseAst` (OXC/acorn) for parsing and `MagicString` for
19
- * surgical source replacement. Returns `null` when no changes are made.
20
- */
21
5
  const SCOPE_ENTERING_TYPES = /* @__PURE__ */ new Set([
22
6
  "FunctionDeclaration",
23
7
  "FunctionExpression",
@@ -36,7 +20,7 @@ function removeConsoleCalls(code, config) {
36
20
  if (!code.match(/\bconsole\b/)) return null;
37
21
  let ast;
38
22
  try {
39
- ast = parseAst(code);
23
+ ast = parseProgram(code);
40
24
  } catch {
41
25
  return null;
42
26
  }
@@ -1,10 +1,10 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { SCRIPT_MODULE_ID_RE, collectBindingNames, forEachAstChild, isIdentifierNamed, scriptParserLanguage, staticStringValue, unwrapExpression } from "./ast-utils.js";
2
+ import { SCRIPT_MODULE_ID_RE, collectBindingNames, forEachAstChild, isIdentifierNamed, parseProgram, scriptParserLanguage, staticStringValue, unwrapExpression } from "./ast-utils.js";
3
3
  import { magicStringTransformResult } from "./transform-result.js";
4
4
  import { stripViteModuleQuery } from "../utils/path.js";
5
5
  import { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, createAstScope, hasAstBinding, isFunctionNode } from "./ast-scope.js";
6
6
  import { readFile } from "node:fs/promises";
7
- import { createIdResolver, parseAst } from "vite";
7
+ import { createIdResolver } from "vite";
8
8
  import MagicString from "magic-string";
9
9
  //#region src/plugins/require-condition-resolution.ts
10
10
  const LITERAL_REQUIRE_RE = /\brequire\s*\(/;
@@ -30,7 +30,7 @@ function isPackageSpecifier(specifier) {
30
30
  function collectLiteralRequires(code, id) {
31
31
  let ast;
32
32
  try {
33
- ast = parseAst(code, { lang: scriptParserLanguage(id) ?? "jsx" });
33
+ ast = parseProgram(code, { lang: scriptParserLanguage(id) ?? "jsx" });
34
34
  } catch {
35
35
  return [];
36
36
  }
@@ -1,9 +1,8 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { SCRIPT_MODULE_ID_RE, booleanLiteralValue, scriptParserLanguage, stringLiteralValue, walkAst } from "./ast-utils.js";
2
+ import { SCRIPT_MODULE_ID_RE, booleanLiteralValue, parseProgram, scriptParserLanguage, stringLiteralValue, walkAst } from "./ast-utils.js";
3
3
  import { magicStringTransformResult } from "./transform-result.js";
4
4
  import { stripViteModuleQuery } from "../utils/path.js";
5
5
  import { readdir, realpath, stat } from "node:fs/promises";
6
- import { parseAst } from "vite";
7
6
  import MagicString from "magic-string";
8
7
  //#region src/plugins/require-context.ts
9
8
  function createRequireContextPlugin() {
@@ -82,7 +81,7 @@ async function transformRequireContext(code, id) {
82
81
  const lang = scriptParserLanguage(id);
83
82
  let ast;
84
83
  try {
85
- ast = parseAst(code, { lang });
84
+ ast = parseProgram(code, { lang });
86
85
  } catch {
87
86
  return null;
88
87
  }
@@ -1,7 +1,6 @@
1
- import { getAstName } from "./ast-utils.js";
1
+ import { getAstName, parseProgram } from "./ast-utils.js";
2
2
  import { magicStringTransformResult } from "./transform-result.js";
3
3
  import { findAstBindingScope, walkAstScopes } from "./ast-scope.js";
4
- import { parseAst } from "vite";
5
4
  import MagicString from "magic-string";
6
5
  //#region src/plugins/strip-server-exports.ts
7
6
  const SERVER_EXPORTS = /* @__PURE__ */ new Set([
@@ -225,7 +224,7 @@ function validatePageExports(code) {
225
224
  if (!hasExportAllCandidate(code)) return;
226
225
  let ast;
227
226
  try {
228
- ast = parseAst(code);
227
+ ast = parseProgram(code);
229
228
  } catch {
230
229
  return;
231
230
  }
@@ -243,7 +242,7 @@ function stripServerExports(code) {
243
242
  if (!hasServerExportCandidate(code) && !hasExportAllCandidate(code)) return null;
244
243
  let ast;
245
244
  try {
246
- ast = parseAst(code);
245
+ ast = parseProgram(code);
247
246
  } catch {
248
247
  return null;
249
248
  }
@@ -1,9 +1,8 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { SCRIPT_MODULE_ID_RE, walkAst } from "./ast-utils.js";
2
+ import { SCRIPT_MODULE_ID_RE, parseProgram, walkAst } from "./ast-utils.js";
3
3
  import { NODE_MODULES_PATH_RE, stripViteModuleQuery } from "../utils/path.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { pathToFileURL } from "node:url";
6
- import { parseAst } from "vite";
7
6
  //#region src/plugins/styled-jsx.ts
8
7
  const STYLED_JSX_IMPORT_RE = /^styled-jsx(?:\/.*)?$/;
9
8
  const STYLED_JSX_SOURCE_RE = /(?:<style\b|from\s+["']styled-jsx\/css["']|require\s*\(\s*["']styled-jsx\/css["']\s*\))/;
@@ -14,7 +13,7 @@ function hasStyledJsxTag(source, id) {
14
13
  const lang = extension === ".ts" || extension === ".mts" || extension === ".cts" ? "ts" : "tsx";
15
14
  let ast;
16
15
  try {
17
- ast = parseAst(source, { lang });
16
+ ast = parseProgram(source, { lang });
18
17
  } catch {
19
18
  return false;
20
19
  }
@@ -1,9 +1,28 @@
1
1
  import { Plugin } from "vite";
2
2
  //#region src/plugins/use-cache-callable.d.ts
3
+ type RscTransforms = typeof import("@vitejs/plugin-rsc/transforms");
3
4
  type Options = {
4
5
  projectRoot: string;
5
6
  cacheRuntime: string;
6
7
  allowMissingRsc?: boolean;
7
8
  };
8
- export declare function createUseCacheCallablePlugin(options: Options): Promise<Plugin>;
9
- //#endregion
9
+ type BrowserTransformOptions = {
10
+ environmentName: string;
11
+ reference: {
12
+ referenceKey: string;
13
+ };
14
+ secureExportName: (name: string) => string;
15
+ createReference: (referenceId: string, name: string) => string;
16
+ };
17
+ type BrowserTransformOutput = NonNullable<ReturnType<RscTransforms["transformDirectiveProxyExport"]>>;
18
+ export type UseCacheCallableApi = {
19
+ /**
20
+ * Compile a browser Web Worker module like the client environment does,
21
+ * with the same reference ids, without claiming references.
22
+ */
23
+ transformWorkerModule(code: string, id: string, createReference: BrowserTransformOptions["createReference"]): Promise<BrowserTransformOutput["output"] | undefined>;
24
+ };
25
+ declare const PLUGIN_NAME = "vinext:server-function-directives";
26
+ export declare function createUseCacheCallablePlugin(options: Options): Promise<Plugin<UseCacheCallableApi>>;
27
+ //#endregion
28
+ export { PLUGIN_NAME as USE_CACHE_PLUGIN_NAME };
@@ -1,9 +1,9 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
+ import { parseProgramAsync } from "./ast-utils.js";
2
3
  import { magicStringTransformResult } from "./transform-result.js";
3
4
  import { NODE_MODULES_PATH_RE } from "../utils/path.js";
4
5
  import { createRequire } from "node:module";
5
6
  import { pathToFileURL } from "node:url";
6
- import { parseAstAsync } from "vite";
7
7
  import { createHmac, randomBytes } from "node:crypto";
8
8
  //#region src/plugins/use-cache-callable.ts
9
9
  const PLUGIN_NAME = "vinext:server-function-directives";
@@ -79,6 +79,41 @@ function getCacheWrapperOptions(meta) {
79
79
  ...captureCount === 0 ? {} : { captureCount }
80
80
  };
81
81
  }
82
+ /**
83
+ * Compile a module for a browser graph: module-level cache functions become
84
+ * server references, and inline ones are rejected as in Next.js Client
85
+ * Components. Returns undefined when the module is unchanged.
86
+ */
87
+ function transformBrowserModule(transforms, code, ast, id, moduleDirective, options) {
88
+ if (!moduleDirective) {
89
+ transforms.transformHoistInlineDirective(code, ast, {
90
+ directive: USE_CACHE_DIRECTIVE_CANDIDATE,
91
+ rejectNonAsyncFunction: true,
92
+ runtime: (_value, _name, meta) => {
93
+ matchUseCacheDirective(meta.directiveMatch[0]);
94
+ throw new Error(`It is not allowed to define inline "use cache" annotated functions in Client Components. Export them from a separate file with a module-level "use cache" or "use server" directive, or pass them down through props from a Server Component. (${options.environmentName}: ${id})`);
95
+ }
96
+ });
97
+ return;
98
+ }
99
+ const useServerExportNames = getFunctionDirectiveExportNames(transforms, ast, "use server");
100
+ const referenceName = (name) => useServerExportNames.has(name) ? name : options.secureExportName(name);
101
+ const result = transforms.transformDirectiveProxyExport(ast, {
102
+ code,
103
+ directive: moduleDirective,
104
+ filter: (name, meta) => {
105
+ if (!shouldTransformModuleExport(name, id, meta)) return false;
106
+ validateModuleExport(transforms, meta);
107
+ return true;
108
+ },
109
+ runtime: (name) => options.createReference(`${options.reference.referenceKey}#${referenceName(name)}`, name)
110
+ });
111
+ if (!result?.output.hasChanged()) return;
112
+ return {
113
+ output: result.output,
114
+ referenceNames: result.exportNames.map(referenceName)
115
+ };
116
+ }
82
117
  async function createUseCacheCallablePlugin(options) {
83
118
  const rscModulePath = resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc");
84
119
  const transformsPath = resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/transforms");
@@ -86,8 +121,39 @@ async function createUseCacheCallablePlugin(options) {
86
121
  const transforms = await import(pathToFileURL(transformsPath).href);
87
122
  const referenceSecret = randomBytes(32);
88
123
  let manager;
124
+ async function analyzeModule(manager, code, id) {
125
+ const ast = await parseProgramAsync(code);
126
+ const moduleDirective = findModuleUseCacheDirective(ast);
127
+ const useServerBoundary = transforms.hasDirective(ast.body, "use server");
128
+ if (moduleDirective && useServerBoundary) throw new Error(`A module cannot contain both ${JSON.stringify(moduleDirective)} and "use server" directives.`);
129
+ const reference = manager.serverReferences.resolve(id, "rsc");
130
+ const relativeImportId = manager.toRelativeId(reference.importId);
131
+ const secureExportName = (name) => `$$vinext_cache_${createHmac("sha256", referenceSecret).update(relativeImportId).update("\0").update(name).digest("hex")}`;
132
+ return {
133
+ ast,
134
+ moduleDirective,
135
+ useServerBoundary,
136
+ reference,
137
+ secureExportName
138
+ };
139
+ }
89
140
  return {
90
141
  name: PLUGIN_NAME,
142
+ api: { async transformWorkerModule(code, id, createReference) {
143
+ if (!code.includes("use cache")) return;
144
+ if (!manager) {
145
+ if (findModuleUseCacheDirective(await parseProgramAsync(code))) throw new Error("vinext: cannot compile a \"use cache\" module in a Web Worker without @vitejs/plugin-rsc.");
146
+ return;
147
+ }
148
+ const { ast, moduleDirective, useServerBoundary, reference, secureExportName } = await analyzeModule(manager, code, id);
149
+ if (useServerBoundary) return;
150
+ return transformBrowserModule(transforms, code, ast, id, moduleDirective, {
151
+ environmentName: "worker",
152
+ reference,
153
+ secureExportName,
154
+ createReference
155
+ })?.output;
156
+ } },
91
157
  configResolved(config) {
92
158
  const pluginApi = rscModule.getPluginApi(config);
93
159
  const hasRscPlugin = config.plugins.some((plugin) => plugin.name === "rsc");
@@ -111,48 +177,25 @@ async function createUseCacheCallablePlugin(options) {
111
177
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
112
178
  return;
113
179
  }
114
- const ast = await parseAstAsync(code);
115
- const moduleDirective = findModuleUseCacheDirective(ast);
116
- const useServerBoundary = transforms.hasDirective(ast.body, "use server");
117
- if (moduleDirective && useServerBoundary) throw new Error(`A module cannot contain both ${JSON.stringify(moduleDirective)} and "use server" directives.`);
118
- const reference = manager.serverReferences.resolve(id, "rsc");
119
- const relativeImportId = manager.toRelativeId(reference.importId);
120
- const secureExportName = (name) => `$$vinext_cache_${createHmac("sha256", referenceSecret).update(relativeImportId).update("\0").update(name).digest("hex")}`;
180
+ const { ast, moduleDirective, useServerBoundary, reference, secureExportName } = await analyzeModule(manager, code, id);
121
181
  if (!(this.environment.name === "rsc")) {
122
182
  if (useServerBoundary) {
123
183
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
124
184
  return;
125
185
  }
126
- if (!moduleDirective) {
127
- transforms.transformHoistInlineDirective(code, ast, {
128
- directive: USE_CACHE_DIRECTIVE_CANDIDATE,
129
- rejectNonAsyncFunction: true,
130
- runtime: (_value, _name, meta) => {
131
- matchUseCacheDirective(meta.directiveMatch[0]);
132
- throw new Error(`It is not allowed to define inline "use cache" annotated functions in Client Components. Export them from a separate file with a module-level "use cache" or "use server" directive, or pass them down through props from a Server Component. (${this.environment.name}: ${id})`);
133
- }
134
- });
135
- manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
136
- return;
137
- }
138
- const useServerExportNames = getFunctionDirectiveExportNames(transforms, ast, "use server");
139
- const result = transforms.transformDirectiveProxyExport(ast, {
140
- code,
141
- directive: moduleDirective,
142
- filter: (name, meta) => {
143
- if (!shouldTransformModuleExport(name, id, meta)) return false;
144
- validateModuleExport(transforms, meta);
145
- return true;
146
- },
147
- runtime: (name) => `$$ReactClient.createServerReference(${JSON.stringify(`${reference.referenceKey}#${useServerExportNames.has(name) ? name : secureExportName(name)}`)},$$ReactClient.callServer,undefined,${this.environment.mode === "dev" ? "$$ReactClient.findSourceMapURL" : "undefined"},${JSON.stringify(name)})`
186
+ const result = transformBrowserModule(transforms, code, ast, id, moduleDirective, {
187
+ environmentName: this.environment.name,
188
+ reference,
189
+ secureExportName,
190
+ createReference: (referenceId, name) => `$$ReactClient.createServerReference(${JSON.stringify(referenceId)},$$ReactClient.callServer,undefined,${this.environment.mode === "dev" ? "$$ReactClient.findSourceMapURL" : "undefined"},${JSON.stringify(name)})`
148
191
  });
149
- if (!result?.output.hasChanged()) {
192
+ if (!result) {
150
193
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
151
194
  return;
152
195
  }
153
196
  manager.serverReferences.replaceClaim(PLUGIN_NAME, id, {
154
197
  ...reference,
155
- exportNames: result.exportNames.map((name) => useServerExportNames.has(name) ? name : secureExportName(name))
198
+ exportNames: result.referenceNames
156
199
  });
157
200
  const runtimeEnvironment = this.environment.name === "client" ? "browser" : "ssr";
158
201
  result.output.prepend(`import * as $$ReactClient from "@vitejs/plugin-rsc/react/${runtimeEnvironment}";\n`);
@@ -223,4 +266,4 @@ async function createUseCacheCallablePlugin(options) {
223
266
  };
224
267
  }
225
268
  //#endregion
226
- export { createUseCacheCallablePlugin };
269
+ export { PLUGIN_NAME as USE_CACHE_PLUGIN_NAME, createUseCacheCallablePlugin };
@@ -1,10 +1,9 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { staticStringValue, walkAst } from "./ast-utils.js";
2
+ import { parseModuleRecord, quotedStringAt } from "./ast-utils.js";
3
3
  import { magicStringTransformResult } from "./transform-result.js";
4
4
  import { appendDeploymentIdQuery } from "../utils/deployment-id.js";
5
5
  import { NODE_MODULES_PATH_RE, stripViteModuleQuery } from "../utils/path.js";
6
6
  import fs from "node:fs";
7
- import { parseAst } from "vite";
8
7
  import MagicString from "magic-string";
9
8
  //#region src/plugins/worker-image-imports.ts
10
9
  const WORKER_IMAGE_METADATA_PREFIX = "\0vinext-worker-image-meta:";
@@ -70,24 +69,19 @@ function createWorkerImageImportsPlugin(options = {}) {
70
69
  },
71
70
  async handler(code, id) {
72
71
  const sourceId = toSlash(stripViteModuleQuery(id));
73
- let ast;
74
- try {
75
- ast = parseAst(code, { lang: /\.(?:[cm]?ts)$/.test(sourceId) ? "ts" : "tsx" });
76
- } catch {
77
- return null;
78
- }
72
+ const record = parseModuleRecord(code, /\.(?:[cm]?ts)$/.test(sourceId) ? "ts" : "tsx");
73
+ if (!record) return null;
79
74
  const output = new MagicString(code);
80
75
  const imageImports = [];
81
- walkAst(ast, (node) => {
82
- if (node.type === "ImportExpression" && node.source.type === "Literal" && typeof node.source.value === "string" && WORKER_IMAGE_EXTENSION_RE.test(node.source.value)) {
83
- imageImports.push({
84
- end: node.end,
85
- importPath: node.source.value,
86
- start: node.start
87
- });
88
- return false;
89
- }
90
- });
76
+ for (const dynamicImport of record.dynamicImports) {
77
+ const importPath = quotedStringAt(code, dynamicImport.moduleRequest);
78
+ if (importPath === null || !WORKER_IMAGE_EXTENSION_RE.test(importPath)) continue;
79
+ imageImports.push({
80
+ end: dynamicImport.end,
81
+ importPath,
82
+ start: dynamicImport.start
83
+ });
84
+ }
91
85
  let changed = false;
92
86
  for (const imageImport of imageImports) {
93
87
  const resolved = await this.resolve(imageImport.importPath, sourceId, { skipSelf: true });
@@ -107,23 +101,26 @@ function createWorkerImageImportsPlugin(options = {}) {
107
101
  if (!options.deploymentId) return null;
108
102
  const chunkSpecifiers = new Set([...chunk.imports, ...chunk.dynamicImports].map((target) => workerChunkSpecifier(chunk.fileName, target)));
109
103
  if (chunkSpecifiers.size === 0) return null;
110
- let ast;
111
- try {
112
- ast = parseAst(code);
113
- } catch {
114
- return null;
115
- }
104
+ const record = parseModuleRecord(code, "js");
105
+ if (!record) return null;
106
+ const sources = [
107
+ ...record.staticImports.map((statement) => statement.moduleRequest),
108
+ ...record.staticExports.flatMap((statement) => statement.entries[0]?.moduleRequest ?? []),
109
+ ...record.dynamicImports.map((dynamicImport) => {
110
+ const value = quotedStringAt(code, dynamicImport.moduleRequest);
111
+ return value === null ? [] : {
112
+ ...dynamicImport.moduleRequest,
113
+ value
114
+ };
115
+ })
116
+ ].flat();
116
117
  const output = new MagicString(code);
117
118
  let changed = false;
118
- walkAst(ast, (node) => {
119
- const source = node.type === "ImportExpression" || node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration" ? node.source : null;
120
- const specifier = staticStringValue(source);
121
- if (specifier !== null && chunkSpecifiers.has(specifier) && source) {
122
- output.overwrite(source.start, source.end, JSON.stringify(appendDeploymentIdQuery(specifier, options.deploymentId)));
123
- changed = true;
124
- return false;
125
- }
126
- });
119
+ for (const source of sources) {
120
+ if (!chunkSpecifiers.has(source.value)) continue;
121
+ output.overwrite(source.start, source.end, JSON.stringify(appendDeploymentIdQuery(source.value, options.deploymentId)));
122
+ changed = true;
123
+ }
127
124
  if (!changed) return null;
128
125
  return magicStringTransformResult(output);
129
126
  }