rwsdk 0.2.0 → 0.3.0

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 (58) hide show
  1. package/dist/lib/constants.d.mts +6 -1
  2. package/dist/lib/constants.mjs +6 -1
  3. package/dist/lib/smokeTests/browser.mjs +5 -21
  4. package/dist/lib/smokeTests/codeUpdates.d.mts +1 -1
  5. package/dist/lib/smokeTests/codeUpdates.mjs +41 -5
  6. package/dist/lib/smokeTests/development.d.mts +1 -1
  7. package/dist/lib/smokeTests/development.mjs +4 -10
  8. package/dist/lib/smokeTests/release.d.mts +1 -1
  9. package/dist/lib/smokeTests/release.mjs +4 -9
  10. package/dist/lib/smokeTests/runSmokeTests.mjs +2 -2
  11. package/dist/lib/smokeTests/templates/SmokeTest.template.js +3 -2
  12. package/dist/lib/testUtils/stubEnvVars.d.mts +2 -0
  13. package/dist/lib/testUtils/stubEnvVars.mjs +11 -0
  14. package/dist/runtime/imports/client.js +4 -9
  15. package/dist/runtime/imports/worker.js +2 -1
  16. package/dist/runtime/register/ssr.js +2 -1
  17. package/dist/runtime/requestInfo/worker.js +9 -1
  18. package/dist/runtime/worker.d.ts +0 -3
  19. package/dist/runtime/worker.js +1 -10
  20. package/dist/scripts/debug-sync.mjs +0 -23
  21. package/dist/scripts/smoke-test.mjs +0 -10
  22. package/dist/vite/buildApp.d.mts +15 -0
  23. package/dist/vite/buildApp.mjs +53 -0
  24. package/dist/vite/configPlugin.d.mts +4 -2
  25. package/dist/vite/configPlugin.mjs +69 -62
  26. package/dist/vite/createDirectiveLookupPlugin.d.mts +0 -6
  27. package/dist/vite/createDirectiveLookupPlugin.mjs +61 -145
  28. package/dist/vite/directiveModulesDevPlugin.d.mts +8 -0
  29. package/dist/vite/directiveModulesDevPlugin.mjs +62 -0
  30. package/dist/vite/directivesFilteringPlugin.d.mts +6 -0
  31. package/dist/vite/directivesFilteringPlugin.mjs +31 -0
  32. package/dist/vite/directivesPlugin.mjs +28 -42
  33. package/dist/vite/getViteEsbuild.d.mts +1 -0
  34. package/dist/vite/getViteEsbuild.mjs +12 -0
  35. package/dist/vite/injectVitePreamblePlugin.d.mts +3 -2
  36. package/dist/vite/injectVitePreamblePlugin.mjs +4 -2
  37. package/dist/vite/linkerPlugin.d.mts +4 -0
  38. package/dist/vite/linkerPlugin.mjs +41 -0
  39. package/dist/vite/manifestPlugin.d.mts +2 -2
  40. package/dist/vite/manifestPlugin.mjs +12 -37
  41. package/dist/vite/moveStaticAssetsPlugin.mjs +2 -1
  42. package/dist/vite/prismaPlugin.mjs +1 -1
  43. package/dist/vite/reactConditionsResolverPlugin.mjs +11 -16
  44. package/dist/vite/redwoodPlugin.d.mts +0 -1
  45. package/dist/vite/redwoodPlugin.mjs +27 -9
  46. package/dist/vite/runDirectivesScan.d.mts +7 -0
  47. package/dist/vite/runDirectivesScan.mjs +152 -0
  48. package/dist/vite/ssrBridgePlugin.mjs +13 -14
  49. package/dist/vite/transformClientComponents.d.mts +0 -1
  50. package/dist/vite/transformClientComponents.mjs +1 -9
  51. package/dist/vite/transformJsxScriptTagsPlugin.d.mts +4 -3
  52. package/dist/vite/transformJsxScriptTagsPlugin.mjs +62 -84
  53. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +67 -41
  54. package/dist/vite/transformServerFunctions.d.mts +1 -1
  55. package/dist/vite/transformServerFunctions.mjs +11 -12
  56. package/package.json +7 -1
  57. package/dist/runtime/clientNavigation.d.ts +0 -9
  58. package/dist/runtime/clientNavigation.js +0 -88
