rwsdk 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (158) hide show
  1. package/dist/lib/e2e/browser.d.mts +10 -0
  2. package/dist/lib/e2e/browser.mjs +124 -0
  3. package/dist/lib/e2e/dev.d.mts +8 -0
  4. package/dist/lib/e2e/dev.mjs +242 -0
  5. package/dist/lib/e2e/environment.d.mts +14 -0
  6. package/dist/lib/e2e/environment.mjs +266 -0
  7. package/dist/lib/e2e/index.d.mts +8 -0
  8. package/dist/lib/e2e/index.mjs +8 -0
  9. package/dist/lib/e2e/poll.d.mts +8 -0
  10. package/dist/lib/e2e/poll.mjs +31 -0
  11. package/dist/lib/e2e/release.d.mts +56 -0
  12. package/dist/lib/e2e/release.mjs +559 -0
  13. package/dist/lib/e2e/retry.d.mts +4 -0
  14. package/dist/lib/e2e/retry.mjs +16 -0
  15. package/dist/lib/e2e/setup.d.mts +2 -0
  16. package/dist/lib/e2e/setup.mjs +1 -0
  17. package/dist/lib/e2e/tarball.d.mts +14 -0
  18. package/dist/lib/e2e/tarball.mjs +99 -0
  19. package/dist/lib/e2e/testHarness.d.mts +132 -0
  20. package/dist/lib/e2e/testHarness.mjs +437 -0
  21. package/dist/lib/e2e/types.d.mts +32 -0
  22. package/dist/lib/getShortName.mjs +6 -1
  23. package/dist/lib/getShortName.test.d.mts +1 -0
  24. package/dist/lib/getShortName.test.mjs +25 -0
  25. package/dist/lib/hasPkgScript.d.mts +4 -1
  26. package/dist/lib/hasPkgScript.mjs +9 -6
  27. package/dist/lib/hasPkgScript.test.d.mts +1 -0
  28. package/dist/lib/hasPkgScript.test.mjs +33 -0
  29. package/dist/lib/jsonUtils.mjs +3 -0
  30. package/dist/lib/jsonUtils.test.d.mts +1 -0
  31. package/dist/lib/jsonUtils.test.mjs +90 -0
  32. package/dist/lib/normalizeModulePath.d.mts +5 -0
  33. package/dist/lib/normalizeModulePath.mjs +1 -1
  34. package/dist/lib/normalizeModulePath.test.d.mts +1 -0
  35. package/dist/lib/{normalizeModulePath.test.js → normalizeModulePath.test.mjs} +20 -1
  36. package/dist/lib/smokeTests/browser.mjs +3 -94
  37. package/dist/lib/smokeTests/development.mjs +2 -223
  38. package/dist/lib/smokeTests/environment.d.mts +4 -11
  39. package/dist/lib/smokeTests/environment.mjs +10 -158
  40. package/dist/lib/smokeTests/release.d.mts +2 -49
  41. package/dist/lib/smokeTests/release.mjs +3 -503
  42. package/dist/llms/rules/middleware.d.ts +1 -1
  43. package/dist/llms/rules/middleware.js +4 -4
  44. package/dist/runtime/entries/worker.d.ts +0 -1
  45. package/dist/runtime/entries/worker.js +0 -1
  46. package/dist/runtime/lib/auth/session.d.ts +2 -2
  47. package/dist/runtime/lib/auth/session.js +4 -4
  48. package/dist/runtime/lib/memoizeOnId.test.d.ts +1 -0
  49. package/dist/runtime/lib/memoizeOnId.test.js +49 -0
  50. package/dist/runtime/lib/realtime/protocol.test.d.ts +1 -0
  51. package/dist/runtime/lib/realtime/protocol.test.js +107 -0
  52. package/dist/runtime/lib/realtime/shared.test.d.ts +1 -0
  53. package/dist/runtime/lib/realtime/shared.test.js +18 -0
  54. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.d.ts +1 -0
  55. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +66 -0
  56. package/dist/runtime/lib/realtime/worker.d.ts +1 -1
  57. package/dist/runtime/lib/router.js +40 -22
  58. package/dist/runtime/lib/router.test.js +590 -2
  59. package/dist/runtime/lib/rwContext.d.ts +22 -0
  60. package/dist/runtime/lib/rwContext.js +1 -0
  61. package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +18 -0
  62. package/dist/runtime/lib/stitchDocumentAndAppStreams.js +143 -0
  63. package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +2 -1
  64. package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +6 -6
  65. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.d.ts +1 -0
  66. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +49 -0
  67. package/dist/runtime/register/worker.d.ts +1 -1
  68. package/dist/runtime/register/worker.js +33 -21
  69. package/dist/runtime/render/assembleDocument.d.ts +6 -0
  70. package/dist/runtime/render/assembleDocument.js +22 -0
  71. package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
  72. package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
  73. package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
  74. package/dist/runtime/render/normalizeActionResult.js +43 -0
  75. package/dist/runtime/render/preloads.d.ts +2 -2
  76. package/dist/runtime/render/preloads.js +2 -3
  77. package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
  78. package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
  79. package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
  80. package/dist/runtime/render/renderHtmlStream.js +31 -0
  81. package/dist/runtime/render/renderToRscStream.d.ts +5 -3
  82. package/dist/runtime/render/renderToRscStream.js +12 -41
  83. package/dist/runtime/render/renderToStream.d.ts +2 -1
  84. package/dist/runtime/render/renderToStream.js +15 -8
  85. package/dist/runtime/render/stylesheets.d.ts +2 -2
  86. package/dist/runtime/render/stylesheets.js +2 -3
  87. package/dist/runtime/requestInfo/types.d.ts +0 -2
  88. package/dist/runtime/requestInfo/worker.js +1 -9
  89. package/dist/runtime/ssrBridge.d.ts +2 -1
  90. package/dist/runtime/ssrBridge.js +2 -1
  91. package/dist/runtime/worker.d.ts +1 -0
  92. package/dist/runtime/worker.js +11 -14
  93. package/dist/scripts/debug-sync.mjs +102 -133
  94. package/dist/vite/buildApp.d.mts +2 -1
  95. package/dist/vite/buildApp.mjs +9 -5
  96. package/dist/vite/checkIsUsingPrisma.d.mts +4 -0
  97. package/dist/vite/checkIsUsingPrisma.mjs +2 -2
  98. package/dist/vite/checkIsUsingPrisma.test.d.mts +1 -0
  99. package/dist/vite/checkIsUsingPrisma.test.mjs +30 -0
  100. package/dist/vite/configPlugin.mjs +54 -14
  101. package/dist/vite/createDirectiveLookupPlugin.d.mts +9 -0
  102. package/dist/vite/createDirectiveLookupPlugin.mjs +33 -29
  103. package/dist/vite/createDirectiveLookupPlugin.test.d.mts +1 -0
  104. package/dist/vite/createDirectiveLookupPlugin.test.mjs +40 -0
  105. package/dist/vite/directiveModulesDevPlugin.d.mts +4 -1
  106. package/dist/vite/directiveModulesDevPlugin.mjs +6 -5
  107. package/dist/vite/directiveModulesDevPlugin.test.d.mts +1 -0
  108. package/dist/vite/directiveModulesDevPlugin.test.mjs +59 -0
  109. package/dist/vite/directivesPlugin.d.mts +1 -0
  110. package/dist/vite/directivesPlugin.mjs +1 -1
  111. package/dist/vite/directivesPlugin.test.d.mts +1 -0
  112. package/dist/vite/directivesPlugin.test.mjs +24 -0
  113. package/dist/vite/ensureAliasArray.test.d.mts +1 -0
  114. package/dist/vite/ensureAliasArray.test.mjs +71 -0
  115. package/dist/vite/findSpecifiers.mjs +2 -1
  116. package/dist/vite/findSpecifiers.test.d.mts +1 -0
  117. package/dist/vite/findSpecifiers.test.mjs +202 -0
  118. package/dist/vite/findSsrSpecifiers.test.d.mts +1 -0
  119. package/dist/vite/findSsrSpecifiers.test.mjs +99 -0
  120. package/dist/vite/hasDirective.d.mts +6 -3
  121. package/dist/vite/hasDirective.mjs +43 -27
  122. package/dist/vite/hasDirective.test.d.mts +1 -0
  123. package/dist/vite/hasDirective.test.mjs +107 -0
  124. package/dist/vite/isJsFile.test.d.mts +1 -0
  125. package/dist/vite/isJsFile.test.mjs +38 -0
  126. package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +2 -2
  127. package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +28 -23
  128. package/dist/vite/linkerPlugin.d.mts +8 -0
  129. package/dist/vite/linkerPlugin.mjs +30 -22
  130. package/dist/vite/linkerPlugin.test.d.mts +1 -0
  131. package/dist/vite/linkerPlugin.test.mjs +41 -0
  132. package/dist/vite/miniflareHMRPlugin.d.mts +5 -0
  133. package/dist/vite/miniflareHMRPlugin.mjs +2 -2
  134. package/dist/vite/miniflareHMRPlugin.test.d.mts +1 -0
  135. package/dist/vite/miniflareHMRPlugin.test.mjs +42 -0
  136. package/dist/vite/redwoodPlugin.d.mts +9 -0
  137. package/dist/vite/redwoodPlugin.mjs +29 -5
  138. package/dist/vite/redwoodPlugin.test.d.mts +1 -0
  139. package/dist/vite/redwoodPlugin.test.mjs +34 -0
  140. package/dist/vite/resolveForcedPaths.d.mts +4 -0
  141. package/dist/vite/resolveForcedPaths.mjs +9 -0
  142. package/dist/vite/runDirectivesScan.d.mts +22 -1
  143. package/dist/vite/runDirectivesScan.mjs +105 -58
  144. package/dist/vite/runDirectivesScan.test.d.mts +1 -0
  145. package/dist/vite/runDirectivesScan.test.mjs +73 -0
  146. package/dist/vite/ssrBridgePlugin.mjs +8 -1
  147. package/dist/vite/transformClientComponents.mjs +6 -4
  148. package/dist/vite/transformClientComponents.test.mjs +116 -58
  149. package/dist/vite/transformServerFunctions.d.mts +1 -1
  150. package/dist/vite/transformServerFunctions.mjs +1 -1
  151. package/dist/vite/transformServerFunctions.test.mjs +3 -3
  152. package/package.json +56 -47
  153. package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
  154. package/dist/runtime/imports/resolveSSRValue.js +0 -8
  155. package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
  156. package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
  157. package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
  158. /package/dist/lib/{normalizeModulePath.test.d.ts → e2e/types.mjs} +0 -0
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { findSsrImportCallSites } from "./findSsrSpecifiers.mjs";
3
+ describe("findSsrImportCallSites", () => {
4
+ it("should find __vite_ssr_import__ with double quotes", () => {
5
+ const code = `const a = __vite_ssr_import__("module-a");`;
6
+ const results = findSsrImportCallSites("test.ts", code);
7
+ expect(results).toHaveLength(1);
8
+ expect(results[0]).toEqual({
9
+ start: 10,
10
+ end: 41,
11
+ specifier: "module-a",
12
+ kind: "import",
13
+ });
14
+ });
15
+ it("should find __vite_ssr_import__ with single quotes", () => {
16
+ const code = `const a = __vite_ssr_import__('module-a');`;
17
+ const results = findSsrImportCallSites("test.ts", code);
18
+ expect(results).toHaveLength(1);
19
+ expect(results[0]).toEqual({
20
+ start: 10,
21
+ end: 41,
22
+ specifier: "module-a",
23
+ kind: "import",
24
+ });
25
+ });
26
+ it("should find __vite_ssr_dynamic_import__ with double quotes", () => {
27
+ const code = `const a = __vite_ssr_dynamic_import__("module-a");`;
28
+ const results = findSsrImportCallSites("test.ts", code);
29
+ expect(results).toHaveLength(1);
30
+ expect(results[0]).toEqual({
31
+ start: 10,
32
+ end: 49,
33
+ specifier: "module-a",
34
+ kind: "dynamic_import",
35
+ });
36
+ });
37
+ it("should find __vite_ssr_dynamic_import__ with single quotes", () => {
38
+ const code = `const a = __vite_ssr_dynamic_import__('module-a');`;
39
+ const results = findSsrImportCallSites("test.ts", code);
40
+ expect(results).toHaveLength(1);
41
+ expect(results[0]).toEqual({
42
+ start: 10,
43
+ end: 49,
44
+ specifier: "module-a",
45
+ kind: "dynamic_import",
46
+ });
47
+ });
48
+ it("should find calls with additional arguments", () => {
49
+ const code = `const a = __vite_ssr_import__('module-a', { ssr: true });`;
50
+ const results = findSsrImportCallSites("test.ts", code);
51
+ expect(results).toHaveLength(1);
52
+ expect(results[0]).toEqual({
53
+ start: 10,
54
+ end: 56,
55
+ specifier: "module-a",
56
+ kind: "import",
57
+ });
58
+ });
59
+ it("should find a mix of different calls", () => {
60
+ const code = `
61
+ const a = __vite_ssr_import__("module-a");
62
+ const b = __vite_ssr_dynamic_import__('module-b');
63
+ `;
64
+ const results = findSsrImportCallSites("test.ts", code);
65
+ expect(results).toHaveLength(2);
66
+ expect(results).toEqual(expect.arrayContaining([
67
+ {
68
+ start: 17,
69
+ end: 48,
70
+ specifier: "module-a",
71
+ kind: "import",
72
+ },
73
+ {
74
+ start: 66,
75
+ end: 105,
76
+ specifier: "module-b",
77
+ kind: "dynamic_import",
78
+ },
79
+ ]));
80
+ });
81
+ it("should return correct ranges for replacement", () => {
82
+ const code = `__vite_ssr_import__("module-a")`;
83
+ const results = findSsrImportCallSites("test.ts", code);
84
+ expect(results).toHaveLength(1);
85
+ const { start, end } = results[0];
86
+ expect(code.substring(start, end)).toBe('__vite_ssr_import__("module-a")');
87
+ });
88
+ it("should return an empty array when no calls are found", () => {
89
+ const code = `import a from "module-a";`;
90
+ const results = findSsrImportCallSites("test.ts", code);
91
+ expect(results).toHaveLength(0);
92
+ });
93
+ it("should handle tsx files correctly", () => {
94
+ const code = `const a = () => <div>{__vite_ssr_import__("module-a")}</div>;`;
95
+ const results = findSsrImportCallSites("test.tsx", code);
96
+ expect(results).toHaveLength(1);
97
+ expect(results[0].specifier).toBe("module-a");
98
+ });
99
+ });
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Efficiently checks if a React directive (e.g., "use server", "use client")
3
- * is present in the code. Optimized for performance with a two-step approach:
4
- * 1. Quick string search to see if directive exists anywhere
5
- * 2. Line-by-line check only if the directive might be present
3
+ * is present in the code.
4
+ *
5
+ * This function is optimized for performance by only checking the first few
6
+ * lines of the code, as directives must appear at the very top of a file.
7
+ * It handles comments, whitespace, and any valid directive prologue
8
+ * (e.g., "use strict").
6
9
  */
