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
package/dist/index.js CHANGED
@@ -2,31 +2,32 @@ import path, { toSlash } from "./deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/
2
2
  import { detectPackageManager, formatMissingCloudflarePluginError, hasWranglerConfig } from "./utils/project.js";
3
3
  import { buildViteResolveExtensions, createValidFileMatcher, findFileWithExts, normalizeViteResolveExtensions } from "./routing/file-matcher.js";
4
4
  import { resolvePostcssStringPlugins } from "./plugins/postcss.js";
5
+ import { TRIVIA_PATTERN, hasImportAttributes, isTypeOnlyImport, parseModuleRecord, parseProgram } from "./plugins/ast-utils.js";
5
6
  import { claimViteCliBuildInvocation, getViteCliInvocation, isViteCliConfigFile } from "./utils/vite-cli-invocation.js";
6
7
  import { VINEXT_DEV_CLI_LIFECYCLE, applyDevServerDefaults, claimViteCliDevInvocation, createDevServerLifecyclePlugin, reserveViteCliDevInvocation } from "./cli-dev-config.js";
7
8
  import { isUnknownRecord } from "./utils/record.js";
8
- import { getReactUpgradeDeps } from "./utils/react-version.js";
9
+ import { getReactUpgradeDeps, reactUpgradeRequiredMessage } from "./utils/react-version.js";
9
10
  import { loadDotenv } from "./config/dotenv.js";
10
11
  import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
11
12
  import { VINEXT_MW_CTX_HEADER } from "./utils/protocol-headers.js";
12
13
  import { encodeMiddlewareRequestHeaders } from "./utils/middleware-request-headers.js";
14
+ import { hasBasePath, stripBasePath } from "./utils/base-path.js";
15
+ import { INTERNAL_HEADERS, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_TIMING_HEADER } from "./server/headers.js";
16
+ import { methodNotAllowedResponse, notFoundStaticAssetResponse } from "./server/http-error-responses.js";
17
+ import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./server/open-redirect.js";
18
+ import { canonicalizeRequestUrlPathname, filterInternalHeaders, normalizeTrailingSlash } from "./server/request-pipeline.js";
13
19
  import { matchesRewriteSource, proxyExternalRequest } from "./config/config-matchers.js";
14
20
  import { omitUnusedBuildSourcemap } from "./plugins/transform-result.js";
15
21
  import { assetPrefixPathname, isNextStaticPath, resolveAssetsDir } from "./utils/asset-prefix.js";
16
22
  import { RESOLVED_VIRTUAL_GOOGLE_FONTS, VIRTUAL_GOOGLE_FONTS, createGoogleFontsPlugin, createLocalFontsPlugin, generateGoogleFontsVirtualModule, parseStaticObjectLiteral } from "./plugins/fonts.js";
17
23
  import { VINEXT_NEXT_CONFIG_PLUGIN_PROPERTY, createRscCompatibilityId, findNextConfigPath, lightningCssFeatureNamesToMask, loadNextConfig, resolveNextConfig, resolveNextConfigInput } from "./config/next-config.js";
18
24
  import { normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict } from "./routing/utils.js";
19
- import { hasBasePath, stripBasePath } from "./utils/base-path.js";
20
25
  import { appRouteGraph, appRouter, invalidateAppRouteCache, matchAppRoute } from "./routing/app-router.js";
21
26
  import { generateRouteTypes } from "./typegen.js";
22
27
  import { apiRouter, invalidateRouteCache, matchRoute, pagesRouter } from "./routing/pages-router.js";
23
28
  import { resolveRuntimeEntryModule } from "./entries/runtime-entry-module.js";
24
- import { INTERNAL_HEADERS, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_TIMING_HEADER } from "./server/headers.js";
25
29
  import { normalizePath as normalizePath$1 } from "./server/normalize-path.js";
26
30
  import { validateMiddlewareMatcherPatterns } from "./server/middleware-matcher-pattern.js";
27
- import { methodNotAllowedResponse, notFoundStaticAssetResponse } from "./server/http-error-responses.js";
28
- import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./server/open-redirect.js";
29
- import { canonicalizeRequestUrlPathname, filterInternalHeaders, normalizeTrailingSlash } from "./server/request-pipeline.js";
30
31
  import { resolvePagesI18nRequest, stripI18nLocaleForApiRoute } from "./server/pages-i18n.js";
31
32
  import { encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./server/pages-data-route.js";
32
33
  import { findInstrumentationClientFile, findInstrumentationFile, runInstrumentation } from "./server/instrumentation.js";
@@ -43,6 +44,7 @@ import { handleApiRoute } from "./server/api-handler.js";
43
44
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, resolveDevImageRedirect } from "./server/image-optimization.js";
44
45
  import { CACHEABILITY_MANIFEST_MODULE, CACHEABILITY_REQUEST_PROJECTION_MODULE } from "./server/cacheability-manifest.js";
45
46
  import { PREGENERATED_CONCRETE_PATHS_MODULE } from "./server/pregenerated-concrete-paths.js";
47
+ import { rewriteOutputRootExternals } from "./build/output-root-externals.js";
46
48
  import { installSocketErrorBackstop } from "./server/socket-error-backstop.js";
47
49
  import { invalidateMetadataFileCache, scanMetadataFiles } from "./server/metadata-routes.js";
48
50
  import { shouldInvalidateAppRouteFile } from "./server/dev-route-files.js";