@@ -0,0 +1,152 @@
1
+ import fsp from "node:fs/promises";
2
+ import { hasDirective } from "./hasDirective.mjs";
3
+ import path from "node:path";
4
+ import debug from "debug";
5
+ import { ensureAliasArray } from "./ensureAliasArray.mjs";
6
+ import { getViteEsbuild } from "./getViteEsbuild.mjs";
7
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
8
+ const log = debug("rwsdk:vite:run-directives-scan");
9
+ // Copied from Vite's source code.
10
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/shared/utils.ts
11
+ const isObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
12
+ // Copied from Vite's source code.
13
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts
14
+ const externalRE = /^(https?:)?\/\//;
15
+ const isExternalUrl = (url) => externalRE.test(url);
16
+ function createEsbuildScanPlugin({ clientFiles, serverFiles, aliases, projectRootDir, }) {
17
+ return {
18
+ name: "rwsdk:esbuild-scan-plugin",
19
+ setup(build) {
20
+ // Match Vite's behavior by externalizing assets and special queries.
21
+ // This prevents esbuild from trying to bundle them, which would fail.
22
+ const scriptFilter = /\.(c|m)?[jt]sx?$/;
23
+ const specialQueryFilter = /[?&](?:url|raw|worker|sharedworker|inline)\b/;
24
+ // This regex is used to identify if a path has any file extension.
25
+ const hasExtensionRegex = /\.[^/]+$/;
26
+ build.onResolve({ filter: specialQueryFilter }, (args) => {
27
+ log("Externalizing special query:", args.path);
28
+ return { external: true };
29
+ });
30
+ build.onResolve({ filter: /.*/, namespace: "file" }, (args) => {
31
+ // Externalize if the path has an extension AND that extension is not a
32
+ // script extension. Extensionless paths are assumed to be scripts and
33
+ // are allowed to pass through for resolution.
34
+ if (hasExtensionRegex.test(args.path) &&
35
+ !scriptFilter.test(args.path)) {
36
+ log("Externalizing non-script import:", args.path);
37
+ return { external: true };
38
+ }
39
+ });
40
+ build.onResolve({ filter: /.*/ }, async (args) => {
41
+ // Prevent infinite recursion.
42
+ if (args.pluginData?.rwsdkScanResolver) {
43
+ return null;
44
+ }
45
+ // 1. First, try to resolve aliases.
46
+ for (const { find, replacement } of aliases) {
47
+ const findPattern = find instanceof RegExp ? find : new RegExp(`^${find}(\\/.*)?$`);
48
+ if (findPattern.test(args.path)) {
49
+ const newPath = args.path.replace(findPattern, (_match, rest) => {
50
+ // `rest` is the captured group `(\\/.*)?` from the regex.
51
+ return replacement + (rest || "");
52
+ });
53
+ const resolved = await build.resolve(newPath, {
54
+ importer: args.importer,
55
+ resolveDir: args.resolveDir,
56
+ kind: args.kind,
57
+ pluginData: { rwsdkScanResolver: true },
58
+ });
59
+ if (resolved.errors.length === 0) {
60
+ return resolved;
61
+ }
62
+ log("Could not resolve aliased path '%s' (from '%s'). Marking as external. Errors: %s", newPath, args.path, resolved.errors.map((e) => e.text).join(", "));
63
+ return { external: true };
64
+ }
65
+ }
66
+ // 2. If no alias matches, try esbuild's default resolver.
67
+ const resolved = await build.resolve(args.path, {
68
+ importer: args.importer,
69
+ resolveDir: args.resolveDir,
70
+ kind: args.kind,
71
+ pluginData: { rwsdkScanResolver: true },
72
+ });
73
+ // If it fails, mark as external but don't crash.
74
+ if (resolved.errors.length > 0) {
75
+ log("Could not resolve '%s'. Marking as external. Errors: %s", args.path, resolved.errors.map((e) => e.text).join(", "));
76
+ return { external: true };
77
+ }
78
+ return resolved;
79
+ });
80
+ build.onLoad({ filter: /\.(m|c)?[jt]sx?$/ }, async (args) => {
81
+ if (!args.path.startsWith("/") ||
82
+ args.path.includes("virtual:") ||
83
+ isExternalUrl(args.path)) {
84
+ return null;
85
+ }
86
+ try {
87
+ const contents = await fsp.readFile(args.path, "utf-8");
88
+ if (hasDirective(contents, "use client")) {
89
+ log("Discovered 'use client' in:", args.path);
90
+ clientFiles.add(normalizeModulePath(args.path, projectRootDir));
91
+ }
92
+ if (hasDirective(contents, "use server")) {
93
+ log("Discovered 'use server' in:", args.path);
94
+ serverFiles.add(normalizeModulePath(args.path, projectRootDir));
95
+ }
96
+ return { contents, loader: "default" };
97
+ }
98
+ catch (e) {
99
+ log("Could not read file during scan, skipping:", args.path, e);
100
+ return null;
101
+ }
102
+ });
103
+ },
104
+ };
105
+ }
106
+ export async function runDirectivesScan({ rootConfig, envName, clientFiles, serverFiles, }) {
107
+ const esbuild = await getViteEsbuild(rootConfig.root);
108
+ const env = rootConfig.environments[envName];
109
+ const input = env.build.rollupOptions?.input;
110
+ let entries;
111
+ if (Array.isArray(input)) {
112
+ entries = input;
113
+ }
114
+ else if (typeof input === "string") {
115
+ entries = [input];
116
+ }
117
+ else if (isObject(input)) {
118
+ entries = Object.values(input);
119
+ }
120
+ else {
121
+ entries = [];
122
+ }
123
+ if (entries.length === 0) {
124
+ log("No entries found for directives scan in environment '%s', skipping.", envName);
125
+ return;
126
+ }
127
+ const absoluteEntries = entries.map((entry) => path.resolve(rootConfig.root, entry));
128
+ log("Starting directives scan for environment '%s' with entries:", envName, absoluteEntries);
129
+ try {
130
+ const result = await esbuild.build({
131
+ entryPoints: absoluteEntries,
132
+ bundle: true,
133
+ write: false,
134
+ platform: "node",
135
+ format: "esm",
136
+ logLevel: "silent",
137
+ metafile: true,
138
+ plugins: [
139
+ createEsbuildScanPlugin({
140
+ clientFiles,
141
+ serverFiles,
142
+ aliases: ensureAliasArray(env),
143
+ projectRootDir: rootConfig.root,
144
+ }),
145
+ ],
146
+ });
147
+ return result.metafile;
148
+ }
149
+ catch (e) {
150
+ throw new Error(`RWSDK directive scan failed:\n${e.message}`);
151
+ }
152
+ }
@@ -1,11 +1,10 @@
1
1
  import debug from "debug";
