rwsdk 0.2.0 → 0.3.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.
- 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 -4
- package/dist/vite/configPlugin.mjs +70 -76
- package/dist/vite/constants.d.mts +2 -0
- package/dist/vite/constants.mjs +12 -0
- package/dist/vite/createDirectiveLookupPlugin.d.mts +0 -6
- package/dist/vite/createDirectiveLookupPlugin.mjs +69 -145
- package/dist/vite/createViteAwareResolver.d.mts +4 -0
- package/dist/vite/createViteAwareResolver.mjs +208 -0
- package/dist/vite/directiveModulesDevPlugin.d.mts +8 -0
- package/dist/vite/directiveModulesDevPlugin.mjs +87 -0
- package/dist/vite/directivesFilteringPlugin.d.mts +6 -0
- package/dist/vite/directivesFilteringPlugin.mjs +31 -0
- package/dist/vite/directivesPlugin.mjs +32 -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 +8 -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 +20 -37
- package/dist/vite/moveStaticAssetsPlugin.mjs +2 -1
- package/dist/vite/prismaPlugin.mjs +1 -1
- package/dist/vite/reactConditionsResolverPlugin.mjs +15 -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 +156 -0
- package/dist/vite/ssrBridgePlugin.mjs +21 -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 +66 -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/dist/vite/virtualPlugin.mjs +8 -0
- package/package.json +7 -1
- package/dist/runtime/clientNavigation.d.ts +0 -9
- package/dist/runtime/clientNavigation.js +0 -88
|
@@ -1,57 +1,40 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import debug from "debug";
|
|
3
|
-
import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
|
|
4
2
|
const log = debug("rwsdk:vite:manifest-plugin");
|
|
5
3
|
const virtualModuleId = "virtual:rwsdk:manifest.js";
|
|
6
4
|
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
7
|
-
export const manifestPlugin = ({
|
|
5
|
+
export const manifestPlugin = ({ projectRootDir, }) => {
|
|
8
6
|
let isBuild = false;
|
|
9
|
-
let root;
|
|
10
7
|
return {
|
|
11
|
-
name: "rwsdk:manifest",
|
|
8
|
+
name: "rwsdk:vite:manifest-plugin",
|
|
9
|
+
enforce: "pre",
|
|
12
10
|
configResolved(config) {
|
|
13
|
-
log("Config resolved, command=%s", config.command);
|
|
14
11
|
isBuild = config.command === "build";
|
|
15
|
-
root = config.root;
|
|
16
12
|
},
|
|
17
13
|
resolveId(id) {
|
|
14
|
+
// Skip during directive scanning to avoid performance issues
|
|
15
|
+
if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
18
|
if (id === virtualModuleId) {
|
|
19
|
-
process.env.VERBOSE && log("Resolving virtual module id=%s", id);
|
|
20
19
|
return resolvedVirtualModuleId;
|
|
21
20
|
}
|
|
22
21
|
},
|
|
23
22
|
async load(id) {
|
|
23
|
+
// Skip during directive scanning to avoid performance issues
|
|
24
|
+
if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
24
27
|
if (id === resolvedVirtualModuleId) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const manifestContent = await readFile(manifestPath, "utf-8");
|
|
32
|
-
const manifest = JSON.parse(manifestContent);
|
|
33
|
-
const normalizedManifest = {};
|
|
34
|
-
for (const key in manifest) {
|
|
35
|
-
const normalizedKey = normalizeModulePath(key, root, {
|
|
36
|
-
isViteStyle: false,
|
|
37
|
-
});
|
|
38
|
-
const entry = manifest[key];
|
|
39
|
-
delete manifest[key];
|
|
40
|
-
normalizedManifest[normalizedKey] = entry;
|
|
41
|
-
entry.file = normalizeModulePath(entry.file, root, {
|
|
42
|
-
isViteStyle: false,
|
|
43
|
-
});
|
|
44
|
-
const normalizedCss = [];
|
|
45
|
-
if (entry.css) {
|
|
46
|
-
for (const css of entry.css) {
|
|
47
|
-
normalizedCss.push(normalizeModulePath(css, root, {
|
|
48
|
-
isViteStyle: false,
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
entry.css = normalizedCss;
|
|
52
|
-
}
|
|
28
|
+
if (isBuild) {
|
|
29
|
+
// context(justinvdm, 28 Aug 2025): During the build, we don't have
|
|
30
|
+
// the manifest yet. We insert a placeholder that the linker plugin
|
|
31
|
+
// will replace in the final phase.
|
|
32
|
+
log("Returning manifest placeholder for build");
|
|
33
|
+
return `export default "__RWSDK_MANIFEST_PLACEHOLDER__"`;
|
|
53
34
|
}
|
|
54
|
-
return
|
|
35
|
+
// In dev, we can return an empty object.
|
|
36
|
+
log("Not a build, returning empty manifest");
|
|
37
|
+
return `export default {}`;
|
|
55
38
|
}
|
|
56
39
|
},
|
|
57
40
|
configEnvironment(name, config) {
|
|
@@ -3,7 +3,8 @@ export const moveStaticAssetsPlugin = ({ rootDir, }) => ({
|
|
|
3
3
|
name: "rwsdk:move-static-assets",
|
|
4
4
|
apply: "build",
|
|
5
5
|
async closeBundle() {
|
|
6
|
-
if (this.environment.name === "worker"
|
|
6
|
+
if (this.environment.name === "worker" &&
|
|
7
|
+
process.env.RWSDK_BUILD_PASS === "linker") {
|
|
7
8
|
await $sh({
|
|
8
9
|
cwd: rootDir,
|
|
9
10
|
}) `mv dist/worker/assets/*.css dist/client/assets/ || true`;
|
|
@@ -17,7 +17,7 @@ export const prismaPlugin = async ({ projectRootDir, }) => {
|
|
|
17
17
|
return {
|
|
18
18
|
name: "rwsdk:prisma",
|
|
19
19
|
configEnvironment(name, config) {
|
|
20
|
-
if (name !== "worker") {
|
|
20
|
+
if (name !== "worker" || !process.env.VITE_IS_DEV_SERVER) {
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
const wasmPath = resolve(projectRootDir, "node_modules/.prisma/client/wasm.js");
|
|
@@ -79,10 +79,12 @@ function resolveEnvImportMappings(env, projectRootDir) {
|
|
|
79
79
|
const resolved = resolveReactImport(importRequest, env, projectRootDir, true);
|
|
80
80
|
if (resolved) {
|
|
81
81
|
mappings.set(importRequest, resolved);
|
|
82
|
-
|
|
82
|
+
process.env.VERBOSE &&
|
|
83
|
+
log("Added mapping for %s -> %s in env=%s", importRequest, resolved, env);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
+
process.env.VERBOSE &&
|
|
87
|
+
log("Environment import mappings complete for env=%s: %d mappings", env, mappings.size);
|
|
86
88
|
return mappings;
|
|
87
89
|
}
|
|
88
90
|
export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
@@ -92,6 +94,9 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
92
94
|
env,
|
|
93
95
|
resolveEnvImportMappings(env, projectRootDir),
|
|
94
96
|
]));
|
|
97
|
+
// Log a clean summary instead of all the individual mappings
|
|
98
|
+
const totalMappings = Object.values(ENV_IMPORT_MAPPINGS).reduce((sum, mappings) => sum + mappings.size, 0);
|
|
99
|
+
log("React conditions resolver configured with %d total mappings across %d environments", totalMappings, Object.keys(ENV_IMPORT_MAPPINGS).length);
|
|
95
100
|
function createEsbuildResolverPlugin(envName, mappings) {
|
|
96
101
|
if (!mappings) {
|
|
97
102
|
return null;
|
|
@@ -100,15 +105,11 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
100
105
|
name: `rwsdk:react-conditions-resolver-esbuild-${envName}`,
|
|
101
106
|
setup(build) {
|
|
102
107
|
build.onResolve({ filter: /.*/ }, (args) => {
|
|
103
|
-
process.env.VERBOSE &&
|
|
104
|
-
log("ESBuild resolving %s for env=%s, args=%O", args.path, envName, args);
|
|
105
108
|
let resolved = mappings.get(args.path);
|
|
106
109
|
if (!resolved) {
|
|
107
110
|
resolved = resolveReactImport(args.path, envName, projectRootDir);
|
|
108
111
|
}
|
|
109
112
|
if (resolved && args.importer !== "") {
|
|
110
|
-
process.env.VERBOSE &&
|
|
111
|
-
log("ESBuild resolving %s -> %s for env=%s", args.path, resolved, envName);
|
|
112
113
|
if (args.path === "react-server-dom-webpack/client.edge") {
|
|
113
114
|
return;
|
|
114
115
|
}
|
|
@@ -116,10 +117,6 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
116
117
|
path: resolved,
|
|
117
118
|
};
|
|
118
119
|
}
|
|
119
|
-
else {
|
|
120
|
-
process.env.VERBOSE &&
|
|
121
|
-
log("ESBuild no resolution found for %s for env=%s", args.path, envName);
|
|
122
|
-
}
|
|
123
120
|
});
|
|
124
121
|
},
|
|
125
122
|
};
|
|
@@ -161,7 +158,8 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
161
158
|
for (const [find, replacement] of mappings) {
|
|
162
159
|
const findRegex = new RegExp(`^${find.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")}$`);
|
|
163
160
|
aliases.push({ find: findRegex, replacement });
|
|
164
|
-
|
|
161
|
+
process.env.VERBOSE &&
|
|
162
|
+
log("Added alias for env=%s: %s -> %s", envName, find, replacement);
|
|
165
163
|
}
|
|
166
164
|
log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, reactImports.length);
|
|
167
165
|
}
|
|
@@ -171,6 +169,10 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
171
169
|
name: "rwsdk:react-conditions-resolver:resolveId",
|
|
172
170
|
enforce: "pre",
|
|
173
171
|
async resolveId(id, importer) {
|
|
172
|
+
// Skip during directive scanning to avoid performance issues
|
|
173
|
+
if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
174
176
|
if (!isBuild) {
|
|
175
177
|
return;
|
|
176
178
|
}
|
|
@@ -178,8 +180,6 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
178
180
|
if (!envName) {
|
|
179
181
|
return;
|
|
180
182
|
}
|
|
181
|
-
process.env.VERBOSE &&
|
|
182
|
-
log("Resolving id=%s, environment=%s, importer=%s", id, envName, importer);
|
|
183
183
|
const mappings = ENV_IMPORT_MAPPINGS[envName];
|
|
184
184
|
if (!mappings) {
|
|
185
185
|
process.env.VERBOSE &&
|
|
@@ -191,11 +191,10 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
|
|
|
191
191
|
resolved = resolveReactImport(id, envName, projectRootDir);
|
|
192
192
|
}
|
|
193
193
|
if (resolved) {
|
|
194
|
-
|
|
194
|
+
process.env.VERBOSE &&
|
|
195
|
+
log("Resolved %s -> %s for env=%s", id, resolved, envName);
|
|
195
196
|
return resolved;
|
|
196
197
|
}
|
|
197
|
-
process.env.VERBOSE &&
|
|
198
|
-
log("No resolution found for id=%s in env=%s", id, envName);
|
|
199
198
|
},
|
|
200
199
|
},
|
|
201
200
|
];
|
|
@@ -24,6 +24,9 @@ import { ssrBridgePlugin } from "./ssrBridgePlugin.mjs";
|
|
|
24
24
|
import { hasPkgScript } from "../lib/hasPkgScript.mjs";
|
|
25
25
|
import { devServerTimingPlugin } from "./devServerTimingPlugin.mjs";
|
|
26
26
|
import { manifestPlugin } from "./manifestPlugin.mjs";
|
|
27
|
+
import { linkerPlugin } from "./linkerPlugin.mjs";
|
|
28
|
+
import { directiveModulesDevPlugin } from "./directiveModulesDevPlugin.mjs";
|
|
29
|
+
import { directivesFilteringPlugin } from "./directivesFilteringPlugin.mjs";
|
|
27
30
|
const determineWorkerEntryPathname = async (projectRootDir, workerConfigPath, options) => {
|
|
28
31
|
if (options.entry?.worker) {
|
|
29
32
|
return resolve(projectRootDir, options.entry.worker);
|
|
@@ -31,6 +34,9 @@ const determineWorkerEntryPathname = async (projectRootDir, workerConfigPath, op
|
|
|
31
34
|
const workerConfig = unstable_readConfig({ config: workerConfigPath });
|
|
32
35
|
return resolve(projectRootDir, workerConfig.main ?? "src/worker.tsx");
|
|
33
36
|
};
|
|
37
|
+
const clientFiles = new Set();
|
|
38
|
+
const serverFiles = new Set();
|
|
39
|
+
const clientEntryPoints = new Set();
|
|
34
40
|
export const redwoodPlugin = async (options = {}) => {
|
|
35
41
|
const projectRootDir = process.cwd();
|
|
36
42
|
const workerConfigPath = options.configPath ??
|
|
@@ -38,11 +44,6 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
38
44
|
? resolve(projectRootDir, process.env.RWSDK_WRANGLER_CONFIG)
|
|
39
45
|
: await findWranglerConfig(projectRootDir));
|
|
40
46
|
const workerEntryPathname = await determineWorkerEntryPathname(projectRootDir, workerConfigPath, options);
|
|
41
|
-
const clientEntryPathnames = (Array.isArray(options.entry?.client)
|
|
42
|
-
? options.entry.client
|
|
43
|
-
: [options.entry?.client ?? "src/client.tsx"]).map((entry) => resolve(projectRootDir, entry));
|
|
44
|
-
const clientFiles = new Set();
|
|
45
|
-
const serverFiles = new Set();
|
|
46
47
|
const shouldIncludeCloudflarePlugin = options.includeCloudflarePlugin ??
|
|
47
48
|
!(await hasOwnCloudflareVitePlugin({ rootProjectDir: projectRootDir }));
|
|
48
49
|
const shouldIncludeReactPlugin = options.includeReactPlugin ??
|
|
@@ -63,11 +64,18 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
63
64
|
return [
|
|
64
65
|
devServerTimingPlugin(),
|
|
65
66
|
devServerConstantPlugin(),
|
|
67
|
+
directiveModulesDevPlugin({
|
|
68
|
+
clientFiles,
|
|
69
|
+
serverFiles,
|
|
70
|
+
projectRootDir,
|
|
71
|
+
}),
|
|
66
72
|
configPlugin({
|
|
67
73
|
silent: options.silent ?? false,
|
|
68
74
|
projectRootDir,
|
|
69
|
-
clientEntryPathnames,
|
|
70
75
|
workerEntryPathname,
|
|
76
|
+
clientFiles,
|
|
77
|
+
serverFiles,
|
|
78
|
+
clientEntryPoints,
|
|
71
79
|
}),
|
|
72
80
|
ssrBridgePlugin({
|
|
73
81
|
clientFiles,
|
|
@@ -96,7 +104,10 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
96
104
|
serverFiles,
|
|
97
105
|
}),
|
|
98
106
|
vitePreamblePlugin(),
|
|
99
|
-
injectVitePreamble({
|
|
107
|
+
injectVitePreamble({
|
|
108
|
+
clientEntryPoints,
|
|
109
|
+
projectRootDir,
|
|
110
|
+
}),
|
|
100
111
|
useClientLookupPlugin({
|
|
101
112
|
projectRootDir,
|
|
102
113
|
clientFiles,
|
|
@@ -106,12 +117,19 @@ export const redwoodPlugin = async (options = {}) => {
|
|
|
106
117
|
serverFiles,
|
|
107
118
|
}),
|
|
108
119
|
transformJsxScriptTagsPlugin({
|
|
109
|
-
|
|
120
|
+
clientEntryPoints,
|
|
121
|
+
projectRootDir,
|
|
110
122
|
}),
|
|
111
123
|
manifestPlugin({
|
|
112
|
-
|
|
124
|
+
projectRootDir,
|
|
113
125
|
}),
|
|
114
126
|
moveStaticAssetsPlugin({ rootDir: projectRootDir }),
|
|
115
127
|
prismaPlugin({ projectRootDir }),
|
|
128
|
+
linkerPlugin({ projectRootDir }),
|
|
129
|
+
directivesFilteringPlugin({
|
|
130
|
+
clientFiles,
|
|
131
|
+
serverFiles,
|
|
132
|
+
projectRootDir,
|
|
133
|
+
}),
|
|
116
134
|
];
|
|
117
135
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Environment, ResolvedConfig } from "vite";
|
|
2
|
+
export declare const runDirectivesScan: ({ rootConfig, environment, clientFiles, serverFiles, }: {
|
|
3
|
+
rootConfig: ResolvedConfig;
|
|
4
|
+
environment: Environment;
|
|
5
|
+
clientFiles: Set<string>;
|
|
6
|
+
serverFiles: Set<string>;
|
|
7
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,156 @@
|
|
|
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 { getViteEsbuild } from "./getViteEsbuild.mjs";
|
|
6
|
+
import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
|
|
7
|
+
import { externalModules } from "./constants.mjs";
|
|
8
|
+
import { createViteAwareResolver } from "./createViteAwareResolver.mjs";
|
|
9
|
+
const log = debug("rwsdk:vite:run-directives-scan");
|
|
10
|
+
// Copied from Vite's source code.
|
|
11
|
+
// https://github.com/vitejs/vite/blob/main/packages/vite/src/shared/utils.ts
|
|
12
|
+
const isObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
|
|
13
|
+
// Copied from Vite's source code.
|
|
14
|
+
// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts
|
|
15
|
+
const externalRE = /^(https?:)?\/\//;
|
|
16
|
+
const isExternalUrl = (url) => externalRE.test(url);
|
|
17
|
+
export const runDirectivesScan = async ({ rootConfig, environment, clientFiles, serverFiles, }) => {
|
|
18
|
+
console.log("\n🔍 Scanning for 'use client' and 'use server' directives...");
|
|
19
|
+
// Set environment variable to indicate scanning is in progress
|
|
20
|
+
process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE = "true";
|
|
21
|
+
try {
|
|
22
|
+
const esbuild = await getViteEsbuild(rootConfig.root);
|
|
23
|
+
const input = environment.config.build.rollupOptions?.input;
|
|
24
|
+
let entries;
|
|
25
|
+
if (Array.isArray(input)) {
|
|
26
|
+
entries = input;
|
|
27
|
+
}
|
|
28
|
+
else if (typeof input === "string") {
|
|
29
|
+
entries = [input];
|
|
30
|
+
}
|
|
31
|
+
else if (isObject(input)) {
|
|
32
|
+
entries = Object.values(input);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
entries = [];
|
|
36
|
+
}
|
|
37
|
+
if (entries.length === 0) {
|
|
38
|
+
log("No entries found for directives scan in environment '%s', skipping.", environment.name);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const absoluteEntries = entries.map((entry) => path.resolve(rootConfig.root, entry));
|
|
42
|
+
log("Starting directives scan for environment '%s' with entries:", environment.name, absoluteEntries);
|
|
43
|
+
// Use enhanced-resolve with Vite plugin integration for full compatibility
|
|
44
|
+
const resolver = createViteAwareResolver(rootConfig, environment.name, environment);
|
|
45
|
+
const resolveId = async (id, importer) => {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
resolver({}, importer || rootConfig.root, id, {}, (err, result) => {
|
|
48
|
+
if (!err && result) {
|
|
49
|
+
resolve({ id: result });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
if (err) {
|
|
53
|
+
// Handle specific enhanced-resolve errors gracefully
|
|
54
|
+
const errorMessage = err.message || String(err);
|
|
55
|
+
if (errorMessage.includes("Package path . is not exported")) {
|
|
56
|
+
log("Package exports error for %s, marking as external", id);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
log("Resolution failed for %s: %s", id, errorMessage);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
resolve(null);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const esbuildScanPlugin = {
|
|
68
|
+
name: "rwsdk:esbuild-scan-plugin",
|
|
69
|
+
setup(build) {
|
|
70
|
+
// Match Vite's behavior by externalizing assets and special queries.
|
|
71
|
+
// This prevents esbuild from trying to bundle them, which would fail.
|
|
72
|
+
const scriptFilter = /\.(c|m)?[jt]sx?$/;
|
|
73
|
+
const specialQueryFilter = /[?&](?:url|raw|worker|sharedworker|inline)\b/;
|
|
74
|
+
// This regex is used to identify if a path has any file extension.
|
|
75
|
+
const hasExtensionRegex = /\.[^/]+$/;
|
|
76
|
+
build.onResolve({ filter: specialQueryFilter }, (args) => {
|
|
77
|
+
log("Externalizing special query:", args.path);
|
|
78
|
+
return { external: true };
|
|
79
|
+
});
|
|
80
|
+
build.onResolve({ filter: /.*/, namespace: "file" }, (args) => {
|
|
81
|
+
// Externalize if the path has an extension AND that extension is not a
|
|
82
|
+
// script extension. Extensionless paths are assumed to be scripts and
|
|
83
|
+
// are allowed to pass through for resolution.
|
|
84
|
+
if (hasExtensionRegex.test(args.path) &&
|
|
85
|
+
!scriptFilter.test(args.path)) {
|
|
86
|
+
log("Externalizing non-script import:", args.path);
|
|
87
|
+
return { external: true };
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
91
|
+
if (externalModules.includes(args.path)) {
|
|
92
|
+
return { external: true };
|
|
93
|
+
}
|
|
94
|
+
log("onResolve called for:", args.path, "from:", args.importer);
|
|
95
|
+
const resolved = await resolveId(args.path, args.importer);
|
|
96
|
+
log("Resolution result:", resolved);
|
|
97
|
+
const resolvedPath = resolved?.id;
|
|
98
|
+
if (resolvedPath && path.isAbsolute(resolvedPath)) {
|
|
99
|
+
// Normalize the path for esbuild compatibility
|
|
100
|
+
const normalizedPath = normalizeModulePath(resolvedPath, rootConfig.root, { absolute: true });
|
|
101
|
+
log("Normalized path:", normalizedPath);
|
|
102
|
+
return { path: normalizedPath };
|
|
103
|
+
}
|
|
104
|
+
log("Marking as external:", args.path, "resolved to:", resolvedPath);
|
|
105
|
+
return { external: true };
|
|
106
|
+
});
|
|
107
|
+
build.onLoad({ filter: /\.(m|c)?[jt]sx?$/ }, async (args) => {
|
|
108
|
+
log("onLoad called for:", args.path);
|
|
109
|
+
if (!args.path.startsWith("/") ||
|
|
110
|
+
args.path.includes("virtual:") ||
|
|
111
|
+
isExternalUrl(args.path)) {
|
|
112
|
+
log("Skipping file due to filter:", args.path, {
|
|
113
|
+
startsWithSlash: args.path.startsWith("/"),
|
|
114
|
+
hasVirtual: args.path.includes("virtual:"),
|
|
115
|
+
isExternal: isExternalUrl(args.path),
|
|
116
|
+
});
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const contents = await fsp.readFile(args.path, "utf-8");
|
|
121
|
+
if (hasDirective(contents, "use client")) {
|
|
122
|
+
log("Discovered 'use client' in:", args.path);
|
|
123
|
+
clientFiles.add(normalizeModulePath(args.path, rootConfig.root));
|
|
124
|
+
}
|
|
125
|
+
if (hasDirective(contents, "use server")) {
|
|
126
|
+
log("Discovered 'use server' in:", args.path);
|
|
127
|
+
serverFiles.add(normalizeModulePath(args.path, rootConfig.root));
|
|
128
|
+
}
|
|
129
|
+
return { contents, loader: "default" };
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
log("Could not read file during scan, skipping:", args.path, e);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
await esbuild.build({
|
|
139
|
+
entryPoints: absoluteEntries,
|
|
140
|
+
bundle: true,
|
|
141
|
+
write: false,
|
|
142
|
+
platform: "node",
|
|
143
|
+
format: "esm",
|
|
144
|
+
logLevel: "silent",
|
|
145
|
+
plugins: [esbuildScanPlugin],
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
throw new Error(`RWSDK directive scan failed:\n${e.stack}`);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
// Always clear the scanning flag when done
|
|
153
|
+
delete process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE;
|
|
154
|
+
console.log("✅ Scan complete.");
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -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,10 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
49
48
|
}
|
|
50
49
|
},
|
|
51
50
|
async resolveId(id) {
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
// Skip during directive scanning to avoid performance issues
|
|
52
|
+
if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
54
55
|
if (isDev) {
|
|
55
56
|
// context(justinvdm, 27 May 2025): In dev, we need to dynamically load
|
|
56
57
|
// SSR modules, so we return the virtual id so that the dynamic loading
|
|
@@ -75,20 +76,27 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
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
|
|
79
|
+
// In build mode, the behavior depends on the build pass
|
|
82
80
|
if (id === "rwsdk/__ssr_bridge" && this.environment.name === "worker") {
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
if (process.env.RWSDK_BUILD_PASS === "worker") {
|
|
82
|
+
// First pass: resolve to a temporary, external path
|
|
83
|
+
log("Bridge module case (build-worker pass): resolving to external path");
|
|
84
|
+
return { id: INTERMEDIATE_SSR_BRIDGE_PATH, external: true };
|
|
85
|
+
}
|
|
86
|
+
else if (process.env.RWSDK_BUILD_PASS === "linker") {
|
|
87
|
+
// Second pass (linker): resolve to the real intermediate build
|
|
88
|
+
// artifact so it can be bundled in.
|
|
89
|
+
log("Bridge module case (build-linker pass): resolving to bundleable path");
|
|
90
|
+
return { id: INTERMEDIATE_SSR_BRIDGE_PATH, external: false };
|
|
91
|
+
}
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
|
-
process.env.VERBOSE && log("No resolution for id=%s", id);
|
|
88
94
|
},
|
|
89
95
|
async load(id) {
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
// Skip during directive scanning to avoid performance issues
|
|
97
|
+
if (process.env.RWSDK_DIRECTIVE_SCAN_ACTIVE) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
92
100
|
if (id.startsWith(VIRTUAL_SSR_PREFIX) &&
|
|
93
101
|
this.environment.name === "worker") {
|
|
94
102
|
const realId = id.slice(VIRTUAL_SSR_PREFIX.length);
|
|
@@ -132,7 +140,6 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
|
|
|
132
140
|
return out;
|
|
133
141
|
}
|
|
134
142
|
}
|
|
135
|
-
process.env.VERBOSE && log("No load handling for id=%s", id);
|
|
136
143
|
},
|
|
137
144
|
};
|
|
138
145
|
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;
|