7
10
  export declare function hasDirective(code: string, directive: string): boolean;
@@ -1,54 +1,70 @@
1
1
  /**
2
2
  * Efficiently checks if a React directive (e.g., "use server", "use client")
3
- * is present in the code. Optimized for performance with a two-step approach:
4
- * 1. Quick string search to see if directive exists anywhere
5
- * 2. Line-by-line check only if the directive might be present
3
+ * is present in the code.
4
+ *
5
+ * This function is optimized for performance by only checking the first few
6
+ * lines of the code, as directives must appear at the very top of a file.
7
+ * It handles comments, whitespace, and any valid directive prologue
8
+ * (e.g., "use strict").
6
9
  */
7
10
  export function hasDirective(code, directive) {
8
- // Quick performance check: if directive doesn't exist anywhere, skip line checking
9
- const singleQuoteDirective = `'${directive}'`;
10
- const doubleQuoteDirective = `"${directive}"`;
11
- if (!code.includes(singleQuoteDirective) &&
12
- !code.includes(doubleQuoteDirective)) {
13
- return false;
14
- }
15
- // Split into lines and check each one
16
- const lines = code.split("\n");
11
+ const lines = code.slice(0, 512).split("\n"); // Check first ~512 chars
17
12
  let inMultiLineComment = false;
13
+ let foundUseClient = false;
14
+ let foundTargetDirective = false;
15
+ const doubleQuoteDirective = `"${directive}"`;
16
+ const singleQuoteDirective = `'${directive}'`;
17
+ const doubleQuoteUseClient = `"use client"`;
18
+ const singleQuoteUseClient = `'use client'`;
18
19
  for (const line of lines) {
19
20
  const trimmedLine = line.trim();
20
- // Skip empty lines
21
21
  if (trimmedLine.length === 0) {
22
22
  continue;
23
23
  }
24
- // Handle multi-line comments
25
- if (trimmedLine.startsWith("/*")) {
26
- inMultiLineComment = true;
27
- // Check if the comment ends on the same line
24
+ if (inMultiLineComment) {
28
25
  if (trimmedLine.includes("*/")) {
29
26
  inMultiLineComment = false;
30
27
  }
31
28
  continue;
32
29
  }
33
- if (inMultiLineComment) {
34
- // Check if this line ends the multi-line comment
35
- if (trimmedLine.includes("*/")) {
36
- inMultiLineComment = false;
30
+ if (trimmedLine.startsWith("/*")) {
31
+ if (!trimmedLine.includes("*/")) {
32
+ inMultiLineComment = true;
37
33
  }
38
34
  continue;
39
35
  }
40
- // Skip single-line comments
41
36
  if (trimmedLine.startsWith("//")) {
42
37
  continue;
43
38
  }
44
- // Check if this line starts with the directive
39
+ const cleanedLine = trimmedLine.endsWith(";")
40
+ ? trimmedLine.slice(0, -1)
41
+ : trimmedLine;
42
+ if (trimmedLine.startsWith(doubleQuoteUseClient) ||
43
+ trimmedLine.startsWith(singleQuoteUseClient)) {
44
+ foundUseClient = true;
45
+ if (directive === "use client") {
46
+ return true;
47
+ }
48
+ }
45
49
  if (trimmedLine.startsWith(doubleQuoteDirective) ||
46
50
  trimmedLine.startsWith(singleQuoteDirective)) {
47
- return true;
51
+ foundTargetDirective = true;
52
+ if (directive !== "use server") {
53
+ return true;
54
+ }
48
55
  }
49
- // If we hit a non-empty, non-comment line that's not a directive, we can stop
50
- // (directives must be at the top of the file/scope, after comments)
56
+ // Any other string literal is part of a valid directive prologue.
57
+ // We can continue searching.
58
+ if (trimmedLine.startsWith('"') || trimmedLine.startsWith("'")) {
59
+ continue;
60
+ }
61
+ // If we encounter any other non-directive, non-comment, non-string-literal
62
+ // line of code, the directive prologue is over. Stop.
51
63
  break;
52
64
  }
53
- return false;
65
+ // If looking for 'use server' and 'use client' was found, return false (client takes priority)
66
+ if (directive === "use server" && foundUseClient) {
67
+ return false;
68
+ }
69
+ return foundTargetDirective;
54
70
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { hasDirective } from "./hasDirective.mjs";
3
+ describe("hasDirective", () => {
4
+ it('should find "use client" directive', () => {
5
+ const code = `"use client"; import React from "react";`;
6
+ expect(hasDirective(code, "use client")).toBe(true);
7
+ });
8
+ it('should find "use server" directive', () => {
9
+ const code = `'use server'; export async function myAction() {}`;
10
+ expect(hasDirective(code, "use server")).toBe(true);
11
+ });
12
+ it("should not find a directive that is not there", () => {
13
+ const code = `import React from "react";`;
14
+ expect(hasDirective(code, "use client")).toBe(false);
15
+ });
16
+ it('should find "use client" directive with single quotes', () => {
17
+ const code = `'use client'; import React from "react";`;
18
+ expect(hasDirective(code, "use client")).toBe(true);
19
+ });
20
+ it("should find directive when preceded by comments and whitespace", () => {
21
+ const code = `
22
+ // This is a client component
23
+ /* And here is another comment */
24
+
25
+ "use client";
26
+ import React from 'react';
27
+ export default () => <div>Hello</div>;
28
+ `;
29
+ expect(hasDirective(code, "use client")).toBe(true);
30
+ });
31
+ it('should find "use client" directive when preceded by "use strict"', () => {
32
+ const code = `
33
+ "use strict";
34
+ "use client";
35
+ import React from 'react';
36
+ export default () => <div>Hello</div>;
37
+ `;
38
+ expect(hasDirective(code, "use client")).toBe(true);
39
+ });
40
+ it('should find "use server" directive when preceded by "use strict" and comments', () => {
41
+ const code = `
42
+ // server stuff
43
+ "use strict";
44
+ /* another comment */
45
+ "use server";
46
+ export async function myAction() {}
47
+ `;
48
+ expect(hasDirective(code, "use server")).toBe(true);
49
+ });
50
+ it("should find directive when preceded by another string literal directive", () => {
51
+ const code = `
52
+ "use awesome"; // Some other directive
53
+ "use client";
54
+ import React from 'react';
55
+ export default () => <div>Hello</div>;
56
+ `;
57
+ expect(hasDirective(code, "use client")).toBe(true);
58
+ });
59
+ it("should return false if no directive is present", () => {
60
+ const code = `import React from "react";
61
+ export default () => <div>Hello</div>;`;
62
+ expect(hasDirective(code, "use client")).toBe(false);
63
+ });
64
+ it("should return false if the directive is commented out", () => {
65
+ const code = `// "use client";
66
+ import React from "react";
67
+ export default () => <div>Hello</div>;`;
68
+ expect(hasDirective(code, "use client")).toBe(false);
69
+ });
70
+ it("should return false if the directive appears after code", () => {
71
+ const code = `import React from "react";
72
+ "use client";
73
+ export default () => <div>Hello</div>;`;
74
+ expect(hasDirective(code, "use client")).toBe(false);
75
+ });
76
+ it("should handle multi-line comments correctly", () => {
77
+ const code = `
78
+ /*
79
+ * "use client";
80
+ */
81
+ import React from "react";
82
+ `;
83
+ expect(hasDirective(code, "use client")).toBe(false);
84
+ });
85
+ it("should handle code with no whitespace", () => {
86
+ const code = `"use client";import React from "react";`;
87
+ expect(hasDirective(code, "use client")).toBe(true);
88
+ });
89
+ it("should handle empty code", () => {
90
+ const code = "";
91
+ expect(hasDirective(code, "use client")).toBe(false);
92
+ });
93
+ it("should handle code with only whitespace", () => {
94
+ const code = " \n\t ";
95
+ expect(hasDirective(code, "use client")).toBe(false);
96
+ });
97
+ it("should handle files with only comments", () => {
98
+ const code = `// comment 1
99
+ /* comment 2 */`;
100
+ expect(hasDirective(code, "use client")).toBe(false);
101
+ });
102
+ it("should prioritize 'use client' over 'use server'", () => {
103
+ const code = `'use client';\n'use server';\nconsole.log('hello');`;
104
+ expect(hasDirective(code, "use client")).toBe(true);
105
+ expect(hasDirective(code, "use server")).toBe(false);
106
+ });
107
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { isJsFile } from "./isJsFile.mjs";
3
+ describe("isJsFile", () => {
4
+ const matchingExtensions = [
5
+ "file.js",
6
+ "file.jsx",
7
+ "file.ts",
8
+ "file.tsx",
9
+ "file.mjs",
10
+ "file.mts",
11
+ "file.cjs",
12
+ "file.cts",
13
+ "/path/to/component.js",
14
+ "../relative/path.tsx",
15
+ ];
16
+ const nonMatchingExtensions = [
17
+ "file.css",
18
+ "file.html",
19
+ "file.json",
20
+ "file.txt",
21
+ "file.js.map",
22
+ "file_js",
23
+ "filejs",
24
+ "",
25
+ "no-extension",
26
+ "/path/to/image.png",
27
+ ];
28
+ matchingExtensions.forEach((filepath) => {
29
+ it(`should return true for "${filepath}"`, () => {
30
+ expect(isJsFile(filepath)).toBe(true);
31
+ });
32
+ });
33
+ nonMatchingExtensions.forEach((filepath) => {
34
+ it(`should return false for "${filepath}"`, () => {
35
+ expect(isJsFile(filepath)).toBe(false);
36
+ });
37
+ });
38
+ });
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from "vite";
2
2
  import enhancedResolve from "enhanced-resolve";
3
- export declare const ENV_REACT_IMPORTS: {
3
+ export declare const ENV_PREDEFINED_IMPORTS: {
4
4
  worker: string[];
5
5
  ssr: string[];
6
6
  client: string[];
@@ -10,6 +10,6 @@ export declare const ENV_RESOLVERS: {
10
10
  worker: enhancedResolve.ResolveFunction;
11
11
  client: enhancedResolve.ResolveFunction;
12
12
  };
13
- export declare const reactConditionsResolverPlugin: ({ projectRootDir, }: {
13
+ export declare const knownDepsResolverPlugin: ({ projectRootDir, }: {
14
14
  projectRootDir: string;
15
15
  }) => Plugin[];
@@ -2,9 +2,14 @@ import debug from "debug";
2
2
  import { ROOT_DIR } from "../lib/constants.mjs";
3
3
  import enhancedResolve from "enhanced-resolve";
4
4
  import { ensureAliasArray } from "./ensureAliasArray.mjs";
5
- const log = debug("rwsdk:vite:react-conditions-resolver-plugin");
6
- const REACT_PREFIXES = ["react", "react-dom", "react-server-dom-webpack"];
7
- export const ENV_REACT_IMPORTS = {
5
+ const log = debug("rwsdk:vite:known-deps-resolver-plugin");
6
+ const KNOWN_PREFIXES = [
7
+ "react",
8
+ "react-dom",
9
+ "react-server-dom-webpack",
10
+ "rwsdk",
11
+ ];
12
+ export const ENV_PREDEFINED_IMPORTS = {
8
13
  worker: [
9
14
  "react",
10
15
  "react-dom",
@@ -42,10 +47,10 @@ export const ENV_RESOLVERS = {
42
47
  conditionNames: ["browser", "default"],
43
48
  }),
44
49
  };
45
- function resolveReactImport(id, envName, projectRootDir, isReactImportKnown = false) {
46
- if (!isReactImportKnown) {
47
- const isReactImport = REACT_PREFIXES.some((prefix) => id === prefix || id.startsWith(`${prefix}/`));
48
- if (!isReactImport) {
50
+ function resolveKnownImport(id, envName, projectRootDir, isPrefixedImport = false) {
51
+ if (!isPrefixedImport) {
52
+ const isKnownImport = KNOWN_PREFIXES.some((prefix) => id === prefix || id.startsWith(`${prefix}/`));
53
+ if (!isKnownImport) {
49
54
  return undefined;
50
55
  }
51
56
  }
@@ -70,13 +75,13 @@ function resolveReactImport(id, envName, projectRootDir, isReactImportKnown = fa
70
75
  }
71
76
  return resolved;
72
77
  }
73
- function resolveEnvImportMappings(env, projectRootDir) {
78
+ function resolvePredefinedEnvImportMappings(env, projectRootDir) {
74
79
  process.env.VERBOSE &&
75
80
  log("Resolving environment import mappings for env=%s", env);
76
81
  const mappings = new Map();
77
- const reactImports = ENV_REACT_IMPORTS[env];
78
- for (const importRequest of reactImports) {
79
- const resolved = resolveReactImport(importRequest, env, projectRootDir, true);
82
+ const predefinedImports = ENV_PREDEFINED_IMPORTS[env];
83
+ for (const importRequest of predefinedImports) {
84
+ const resolved = resolveKnownImport(importRequest, env, projectRootDir, true);
80
85
  if (resolved) {
81
86
  mappings.set(importRequest, resolved);
82
87
  process.env.VERBOSE &&
@@ -87,27 +92,27 @@ function resolveEnvImportMappings(env, projectRootDir) {
87
92
  log("Environment import mappings complete for env=%s: %d mappings", env, mappings.size);
88
93
  return mappings;
89
94
  }
90
- export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
91
- log("Initializing react conditions resolver plugin");
95
+ export const knownDepsResolverPlugin = ({ projectRootDir, }) => {
96
+ log("Initializing known dependencies resolver plugin");
92
97
  let isBuild = false;
93
98
  const ENV_IMPORT_MAPPINGS = Object.fromEntries(Object.keys(ENV_RESOLVERS).map((env) => [
94
99
  env,
95
- resolveEnvImportMappings(env, projectRootDir),
100
+ resolvePredefinedEnvImportMappings(env, projectRootDir),
96
101
  ]));
97
102
  // Log a clean summary instead of all the individual mappings
98
103
  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);
104
+ log("Known dependencies resolver configured with %d total mappings across %d environments", totalMappings, Object.keys(ENV_IMPORT_MAPPINGS).length);
100
105
  function createEsbuildResolverPlugin(envName, mappings) {
101
106
  if (!mappings) {
102
107
  return null;
103
108
  }
104
109
  return {
105
- name: `rwsdk:react-conditions-resolver-esbuild-${envName}`,
110
+ name: `rwsdk:known-dependencies-resolver-esbuild-${envName}`,
106
111
  setup(build) {
107
112
  build.onResolve({ filter: /.*/ }, (args) => {
108
113
  let resolved = mappings.get(args.path);
109
114
  if (!resolved) {
110
- resolved = resolveReactImport(args.path, envName, projectRootDir);
115
+ resolved = resolveKnownImport(args.path, envName, projectRootDir);
111
116
  }
112
117
  if (resolved && args.importer !== "") {
113
118
  if (args.path === "react-server-dom-webpack/client.edge") {
@@ -123,7 +128,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
123
128
  }
124
129
  return [
125
130
  {
126
- name: "rwsdk:react-conditions-resolver:config",
131
+ name: "rwsdk:known-dependencies-resolver:config",
127
132
  enforce: "post",
128
133
  config(config, { command }) {
129
134
  isBuild = command === "build";
@@ -133,7 +138,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
133
138
  log("Setting up resolve aliases and optimizeDeps for each environment");
134
139
  // Set up aliases and optimizeDeps for each environment
135
140
  for (const [envName, mappings] of Object.entries(ENV_IMPORT_MAPPINGS)) {
136
- const reactImports = ENV_REACT_IMPORTS[envName];
141
+ const predefinedImports = ENV_PREDEFINED_IMPORTS[envName];
137
142
  // Ensure environment config exists
138
143
  if (!config.environments) {
139
144
  config.environments = {};
@@ -151,7 +156,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
151
156
  envConfig.optimizeDeps.esbuildOptions.plugins ??= [];
152
157
  envConfig.optimizeDeps.esbuildOptions.plugins.push(esbuildPlugin);
153
158
  envConfig.optimizeDeps.include ??= [];
154
- envConfig.optimizeDeps.include.push(...reactImports);
159
+ envConfig.optimizeDeps.include.push(...predefinedImports);
155
160
  log("Added esbuild plugin and optimizeDeps includes for environment: %s", envName);
156
161
  }
157
162
  const aliases = ensureAliasArray(envConfig);
@@ -161,12 +166,12 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
161
166
  process.env.VERBOSE &&
162
167
  log("Added alias for env=%s: %s -> %s", envName, find, replacement);
163
168
  }
164
- log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, reactImports.length);
169
+ log("Environment %s configured with %d aliases and %d optimizeDeps includes", envName, mappings.size, predefinedImports.length);
165
170
  }
166
171
  },
167
172
  },
168
173
  {
169
- name: "rwsdk:react-conditions-resolver:resolveId",
174
+ name: "rwsdk:known-dependencies-resolver:resolveId",
170
175
  enforce: "pre",
171
176
  async resolveId(id, importer) {
172
177
  // Skip during directive scanning to avoid performance issues
@@ -188,7 +193,7 @@ export const reactConditionsResolverPlugin = ({ projectRootDir, }) => {
188
193
  }
189
194
  let resolved = mappings.get(id);
190
195
  if (!resolved) {
191
- resolved = resolveReactImport(id, envName, projectRootDir);
196
+ resolved = resolveKnownImport(id, envName, projectRootDir);
192
197
  }
193
198
  if (resolved) {
194
199
  process.env.VERBOSE &&
@@ -1,4 +1,12 @@
1
1
  import type { Plugin } from "vite";
2
+ export declare function linkWorkerBundle({ code, manifestContent, projectRootDir, }: {
3
+ code: string;
4
+ manifestContent: string;
5
+ projectRootDir: string;
6
+ }): {
7
+ code: string;
8
+ map: null;
9
+ };
2
10
  export declare const linkerPlugin: ({ projectRootDir, }: {
3
11
  projectRootDir: string;
4
12
  }) => Plugin;
@@ -4,6 +4,29 @@ import { CLIENT_MANIFEST_RELATIVE_PATH } from "../lib/constants.mjs";
4
4
  import debug from "debug";
5
5
  import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
6
6
  const log = debug("rwsdk:vite:linker-plugin");
7
+ export function linkWorkerBundle({ code, manifestContent, projectRootDir, }) {
8
+ let newCode = code;
9
+ const manifest = JSON.parse(manifestContent);
10
+ // 1. Replace the manifest placeholder with the actual manifest content.
11
+ log("Injecting manifest into worker bundle");
12
+ newCode = newCode.replace('"__RWSDK_MANIFEST_PLACEHOLDER__"', manifestContent);
13
+ // 2. Replace asset placeholders with their final hashed paths.
14
+ log("Replacing asset placeholders in final worker bundle");
15
+ for (const [key, value] of Object.entries(manifest)) {
16
+ const normalizedKey = normalizeModulePath(key, projectRootDir, {
17
+ isViteStyle: false,
18
+ });
19
+ newCode = newCode.replaceAll(`rwsdk_asset:${normalizedKey}`, `/${value.file}`);
20
+ }
21
+ // 3. Deprefix any remaining placeholders that were not in the manifest.
22
+ // This handles public assets that don't go through the bundler.
23
+ log("Deprefixing remaining asset placeholders");
24
+ newCode = newCode.replaceAll("rwsdk_asset:", "");
25
+ return {
26
+ code: newCode,
27
+ map: null,
28
+ };
29
+ }
7
30
  export const linkerPlugin = ({ projectRootDir, }) => {
8
31
  return {
9
32
  name: "rwsdk:linker",
@@ -13,29 +36,14 @@ export const linkerPlugin = ({ projectRootDir, }) => {
13
36
  return null;
14
37
  }
15
38
  log("Rendering final worker chunk");
16
- let newCode = code;
17
- // Read the manifest from the filesystem.
18
39
  const manifestContent = await fsp.readFile(path.resolve(projectRootDir, CLIENT_MANIFEST_RELATIVE_PATH), "utf-8");
19
- const manifest = JSON.parse(manifestContent);
20
- // 1. Replace the manifest placeholder with the actual manifest content.
21
- log("Injecting manifest into worker bundle");
22
- newCode = newCode.replace('"__RWSDK_MANIFEST_PLACEHOLDER__"', manifestContent);
23
- // 2. Replace asset placeholders with their final hashed paths.
24
- log("Replacing asset placeholders in final worker bundle");
25
- for (const [key, value] of Object.entries(manifest)) {
26
- const normalizedKey = normalizeModulePath(key, projectRootDir, {
27
- isViteStyle: false,
28
- });
29
- newCode = newCode.replaceAll(`rwsdk_asset:${normalizedKey}`, `/${value.file}`);
30
- }
31
- // 3. Deprefix any remaining placeholders that were not in the manifest.
32
- // This handles public assets that don't go through the bundler.
33
- log("Deprefixing remaining asset placeholders");
34
- newCode = newCode.replaceAll("rwsdk_asset:", "");
35
- return {
36
- code: newCode,
37
- map: null,
38
- };
40
+ const result = linkWorkerBundle({
41
+ code,
42
+ manifestContent,
43
+ projectRootDir,
44
+ });
45
+ log("Final worker chunk rendered");
46
+ return result;
39
47
  },
40
48
  };
41
49
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { linkWorkerBundle } from "./linkerPlugin.mjs";
3
+ describe("linkWorkerBundle", () => {
4
+ const projectRootDir = "/test/project";
5
+ const manifest = {
6
+ "src/styles.css": { file: "assets/styles.123.css" },
7
+ "src/logo.svg": { file: "assets/logo.abc.svg" },
8
+ };
9
+ const manifestContent = JSON.stringify(manifest);
10
+ it("should replace the manifest placeholder", () => {
11
+ const code = `const manifest = "__RWSDK_MANIFEST_PLACEHOLDER__";`;
12
+ const result = linkWorkerBundle({
13
+ code,
14
+ manifestContent,
15
+ projectRootDir,
16
+ });
17
+ expect(result.code).toContain(`const manifest = ${manifestContent};`);
18
+ });
19
+ it("should replace asset placeholders with hashed paths from the manifest", () => {
20
+ const code = `
21
+ const stylesheet = "rwsdk_asset:/src/styles.css";
22
+ const logo = "rwsdk_asset:/src/logo.svg";
23
+ `;
24
+ const result = linkWorkerBundle({
25
+ code,
26
+ manifestContent,
27
+ projectRootDir,
28
+ });
29
+ expect(result.code).toContain(`const stylesheet = "/assets/styles.123.css";`);
30
+ expect(result.code).toContain(`const logo = "/assets/logo.abc.svg";`);
31
+ });
32
+ it("should deprefix remaining asset placeholders not in the manifest", () => {
33
+ const code = `const publicImg = "rwsdk_asset:/images/photo.jpg";`;
34
+ const result = linkWorkerBundle({
35
+ code,
36
+ manifestContent,
37
+ projectRootDir,
38
+ });
39
+ expect(result.code).toContain(`const publicImg = "/images/photo.jpg";`);
40
+ });
41
+ });
@@ -1,4 +1,9 @@
1
1
  import { Plugin } from "vite";
2
+ export declare const hasEntryAsAncestor: ({ module, entryFile, seen, }: {
3
+ module: any;
4
+ entryFile: string;
5
+ seen?: Set<any>;
6
+ }) => boolean;
2
7
  export declare const miniflareHMRPlugin: (givenOptions: {
3
8
  clientFiles: Set<string>;
4
9
  serverFiles: Set<string>;