2
- import { SSR_BRIDGE_PATH } from "../lib/constants.mjs";
3
2
  import { findSsrImportCallSites } from "./findSsrSpecifiers.mjs";
3
+ import { INTERMEDIATE_SSR_BRIDGE_PATH } from "../lib/constants.mjs";
4
4
  import MagicString from "magic-string";
5
5
  const log = debug("rwsdk:vite:ssr-bridge-plugin");
6
6
  export const VIRTUAL_SSR_PREFIX = "virtual:rwsdk:ssr:";
7
7
  export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
8
- log("Initializing SSR bridge plugin with SSR_BRIDGE_PATH=%s", SSR_BRIDGE_PATH);
9
8
  let devServer;
10
9
  let isDev = false;
11
10
  const ssrBridgePlugin = {
@@ -49,8 +48,6 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
49
48
  }
50
49
  },
51
50
  async resolveId(id) {
52
- process.env.VERBOSE &&
53
- log("Resolving id=%s, environment=%s, isDev=%s", id, this.environment?.name, isDev);
54
51
  if (isDev) {
55
52
  // context(justinvdm, 27 May 2025): In dev, we need to dynamically load
56
53
  // SSR modules, so we return the virtual id so that the dynamic loading
@@ -75,20 +72,23 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
75
72
  }
76
73
  }
