rwsdk 1.0.0-alpha.1-test.20250911154541 → 1.0.0-alpha.11
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/e2e/browser.d.mts +10 -0
- package/dist/lib/e2e/browser.mjs +124 -0
- package/dist/lib/e2e/dev.d.mts +8 -0
- package/dist/lib/e2e/dev.mjs +242 -0
- package/dist/lib/e2e/environment.d.mts +14 -0
- package/dist/lib/e2e/environment.mjs +266 -0
- package/dist/lib/e2e/index.d.mts +8 -0
- package/dist/lib/e2e/index.mjs +8 -0
- package/dist/lib/e2e/poll.d.mts +8 -0
- package/dist/lib/e2e/poll.mjs +31 -0
- package/dist/lib/e2e/release.d.mts +56 -0
- package/dist/lib/e2e/release.mjs +559 -0
- package/dist/lib/e2e/retry.d.mts +4 -0
- package/dist/lib/e2e/retry.mjs +16 -0
- package/dist/lib/e2e/setup.d.mts +2 -0
- package/dist/lib/e2e/setup.mjs +1 -0
- package/dist/lib/e2e/tarball.d.mts +14 -0
- package/dist/lib/e2e/tarball.mjs +99 -0
- package/dist/lib/e2e/testHarness.d.mts +126 -0
- package/dist/lib/e2e/testHarness.mjs +418 -0
- package/dist/lib/e2e/types.d.mts +32 -0
- package/dist/lib/getShortName.mjs +6 -1
- package/dist/lib/getShortName.test.d.mts +1 -0
- package/dist/lib/getShortName.test.mjs +25 -0
- package/dist/lib/hasPkgScript.d.mts +4 -1
- package/dist/lib/hasPkgScript.mjs +9 -6
- package/dist/lib/hasPkgScript.test.d.mts +1 -0
- package/dist/lib/hasPkgScript.test.mjs +33 -0
- package/dist/lib/jsonUtils.mjs +3 -0
- package/dist/lib/jsonUtils.test.d.mts +1 -0
- package/dist/lib/jsonUtils.test.mjs +90 -0
- package/dist/lib/normalizeModulePath.d.mts +5 -0
- package/dist/lib/normalizeModulePath.mjs +1 -1
- package/dist/lib/normalizeModulePath.test.d.mts +1 -0
- package/dist/lib/{normalizeModulePath.test.js → normalizeModulePath.test.mjs} +20 -1
- package/dist/lib/smokeTests/browser.mjs +3 -94
- package/dist/lib/smokeTests/development.mjs +2 -223
- package/dist/lib/smokeTests/environment.d.mts +4 -11
- package/dist/lib/smokeTests/environment.mjs +10 -158
- package/dist/lib/smokeTests/release.d.mts +2 -49
- package/dist/lib/smokeTests/release.mjs +3 -503
- package/dist/runtime/lib/injectHtmlAtMarker.d.ts +11 -0
- package/dist/runtime/lib/injectHtmlAtMarker.js +90 -0
- package/dist/runtime/lib/memoizeOnId.test.d.ts +1 -0
- package/dist/runtime/lib/memoizeOnId.test.js +49 -0
- package/dist/runtime/lib/realtime/protocol.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/protocol.test.js +107 -0
- package/dist/runtime/lib/realtime/shared.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/shared.test.js +18 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.d.ts +1 -0
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +66 -0
- package/dist/runtime/lib/realtime/worker.d.ts +1 -1
- package/dist/runtime/lib/router.js +40 -22
- package/dist/runtime/lib/router.test.js +591 -2
- package/dist/runtime/lib/rwContext.d.ts +22 -0
- package/dist/runtime/lib/rwContext.js +1 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +2 -1
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +6 -6
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.d.ts +1 -0
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +49 -0
- package/dist/runtime/register/worker.d.ts +1 -1
- package/dist/runtime/register/worker.js +26 -21
- package/dist/runtime/render/assembleDocument.d.ts +6 -0
- package/dist/runtime/render/assembleDocument.js +22 -0
- package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
- package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
- package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
- package/dist/runtime/render/normalizeActionResult.js +43 -0
- package/dist/runtime/render/preloads.d.ts +2 -2
- package/dist/runtime/render/preloads.js +2 -3
- package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
- package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
- package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
- package/dist/runtime/render/renderHtmlStream.js +31 -0
- package/dist/runtime/render/renderToRscStream.d.ts +2 -3
- package/dist/runtime/render/renderToRscStream.js +2 -41
- package/dist/runtime/render/renderToStream.d.ts +2 -1
- package/dist/runtime/render/renderToStream.js +15 -8
- package/dist/runtime/render/stylesheets.d.ts +2 -2
- package/dist/runtime/render/stylesheets.js +2 -3
- package/dist/runtime/ssrBridge.d.ts +2 -1
- package/dist/runtime/ssrBridge.js +2 -1
- package/dist/runtime/worker.d.ts +1 -0
- package/dist/runtime/worker.js +11 -6
- package/dist/scripts/debug-sync.mjs +102 -133
- package/dist/vite/buildApp.d.mts +2 -1
- package/dist/vite/buildApp.mjs +9 -5
- package/dist/vite/checkIsUsingPrisma.d.mts +4 -0
- package/dist/vite/checkIsUsingPrisma.mjs +2 -2
- package/dist/vite/checkIsUsingPrisma.test.d.mts +1 -0
- package/dist/vite/checkIsUsingPrisma.test.mjs +30 -0
- package/dist/vite/configPlugin.mjs +35 -14
- package/dist/vite/createDirectiveLookupPlugin.d.mts +9 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +33 -29
- package/dist/vite/createDirectiveLookupPlugin.test.d.mts +1 -0
- package/dist/vite/createDirectiveLookupPlugin.test.mjs +40 -0
- package/dist/vite/directiveModulesDevPlugin.d.mts +4 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +5 -4
- package/dist/vite/directiveModulesDevPlugin.test.d.mts +1 -0
- package/dist/vite/directiveModulesDevPlugin.test.mjs +59 -0
- package/dist/vite/directivesPlugin.d.mts +1 -0
- package/dist/vite/directivesPlugin.mjs +1 -1
- package/dist/vite/directivesPlugin.test.d.mts +1 -0
- package/dist/vite/directivesPlugin.test.mjs +24 -0
- package/dist/vite/ensureAliasArray.test.d.mts +1 -0
- package/dist/vite/ensureAliasArray.test.mjs +71 -0
- package/dist/vite/findSpecifiers.mjs +2 -1
- package/dist/vite/findSpecifiers.test.d.mts +1 -0
- package/dist/vite/findSpecifiers.test.mjs +202 -0
- package/dist/vite/findSsrSpecifiers.test.d.mts +1 -0
- package/dist/vite/findSsrSpecifiers.test.mjs +99 -0
- package/dist/vite/hasDirective.d.mts +6 -3
- package/dist/vite/hasDirective.mjs +43 -27
- package/dist/vite/hasDirective.test.d.mts +1 -0
- package/dist/vite/hasDirective.test.mjs +107 -0
- package/dist/vite/isJsFile.test.d.mts +1 -0
- package/dist/vite/isJsFile.test.mjs +38 -0
- package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +2 -2
- package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +28 -23
- package/dist/vite/linkerPlugin.d.mts +8 -0
- package/dist/vite/linkerPlugin.mjs +30 -22
- package/dist/vite/linkerPlugin.test.d.mts +1 -0
- package/dist/vite/linkerPlugin.test.mjs +41 -0
- package/dist/vite/miniflareHMRPlugin.d.mts +5 -0
- package/dist/vite/miniflareHMRPlugin.mjs +2 -2
- package/dist/vite/miniflareHMRPlugin.test.d.mts +1 -0
- package/dist/vite/miniflareHMRPlugin.test.mjs +42 -0
- package/dist/vite/redwoodPlugin.d.mts +9 -0
- package/dist/vite/redwoodPlugin.mjs +29 -5
- package/dist/vite/redwoodPlugin.test.d.mts +1 -0
- package/dist/vite/redwoodPlugin.test.mjs +34 -0
- package/dist/vite/resolveForcedPaths.d.mts +4 -0
- package/dist/vite/resolveForcedPaths.mjs +9 -0
- package/dist/vite/runDirectivesScan.d.mts +21 -1
- package/dist/vite/runDirectivesScan.mjs +67 -52
- package/dist/vite/runDirectivesScan.test.d.mts +1 -0
- package/dist/vite/runDirectivesScan.test.mjs +73 -0
- package/dist/vite/ssrBridgePlugin.mjs +8 -1
- package/dist/vite/transformClientComponents.mjs +6 -4
- package/dist/vite/transformClientComponents.test.mjs +116 -58
- package/dist/vite/transformServerFunctions.d.mts +1 -1
- package/dist/vite/transformServerFunctions.mjs +1 -1
- package/dist/vite/transformServerFunctions.test.mjs +3 -3
- package/package.json +12 -4
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
- package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
- /package/dist/lib/{normalizeModulePath.test.d.ts → e2e/types.mjs} +0 -0
|
@@ -4,7 +4,7 @@ import { writeFileSync, mkdirSync, mkdtempSync } from "node:fs";
|
|
|
4
4
|
import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
|
|
5
5
|
import { VENDOR_CLIENT_BARREL_PATH, VENDOR_SERVER_BARREL_PATH, VENDOR_CLIENT_BARREL_EXPORT_PATH, VENDOR_SERVER_BARREL_EXPORT_PATH, } from "../lib/constants.mjs";
|
|
6
6
|
import { runDirectivesScan } from "./runDirectivesScan.mjs";
|
|
7
|
-
const generateVendorBarrelContent = (files, projectRootDir) => {
|
|
7
|
+
export const generateVendorBarrelContent = (files, projectRootDir) => {
|
|
8
8
|
const imports = [...files]
|
|
9
9
|
.filter((file) => file.includes("node_modules"))
|
|
10
10
|
.map((file, i) => `import * as M${i} from '${normalizeModulePath(file, projectRootDir, {
|
|
@@ -19,7 +19,7 @@ const generateVendorBarrelContent = (files, projectRootDir) => {
|
|
|
19
19
|
"\n};";
|
|
20
20
|
return `${imports}\n\n${exports}`;
|
|
21
21
|
};
|
|
22
|
-
const generateAppBarrelContent = (files, projectRootDir
|
|
22
|
+
export const generateAppBarrelContent = (files, projectRootDir) => {
|
|
23
23
|
return [...files]
|
|
24
24
|
.filter((file) => !file.includes("node_modules"))
|
|
25
25
|
.map((file) => {
|
|
@@ -30,7 +30,7 @@ const generateAppBarrelContent = (files, projectRootDir, barrelFilePath) => {
|
|
|
30
30
|
})
|
|
31
31
|
.join("\n");
|
|
32
32
|
};
|
|
33
|
-
export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRootDir, }) => {
|
|
33
|
+
export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRootDir, workerEntryPathname, }) => {
|
|
34
34
|
const { promise: scanPromise, resolve: resolveScanPromise } = Promise.withResolvers();
|
|
35
35
|
const tempDir = mkdtempSync(path.join(os.tmpdir(), "rwsdk-"));
|
|
36
36
|
const APP_CLIENT_BARREL_PATH = path.join(tempDir, "app-client-barrel.js");
|
|
@@ -47,6 +47,7 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
|
|
|
47
47
|
environments: server.environments,
|
|
48
48
|
clientFiles,
|
|
49
49
|
serverFiles,
|
|
50
|
+
entries: [workerEntryPathname],
|
|
50
51
|
}).then(() => {
|
|
51
52
|
// context(justinvdm, 11 Sep 2025): For vendor barrels, we write the
|
|
52
53
|
// files directly to disk after the scan. For app barrels, we use a
|
|
@@ -129,7 +130,7 @@ export const directiveModulesDevPlugin = ({ clientFiles, serverFiles, projectRoo
|
|
|
129
130
|
build.onLoad({ filter: /.*/, namespace: "rwsdk-app-barrel-ns" }, (args) => {
|
|
130
131
|
const isServerBarrel = args.path.includes("app-server-barrel");
|
|
131
132
|
const files = isServerBarrel ? serverFiles : clientFiles;
|
|
132
|
-
const content = generateAppBarrelContent(files, projectRootDir
|
|
133
|
+
const content = generateAppBarrelContent(files, projectRootDir);
|
|
133
134
|
return {
|
|
134
135
|
contents: content,
|
|
135
136
|
loader: "js",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { generateVendorBarrelContent, generateAppBarrelContent, } from "./directiveModulesDevPlugin.mjs";
|
|
3
|
+
describe("directiveModulesDevPlugin helpers", () => {
|
|
4
|
+
const projectRootDir = "/Users/test/project";
|
|
5
|
+
describe("generateVendorBarrelContent", () => {
|
|
6
|
+
it("should generate correct content for vendor files", () => {
|
|
7
|
+
const files = new Set([
|
|
8
|
+
"node_modules/lib-a/index.js",
|
|
9
|
+
"src/app.js",
|
|
10
|
+
"node_modules/lib-b/component.tsx",
|
|
11
|
+
]);
|
|
12
|
+
const content = generateVendorBarrelContent(files, projectRootDir);
|
|
13
|
+
const expected = `import * as M0 from '${projectRootDir}/node_modules/lib-a/index.js';
|
|
14
|
+
import * as M1 from '${projectRootDir}/node_modules/lib-b/component.tsx';
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
'/node_modules/lib-a/index.js': M0,
|
|
18
|
+
'/node_modules/lib-b/component.tsx': M1,
|
|
19
|
+
};`;
|
|
20
|
+
expect(content).toEqual(expected);
|
|
21
|
+
});
|
|
22
|
+
it("should return empty content if no vendor files", () => {
|
|
23
|
+
const files = new Set(["src/app.js", "src/component.tsx"]);
|
|
24
|
+
const content = generateVendorBarrelContent(files, projectRootDir);
|
|
25
|
+
expect(content).toEqual("\n\nexport default {\n\n};");
|
|
26
|
+
});
|
|
27
|
+
it("should handle an empty file set", () => {
|
|
28
|
+
const files = new Set();
|
|
29
|
+
const content = generateVendorBarrelContent(files, projectRootDir);
|
|
30
|
+
expect(content).toEqual("\n\nexport default {\n\n};");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("generateAppBarrelContent", () => {
|
|
34
|
+
it("should generate correct content for app files", () => {
|
|
35
|
+
const files = new Set([
|
|
36
|
+
"src/app.js",
|
|
37
|
+
"node_modules/lib-a/index.js",
|
|
38
|
+
"src/component.tsx",
|
|
39
|
+
]);
|
|
40
|
+
const content = generateAppBarrelContent(files, projectRootDir);
|
|
41
|
+
const expected = `import "${projectRootDir}/src/app.js";
|
|
42
|
+
import "${projectRootDir}/src/component.tsx";`;
|
|
43
|
+
expect(content).toEqual(expected);
|
|
44
|
+
});
|
|
45
|
+
it("should return empty content if no app files", () => {
|
|
46
|
+
const files = new Set([
|
|
47
|
+
"node_modules/lib-a/index.js",
|
|
48
|
+
"node_modules/lib-b/component.tsx",
|
|
49
|
+
]);
|
|
50
|
+
const content = generateAppBarrelContent(files, projectRootDir);
|
|
51
|
+
expect(content).toEqual("");
|
|
52
|
+
});
|
|
53
|
+
it("should handle an empty file set", () => {
|
|
54
|
+
const files = new Set();
|
|
55
|
+
const content = generateAppBarrelContent(files, projectRootDir);
|
|
56
|
+
expect(content).toEqual("");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -5,7 +5,7 @@ import { transformClientComponents } from "./transformClientComponents.mjs";
|
|
|
5
5
|
import { transformServerFunctions } from "./transformServerFunctions.mjs";
|
|
6
6
|
import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
|
|
7
7
|
const log = debug("rwsdk:vite:rsc-directives-plugin");
|
|
8
|
-
const getLoader = (filePath) => {
|
|
8
|
+
export const getLoader = (filePath) => {
|
|
9
9
|
const ext = path.extname(filePath).slice(1);
|
|
10
10
|
switch (ext) {
|
|
11
11
|
case "mjs":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { getLoader } from "./directivesPlugin.mjs";
|
|
3
|
+
describe("getLoader", () => {
|
|
4
|
+
const testCases = [
|
|
5
|
+
{ path: "file.js", expected: "js" },
|
|
6
|
+
{ path: "file.mjs", expected: "js" },
|
|
7
|
+
{ path: "file.cjs", expected: "js" },
|
|
8
|
+
{ path: "file.ts", expected: "ts" },
|
|
9
|
+
{ path: "file.mts", expected: "ts" },
|
|
10
|
+
{ path: "file.cts", expected: "ts" },
|
|
11
|
+
{ path: "file.jsx", expected: "jsx" },
|
|
12
|
+
{ path: "file.tsx", expected: "tsx" },
|
|
13
|
+
{ path: "/path/to/component.ts", expected: "ts" },
|
|
14
|
+
{ path: "../relative/path.jsx", expected: "jsx" },
|
|
15
|
+
{ path: "file.css", expected: "js" }, // default case
|
|
16
|
+
{ path: "file.json", expected: "js" }, // default case
|
|
17
|
+
{ path: "file", expected: "js" }, // no extension
|
|
18
|
+
];
|
|
19
|
+
testCases.forEach(({ path, expected }) => {
|
|
20
|
+
it(`should return "${expected}" for "${path}"`, () => {
|
|
21
|
+
expect(getLoader(path)).toBe(expected);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { ensureAliasArray } from "./ensureAliasArray.mjs";
|
|
3
|
+
describe("ensureAliasArray", () => {
|
|
4
|
+
it("should create resolve and alias array if resolve is undefined", () => {
|
|
5
|
+
const config = {};
|
|
6
|
+
const result = ensureAliasArray(config);
|
|
7
|
+
expect(result).toEqual([]);
|
|
8
|
+
expect(config.resolve?.alias).toEqual([]);
|
|
9
|
+
expect(result).toBe(config.resolve?.alias);
|
|
10
|
+
});
|
|
11
|
+
it("should create alias array if alias is undefined", () => {
|
|
12
|
+
const config = { resolve: {} };
|
|
13
|
+
const result = ensureAliasArray(config);
|
|
14
|
+
expect(result).toEqual([]);
|
|
15
|
+
expect(config.resolve?.alias).toEqual([]);
|
|
16
|
+
expect(result).toBe(config.resolve?.alias);
|
|
17
|
+
});
|
|
18
|
+
it("should convert an alias object to an array", () => {
|
|
19
|
+
const config = {
|
|
20
|
+
resolve: {
|
|
21
|
+
alias: {
|
|
22
|
+
find: "/replacement",
|
|
23
|
+
another: "/another-replacement",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const result = ensureAliasArray(config);
|
|
28
|
+
const expected = [
|
|
29
|
+
{ find: "find", replacement: "/replacement" },
|
|
30
|
+
{ find: "another", replacement: "/another-replacement" },
|
|
31
|
+
];
|
|
32
|
+
expect(result).toEqual(expected);
|
|
33
|
+
expect(config.resolve?.alias).toEqual(expected);
|
|
34
|
+
expect(result).toBe(config.resolve?.alias);
|
|
35
|
+
});
|
|
36
|
+
it("should return a clone of an existing alias array", () => {
|
|
37
|
+
const originalAlias = [{ find: "find", replacement: "/replacement" }];
|
|
38
|
+
const config = {
|
|
39
|
+
resolve: {
|
|
40
|
+
alias: originalAlias,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const result = ensureAliasArray(config);
|
|
44
|
+
expect(result).toEqual(originalAlias);
|
|
45
|
+
expect(result).not.toBe(originalAlias);
|
|
46
|
+
expect(config.resolve?.alias).toEqual(originalAlias);
|
|
47
|
+
expect(config.resolve?.alias).not.toBe(originalAlias);
|
|
48
|
+
});
|
|
49
|
+
it("should handle an empty alias object", () => {
|
|
50
|
+
const config = {
|
|
51
|
+
resolve: {
|
|
52
|
+
alias: {},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
const result = ensureAliasArray(config);
|
|
56
|
+
expect(result).toEqual([]);
|
|
57
|
+
expect(config.resolve?.alias).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
it("should handle an empty alias array", () => {
|
|
60
|
+
const config = {
|
|
61
|
+
resolve: {
|
|
62
|
+
alias: [],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const originalAlias = config.resolve?.alias;
|
|
66
|
+
const result = ensureAliasArray(config);
|
|
67
|
+
expect(result).toEqual([]);
|
|
68
|
+
expect(config.resolve?.alias).toEqual([]);
|
|
69
|
+
expect(result).not.toBe(originalAlias);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -178,7 +178,8 @@ export function findExports(id, code, log) {
|
|
|
178
178
|
});
|
|
179
179
|
logger("Found default export: %s", name);
|
|
180
180
|
}
|
|
181
|
-
else if (matchText.includes("export {")
|
|
181
|
+
else if (matchText.includes("export {") &&
|
|
182
|
+
!match.getMatch("MODULE")) {
|
|
182
183
|
// Local export declaration
|
|
183
184
|
const exportListMatch = matchText.match(/export\s*\{\s*([^}]+)\s*\}/);
|
|
184
185
|
if (exportListMatch) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { findImportSpecifiers, findExports } from "./findSpecifiers.mjs";
|
|
3
|
+
function dedupeImports(imports) {
|
|
4
|
+
const seen = new Set();
|
|
5
|
+
return imports.filter((imp) => {
|
|
6
|
+
const key = `${imp.s}:${imp.e}`;
|
|
7
|
+
if (seen.has(key)) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
seen.add(key);
|
|
11
|
+
return true;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
describe("findSpecifiers", () => {
|
|
15
|
+
describe("findImportSpecifiers", () => {
|
|
16
|
+
it("should find various import types", () => {
|
|
17
|
+
const code = `
|
|
18
|
+
import { a } from "module-a";
|
|
19
|
+
import b from 'module-b';
|
|
20
|
+
import * as c from "module-c";
|
|
21
|
+
import "module-d";
|
|
22
|
+
const e = import('module-e');
|
|
23
|
+
const f = require("module-f");
|
|
24
|
+
`;
|
|
25
|
+
const results = findImportSpecifiers("test.ts", code, []);
|
|
26
|
+
const specifiers = dedupeImports(results).map((r) => r.raw);
|
|
27
|
+
expect(specifiers).toEqual([
|
|
28
|
+
"module-a",
|
|
29
|
+
"module-c",
|
|
30
|
+
"module-b",
|
|
31
|
+
"module-d",
|
|
32
|
+
"module-e",
|
|
33
|
+
"module-f",
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
it("should find re-exports", () => {
|
|
37
|
+
const code = `
|
|
38
|
+
export { a } from "module-a";
|
|
39
|
+
export * from 'module-b';
|
|
40
|
+
`;
|
|
41
|
+
const results = findImportSpecifiers("test.ts", code, []);
|
|
42
|
+
const specifiers = dedupeImports(results).map((r) => r.raw);
|
|
43
|
+
expect(specifiers).toEqual(["module-a", "module-b"]);
|
|
44
|
+
});
|
|
45
|
+
it("should ignore specified patterns", () => {
|
|
46
|
+
const code = `
|
|
47
|
+
import { a } from "module-a";
|
|
48
|
+
import b from 'module-b';
|
|
49
|
+
import c from "ignored-module";
|
|
50
|
+
`;
|
|
51
|
+
const results = findImportSpecifiers("test.ts", code, [/ignored/]);
|
|
52
|
+
const specifiers = dedupeImports(results).map((r) => r.raw);
|
|
53
|
+
expect(specifiers).toEqual(["module-a", "module-b"]);
|
|
54
|
+
});
|
|
55
|
+
it("should ignore virtual modules", () => {
|
|
56
|
+
const code = `import a from "virtual:my-virtual-module";`;
|
|
57
|
+
const results = findImportSpecifiers("test.ts", code, []);
|
|
58
|
+
expect(results).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
it("should ignore __rwsdknossr modules", () => {
|
|
61
|
+
const code = `import a from "some-module?__rwsdknossr";`;
|
|
62
|
+
const results = findImportSpecifiers("test.ts", code, []);
|
|
63
|
+
expect(results).toHaveLength(0);
|
|
64
|
+
});
|
|
65
|
+
it("should handle tsx files", () => {
|
|
66
|
+
const code = `
|
|
67
|
+
import React from 'react';
|
|
68
|
+
const App = () => <div>Hello</div>;
|
|
69
|
+
export default App;
|
|
70
|
+
`;
|
|
71
|
+
const results = findImportSpecifiers("test.tsx", code, []);
|
|
72
|
+
const specifiers = dedupeImports(results).map((r) => r.raw);
|
|
73
|
+
expect(specifiers).toEqual(["react"]);
|
|
74
|
+
});
|
|
75
|
+
it("should return correct positions", () => {
|
|
76
|
+
const code = `import { a } from "module-a";`;
|
|
77
|
+
const results = findImportSpecifiers("test.ts", code, []);
|
|
78
|
+
const deduped = dedupeImports(results);
|
|
79
|
+
expect(deduped).toHaveLength(1);
|
|
80
|
+
expect(deduped[0].s).toBe(19);
|
|
81
|
+
expect(deduped[0].e).toBe(27);
|
|
82
|
+
expect(code.substring(deduped[0].s, deduped[0].e)).toBe("module-a");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("findExports", () => {
|
|
86
|
+
it("should find named exports (const, let, function)", () => {
|
|
87
|
+
const code = `
|
|
88
|
+
export const a = 1;
|
|
89
|
+
export let b = 2;
|
|
90
|
+
export function c() {}
|
|
91
|
+
export async function d() {}
|
|
92
|
+
`;
|
|
93
|
+
const results = findExports("test.ts", code);
|
|
94
|
+
expect(results).toEqual([
|
|
95
|
+
{ name: "a", isDefault: false },
|
|
96
|
+
{ name: "b", isDefault: false },
|
|
97
|
+
{ name: "c", isDefault: false },
|
|
98
|
+
{ name: "d", isDefault: false },
|
|
99
|
+
]);
|
|
100
|
+
});
|
|
101
|
+
it("should find default exports", () => {
|
|
102
|
+
const code = `
|
|
103
|
+
export default function myFunc() {}
|
|
104
|
+
const b = 1;
|
|
105
|
+
export default b;
|
|
106
|
+
`;
|
|
107
|
+
const results = findExports("test.ts", code);
|
|
108
|
+
expect(results).toEqual([
|
|
109
|
+
{ name: "myFunc", isDefault: true },
|
|
110
|
+
{ name: "default", isDefault: true },
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
it("should find export declarations", () => {
|
|
114
|
+
const code = `
|
|
115
|
+
const a = 1;
|
|
116
|
+
const b = 2;
|
|
117
|
+
export { a, b as c };
|
|
118
|
+
`;
|
|
119
|
+
const results = findExports("test.ts", code);
|
|
120
|
+
expect(results).toEqual([
|
|
121
|
+
{
|
|
122
|
+
name: "a",
|
|
123
|
+
isDefault: false,
|
|
124
|
+
alias: undefined,
|
|
125
|
+
originalName: "a",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "c",
|
|
129
|
+
isDefault: false,
|
|
130
|
+
alias: "c",
|
|
131
|
+
originalName: "b",
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
134
|
+
});
|
|
135
|
+
it("should find re-exports", () => {
|
|
136
|
+
const code = `
|
|
137
|
+
export { a, b as c } from 'module-a';
|
|
138
|
+
export { default as d } from 'module-b';
|
|
139
|
+
`;
|
|
140
|
+
const results = findExports("test.ts", code);
|
|
141
|
+
expect(results).toEqual([
|
|
142
|
+
{
|
|
143
|
+
name: "a",
|
|
144
|
+
isDefault: false,
|
|
145
|
+
alias: undefined,
|
|
146
|
+
originalName: "a",
|
|
147
|
+
isReExport: true,
|
|
148
|
+
moduleSpecifier: "module-a",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "c",
|
|
152
|
+
isDefault: false,
|
|
153
|
+
alias: "c",
|
|
154
|
+
originalName: "b",
|
|
155
|
+
isReExport: true,
|
|
156
|
+
moduleSpecifier: "module-a",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "d",
|
|
160
|
+
isDefault: true,
|
|
161
|
+
alias: "d",
|
|
162
|
+
originalName: "default",
|
|
163
|
+
isReExport: true,
|
|
164
|
+
moduleSpecifier: "module-b",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
alias: undefined,
|
|
168
|
+
isDefault: false,
|
|
169
|
+
name: "a",
|
|
170
|
+
originalName: "a",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
alias: "c",
|
|
174
|
+
isDefault: false,
|
|
175
|
+
name: "c",
|
|
176
|
+
originalName: "b",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
alias: "d",
|
|
180
|
+
isDefault: true,
|
|
181
|
+
name: "d",
|
|
182
|
+
originalName: "default",
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
});
|
|
186
|
+
it("should handle mixed exports", () => {
|
|
187
|
+
const code = `
|
|
188
|
+
export const a = 1;
|
|
189
|
+
export default function b() {}
|
|
190
|
+
const c = 3;
|
|
191
|
+
export { c };
|
|
192
|
+
`;
|
|
193
|
+
const results = findExports("test.tsx", code);
|
|
194
|
+
expect(results).toEqual([
|
|
195
|
+
{ name: "a", isDefault: false },
|
|
196
|
+
{ name: "b", isDefault: true },
|
|
197
|
+
{ isDefault: true, name: "default" },
|
|
198
|
+
{ name: "c", isDefault: false, originalName: "c", alias: undefined },
|
|
199
|
+
]);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
+
foundTargetDirective = true;
|
|
52
|
+
if (directive !== "use server") {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
|
-
//
|
|
50
|
-
//
|
|
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 {};
|