rwsdk 0.1.26 → 0.2.0-alpha.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 (41) hide show
  1. package/dist/lib/smokeTests/browser.d.mts +4 -2
  2. package/dist/lib/smokeTests/browser.mjs +159 -7
  3. package/dist/lib/smokeTests/codeUpdates.d.mts +1 -0
  4. package/dist/lib/smokeTests/codeUpdates.mjs +47 -0
  5. package/dist/lib/smokeTests/development.d.mts +1 -1
  6. package/dist/lib/smokeTests/development.mjs +10 -3
  7. package/dist/lib/smokeTests/environment.mjs +1 -14
  8. package/dist/lib/smokeTests/release.d.mts +1 -1
  9. package/dist/lib/smokeTests/release.mjs +3 -2
  10. package/dist/lib/smokeTests/reporting.mjs +30 -2
  11. package/dist/lib/smokeTests/runSmokeTests.mjs +2 -2
  12. package/dist/lib/smokeTests/state.d.mts +8 -0
  13. package/dist/lib/smokeTests/state.mjs +10 -0
  14. package/dist/lib/smokeTests/templates/SmokeTestClient.template.js +17 -2
  15. package/dist/lib/smokeTests/templates/smokeTestClientStyles.module.css.template.d.ts +1 -0
  16. package/dist/lib/smokeTests/templates/smokeTestClientStyles.module.css.template.js +9 -0
  17. package/dist/lib/smokeTests/templates/smokeTestUrlStyles.css.template.d.ts +1 -0
  18. package/dist/lib/smokeTests/templates/smokeTestUrlStyles.css.template.js +9 -0
  19. package/dist/lib/smokeTests/types.d.mts +1 -0
  20. package/dist/runtime/clientNavigation.d.ts +6 -3
  21. package/dist/runtime/clientNavigation.js +72 -8
  22. package/dist/runtime/entries/types/client.d.ts +5 -0
  23. package/dist/runtime/lib/manifest.d.ts +2 -0
  24. package/dist/runtime/lib/manifest.js +17 -0
  25. package/dist/runtime/lib/router.d.ts +1 -0
  26. package/dist/runtime/register/worker.js +17 -5
  27. package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +3 -3
  28. package/dist/runtime/render/renderRscThenableToHtmlStream.js +7 -3
  29. package/dist/runtime/render/stylesheets.d.ts +9 -0
  30. package/dist/runtime/render/stylesheets.js +45 -0
  31. package/dist/runtime/worker.js +1 -0
  32. package/dist/scripts/debug-sync.mjs +125 -13
  33. package/dist/scripts/ensure-deploy-env.mjs +2 -2
  34. package/dist/scripts/smoke-test.mjs +6 -0
  35. package/dist/vite/manifestPlugin.d.mts +4 -0
  36. package/dist/vite/manifestPlugin.mjs +151 -0
  37. package/dist/vite/redwoodPlugin.mjs +4 -0
  38. package/dist/vite/ssrBridgePlugin.mjs +17 -8
  39. package/dist/vite/transformJsxScriptTagsPlugin.mjs +74 -33
  40. package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +43 -15
  41. package/package.json +1 -1