77
74
  else {
78
- // context(justinvdm, 27 May 2025): In builds, since all SSR import chains
79
- // originate at SSR bridge module, we return the path to the already built
80
- // SSR bridge bundle - SSR env builds it, worker build tries to resolve it
81
- // here and uses it
75
+ // In build mode, the behavior depends on the build pass
82
76
  if (id === "rwsdk/__ssr_bridge" && this.environment.name === "worker") {
83
- log("Bridge module case (build): id=%s matches rwsdk/__ssr_bridge in worker environment, returning SSR_BRIDGE_PATH=%s", id, SSR_BRIDGE_PATH);
84
- return SSR_BRIDGE_PATH;
77
+ if (process.env.RWSDK_BUILD_PASS === "worker") {
78
+ // First pass: resolve to a temporary, external path
79
+ log("Bridge module case (build-worker pass): resolving to external path");
80
+ return { id: INTERMEDIATE_SSR_BRIDGE_PATH, external: true };
81
+ }
82
+ else if (process.env.RWSDK_BUILD_PASS === "linker") {
83
+ // Second pass (linker): resolve to the real intermediate build
84
+ // artifact so it can be bundled in.
85
+ log("Bridge module case (build-linker pass): resolving to bundleable path");
86
+ return { id: INTERMEDIATE_SSR_BRIDGE_PATH, external: false };
87
+ }
85
88
  }
86
89
  }
87
- process.env.VERBOSE && log("No resolution for id=%s", id);
88
90
  },
89
91
  async load(id) {
90
- process.env.VERBOSE &&
91
- log("Loading id=%s, isDev=%s, environment=%s", id, isDev, this.environment.name);
92
92
  if (id.startsWith(VIRTUAL_SSR_PREFIX) &&
93
93
  this.environment.name === "worker") {
94
94
  const realId = id.slice(VIRTUAL_SSR_PREFIX.length);
@@ -132,7 +132,6 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
132
132
  return out;
133
133
  }
134
134
  }
135
- process.env.VERBOSE && log("No load handling for id=%s", id);
136
135
  },
137
136
  };
138
137
  return ssrBridgePlugin;
@@ -2,7 +2,6 @@ interface TransformContext {
2
2
  environmentName: string;
3
3
  clientFiles?: Set<string>;
4
4
  isEsbuild?: boolean;
5
- addClientModule?: (environment: string, id: string) => void;
6
5
  }
