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.
- package/dist/lib/constants.d.mts +6 -1
- package/dist/lib/constants.mjs +6 -1
- package/dist/lib/smokeTests/browser.mjs +5 -21
- package/dist/lib/smokeTests/codeUpdates.d.mts +1 -1
- package/dist/lib/smokeTests/codeUpdates.mjs +41 -5
- package/dist/lib/smokeTests/development.d.mts +1 -1
- package/dist/lib/smokeTests/development.mjs +4 -10
- package/dist/lib/smokeTests/release.d.mts +1 -1
- package/dist/lib/smokeTests/release.mjs +4 -9
- package/dist/lib/smokeTests/runSmokeTests.mjs +2 -2
- package/dist/lib/smokeTests/templates/SmokeTest.template.js +3 -2
- package/dist/lib/testUtils/stubEnvVars.d.mts +2 -0
- package/dist/lib/testUtils/stubEnvVars.mjs +11 -0
- package/dist/runtime/imports/client.js +4 -9
- package/dist/runtime/imports/worker.js +2 -1
- package/dist/runtime/register/ssr.js +2 -1
- package/dist/runtime/requestInfo/worker.js +9 -1
- package/dist/runtime/worker.d.ts +0 -3
- package/dist/runtime/worker.js +1 -10
- package/dist/scripts/debug-sync.mjs +0 -23
- package/dist/scripts/smoke-test.mjs +0 -10
- package/dist/vite/buildApp.d.mts +15 -0
- package/dist/vite/buildApp.mjs +53 -0
- package/dist/vite/configPlugin.d.mts +4 -2
- package/dist/vite/configPlugin.mjs +69 -62
- package/dist/vite/createDirectiveLookupPlugin.d.mts +0 -6
- package/dist/vite/createDirectiveLookupPlugin.mjs +61 -145
- package/dist/vite/directiveModulesDevPlugin.d.mts +8 -0
- package/dist/vite/directiveModulesDevPlugin.mjs +62 -0
- package/dist/vite/directivesFilteringPlugin.d.mts +6 -0
- package/dist/vite/directivesFilteringPlugin.mjs +31 -0
- package/dist/vite/directivesPlugin.mjs +28 -42
- package/dist/vite/getViteEsbuild.d.mts +1 -0
- package/dist/vite/getViteEsbuild.mjs +12 -0
- package/dist/vite/injectVitePreamblePlugin.d.mts +3 -2
- package/dist/vite/injectVitePreamblePlugin.mjs +4 -2
- package/dist/vite/linkerPlugin.d.mts +4 -0
- package/dist/vite/linkerPlugin.mjs +41 -0
- package/dist/vite/manifestPlugin.d.mts +2 -2
- package/dist/vite/manifestPlugin.mjs +12 -37
- package/dist/vite/moveStaticAssetsPlugin.mjs +2 -1
- package/dist/vite/prismaPlugin.mjs +1 -1
- package/dist/vite/reactConditionsResolverPlugin.mjs +11 -16
- package/dist/vite/redwoodPlugin.d.mts +0 -1
- package/dist/vite/redwoodPlugin.mjs +27 -9
- package/dist/vite/runDirectivesScan.d.mts +7 -0
- package/dist/vite/runDirectivesScan.mjs +152 -0
- package/dist/vite/ssrBridgePlugin.mjs +13 -14
- package/dist/vite/transformClientComponents.d.mts +0 -1
- package/dist/vite/transformClientComponents.mjs +1 -9
- package/dist/vite/transformJsxScriptTagsPlugin.d.mts +4 -3
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +62 -84
- package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +67 -41
- package/dist/vite/transformServerFunctions.d.mts +1 -1
- package/dist/vite/transformServerFunctions.mjs +11 -12
- package/package.json +7 -1
- package/dist/runtime/clientNavigation.d.ts +0 -9
- 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
|
-
//
|
|
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
|
-
|
|
84
|
-
|
|
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;
|
|
@@ -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
|
|
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
|
|
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: ({
|
|
7
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
209
|
-
|
|
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 = ({
|
|
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
|
-
|
|
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 &&
|