@@ -0,0 +1,151 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import debug from "debug";
3
+ import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
4
+ const log = debug("rwsdk:vite:manifest-plugin");
5
+ const virtualModuleId = "virtual:rwsdk:manifest.js";
6
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
7
+ const getCssForModule = (server, moduleId, css) => {
8
+ const stack = [moduleId];
9
+ const visited = new Set();
10
+ while (stack.length > 0) {
11
+ const currentModuleId = stack.pop();
12
+ if (visited.has(currentModuleId)) {
13
+ continue;
14
+ }
15
+ visited.add(currentModuleId);
16
+ const moduleNode = server.environments.client.moduleGraph.getModuleById(currentModuleId);
17
+ if (!moduleNode) {
18
+ continue;
19
+ }
20
+ for (const importedModule of moduleNode.importedModules) {
21
+ if (importedModule.url.endsWith(".css")) {
22
+ const absolutePath = importedModule.file;
23
+ css.add({
24
+ url: importedModule.url,
25
+ // The `ssrTransformResult` has the CSS content, because the default
26
+ // transform for CSS is to a string of the CSS content.
27
+ content: importedModule.ssrTransformResult?.code ?? "",
28
+ absolutePath,
29
+ });
30
+ }
31
+ if (importedModule.id) {
32
+ stack.push(importedModule.id);
33
+ }
34
+ }
35
+ }
36
+ };
37
+ export const manifestPlugin = ({ manifestPath, }) => {
38
+ let isBuild = false;
39
+ let root;
40
+ return {
41
+ name: "rwsdk:manifest",
42
+ configResolved(config) {
43
+ log("Config resolved, command=%s", config.command);
44
+ isBuild = config.command === "build";
45
+ root = config.root;
46
+ },
47
+ resolveId(id) {
48
+ if (id === virtualModuleId) {
49
+ process.env.VERBOSE && log("Resolving virtual module id=%s", id);
50
+ return resolvedVirtualModuleId;
51
+ }
52
+ },
53
+ async load(id) {
54
+ if (id === resolvedVirtualModuleId) {
55
+ process.env.VERBOSE && log("Loading virtual module id=%s", id);
56
+ if (!isBuild) {
57
+ process.env.VERBOSE && log("Not a build, returning empty manifest");
58
+ return `export default {}`;
59
+ }
60
+ log("Reading manifest from %s", manifestPath);
61
+ const manifestContent = await readFile(manifestPath, "utf-8");
62
+ const manifest = JSON.parse(manifestContent);
63
+ const normalizedManifest = {};
64
+ for (const key in manifest) {
65
+ const normalizedKey = normalizeModulePath(key, root, {
66
+ isViteStyle: false,
67
+ });
68
+ const entry = manifest[key];
69
+ delete manifest[key];
70
+ normalizedManifest[normalizedKey] = entry;
71
+ entry.file = normalizeModulePath(entry.file, root, {
72
+ isViteStyle: false,
73
+ });
74
+ const normalizedCss = [];
75
+ if (entry.css) {
76
+ for (const css of entry.css) {
77
+ normalizedCss.push(normalizeModulePath(css, root, {
78
+ isViteStyle: false,
79
+ }));
80
+ }
81
+ entry.css = normalizedCss;
82
+ }
83
+ }
84
+ return `export default ${JSON.stringify(normalizedManifest)}`;
85
+ }
86
+ },
87
+ configEnvironment(name, config) {
88
+ if (name !== "worker" && name !== "ssr") {
89
+ return;
90
+ }
91
+ log("Configuring environment: name=%s", name);
92
+ config.optimizeDeps ??= {};
93
+ config.optimizeDeps.esbuildOptions ??= {};
94
+ config.optimizeDeps.esbuildOptions.plugins ??= [];
95
+ config.optimizeDeps.esbuildOptions.plugins.push({
96
+ name: "rwsdk:manifest:esbuild",
97
+ setup(build) {
98
+ log("Setting up esbuild plugin for environment: %s", name);
99
+ build.onResolve({ filter: /^virtual:rwsdk:manifest\.js$/ }, () => {
100
+ log("Resolving virtual manifest module in esbuild");
101
+ return {
102
+ path: "virtual:rwsdk:manifest.js",
103
+ external: true,
104
+ };
105
+ });
106
+ },
107
+ });
108
+ },
109
+ configureServer(server) {
110
+ log("Configuring server middleware for manifest");
111
+ server.middlewares.use("/__rwsdk_manifest", async (req, res, next) => {
112
+ log("Manifest request received: %s", req.url);
113
+ try {
114
+ const url = new URL(req.url, `http://${req.headers.host}`);
115
+ const scripts = JSON.parse(url.searchParams.get("scripts") || "[]");
116
+ process.env.VERBOSE && log("Transforming scripts: %o", scripts);
117
+ for (const script of scripts) {
118
+ await server.environments.client.transformRequest(script);
119
+ }
120
+ const manifest = {};
121
+ log("Building manifest from module graph");
122
+ for (const file of server.environments.client.moduleGraph.fileToModulesMap.keys()) {
123
+ const modules = server.environments.client.moduleGraph.getModulesByFile(file);
124
+ if (!modules) {
125
+ continue;
126
+ }
127
+ for (const module of modules) {
128
+ if (module.file) {
129
+ const css = new Set();
130
+ getCssForModule(server, module.id, css);
131
+ manifest[normalizeModulePath(module.file, server.config.root)] =
132
+ {
133
+ file: module.url,
134
+ css: Array.from(css),
135
+ };
136
+ }
137
+ }
138
+ }
139
+ log("Manifest built successfully");
140
+ process.env.VERBOSE && log("Manifest: %o", manifest);
141
+ res.setHeader("Content-Type", "application/json");
142
+ res.end(JSON.stringify(manifest));
143
+ }
144
+ catch (e) {
145
+ log("Error building manifest: %o", e);
146
+ next(e);
147
+ }
148
+ });
149
+ },
150
+ };
151
+ };
@@ -22,6 +22,7 @@ import { prismaPlugin } from "./prismaPlugin.mjs";
22
22
  import { ssrBridgePlugin } from "./ssrBridgePlugin.mjs";