7
6
  interface TransformResult {
8
7
  code: string;
@@ -5,16 +5,10 @@ import { findExports } from "./findSpecifiers.mjs";
5
5
  const logVite = debug("rwsdk:vite:transform-client-components:vite");
6
6
  const logEsbuild = debug("rwsdk:vite:transform-client-components:esbuild");
7
7
  export async function transformClientComponents(code, normalizedId, ctx) {
8
- const log = ctx.isEsbuild ? logEsbuild : logVite;
9
- log("Called transformClientComponents for id: id=%s", normalizedId);
10
8
  if (!hasDirective(code, "use client")) {
11
- log("Skipping: no 'use client' directive in id=%s", normalizedId);
12
- process.env.VERBOSE &&
13
- log(":VERBOSE: Returning code unchanged for id=%s:\n%s", normalizedId, code);
14
9
  return;
15
10
  }
16
- log("Processing 'use client' module: id=%s", normalizedId);
17
- ctx.addClientModule?.(ctx.environmentName, normalizedId);
11
+ const log = ctx.isEsbuild ? logEsbuild : logVite;
18
12
  // Parse exports using the findExports helper
19
13
  const exportInfos = findExports(normalizedId, code, log);
20
14
  const processedExports = [];
@@ -85,7 +79,6 @@ export async function transformClientComponents(code, normalizedId, ctx) {
85
79
  const computedLocalNames = new Map(processedExports.map((info) => [getComputedLocalName(info), info]));
86
80
  // Add registerClientReference assignments for unique names
87
81
  for (const [computedLocalName, correspondingInfo] of computedLocalNames) {
88
- log(":isEsbuild=%s: Registering client reference for named export: %s as %s", !!ctx.isEsbuild, correspondingInfo.local, correspondingInfo.exported);
89
82
  s.append(`const ${computedLocalName} = registerClientReference("${normalizedId}", "${correspondingInfo.exported}");\n`);
90
83
  }
91
84
  // Add grouped export statement for named exports (preserving order and alias)
@@ -93,7 +86,6 @@ export async function transformClientComponents(code, normalizedId, ctx) {
93
86
  const exportNames = Array.from(computedLocalNames.entries()).map(([computedLocalName, correspondingInfo]) => correspondingInfo.local === correspondingInfo.exported
94
87
  ? computedLocalName
95
88
  : `${computedLocalName} as ${correspondingInfo.exported}`);
96
- log(":isEsbuild=%s: Exporting named exports: %O", !!ctx.isEsbuild, exportNames);
97
89
  s.append(`export { ${exportNames.join(", ")} };\n`);
98
90
  }
99
91
  // Add default export if present
@@ -1,8 +1,9 @@
1
1
  import { type Plugin } from "vite";
2
- export declare function transformJsxScriptTagsCode(code: string, manifest?: Record<string, any>): Promise<{
2
+ export declare function transformJsxScriptTagsCode(code: string, clientEntryPoints: Set<string>, manifest: Record<string, any> | undefined, projectRootDir: string): Promise<{
3
3
  code: string;
4
4
  map: null;
5
5
  } | undefined>;
6
- export declare const transformJsxScriptTagsPlugin: ({ manifestPath, }: {
7
- manifestPath: string;
6
+ export declare const transformJsxScriptTagsPlugin: ({ clientEntryPoints, projectRootDir, }: {
7
+ clientEntryPoints: Set<string>;
8
+ projectRootDir: string;
8
9
  }) => Plugin;
@@ -1,19 +1,16 @@
1
1
  import { Project, Node, SyntaxKind, } from "ts-morph";
2
- import { readFile } from "node:fs/promises";
3
- import { pathExists } from "fs-extra";
4
2
  import debug from "debug";
3
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
5
4
  const log = debug("rwsdk:vite:transform-jsx-script-tags");
6
- let manifestCache;
7
- const readManifest = async (manifestPath) => {
8
- if (manifestCache === undefined) {
9
- const exists = await pathExists(manifestPath);
10
- if (!exists) {
11
- throw new Error(`RedwoodSDK expected client manifest to exist at ${manifestPath}. This is likely a bug. Please report it at https://github.com/redwoodjs/sdk/issues/new`);
12
- }
13
- manifestCache = JSON.parse(await readFile(manifestPath, "utf-8"));
5
+ function transformAssetPath(importPath, projectRootDir) {
6
+ if (process.env.VITE_IS_DEV_SERVER === "1") {
7
+ return importPath;
14
8
  }
15
- return manifestCache;
16
- };
9
+ const normalizedImportPath = normalizeModulePath(importPath, projectRootDir);
10
+ return `rwsdk_asset:${normalizedImportPath}`;
11
+ }
12
+ // Note: This plugin only runs during discovery phase (Phase 1)
13
+ // Manifest reading and asset linking happens later in Phase 5
17
14
  function hasJsxFunctions(text) {
18
15
  return (text.includes('jsx("script"') ||
19
16
  text.includes("jsx('script'") ||
@@ -28,7 +25,7 @@ function hasJsxFunctions(text) {
28
25
  text.includes('jsxDEV("link"') ||
29
26
  text.includes("jsxDEV('link'"));
30
27
  }
31
- function transformScriptImports(scriptContent, manifest) {
28
+ function transformScriptImports(scriptContent, clientEntryPoints, manifest, projectRootDir) {
32
29
  const scriptProject = new Project({ useInMemoryFileSystem: true });
33
30
  try {
34
31
  const wrappedContent = `function __wrapper() {${scriptContent}}`;
@@ -49,12 +46,10 @@ function transformScriptImports(scriptContent, manifest) {
49
46
  if (importPath.startsWith("/")) {
50
47
  log("Found dynamic import with root-relative path: %s", importPath);
51
48
  entryPoints.push(importPath);
52
- const path = importPath.slice(1);
53
- if (manifest[path]) {
54
- const transformedSrc = `/${manifest[path].file}`;
55
- args[0].setLiteralValue(transformedSrc);
56
- hasChanges = true;
57
- }
49
+ clientEntryPoints.add(importPath);
50
+ const transformedImportPath = transformAssetPath(importPath, projectRootDir);
51
+ args[0].setLiteralValue(transformedImportPath);
52
+ hasChanges = true;
58
53
  }
59
54
  }
60
55
  }
@@ -73,7 +68,7 @@ function transformScriptImports(scriptContent, manifest) {
73
68
  return { content: undefined, hasChanges: false, entryPoints: [] };
74
69
  }
75
70
  }
76
- export async function transformJsxScriptTagsCode(code, manifest = {}) {
71
+ export async function transformJsxScriptTagsCode(code, clientEntryPoints, manifest = {}, projectRootDir) {
77
72
  // context(justinvdm, 15 Jun 2025): Optimization to exit early
78
73
  // to avoidunnecessary ts-morph parsing
79
74
  if (!hasJsxFunctions(code)) {
@@ -124,7 +119,7 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
124
119
  let hasStringLiteralChildren = false;
125
120
  let hasSrc = false;
126
121
  let isPreload = false;
127
- let hrefValue = null;
122
+ let hrefProp;
128
123
  for (const prop of properties) {
129
124
  if (Node.isPropertyAssignment(prop)) {
130
125
  const propName = prop.getName();
@@ -142,15 +137,13 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
142
137
  const srcValue = initializer.getLiteralValue();
143
138
  if (srcValue.startsWith("/")) {
144
139
  entryPoints.push(srcValue);
145
- const path = srcValue.slice(1);
146
- if (manifest[path]) {
147
- const transformedSrc = `/${manifest[path].file}`;
148
- modifications.push({
149
- type: "literalValue",
150
- node: initializer,
151
- value: transformedSrc,
152
- });
153
- }
140
+ clientEntryPoints.add(srcValue);
141
+ const transformedSrc = transformAssetPath(srcValue, projectRootDir);
142
+ modifications.push({
143
+ type: "literalValue",
144
+ node: initializer,
145
+ value: transformedSrc,
146
+ });
154
147
  }
155
148
  }
156
149
  }
@@ -160,7 +153,7 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
160
153
  Node.isNoSubstitutionTemplateLiteral(initializer))) {
161
154
  hasStringLiteralChildren = true;
162
155
  const scriptContent = initializer.getLiteralValue();
163
- const { content: transformedContent, hasChanges: contentHasChanges, entryPoints: dynamicEntryPoints, } = transformScriptImports(scriptContent, manifest);
156
+ const { content: transformedContent, hasChanges: contentHasChanges, entryPoints: dynamicEntryPoints, } = transformScriptImports(scriptContent, clientEntryPoints, manifest, projectRootDir);
164
157
  entryPoints.push(...dynamicEntryPoints);
165
158
  if (contentHasChanges && transformedContent) {
166
159
  const isTemplateLiteral = Node.isNoSubstitutionTemplateLiteral(initializer);
@@ -174,21 +167,35 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
174
167
  });
175
168
  }
176
169
  }
177
- if (tagName === "link") {
178
- if (propName === "rel" &&
179
- (Node.isStringLiteral(initializer) ||
180
- Node.isNoSubstitutionTemplateLiteral(initializer))) {
181
- const relValue = initializer.getLiteralValue();
182
- if (relValue === "preload" || relValue === "modulepreload") {
183
- isPreload = true;
184
- }
185
- }
186
- if (propName === "href" &&
187
- (Node.isStringLiteral(initializer) ||
188
- Node.isNoSubstitutionTemplateLiteral(initializer))) {
189
- hrefValue = initializer.getLiteralValue();
170
+ if (tagName === "link" &&
171
+ propName === "rel" &&
172
+ initializer &&
173
+ (Node.isStringLiteral(initializer) ||
174
+ Node.isNoSubstitutionTemplateLiteral(initializer))) {
175
+ const relValue = initializer.getLiteralValue();
176
+ if (relValue === "preload" || relValue === "modulepreload") {
177
+ isPreload = true;
190
178
  }
191
179
  }
180
+ if (tagName === "link" && propName === "href") {
181
+ hrefProp = prop;
182
+ }
183
+ }
184
+ }
185
+ if (isPreload && hrefProp) {
186
+ const initializer = hrefProp.getInitializer();
187
+ if (initializer &&
188
+ (Node.isStringLiteral(initializer) ||
189
+ Node.isNoSubstitutionTemplateLiteral(initializer))) {
190
+ const hrefValue = initializer.getLiteralValue();
191
+ if (hrefValue.startsWith("/")) {
192
+ const transformedHref = transformAssetPath(hrefValue, projectRootDir);
193
+ modifications.push({
194
+ type: "literalValue",
195
+ node: initializer,
196
+ value: transformedHref,
197
+ });
198
+ }
192
199
  }
193
200
  }
194
201
  if (tagName === "script" &&
@@ -205,43 +212,8 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
205
212
  needsRequestInfoImportRef.value = true;
206
213
  }
207
214
  }
208
- if (tagName === "link" &&
209
- isPreload &&
210
- hrefValue &&
211
- hrefValue.startsWith("/") &&
212
- manifest[hrefValue.slice(1)]) {
213
- const path = hrefValue.slice(1);
214
- for (const prop of properties) {
215
- if (Node.isPropertyAssignment(prop) &&
216
- prop.getName() === "href") {
217
- const initializer = prop.getInitializer();
218
- if (Node.isStringLiteral(initializer) ||
219
- Node.isNoSubstitutionTemplateLiteral(initializer)) {
220
- const transformedHref = manifest[path].file;
221
- const originalText = initializer.getText();
222
- const isTemplateLiteral = Node.isNoSubstitutionTemplateLiteral(initializer);
223
- const quote = isTemplateLiteral
224
- ? "`"
225
- : originalText.charAt(0);
226
- let replacementText;
227
- if (isTemplateLiteral) {
228
- replacementText = `\`/${transformedHref}\``;
229
- }
230
- else if (quote === '"') {
231
- replacementText = `"/${transformedHref}"`;
232
- }
233
- else {
234
- replacementText = `'/${transformedHref}'`;
235
- }
236
- modifications.push({
237
- type: "replaceText",
238
- node: initializer,
239
- text: replacementText,
240
- });
241
- }
242
- }
243
- }
244
- }
215
+ // Note: Link preload href transformations happen in Phase 5 (Asset Linking)
216
+ // During discovery phase, we only transform script tags
245
217
  }
246
218
  }
247
219
  if (entryPoints.length > 0) {
@@ -339,7 +311,7 @@ ${mod.callExprText}
339
311
  }
340
312
  return;
341
313
  }
342
- export const transformJsxScriptTagsPlugin = ({ manifestPath, }) => {
314
+ export const transformJsxScriptTagsPlugin = ({ clientEntryPoints, projectRootDir, }) => {
343
315
  let isBuild = false;
344
316
  return {
345
317
  name: "rwsdk:vite:transform-jsx-script-tags",
@@ -347,14 +319,20 @@ export const transformJsxScriptTagsPlugin = ({ manifestPath, }) => {
347
319
  isBuild = config.command === "build";
348
320
  },
349
321
  async transform(code, id) {
322
+ if (isBuild &&
323
+ this.environment?.name === "worker" &&
324
+ process.env.RWSDK_BUILD_PASS !== "worker") {
325
+ return null;
326
+ }
350
327
  if (this.environment?.name === "worker" &&
351
328
  id.endsWith(".tsx") &&
352
329
  !id.includes("node_modules") &&
353
330
  hasJsxFunctions(code)) {
354
331
  log("Transforming JSX script tags in %s", id);
355
332
  process.env.VERBOSE && log("Code:\n%s", code);
356
- const manifest = isBuild ? await readManifest(manifestPath) : {};
357
- const result = await transformJsxScriptTagsCode(code, manifest);
333
+ // During discovery phase, never use manifest - it doesn't exist yet
334
+ const result = await transformJsxScriptTagsCode(code, clientEntryPoints, {}, // Empty manifest during discovery
335
+ projectRootDir);
358
336
  if (result) {
359
337
  log("Transformed JSX script tags in %s", id);
360
338
  process.env.VERBOSE &&