@@ -90,6 +92,7 @@ import { INSTRUMENTATION_CLIENT_EMPTY_MODULE, generateInstrumentationClientInjec
90
92
  import { isServerEnvironment } from "./plugins/environment.js";
91
93
  import { createMiddlewareServerOnlyPlugin } from "./plugins/middleware-server-only.js";
92
94
  import { createPagesNodeExternalsPlugin } from "./plugins/pages-node-externals.js";
95
+ import { createCloudflareDevModuleUrlsPlugin } from "./plugins/cloudflare-dev-module-urls.js";
93
96
  import { validateMiddlewareModuleExports } from "./plugins/middleware-export-validation.js";
94
97
  import { createOptimizeImportsPlugin } from "./plugins/optimize-imports.js";
95
98
  import { augmentSsrManifestFromBundle, relativeWithinRoot, tryRealpathSync } from "./build/ssr-manifest.js";
@@ -98,7 +101,6 @@ import { createTransformCache } from "./plugins/transform-cache.js";
98
101
  import { createOgHarfbuzzPlugin } from "./plugins/og-harfbuzz.js";
99
102
  import { createOgAssetsPlugin, createOgInlineFetchAssetsPlugin } from "./plugins/og-assets.js";
100
103
  import { createUseCacheCallablePlugin } from "./plugins/use-cache-callable.js";
101
- import { cacheFlightCodecPlugin } from "./plugins/cache-flight-codec.js";
102
104
  import { SSR_EXTERNAL_REACT_ENTRIES, VINEXT_OPTIMIZE_DEPS_EXCLUDE, mergeOptimizeDepsExclude } from "./plugins/rsc-client-shim-excludes.js";
103
105
  import { createServerExternalsManifestPlugin } from "./plugins/server-externals-manifest.js";
104
106
  import { createTransitiveExternalsPlugin } from "./plugins/transitive-externals.js";
@@ -126,14 +128,13 @@ import { hasMdxFiles, loadMdxEsmReader, pageExtensionsIncludeMdx } from "./utils
126
128
  import { publicFileRouteKey } from "./utils/public-file-path.js";
127
129
  import { scanPublicFileRoutes } from "./utils/public-routes.js";
128
130
  import { createIgnoreDynamicRequestsPlugin } from "./plugins/ignore-dynamic-requests.js";
129
- import { assertPluginRscVersion } from "./utils/plugin-rsc-version.js";
131
+ import { assertPluginRscVersion, needsAppReactServerDom } from "./utils/plugin-rsc-version.js";
130
132
  import { assertSupportedViteVersion, getDepOptimizeNodeEnvOptions, serializeViteDefine } from "./utils/vite-version.js";
131
133
  import { staticExportApp, staticExportPages } from "./build/static-export.js";
132
134
  import { createRequire } from "node:module";
133
135
  import fs from "node:fs";
134
136
  import { pathToFileURL } from "node:url";
135
- import { createIdResolver, createLogger, isRunnableDevEnvironment, parseAst, transformWithOxc } from "vite";
136
- import { execFileSync } from "node:child_process";
137
+ import { createIdResolver, createLogger, isRunnableDevEnvironment, transformWithOxc } from "vite";
137
138
  import { createHash, randomBytes } from "node:crypto";
138
139
  import MagicString from "magic-string";
139
140
  import commonjs from "vite-plugin-commonjs";
@@ -146,6 +147,7 @@ const PAGES_CLOUDFLARE_WORKER_OPTIMIZE_DEPS_INCLUDE = Object.freeze([
146
147
  "react/jsx-runtime",
147
148
  "react/jsx-dev-runtime"
148
149
  ]);
150
+ const PAGES_CLOUDFLARE_WORKER_OPTIONAL_OPTIMIZE_DEPS_INCLUDE = Object.freeze(["use-sync-external-store/with-selector", "vinext > @vitejs/plugin-rsc/react/rsc"]);
149
151
  const APP_CLIENT_OPTIONAL_OPTIMIZE_DEPS_INCLUDE = Object.freeze([
150
152
  "use-sync-external-store/shim",
151
153
  "use-sync-external-store/shim/index.js",
@@ -199,9 +201,13 @@ function createServerOnlyClientImportError(importer) {
199
201
  }
200
202
  function hasServerOnlyMarkerImport(code) {
201
203
  if (!code.includes("server-only")) return false;
204
+ const record = parseModuleRecord(code, "js");
205
+ if (!record) return false;
206
+ if (record.staticImports.some((statement) => statement.moduleRequest.value === "server-only")) return true;
207
+ if (!code.includes("require")) return false;
202
208
  let ast;
203
209
  try {
204
- ast = parseAst(code);
210
+ ast = parseProgram(code);
205
211
  } catch {
206
212
  return false;
207
213
  }
@@ -209,9 +215,6 @@ function hasServerOnlyMarkerImport(code) {
209
215
  if (!node) return false;
210
216
  if (Array.isArray(node)) return node.some((child) => walk(child));
211
217
  if (typeof node !== "object") return false;
212
- if (node.type === "ImportDeclaration") {
213
- if (node.source?.value === "server-only") return true;
214
- }
215
218
  if (node.type === "CallExpression") {
216
219
  const call = node;
217
220
  if (call.callee?.type === "Identifier" && call.callee.name === "require" && call.arguments?.[0]?.type === "Literal" && call.arguments[0].value === "server-only") return true;
@@ -300,24 +303,16 @@ function isolateMdxStylesheetImports(code) {
300
303
  }
301
304
  function isolateGlobalNotFoundStylesheetImports(code, id) {
302
305
  if (isMdxModuleId(id)) return isolateMdxStylesheetImports(code);
303
- let ast;
304
- try {
305
- ast = parseAst(code, { lang: parserLanguageForScript(id) });
306
- } catch {
307
- return null;
308
- }
306
+ const record = parseModuleRecord(code, parserLanguageForScript(id));
307
+ if (!record) return null;
309
308
  let output = null;
310
- for (const statement of ast.body) {
311
- if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
312
- if (statement.specifiers && statement.specifiers.length > 0) continue;
313
- if (statement.attributes && statement.attributes.length > 0) continue;
314
- const source = statement.source;
315
- const specifier = source?.value;
316
- if (typeof specifier !== "string" || !isStylesheetSpecifier(specifier)) continue;
317
- const range = source;
318
- if (typeof range.start !== "number" || typeof range.end !== "number") continue;
309
+ for (const statement of record.staticImports) {
310
+ if (statement.entries.length > 0 || isTypeOnlyImport(code, statement)) continue;
311
+ const source = statement.moduleRequest;
312
+ if (hasImportAttributes(code, source.end, statement.end)) continue;
313
+ if (!isStylesheetSpecifier(source.value)) continue;
319
314
  output ??= new MagicString(code);
320
- output.overwrite(range.start, range.end, JSON.stringify(specifier + GLOBAL_NOT_FOUND_CSS_QUERY));
315
+ output.overwrite(source.start, source.end, JSON.stringify(source.value + GLOBAL_NOT_FOUND_CSS_QUERY));
321
316
  }
322
317
  return output ? toMagicStringTransformResult(output) : null;
323
318
  }
@@ -330,8 +325,46 @@ function commonjsTransformFilter(id, transformProjectLocalCommonJs = false, tran
330
325
  if (transformBundledDependencies && isBundledCommonJsDependency(cleanId)) return true;
331
326
  if (/\.c[jt]s$/i.test(cleanId) && !cleanId.includes("node_modules")) return transformProjectLocalCommonJs;
332
327
  }
333
- function hasOnlyTypeSpecifiers(statement) {
334
- return statement.specifiers !== void 0 && statement.specifiers.length > 0 && statement.specifiers.every((specifier) => specifier.importKind === "type" || specifier.exportKind === "type");
328
+ const EMPTY_EXPORT_PRESCAN = new RegExp(String.raw`\bexport${TRIVIA_PATTERN}*\{${TRIVIA_PATTERN}*\}`);
329
+ /**
330
+ * Sources of a module's static imports and re-exports, in source order.
331
+ * Type-only declarations and declarations with import attributes are skipped.
332
+ */
333
+ function runtimeStaticDependencySpecifiers(code, record, lang) {
334
+ const dependencies = [];
335
+ for (const statement of record.staticImports) {
336
+ if (isTypeOnlyImport(code, statement)) continue;
337
+ if (hasImportAttributes(code, statement.moduleRequest.end, statement.end)) continue;
338
+ dependencies.push({
339
+ start: statement.start,
340
+ specifier: statement.moduleRequest.value
341
+ });
342
+ }
343
+ for (const statement of record.staticExports) {
344
+ const source = statement.entries[0]?.moduleRequest;
345
+ if (!source || statement.entries.every((entry) => entry.isType)) continue;
346
+ if (hasImportAttributes(code, source.end, statement.end)) continue;
347
+ dependencies.push({
348
+ start: statement.start,
349
+ specifier: source.value
350
+ });
351
+ }
352
+ if (EMPTY_EXPORT_PRESCAN.test(code)) {
353
+ let program = null;
354
+ try {
355
+ program = parseProgram(code, { lang });
356
+ } catch {}
357
+ for (const statement of program?.body ?? []) {
358
+ if (statement.type !== "ExportNamedDeclaration" || !statement.source) continue;
359
+ if (statement.specifiers.length > 0 || statement.exportKind === "type") continue;
360
+ if (statement.attributes.length > 0) continue;
361
+ dependencies.push({
362
+ start: statement.start,
363
+ specifier: statement.source.value
364
+ });
365
+ }
366
+ }
367
+ return dependencies.sort((a, b) => a.start - b.start).map(({ specifier }) => specifier);
335
368
  }
336
369
  function resolvedStylesheetToDevManifestAsset(root, resolvedId) {
337
370
  const cleanId = stripViteModuleQuery(resolvedId);
@@ -363,22 +396,17 @@ function createDevPagesModuleDependencyReader(root, resolve) {
363
396
  async function collectModuleDependencies(modulePath) {
364
397
  const cleanModulePath = stripViteModuleQuery(modulePath);
365
398
  if (!path.isAbsolute(cleanModulePath) || !fs.existsSync(cleanModulePath)) return [];
366
- let ast;
399
+ let code;
367
400
  try {
368
- ast = parseAst(fs.readFileSync(cleanModulePath, "utf-8"), { lang: parserLanguageForScript(cleanModulePath) });
401
+ code = fs.readFileSync(cleanModulePath, "utf-8");
369
402
  } catch {
370
403
  return [];
371
404
  }
405
+ const record = parseModuleRecord(code, parserLanguageForScript(cleanModulePath));
406
+ if (!record) return [];
372
407
  const dependencies = [];
373
- for (const statement of ast.body) {
374
- if (statement.type !== "ImportDeclaration" && statement.type !== "ExportNamedDeclaration" && statement.type !== "ExportAllDeclaration") continue;
375
- if (statement.importKind === "type") continue;
376
- if (statement.exportKind === "type") continue;
377
- if (hasOnlyTypeSpecifiers(statement)) continue;
378
- if (statement.attributes && statement.attributes.length > 0) continue;
379
- const specifier = statement.source?.value;
380
- if (typeof specifier !== "string") continue;
381
- const resolved = await resolve(specifier, cleanModulePath, { skipSelf: true });
408
+ for (const specifier of runtimeStaticDependencySpecifiers(code, record, parserLanguageForScript(cleanModulePath))) {
409
+ const resolved = await resolve(specifier, cleanModulePath, { skipSelf: false });
382
410
  if (!resolved?.id) continue;
383
411
  if (isStylesheetSpecifier(specifier) || isStylesheetSpecifier(resolved.id)) {
384
412
  const asset = resolvedStylesheetToDevManifestAsset(root, resolved.id);
@@ -721,6 +749,11 @@ const VIRTUAL_CACHEABILITY_REQUEST_PROJECTION = "virtual:vinext-cacheability-req
721
749
  const RESOLVED_CACHEABILITY_REQUEST_PROJECTION = "\0" + VIRTUAL_CACHEABILITY_REQUEST_PROJECTION;
722
750
  const VIRTUAL_PREGENERATED_CONCRETE_PATHS = "virtual:vinext-pregenerated-concrete-paths";
723
751
  const RESOLVED_PREGENERATED_CONCRETE_PATHS = "\0" + VIRTUAL_PREGENERATED_CONCRETE_PATHS;
752
+ const OUTPUT_ROOT_SERVER_EXTERNALS = /* @__PURE__ */ new Map([
753
+ [VIRTUAL_CACHEABILITY_MANIFEST, CACHEABILITY_MANIFEST_MODULE],
754
+ [VIRTUAL_CACHEABILITY_REQUEST_PROJECTION, CACHEABILITY_REQUEST_PROJECTION_MODULE],
755
+ [VIRTUAL_PREGENERATED_CONCRETE_PATHS, PREGENERATED_CONCRETE_PATHS_MODULE]
756
+ ]);
724
757
  const VIRTUAL_APP_REQUEST_ENTRY = "virtual:vinext-app-request-entry";
725
758
  const RESOLVED_APP_REQUEST_ENTRY = "\0" + VIRTUAL_APP_REQUEST_ENTRY;
726
759
  const VIRTUAL_APP_RESPONSE_ENTRY = "virtual:vinext-app-response-entry";
@@ -912,7 +945,7 @@ function vinext(options = {}) {
912
945
  let hasPlainPagesBuildEnvironments = false;
913
946
  let originalPlainPagesEnvironments;
914
947
  let buildLifecycleInvocation;
915
- let reactUpgradeChecked = false;
948
+ let reactVersionChecked = false;
916
949
  let pagesOptimizeEntries = [];
917
950
  const importMetaUrlCapability = createImportMetaUrlPlugin({
918
951
  getRoot: () => root,
@@ -922,6 +955,7 @@ function vinext(options = {}) {
922
955
  }
923
956
  });
924
957
  const pagesClientAssetsOutputDirs = /* @__PURE__ */ new Set();
958
+ const pagesClientAssetsExternalTargets = /* @__PURE__ */ new Map();
925
959
  const resolvePagesClientAssetsOutputDir = (environmentName, outputDir) => !selectedMultiStageOutput && !hasAppDir && !hasCloudflarePlugin && environmentName === "ssr" ? path.dirname(outputDir) : outputDir;
926
960
  let pagesClientAssetsModule = internalOptions.__pagesClientAssetsModule ?? null;
927
961
  let clientBuildFiles = [];
@@ -1042,7 +1076,7 @@ function vinext(options = {}) {
1042
1076
  });
1043
1077
  const useServerIndex = plugins.findIndex((plugin) => plugin.name === "rsc:use-server");
1044
1078
  if (useServerIndex === -1) throw new Error("vinext: Failed to locate @vitejs/plugin-rsc use-server plugin.");
1045
- plugins.splice(useServerIndex, 0, cacheFlightCodecPlugin(), useCachePlugin);
1079
+ plugins.splice(useServerIndex, 0, useCachePlugin);
1046
1080
  return plugins.map(scopeRscPlugin);
1047
1081
  }).catch((cause) => {
1048
1082
  throw new Error("vinext: Failed to load @vitejs/plugin-rsc.", { cause });
@@ -1214,17 +1248,13 @@ function vinext(options = {}) {
1214
1248
  shouldDeferPostBuild: () => buildLifecycleInvocation !== void 0,
1215
1249
  shouldPrepare: (config) => buildLifecycleEnabled && config.build?.write !== false && !config.build?.lib && (buildLifecycleInvocation !== void 0 || !config.build?.watch && !config.build?.ssr && getBuildBundlerOptions(config.build)?.input === void 0),
1216
1250
  onPrepare: () => {
1217
- if (!hasAppDir || reactUpgradeChecked) return;
1218
- reactUpgradeChecked = true;
1251
+ if (!hasAppDir || reactVersionChecked) return;
1252
+ reactVersionChecked = true;
1219
1253
  const reactUpgrade = getReactUpgradeDeps(root);
1254
+ const needsAppRsdw = rscVersion !== null && needsAppReactServerDom(process.cwd(), rscVersion);
1255
+ if (needsAppRsdw && !reactUpgrade.includes("react-server-dom-webpack@latest")) reactUpgrade.push("react-server-dom-webpack@latest");
1220
1256
  if (reactUpgrade.length === 0) return;
1221
- const [packageManager, ...packageManagerArgs] = detectPackageManager(root).replace(/ -D$/, "").split(" ");
1222
- console.log(" Upgrading React for RSC compatibility...");
1223
- execFileSync(packageManager, [...packageManagerArgs, ...reactUpgrade], {
1224
- cwd: root,
1225
- stdio: "inherit",
1226
- shell: process.platform === "win32"
1227
- });
1257
+ throw new Error(reactUpgradeRequiredMessage(detectPackageManager(root).replace(/ -D$/, ""), reactUpgrade, needsAppRsdw ? rscVersion : void 0));
1228
1258
  },
1229
1259
  shouldBuildPlainPages: () => !hasAppDir && !hasCloudflarePlugin && !hasNitroPlugin,
1230
1260
  createContext: () => ({
@@ -1990,9 +2020,10 @@ function vinext(options = {}) {
1990
2020
  const optionalWarnings = /* @__PURE__ */ new Set();
1991
2021
  for (const [name, environment] of Object.entries(config.environments)) {
1992
2022
  const optimizer = environment.optimizeDeps;
1993
- const optionalIncludes = hasAppDir ? name === "client" && !optimizer.noDiscovery ? APP_CLIENT_OPTIONAL_OPTIMIZE_DEPS_INCLUDE : [] : name !== "client" ? ["use-sync-external-store/with-selector"] : [];
2023
+ const optionalIncludes = hasAppDir ? name === "client" && !optimizer.noDiscovery ? APP_CLIENT_OPTIONAL_OPTIMIZE_DEPS_INCLUDE : [] : name !== "client" ? PAGES_CLOUDFLARE_WORKER_OPTIONAL_OPTIMIZE_DEPS_INCLUDE : [];
1994
2024
  for (const id of optionalIncludes) {
1995
- if (optimizer.include?.includes(id) || optimizer.exclude?.some((excluded) => id === excluded || id.startsWith(`${excluded}/`))) continue;
2025
+ const depId = id.slice(id.lastIndexOf(">") + 1).trim();
2026
+ if (optimizer.include?.includes(id) || optimizer.include?.includes(depId) || optimizer.exclude?.some((excluded) => depId === excluded || depId.startsWith(`${excluded}/`))) continue;
1996
2027
  (optimizer.include ??= []).push(id);
1997
2028
  optionalWarnings.add(`Failed to resolve dependency: ${id}, present in ${name} 'optimizeDeps.include'`);
1998
2029
  }
@@ -2120,21 +2151,21 @@ function vinext(options = {}) {
2120
2151
  if (cleanId === VIRTUAL_RSC_ENTRY) return RESOLVED_RSC_ENTRY;
2121
2152
  if (cleanId === VIRTUAL_CACHEABILITY_MANIFEST) {
2122
2153
  if ((hasAppDir ? this.environment?.name === "rsc" : this.environment !== void 0 && isServerEnvironment(this.environment)) && this.environment.config?.command === "build") return {
2123
- id: `./${CACHEABILITY_MANIFEST_MODULE}`,
2154
+ id: VIRTUAL_CACHEABILITY_MANIFEST,
2124
2155
  external: true
2125
2156
  };
2126
2157
  return RESOLVED_CACHEABILITY_MANIFEST;
2127
2158
  }
2128
2159
  if (cleanId === VIRTUAL_CACHEABILITY_REQUEST_PROJECTION) {
2129
2160
  if (hasAppDir && this.environment?.name === "rsc" && this.environment.config?.command === "build") return {
2130
- id: `./${CACHEABILITY_REQUEST_PROJECTION_MODULE}`,
2161
+ id: VIRTUAL_CACHEABILITY_REQUEST_PROJECTION,
2131
2162
  external: true
2132
2163
  };
2133
2164
  return RESOLVED_CACHEABILITY_REQUEST_PROJECTION;
2134
2165
  }
2135
2166
  if (cleanId === VIRTUAL_PREGENERATED_CONCRETE_PATHS) {
2136
2167
  if ((hasAppDir ? this.environment?.name === "rsc" : this.environment !== void 0 && isServerEnvironment(this.environment)) && this.environment.config?.command === "build") return {
2137
- id: `./${PREGENERATED_CONCRETE_PATHS_MODULE}`,
2168
+ id: VIRTUAL_PREGENERATED_CONCRETE_PATHS,
2138
2169
  external: true
2139
2170
  };
2140
2171
  return RESOLVED_PREGENERATED_CONCRETE_PATHS;
@@ -2370,15 +2401,19 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
2370
2401
  const buildRoot = this.environment.config.root ?? process.cwd();
2371
2402
  const environmentOutDir = path.resolve(buildRoot, this.environment.config.build.outDir);
2372
2403
  const sidecarDir = resolvePagesClientAssetsOutputDir(this.environment.name, environmentOutDir);
2373
- let externalId = path.relative(environmentOutDir, path.join(sidecarDir, PAGES_CLIENT_ASSETS_MODULE));
2374
- if (!externalId.startsWith(".")) externalId = `./${externalId}`;
2404
+ pagesClientAssetsExternalTargets.set(this.environment.name, /* @__PURE__ */ new Map([[VIRTUAL_PAGES_CLIENT_ASSETS, path.relative(environmentOutDir, path.join(sidecarDir, PAGES_CLIENT_ASSETS_MODULE))]]));
2375
2405
  pagesClientAssetsOutputDirs.add(sidecarDir);
2376
2406
  return {
2377
- id: externalId,
2407
+ id: VIRTUAL_PAGES_CLIENT_ASSETS,
2378
2408
  external: true
2379
2409
  };
2380
2410
  }
2381
2411
  },
2412
+ renderChunk(code, chunk) {
2413
+ const targets = pagesClientAssetsExternalTargets.get(this.environment.name);
2414
+ if (!targets) return null;
2415
+ return rewriteOutputRootExternals(code, chunk.fileName, targets);
2416
+ },
2382
2417
  outputOptions(output) {
2383
2418
  const environment = this.environment;
2384
2419
  if (!selectedMultiStageOutput || !environment || !isMultiStageServerEnvironment(environment)) return;
@@ -2484,6 +2519,9 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
2484
2519
  {
2485
2520
  name: "vinext:cacheability-manifest-asset",
2486
2521
  apply: "build",
2522
+ renderChunk(code, chunk) {
2523
+ return rewriteOutputRootExternals(code, chunk.fileName, OUTPUT_ROOT_SERVER_EXTERNALS);
2524
+ },
2487
2525
  generateBundle() {
2488
2526
  if (!(hasAppDir ? this.environment?.name === "rsc" : this.environment !== void 0 && isServerEnvironment(this.environment))) return;
2489
2527
  this.emitFile({
@@ -2648,7 +2686,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
2648
2686
  return new URL(resolved).origin;
2649
2687
  } catch {}
2650
2688
  const address = server.httpServer?.address();
2651
- return `http://localhost:${typeof address === "object" && address ? address.port : server.config.server.port ?? 3e3}`;
2689
+ return `http://localhost:${typeof address === "object" && address ? address.port : server.config.server.port}`;
2652
2690
  };
2653
2691
  let cachedSSRHandler = null;
2654
2692
  const devPageRouteDataKinds = /* @__PURE__ */ new Map();
@@ -3724,27 +3762,18 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3724
3762
  },
3725
3763
  async handler(code, id) {
3726
3764
  const lang = id.endsWith(".ts") ? "ts" : "tsx";
3727
- let ast;
3728
- try {
3729
- ast = parseAst(code, { lang });
3730
- } catch {
3731
- return null;
3732
- }
3765
+ const record = parseModuleRecord(code, lang);
3766
+ if (!record) return null;
3733
3767
  const s = new MagicString(code);
3734
3768
  let hasChanges = false;
3735
3769
  const imageImports = /* @__PURE__ */ new Set();
3736
- for (const node of ast.body) {
3737
- if (node.type !== "ImportDeclaration") continue;
3738
- const importNode = node;
3739
- const importPath = importNode.source?.value;
3740
- if (typeof importPath !== "string") continue;
3770
+ for (const importNode of record.staticImports) {
3771
+ const importPath = importNode.moduleRequest.value;
3741
3772
  if (!IMAGE_EXT_RE.test(importPath)) continue;
3742
- const specifiers = importNode.specifiers ?? [];
3743
- if (specifiers.length !== 1) continue;
3744
- const specifier = specifiers[0];
3745
- if (specifier.type !== "ImportDefaultSpecifier") continue;
3746
- const varName = specifier.local?.name;
3747
- if (!varName) continue;
3773
+ if (importNode.entries.length !== 1) continue;
3774
+ const [entry] = importNode.entries;
3775
+ if (entry.importName.kind !== "Default") continue;
3776
+ const varName = entry.localName.value;
3748
3777
  const dir = path.dirname(id);
3749
3778
  const resolvedImage = importPath.startsWith(".") ? path.resolve(dir, importPath) : (await this.resolve(importPath, id, { skipSelf: true }))?.id;
3750
3779
  if (!resolvedImage) continue;
@@ -3866,24 +3895,13 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3866
3895
  }
3867
3896
  },
3868
3897
  {
3869
- name: "vinext:nitro-route-rules",
3870
- nitro: { setup: async (nitro) => {
3898
+ name: "vinext:nitro-setup",
3899
+ nitro: { setup: (nitro) => {
3871
3900
  nitroBuildDir = nitro.options.buildDir ? path.resolve(root, nitro.options.buildDir) : void 0;
3872
3901
  nitroHostRuntime = nitro.options.exportConditions?.includes("workerd") ? "worker" : "node";
3873
3902
  if (!nextConfig) return;
3874
3903
  if (!hasAppDir && !hasPagesDir) return;
3875
3904
  if (resolvedServerExternalPackages.length > 0) nitro.options.traceDeps = [.../* @__PURE__ */ new Set([...nitro.options.traceDeps ?? [], ...resolvedServerExternalPackages])];
3876
- if (nitro.options.dev) return;
3877
- const { collectNitroRouteRules, mergeNitroRouteRules } = await import("./build/nitro-route-rules.js");
3878
- const generatedRouteRules = await collectNitroRouteRules({
3879
- appDir: hasAppDir ? appDir : null,
3880
- pagesDir: hasPagesDir ? pagesDir : null,
3881
- pageExtensions: nextConfig.pageExtensions
3882
- });
3883
- if (Object.keys(generatedRouteRules).length === 0) return;
3884
- const { routeRules, skippedRoutes } = mergeNitroRouteRules(nitro.options.routeRules, generatedRouteRules);
3885
- nitro.options.routeRules = routeRules;
3886
- if (skippedRoutes.length > 0) (nitro.logger?.warn ?? console.warn)(`[vinext] Skipping generated Nitro routeRules for routes with existing exact cache config: ${skippedRoutes.join(", ")}`);
3887
3905
  } }
3888
3906
  },
3889
3907
  {
@@ -4236,7 +4254,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
4236
4254
  if (rscPluginPromise) {
4237
4255
  plugins.push(createRscReferenceValidationNormalizerPlugin());
4238
4256
  plugins.push(createRscClientReferenceLoadersPlugin());
4239
- } else if (manualUseCachePluginPromise) plugins.push(cacheFlightCodecPlugin(), manualUseCachePluginPromise);
4257
+ } else if (manualUseCachePluginPromise) plugins.push(manualUseCachePluginPromise);
4240
4258
  plugins.push({
4241
4259
  name: "vinext:plain-pages-build-config",
4242
4260
  apply: "build",
@@ -4257,6 +4275,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
4257
4275
  }
4258
4276
  }
4259
4277
  });
4278
+ plugins.push(createCloudflareDevModuleUrlsPlugin({ isEnabled: () => hasCloudflarePlugin }));
4260
4279
  plugins.push(buildLifecyclePlugins[1]);
4261
4280
  return plugins;
4262
4281
  }
package/dist/init.d.ts CHANGED
@@ -70,7 +70,7 @@ export type InitDependencyGroups = {
70
70
  dependencies: string[];
71
71
  devDependencies: string[];
72
72
  };
73
- export declare function getInitDependencyGroups(isAppRouter: boolean, platform: InitPlatform, cloudflare?: CloudflareInitOptions, hasCssModules?: boolean): InitDependencyGroups;
73
+ export declare function getInitDependencyGroups(isAppRouter: boolean, platform: InitPlatform, cloudflare?: CloudflareInitOptions, hasCssModules?: boolean, tailwindPostcss?: boolean): InitDependencyGroups;
74
74
  export declare function getInitDeps(isAppRouter: boolean, platform: InitPlatform, cloudflare?: CloudflareInitOptions, hasCssModules?: boolean): string[];
75
75
  export declare function isDepInstalled(root: string, dep: string): boolean;
76
76
  /**
package/dist/init.js CHANGED
@@ -14,7 +14,8 @@ import { spawn, spawnSync } from "node:child_process";
14
14
  * 2. Add "type": "module" to package.json
15
15
  * 3. Rename CJS config files to .cjs
16
16
  * 4. Add vinext scripts to package.json
17
- * 5. Generate vite.config.ts and platform files
17
+ * 5. Generate vite.config.ts and platform files, plus a Tailwind PostCSS
18
+ * config when Tailwind is only set up as a Turbopack loader
18
19
  * 6. Update .gitignore to include /dist/ and .vinext/
19
20
  * 7. Install dependencies (vite, @vitejs/plugin-react, and App Router deps)
20
21
  * 8. Print summary
@@ -94,6 +95,77 @@ function scanCssModuleFiles(root) {
94
95
  exclude: (entry) => entry.isDirectory() && (CSS_MODULE_IGNORES.has(entry.name) || toSlash(entry.parentPath) === canonicalRoot && CSS_MODULE_ROOT_IGNORES.has(entry.name))
95
96
  }).some((entry) => /\.module\.(css|scss|sass)$/.test(entry.name));
96
97
  }
98
+ const NEXT_CONFIG_FILES = [
99
+ "next.config.ts",
100
+ "next.config.mts",
101
+ "next.config.mjs",
102
+ "next.config.js",
103
+ "next.config.cjs"
104
+ ];
105
+ const TAILWIND_LOADERS = ["@tailwindcss/turbopack", "@tailwindcss/webpack"];
106
+ const TAILWIND_POSTCSS_CONFIG = `const config = {
107
+ plugins: {
108
+ "@tailwindcss/postcss": {},
109
+ },
110
+ };
111
+
112
+ export default config;
113
+ `;
114
+ /**
115
+ * Whether the project gets Tailwind from a Turbopack loader (as create-next-app
116
+ * 16.4+ sets up in `turbopack.rules`) rather than from PostCSS, which is the
117
+ * only way vinext compiles Tailwind. The config is not evaluated: a Next config
118
+ * that names the loader, or a declared loader package, is enough.
119
+ */
120
+ function usesTailwindTurbopackLoader(root) {
121
+ for (const file of NEXT_CONFIG_FILES) {
122
+ const configPath = path.join(root, file);
123
+ if (!fs.existsSync(configPath)) continue;
124
+ const source = fs.readFileSync(configPath, "utf-8");
125
+ if (TAILWIND_LOADERS.some((loader) => source.includes(loader))) return true;
126
+ }
127
+ return TAILWIND_LOADERS.some((loader) => isDepInstalled(root, loader));
128
+ }
129
+ const POSTCSS_CONFIG_SEARCH_ORDER = [
130
+ ".postcssrc",
131
+ ".postcssrc.json",
132
+ ".postcssrc.yaml",
133
+ ".postcssrc.yml",
134
+ ".postcssrc.ts",
135
+ ".postcssrc.cts",
136
+ ".postcssrc.mts",
137
+ ".postcssrc.js",
138
+ ".postcssrc.cjs",
139
+ ".postcssrc.mjs",
140
+ "postcss.config.ts",
141
+ "postcss.config.cts",
142
+ "postcss.config.mts",
143
+ "postcss.config.js",
144
+ "postcss.config.cjs",
145
+ "postcss.config.mjs"
146
+ ];
147
+ /** The source of the PostCSS config Vite would load, which init never replaces. */
148
+ function readPostcssConfig(root) {
149
+ try {
150
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
151
+ if (pkg.postcss) return JSON.stringify(pkg.postcss);
152
+ } catch {}
153
+ for (const file of POSTCSS_CONFIG_SEARCH_ORDER) {
154
+ const configPath = path.join(root, file);
155
+ if (!fs.existsSync(configPath)) continue;
156
+ const source = fs.readFileSync(configPath, "utf-8");
157
+ if (source.trim() !== "") return source;
158
+ }
159
+ }
160
+ function getTailwindPostcssSetup(root, viteConfigPath) {
161
+ if (!usesTailwindTurbopackLoader(root)) return "none";
162
+ const viteConfig = viteConfigPath ? fs.readFileSync(viteConfigPath, "utf-8") : "";
163
+ const config = /postcss|lightningcss|tailwindcss/.test(viteConfig) ? viteConfig : readPostcssConfig(root);
164
+ if (config === void 0) return "generate";
165
+ if (/lightningcss/.test(config) && !config.includes("@tailwindcss/vite")) return "manual";
166
+ if (config.includes("@tailwindcss/postcss")) return "install";
167
+ return config.includes("tailwindcss") ? "none" : "manual";
168
+ }
97
169
  /**
98
170
  * Add Vite scripts to package.json without overwriting custom scripts.
99
171
  * Exact scripts generated by older vinext versions are migrated in place.
@@ -133,7 +205,7 @@ function addScripts(root, port, platform = "node", options = {}) {
133
205
  return [];
134
206
  }
135
207
  }
136
- function getInitDependencyGroups(isAppRouter, platform, cloudflare, hasCssModules = false) {
208
+ function getInitDependencyGroups(isAppRouter, platform, cloudflare, hasCssModules = false, tailwindPostcss = false) {
137
209
  const dependencies = ["vinext"];
138
210
  const devDependencies = ["vite", "@vitejs/plugin-react"];
139
211
  if (isAppRouter) {
@@ -149,6 +221,7 @@ function getInitDependencyGroups(isAppRouter, platform, cloudflare, hasCssModule
149
221
  } else devDependencies.push("@cloudflare/vite-plugin@1", "wrangler");
150
222
  }
151
223
  if (hasCssModules) devDependencies.push("vite-css-modules", "postcss");
224
+ if (tailwindPostcss) devDependencies.push("@tailwindcss/postcss");
152
225
  return {
153
226
  dependencies,
154
227
  devDependencies
@@ -227,7 +300,7 @@ function addDependencyEntries(root, deps, { dev }) {
227
300
  * Check if react/react-dom need upgrading for react-server-dom-webpack compatibility.
228
301
  *
229
302
  * react-server-dom-webpack versions are pinned to match their React version
230
- * (e.g. rsdw@19.2.6 requires react@^19.2.6). When a project has an older
303
+ * (e.g. rsdw@19.3.1 requires react@^19.3.1). When a project has an older
231
304
  * React (e.g. create-next-app ships react@19.2.3), we need to upgrade
232
305
  * react/react-dom BEFORE installing rsdw to avoid peer-dep conflicts.
233
306
  *
@@ -445,6 +518,7 @@ async function init(options) {
445
518
  const viteConfigExists = hasViteConfig(root);
446
519
  const isApp = detectProject(root).isAppRouter;
447
520
  const hasCssModules = !options.skipCssModules && scanCssModuleFiles(root);
521
+ const tailwindPostcssSetup = getTailwindPostcssSetup(root, platform === "node" && options.force ? void 0 : existingViteConfigPath);
448
522
  const pmName = detectPackageManagerName(root);
449
523
  const shouldInstall = options.install ?? true;
450
524
  const isStaticExport = platform === "cloudflare" && await resolvesToStaticExport(root, existingViteConfigPath);
@@ -495,15 +569,19 @@ async function init(options) {
495
569
  };
496
570
  const platformSetup = platform === "cloudflare" ? setupCloudflarePlatform(setupContext, cloudflareOptions) : setupNodePlatform(setupContext);
497
571
  const { generatedViteConfig, skippedViteConfig, generatedPlatformFiles } = platformSetup;
572
+ if (tailwindPostcssSetup === "generate") fs.writeFileSync(path.join(root, "postcss.config.mjs"), TAILWIND_POSTCSS_CONFIG, "utf-8");
498
573
  const updatedGitignore = updateGitignore(root, platform, options.cloudflare?.legacyWrangler);
499
- const neededDeps = getInitDependencyGroups(isApp, platform, options.cloudflare, hasCssModules);
574
+ const neededDeps = getInitDependencyGroups(isApp, platform, options.cloudflare, hasCssModules, tailwindPostcssSetup === "generate" || tailwindPostcssSetup === "install");
500
575
  const missingDependencies = neededDeps.dependencies.filter((dep) => !isDepInstalled(root, dep));
501
576
  const missingDevDependencies = neededDeps.devDependencies.filter((dep) => !isDepInstalled(root, dep));
502
577
  let dependencyInstallNeedsApproval = false;
503
578
  const dependencyEntriesAdded = [];
504
579
  const devDependencyEntriesAdded = [];
505
- if (isApp && (!shouldInstall || missingDependencies.includes("react-server-dom-webpack"))) {
506
- const reactUpgrade = getReactUpgradeDeps(root, { fromManifest: !shouldInstall });
580
+ if (isApp) {
581
+ const reactUpgrade = shouldInstall ? [.../* @__PURE__ */ new Set([...getReactUpgradeDeps(root), ...getReactUpgradeDeps(root, {
582
+ fromManifest: true,
583
+ installing: true
584
+ })])] : getReactUpgradeDeps(root, { fromManifest: true });
507
585
  if (reactUpgrade.length > 0) {
508
586
  console.log(` ${terminalStyle.cyan(terminalStyle.bold("Upgrading dependencies:"))}`);
509
587
  console.log(formatList(reactUpgrade.map((dep) => dep.replace(/@latest$/, "")), " "));
@@ -576,6 +654,8 @@ async function init(options) {
576
654
  if (skippedViteConfig) console.log(` ${terminalStyle.yellow("-")} Skipped vite.config.ts (already exists, use --force to overwrite)`);
577
655
  if (updatedGitignore) console.log(` ${terminalStyle.green("✓")} Added vinext output directories to .gitignore`);
578
656
  if (hasCssModules) console.log(` ${terminalStyle.green("✓")} Configured vite-css-modules for CSS Modules`);
657
+ if (tailwindPostcssSetup === "generate") console.log(` ${terminalStyle.green("✓")} Generated postcss.config.mjs for Tailwind CSS (vinext does not run turbopack.rules loaders)`);
658
+ else if (tailwindPostcssSetup === "manual") console.log(` ${terminalStyle.yellow("!")} Tailwind CSS is set up as a Turbopack loader, which vinext does not run; set up "@tailwindcss/postcss" or "@tailwindcss/vite" for vinext`);
579
659
  if (configKeepingWorkerName) console.log(` ${terminalStyle.yellow("!")} Kept the Worker name in the existing ${configKeepingWorkerName}; --name was not applied, so update it there if it should be "${cloudflareOptions.name}"`);
580
660
  if (platformSetup.preservedExistingGenerateScopedName) console.log(` ${terminalStyle.yellow("!")} Preserved existing css.modules.generateScopedName; verify it produces identical class names in SSR and client builds`);
581
661
  const nextSteps = [...platformSetup.nextSteps];