23
23
  import { hasPkgScript } from "../lib/hasPkgScript.mjs";
24
24
  import { devServerTimingPlugin } from "./devServerTimingPlugin.mjs";
25
+ import { manifestPlugin } from "./manifestPlugin.mjs";
25
26
  const determineWorkerEntryPathname = async (projectRootDir, workerConfigPath, options) => {
26
27
  if (options.entry?.worker) {
27
28
  return resolve(projectRootDir, options.entry.worker);
@@ -101,6 +102,9 @@ export const redwoodPlugin = async (options = {}) => {
101
102
  transformJsxScriptTagsPlugin({
102
103
  manifestPath: resolve(projectRootDir, "dist", "client", ".vite", "manifest.json"),
103
104
  }),
105
+ manifestPlugin({
106
+ manifestPath: resolve(projectRootDir, "dist", "client", ".vite", "manifest.json"),
107
+ }),
104
108
  moveStaticAssetsPlugin({ rootDir: projectRootDir }),
105
109
  prismaPlugin({ projectRootDir }),
106
110
  ];
@@ -55,6 +55,11 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
55
55
  // SSR modules, so we return the virtual id so that the dynamic loading
56
56
  // can happen in load()
57
57
  if (id.startsWith(VIRTUAL_SSR_PREFIX)) {
58
+ if (id.endsWith(".css")) {
59
+ const newId = id + ".js";
60
+ log("Virtual CSS module, adding .js suffix. old: %s, new: %s", id, newId);
61
+ return newId;
62
+ }
58
63
  log("Returning virtual SSR id for dev: %s", id);
59
64
  return id;
60
65
  }
@@ -86,20 +91,24 @@ export const ssrBridgePlugin = ({ clientFiles, serverFiles, }) => {
86
91
  if (id.startsWith(VIRTUAL_SSR_PREFIX) &&
87
92
  this.environment.name === "worker") {
88
93
  const realId = id.slice(VIRTUAL_SSR_PREFIX.length);
89
- log("Virtual SSR module load: id=%s, realId=%s", id, realId);
94
+ const idForFetch = realId.endsWith(".css.js")
95
+ ? realId.slice(0, -3)
96
+ : realId;
97
+ log("Virtual SSR module load: id=%s, realId=%s, idForFetch=%s", id, realId, idForFetch);
90
98
  if (isDev) {
91
- log("Dev mode: fetching SSR module for realPath=%s", realId);
92
- const result = await devServer?.environments.ssr.fetchModule(realId);
99
+ log("Dev mode: fetching SSR module for realPath=%s", idForFetch);
100
+ const result = await devServer?.environments.ssr.fetchModule(idForFetch);
93
101
  process.env.VERBOSE &&
94
- log("Fetch module result: id=%s, result=%O", realId, result);
102
+ log("Fetch module result: id=%s, result=%O", idForFetch, result);
95
103
  const code = "code" in result ? result.code : undefined;
96
- if (realId.endsWith(".css")) {
104
+ if (idForFetch.endsWith(".css") &&
105
+ !idForFetch.endsWith(".module.css")) {
97
106
  process.env.VERBOSE &&
98
- log("Not a JS file, returning code: %s", code);
99
- return code ?? "";
107
+ log("Plain CSS file, returning empty module for %s", idForFetch);
108
+ return "export default {};";
100
109
  }
101
110
  log("Fetched SSR module code length: %d", code?.length || 0);
102
- const { imports, dynamicImports } = findSsrImportSpecifiers(realId, code || "", log);
111
+ const { imports, dynamicImports } = findSsrImportSpecifiers(idForFetch, code || "", log);
103
112
  const allSpecifiers = [
104
113
  ...new Set([...imports, ...dynamicImports]),
105
114
  ].map((id) => id.startsWith("/@id/") ? id.slice("/@id/".length) : id);
@@ -1,6 +1,8 @@
1
1
  import { Project, Node, SyntaxKind } from "ts-morph";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { pathExists } from "fs-extra";
4
+ import debug from "debug";
5
+ const log = debug("rwsdk:vite:transform-jsx-script-tags");
4
6
  let manifestCache;
5
7
  const readManifest = async (manifestPath) => {
6
8
  if (manifestCache === undefined) {
@@ -34,6 +36,7 @@ function transformScriptImports(scriptContent, manifest) {
34
36
  const wrappedContent = `function __wrapper() {${scriptContent}}`;
35
37
  const scriptFile = scriptProject.createSourceFile("script.js", wrappedContent);
36
38
  let hasChanges = false;
39
+ const entryPoints = [];
37
40
  // Find all CallExpressions that look like import("path")
38
41
  scriptFile
39
42
  .getDescendantsOfKind(SyntaxKind.CallExpression)
@@ -49,10 +52,12 @@ function transformScriptImports(scriptContent, manifest) {
49
52
  if (args.length > 0 && Node.isStringLiteral(args[0])) {
50
53
  const importPath = args[0].getLiteralValue();
51
54
  if (importPath.startsWith("/")) {
55
+ log("Found dynamic import with root-relative path: %s", importPath);
56
+ entryPoints.push(importPath);
52
57
  const path = importPath.slice(1); // Remove leading slash
53
58
  if (manifest[path]) {
54
- const transformedPath = manifest[path].file;
55
- args[0].replaceWithText(`"/${transformedPath}"`);
59
+ const transformedSrc = `/${manifest[path].file}`;
60
+ args[0].setLiteralValue(transformedSrc);
56
61
  hasChanges = true;
57
62
  }
58
63
  }
@@ -66,15 +71,15 @@ function transformScriptImports(scriptContent, manifest) {
66
71
  const startPos = fullText.indexOf("{") + 1;
67
72
  const endPos = fullText.lastIndexOf("}");
68
73
  const transformedContent = fullText.substring(startPos, endPos);
69
- return { content: transformedContent, hasChanges: true };
74
+ return { content: transformedContent, hasChanges: true, entryPoints };
70
75
  }
71
76
  // Return the original content when no changes are made
72
- return { content: scriptContent, hasChanges: false };
77
+ return { content: scriptContent, hasChanges: false, entryPoints };
73
78
  }
74
79
  catch (error) {
75
80
  // If parsing fails, fall back to the original content
76
81
  console.warn("Failed to parse inline script content:", error);
77
- return { content: undefined, hasChanges: false };
82
+ return { content: undefined, hasChanges: false, entryPoints: [] };
78
83
  }
79
84
  }
80
85
  export async function transformJsxScriptTagsCode(code, manifest = {}) {
@@ -86,7 +91,7 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
86
91
  const project = new Project({ useInMemoryFileSystem: true });
87
92
  const sourceFile = project.createSourceFile("temp.tsx", code);
88
93
  let hasModifications = false;
89
- let needsRequestInfoImport = false;
94
+ const needsRequestInfoImportRef = { value: false };
90
95
  // Check for existing imports up front
91
96
  let hasRequestInfoImport = false;
92
97
  let sdkWorkerImportDecl;
@@ -124,6 +129,7 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
124
129
  if (!Node.isStringLiteral(elementType))
125
130
  return;
126
131
  const tagName = elementType.getLiteralValue();
132
+ const entryPoints = [];
127
133
  // Process script and link tags
128
134
  if (tagName === "script" || tagName === "link") {
129
135
  // Second argument should be the props object
@@ -158,25 +164,14 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
158
164
  if (Node.isStringLiteral(initializer) ||
159
165
  Node.isNoSubstitutionTemplateLiteral(initializer)) {
160
166
  const srcValue = initializer.getLiteralValue();
161
- if (srcValue.startsWith("/") && manifest[srcValue.slice(1)]) {
167
+ if (srcValue.startsWith("/")) {
168
+ entryPoints.push(srcValue);
162
169
  const path = srcValue.slice(1); // Remove leading slash
163
- const transformedSrc = manifest[path].file;
164
- const originalText = initializer.getText();
165
- const isTemplateLiteral = Node.isNoSubstitutionTemplateLiteral(initializer);
166
- const quote = isTemplateLiteral
167
- ? "`"
168
- : originalText.charAt(0);
169
- // Preserve the original quote style
170
- if (isTemplateLiteral) {
171
- initializer.replaceWithText(`\`/${transformedSrc}\``);
170
+ if (manifest[path]) {
171
+ const transformedSrc = `/${manifest[path].file}`;
172
+ initializer.setLiteralValue(transformedSrc);
173
+ hasModifications = true;
172
174
  }
173
- else if (quote === '"') {
174
- initializer.replaceWithText(`"/${transformedSrc}"`);
175
- }
176
- else {
177
- initializer.replaceWithText(`'/${transformedSrc}'`);
178
- }
179
- hasModifications = true;
180
175
  }
181
176
  }
182
177
  }
@@ -188,8 +183,9 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
188
183
  hasStringLiteralChildren = true;
189
184
  const scriptContent = initializer.getLiteralValue();
190
185
  // Transform import statements in script content using ts-morph
191
- const { content: transformedContent, hasChanges } = transformScriptImports(scriptContent, manifest);
192
- if (hasChanges && transformedContent) {
186
+ const { content: transformedContent, hasChanges: contentHasChanges, entryPoints: dynamicEntryPoints, } = transformScriptImports(scriptContent, manifest);
187
+ entryPoints.push(...dynamicEntryPoints);
188
+ if (contentHasChanges && transformedContent) {
193
189
  // Get the raw text with quotes to determine the exact format
194
190
  const isTemplateLiteral = Node.isNoSubstitutionTemplateLiteral(initializer);
195
191
  if (isTemplateLiteral) {
@@ -231,7 +227,7 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
231
227
  initializer: "requestInfo.rw.nonce",
232
228
  });
233
229
  if (!hasRequestInfoImport) {
234
- needsRequestInfoImport = true;
230
+ needsRequestInfoImportRef.value = true;
235
231
  }
236
232
  hasModifications = true;
237
233
  }
@@ -271,9 +267,42 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
271
267
  }
272
268
  }
273
269
  }
270
+ if (entryPoints.length > 0) {
271
+ log("Found %d script entry points, adding to scripts to be loaded: %o", entryPoints.length, entryPoints);
272
+ const sideEffects = entryPoints
273
+ .map((p) => `(requestInfo.rw.scriptsToBeLoaded.add("${p}"))`)
274
+ .join(",\n");
275
+ const leadingCommentRanges = callExpr.getLeadingCommentRanges();
276
+ const pureComment = leadingCommentRanges.find((r) => r.getText().includes("@__PURE__"));
277
+ const callExprText = callExpr.getText();
278
+ if (pureComment) {
279
+ const pureCommentText = pureComment.getText();
280
+ const newText = `(
281
+ ${sideEffects},
282
+ ${pureCommentText} ${callExprText}
283
+ )`;
284
+ const fullText = callExpr.getFullText();
285
+ const leadingTriviaText = fullText.substring(0, fullText.length - callExprText.length);
286
+ const newLeadingTriviaText = leadingTriviaText.replace(pureCommentText, "");
287
+ // By replacing from `getFullStart`, we remove the original node and all its leading trivia
288
+ // and replace it with our manually reconstructed string.
289
+ // This should correctly move the pure comment and preserve other comments and whitespace.
290
+ callExpr
291
+ .getSourceFile()
292
+ .replaceText([callExpr.getFullStart(), callExpr.getEnd()], newLeadingTriviaText + newText);
293
+ }
294
+ else {
295
+ callExpr.replaceWithText(`(
296
+ ${sideEffects},
297
+ ${callExprText}
298
+ )`);
299
+ }
300
+ needsRequestInfoImportRef.value = true;
301
+ hasModifications = true;
302
+ }
274
303
  });
275
304
  // Add requestInfo import if needed and not already imported
276
- if (needsRequestInfoImport && hasModifications) {
305
+ if (needsRequestInfoImportRef.value && hasModifications) {
277
306
  if (sdkWorkerImportDecl) {
278
307
  // Module is imported but need to add requestInfo
279
308
  if (!hasRequestInfoImport) {
@@ -300,16 +329,28 @@ export async function transformJsxScriptTagsCode(code, manifest = {}) {
300
329
  export const transformJsxScriptTagsPlugin = ({ manifestPath, }) => {
301
330
  let isBuild = false;
302
331
  return {
303
- name: "rwsdk:transform-jsx-script-tags",
332
+ name: "rwsdk:vite:transform-jsx-script-tags",
304
333
  configResolved(config) {
305
334
  isBuild = config.command === "build";
306
335
  },
307
- async transform(code) {
308
- if (this.environment.name !== "worker") {
309
- return;
336
+ async transform(code, id) {
337
+ if (this.environment?.name === "worker" &&
338
+ id.endsWith(".tsx") &&
339
+ !id.includes("node_modules") &&
340
+ hasJsxFunctions(code)) {
341
+ const manifest = isBuild ? await readManifest(manifestPath) : {};
342
+ const result = await transformJsxScriptTagsCode(code, manifest);
343
+ if (result) {
344
+ log("Transformed JSX script tags in %s", id);
345
+ process.env.VERBOSE &&
346
+ log("New Document code for %s:\n%s", id, result.code);
347
+ return {
348
+ code: result.code,
349
+ map: null,
350
+ };
351
+ }
310
352
  }
311
- const manifest = isBuild ? await readManifest(manifestPath) : {};
312
- return transformJsxScriptTagsCode(code, manifest);
353
+ return null;
313
354
  },
314
355
  };
315
356
  };
@@ -16,11 +16,14 @@ describe("transformJsxScriptTagsCode", () => {
16
16
  const result = await transformJsxScriptTagsCode(code, mockManifest);
17
17
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
18
18
 
19
- jsx("script", {
19
+ (
20
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
21
+ jsx("script", {
20
22
  src: "/assets/client-a1b2c3d4.js",
21
23
  type: "module",
22
24
  nonce: requestInfo.rw.nonce
23
25
  })
26
+ )
24
27
  `);
25
28
  });
26
29
  it("transforms inline scripts with dynamic imports", async () => {
@@ -33,11 +36,14 @@ describe("transformJsxScriptTagsCode", () => {
33
36
  const result = await transformJsxScriptTagsCode(code, mockManifest);
34
37
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
35
38
 
36
- jsx("script", {
39
+ (
40
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
41
+ jsx("script", {
37
42
  type: "module",
38
- children: "import(\\"\/assets\/client-a1b2c3d4.js\\").then(module => { console.log(module); })",
43
+ children: "import('/assets/client-a1b2c3d4.js').then(module => { console.log(module); })",
39
44
  nonce: requestInfo.rw.nonce
40
45
  })
46
+ )
41
47
  `);
42
48
  });
43
49
  it("transforms inline scripts with type=module", async () => {
@@ -47,9 +53,12 @@ describe("transformJsxScriptTagsCode", () => {
47
53
  const result = await transformJsxScriptTagsCode(code, mockManifest);
48
54
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
49
55
 
50
- jsx("script", { type: "module", children: "import(\\"\/assets\/client-a1b2c3d4.js\\")",
56
+ (
57
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
58
+ jsx("script", { type: "module", children: "import('/assets/client-a1b2c3d4.js')",
51
59
  nonce: requestInfo.rw.nonce
52
60
  })
61
+ )
53
62
  `);
54
63
  });
55
64
  it("transforms inline scripts with multiline content", async () => {
@@ -69,18 +78,21 @@ describe("transformJsxScriptTagsCode", () => {
69
78
  const result = await transformJsxScriptTagsCode(code, mockManifest);
70
79
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
71
80
 
72
- jsx("script", {
81
+ (
82
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/entry.js")),
83
+ jsx("script", {
73
84
  type: "module",
74
85
  children: \`
75
86
  // Some comments here
76
87
  const init = async () => {
77
- await import("/assets/entry-e5f6g7h8.js");
88
+ await import('/assets/entry-e5f6g7h8.js');
78
89
  console.log('initialized');
79
90
  };
80
91
  init();
81
92
  \`,
82
93
  nonce: requestInfo.rw.nonce
83
94
  })
95
+ )
84
96
  `);
85
97
  });
86
98
  it("transforms multiple imports in the same inline script", async () => {
@@ -96,14 +108,18 @@ describe("transformJsxScriptTagsCode", () => {
96
108
  const result = await transformJsxScriptTagsCode(code, mockManifest);
97
109
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
98
110
 
99
- jsx("script", {
111
+ (
112
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
113
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/entry.js")),
114
+ jsx("script", {
100
115
  type: "module",
101
116
  children: \`
102
- import("/assets/client-a1b2c3d4.js");
103
- import("/assets/entry-e5f6g7h8.js");
117
+ import('/assets/client-a1b2c3d4.js');
118
+ import('/assets/entry-e5f6g7h8.js');
104
119
  \`,
105
120
  nonce: requestInfo.rw.nonce
106
121
  })
122
+ )
107
123
  `);
108
124
  });
109
125
  it("transforms link href attributes with preload rel", async () => {
@@ -177,9 +193,12 @@ describe("transformJsxScriptTagsCode", () => {
177
193
  jsx("body", {
178
194
  children: [
179
195
  jsx("div", { id: "root", children: props.children }),
180
- jsx("script", { children: "import(\\"\/assets\/client-a1b2c3d4.js\\")",
181
- nonce: requestInfo.rw.nonce
182
- })
196
+ (
197
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
198
+ jsx("script", { children: "import(\\"\/assets\/client-a1b2c3d4.js\\")",
199
+ nonce: requestInfo.rw.nonce
200
+ })
201
+ )
183
202
  ]
184
203
  })
185
204
  ]
@@ -203,11 +222,14 @@ describe("transformJsxScriptTagsCode", () => {
203
222
  const result = await transformJsxScriptTagsCode(code, mockManifest);
204
223
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
205
224
 
206
- jsx("script", {
225
+ (
226
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/non-existent.js")),
227
+ jsx("script", {
207
228
  src: "/src/non-existent.js",
208
229
  type: "module",
209
230
  nonce: requestInfo.rw.nonce
210
231
  })
232
+ )
211
233
  `);
212
234
  });
213
235
  it("adds nonce to script tags with src attribute and imports requestInfo", async () => {
@@ -220,11 +242,14 @@ describe("transformJsxScriptTagsCode", () => {
220
242
  const result = await transformJsxScriptTagsCode(code, mockManifest);
221
243
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
222
244
 
223
- jsx("script", {
245
+ (
246
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
247
+ jsx("script", {
224
248
  src: "/assets/client-a1b2c3d4.js",
225
249
  type: "module",
226
250
  nonce: requestInfo.rw.nonce
227
251
  })
252
+ )
228
253
  `);
229
254
  });
230
255
  it("adds nonce to script tags with string literal children", async () => {
@@ -324,11 +349,14 @@ describe("transformJsxScriptTagsCode", () => {
324
349
  const result = await transformJsxScriptTagsCode(code);
325
350
  expect(result?.code).toEqual(`import { requestInfo } from "rwsdk/worker";
326
351
 
327
- jsx("script", {
352
+ (
353
+ (requestInfo.rw.scriptsToBeLoaded.add("/src/client.tsx")),
354
+ jsx("script", {
328
355
  src: "/src/client.tsx",
329
356
  type: "module",
330
357
  nonce: requestInfo.rw.nonce
331
358
  })
359
+ )
332
360
  `);
333
361
  });
334
362
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.1.26",
3
+ "version": "0.2.0-alpha.0",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {