@prosopo/config 3.4.1 → 3.4.3

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 (74) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +9 -11
  2. package/.turbo/turbo-build$colon$tsc.log +4 -7
  3. package/.turbo/turbo-build.log +10 -15
  4. package/CHANGELOG.md +18 -0
  5. package/LICENSE +201 -0
  6. package/dist/cjs/dependencies.cjs +86 -46
  7. package/dist/cjs/vite/testFileGlobs.cjs +31 -0
  8. package/dist/cjs/vite/vite.frontend.config.cjs +7 -0
  9. package/dist/cjs/vite/vite.test.config.cjs +9 -8
  10. package/dist/cjs/vite/vite.threads.test.config.cjs +9 -8
  11. package/dist/cjs/webpack/webpack.config.cjs +8 -8
  12. package/dist/dependencies.d.ts.map +1 -1
  13. package/dist/dependencies.js +85 -44
  14. package/dist/dependencies.js.map +1 -1
  15. package/dist/getDependencies.test.js +151 -125
  16. package/dist/getDependencies.test.js.map +1 -1
  17. package/dist/vite/configs.test.js +6 -19
  18. package/dist/vite/configs.test.js.map +1 -1
  19. package/dist/vite/testFileGlobs.d.ts +8 -0
  20. package/dist/vite/testFileGlobs.d.ts.map +1 -0
  21. package/dist/vite/testFileGlobs.js +29 -0
  22. package/dist/vite/testFileGlobs.js.map +1 -0
  23. package/dist/vite/testFileGlobs.test.d.ts +2 -0
  24. package/dist/vite/testFileGlobs.test.d.ts.map +1 -0
  25. package/dist/vite/testFileGlobs.test.js +113 -0
  26. package/dist/vite/testFileGlobs.test.js.map +1 -0
  27. package/dist/vite/vite.frontend.config.d.ts.map +1 -1
  28. package/dist/vite/vite.frontend.config.js +8 -1
  29. package/dist/vite/vite.frontend.config.js.map +1 -1
  30. package/dist/vite/vite.test.config.d.ts.map +1 -1
  31. package/dist/vite/vite.test.config.js +9 -8
  32. package/dist/vite/vite.test.config.js.map +1 -1
  33. package/dist/vite/vite.threads.test.config.d.ts.map +1 -1
  34. package/dist/vite/vite.threads.test.config.js +9 -8
  35. package/dist/vite/vite.threads.test.config.js.map +1 -1
  36. package/dist/webpack/webpack.config.d.ts +2 -0
  37. package/dist/webpack/webpack.config.d.ts.map +1 -1
  38. package/dist/webpack/webpack.config.js +8 -8
  39. package/dist/webpack/webpack.config.js.map +1 -1
  40. package/package.json +106 -103
  41. package/src/dependencies.test.ts +0 -249
  42. package/src/dependencies.ts +0 -259
  43. package/src/getDependencies.test.ts +0 -385
  44. package/src/index.ts +0 -16
  45. package/src/vite/CopyAssetsPlugin.ts +0 -49
  46. package/src/vite/NodejsPolarsDirnamePlugin.ts +0 -50
  47. package/src/vite/NodejsPolarsNativeFilePlugin.ts +0 -105
  48. package/src/vite/TsNoCheckPlugin.ts +0 -31
  49. package/src/vite/bundleConfigs.test.ts +0 -461
  50. package/src/vite/configs.test.ts +0 -235
  51. package/src/vite/index.ts +0 -30
  52. package/src/vite/modifyExportsFiles.test.ts +0 -56
  53. package/src/vite/plugins.test.ts +0 -365
  54. package/src/vite/translations.test.ts +0 -164
  55. package/src/vite/types.ts +0 -13
  56. package/src/vite/vite-plugin-close-and-copy.ts +0 -76
  57. package/src/vite/vite-plugin-copy.ts +0 -67
  58. package/src/vite/vite-plugin-externalize-obfuscator-deadcode.ts +0 -43
  59. package/src/vite/vite-plugin-fix-absolute-imports.ts +0 -40
  60. package/src/vite/vite-plugin-modify-exports-files.ts +0 -64
  61. package/src/vite/vite-plugin-remove-unused-translations.ts +0 -128
  62. package/src/vite/vite-plugin-sourcemap-exclude.ts +0 -36
  63. package/src/vite/vite.backend.config.ts +0 -172
  64. package/src/vite/vite.commonjs.config.ts +0 -98
  65. package/src/vite/vite.esm.config.ts +0 -99
  66. package/src/vite/vite.frontend.config.ts +0 -340
  67. package/src/vite/vite.test.config.ts +0 -121
  68. package/src/vite/vite.threads.test.config.ts +0 -106
  69. package/src/webpack/index.ts +0 -14
  70. package/src/webpack/webpack.config.ts +0 -159
  71. package/tsconfig.cjs.json +0 -9
  72. package/tsconfig.json +0 -17
  73. package/tsconfig.tsbuildinfo +0 -1
  74. package/tsconfig.types.json +0 -9
@@ -1,235 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import fs from "node:fs";
16
- import os from "node:os";
17
- import path from "node:path";
18
- import type { Rollup } from "vite";
19
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
20
- import { nodejsPolarsDirnamePlugin } from "./NodejsPolarsDirnamePlugin.js";
21
- import ViteTestConfig from "./vite.test.config.js";
22
- import ViteThreadsTestConfig from "./vite.threads.test.config.js";
23
-
24
- const NATIVE_POLARS = "/repo/node_modules/nodejs-polars/bin/native-polars.js";
25
-
26
- const resolveOptions: Rollup.ResolveIdExtraOptions = {
27
- isEntry: false,
28
- kind: "import-statement",
29
- };
30
-
31
- let root: string;
32
- let cwd: string;
33
- let originalTestType: string | undefined;
34
-
35
- beforeEach(() => {
36
- root = fs.mkdtempSync(path.join(os.tmpdir(), "prosopo-vite-config-test-"));
37
- cwd = process.cwd();
38
- originalTestType = process.env.TEST_TYPE;
39
- process.env.TEST_TYPE = undefined;
40
- // biome-ignore lint/performance/noDelete: the config reads presence, not value
41
- delete process.env.TEST_TYPE;
42
- });
43
-
44
- afterEach(() => {
45
- process.chdir(cwd);
46
- if (originalTestType === undefined) {
47
- // biome-ignore lint/performance/noDelete: restore absence, not ""
48
- delete process.env.TEST_TYPE;
49
- } else {
50
- process.env.TEST_TYPE = originalTestType;
51
- }
52
- fs.rmSync(root, { recursive: true, force: true });
53
- });
54
-
55
- /** Make `root` look like a package (package.json + src) and chdir into it. */
56
- const asPackage = (): void => {
57
- fs.writeFileSync(path.join(root, "package.json"), "{}");
58
- fs.mkdirSync(path.join(root, "src"));
59
- process.chdir(root);
60
- };
61
-
62
- /** Make `root` look like a repo root (no src dir) and chdir into it. */
63
- const asRepoRoot = (): void => {
64
- fs.writeFileSync(path.join(root, "package.json"), "{}");
65
- process.chdir(root);
66
- };
67
-
68
- describe("ViteTestConfig", () => {
69
- it("enables coverage and typechecking by default", () => {
70
- asPackage();
71
- const config = ViteTestConfig();
72
- expect(config.test?.coverage?.enabled).toBe(true);
73
- expect(config.test?.typecheck?.enabled).toBe(true);
74
- expect(config.test?.watch).toBe(false);
75
- });
76
-
77
- it("runs in forks, isolated, because low-level crypto needs it", () => {
78
- asPackage();
79
- const config = ViteTestConfig();
80
- expect(config.test?.pool).toBe("forks");
81
- expect(config.test?.isolate).toBe(true);
82
- });
83
-
84
- it("includes tests with no declared type when no filter is set", () => {
85
- asPackage();
86
- expect(config_include(ViteTestConfig())).toBe(
87
- "src/**/*@(|).@(test|spec).@(mts|cts|mjs|cjs|js|ts|tsx|jsx)",
88
- );
89
- });
90
-
91
- it("filters on a single declared test type", () => {
92
- asPackage();
93
- process.env.TEST_TYPE = "unit";
94
- expect(config_include(ViteTestConfig())).toContain("@(.unit)");
95
- });
96
-
97
- it("filters on several test types", () => {
98
- asPackage();
99
- process.env.TEST_TYPE = "unit,integration";
100
- expect(config_include(ViteTestConfig())).toContain("@(.unit|.integration)");
101
- });
102
-
103
- it("trims whitespace around the test type list", () => {
104
- asPackage();
105
- process.env.TEST_TYPE = " unit ";
106
- expect(config_include(ViteTestConfig())).toContain("@(.unit)");
107
- });
108
-
109
- it("ignores an empty test type, keeping the default glob", () => {
110
- asPackage();
111
- process.env.TEST_TYPE = "";
112
- expect(config_include(ViteTestConfig())).toContain("@(|)");
113
- });
114
-
115
- it("scopes coverage to the local src when run from a package", () => {
116
- // Workspaces outside `packages/` were reporting 0/0 coverage because
117
- // the repo-root globs only match `packages/*/src/**`.
118
- asPackage();
119
- const config = ViteTestConfig();
120
- // Vitest's coverage type is a discriminated union on `provider`; the
121
- // custom-provider variant doesn't declare `include`/`exclude` even
122
- // though they're valid at runtime for every provider. Cast to the v8
123
- // variant (which does declare them) for assertion purposes only.
124
- const coverage = config.test?.coverage as
125
- | { include?: unknown; exclude?: unknown }
126
- | undefined;
127
- expect(coverage?.include).toContain("src/**/*.ts");
128
- expect(coverage?.exclude).toContain("src/**/*.test.ts");
129
- });
130
-
131
- it("falls back to repo-wide globs when there is no src directory", () => {
132
- asRepoRoot();
133
- const config = ViteTestConfig();
134
- const coverage = config.test?.coverage as
135
- | { include?: unknown; exclude?: unknown }
136
- | undefined;
137
- expect(coverage?.include).toEqual([
138
- "packages/*/src/**",
139
- "captcha/packages/*/src/**",
140
- ]);
141
- expect(coverage?.exclude).toContain("**/node_modules/**");
142
- });
143
-
144
- it("adds the tsconfig-paths plugin only when given a tsconfig", () => {
145
- asPackage();
146
- const withoutPaths = ViteTestConfig().plugins ?? [];
147
- const tsConfigPath = path.join(root, "tsconfig.json");
148
- fs.writeFileSync(tsConfigPath, "{}");
149
- const withPaths = ViteTestConfig(tsConfigPath).plugins ?? [];
150
- expect(withPaths.length).toBe(withoutPaths.length + 1);
151
- });
152
-
153
- it("always excludes node_modules and dist from the test run", () => {
154
- asPackage();
155
- expect(ViteTestConfig().test?.exclude).toEqual([
156
- "**/node_modules/**",
157
- "**/dist/**",
158
- ]);
159
- });
160
-
161
- it("disables sourcemaps and minification, which only cost time here", () => {
162
- asPackage();
163
- const config = ViteTestConfig();
164
- expect(config.build?.minify).toBe(false);
165
- expect(config.build?.sourcemap).toBe(false);
166
- });
167
- });
168
-
169
- describe("ViteThreadsTestConfig", () => {
170
- it("differs from the forks config only in its pool", () => {
171
- asPackage();
172
- expect(ViteThreadsTestConfig().test?.pool).toBe("threads");
173
- expect(ViteTestConfig().test?.pool).toBe("forks");
174
- });
175
-
176
- it("keeps coverage and typechecking on", () => {
177
- asPackage();
178
- const config = ViteThreadsTestConfig();
179
- expect(config.test?.coverage?.enabled).toBe(true);
180
- expect(config.test?.typecheck?.enabled).toBe(true);
181
- });
182
- });
183
-
184
- describe("nodejsPolarsDirnamePlugin", () => {
185
- const plugin = nodejsPolarsDirnamePlugin();
186
-
187
- it("claims only the native polars entry point", () => {
188
- expect(plugin.resolveId(NATIVE_POLARS, undefined, resolveOptions)).toBe(
189
- NATIVE_POLARS,
190
- );
191
- expect(plugin.resolveId("zod", undefined, resolveOptions)).toBeNull();
192
- expect(
193
- plugin.resolveId("nodejs-polars/bin/other.js", undefined, resolveOptions),
194
- ).toBeNull();
195
- });
196
-
197
- it("rewrites __dirname to a path derived from import.meta.url", () => {
198
- // The bundled output is a single ESM file, where `__dirname` does not
199
- // exist; polars uses it to locate its native binary.
200
- const code = "const a = __dirname; const b = __dirname;";
201
- const transformed = plugin.transform(code, NATIVE_POLARS);
202
- expect(transformed).not.toContain("__dirname");
203
- expect(transformed).toContain("import.meta.url");
204
- expect(transformed.match(/import\.meta\.url/g)).toHaveLength(2);
205
- });
206
-
207
- it("leaves every other module untouched", () => {
208
- const code = "const a = __dirname;";
209
- expect(plugin.transform(code, "/repo/src/index.ts")).toBe(code);
210
- });
211
-
212
- it("is named", () => {
213
- expect(plugin.name).toBe("nodejs-polars-dirname-plugin");
214
- });
215
- });
216
-
217
- /** The include glob is stored as a single-element array. */
218
- const config_include = (config: ReturnType<typeof ViteTestConfig>): string => {
219
- const include = config.test?.include;
220
- if (!include || !include[0]) {
221
- throw new Error("config has no test include glob");
222
- }
223
- return include[0];
224
- };
225
-
226
- describe("coverage excludes", () => {
227
- it("drops type-test files, which are compiled but never executed", () => {
228
- // `src/**/*.d.ts` does not match `*.test-d.ts`, so without this every
229
- // package reported its type tests as 0%-covered source.
230
- asPackage();
231
- const exclude = ViteTestConfig().test?.coverage?.exclude ?? [];
232
- expect(exclude).toContain("src/**/*.test-d.ts");
233
- expect(exclude).toContain("src/**/*.test-d.tsx");
234
- });
235
- });
package/src/vite/index.ts DELETED
@@ -1,30 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
- export { default as VitePluginCloseAndCopy } from "./vite-plugin-close-and-copy.js";
15
- export { default as VitePluginExternalizeObfuscatorDeadCode } from "./vite-plugin-externalize-obfuscator-deadcode.js";
16
- export { default as ViteTestConfig } from "./vite.test.config.js";
17
- export { default as ViteThreadsTestConfig } from "./vite.threads.test.config.js";
18
- export { default as VitePluginSourceMapExclude } from "./vite-plugin-sourcemap-exclude.js";
19
- export { default as ViteFrontendConfig } from "./vite.frontend.config.js";
20
- export { default as ViteBackendConfig } from "./vite.backend.config.js";
21
- export { default as VitePluginFixAbsoluteImports } from "./vite-plugin-fix-absolute-imports.js";
22
- export { default as ViteCommonJSConfig } from "./vite.commonjs.config.js";
23
- export { default as ViteEsmConfig } from "./vite.esm.config.js";
24
- export { default as VitePluginRemoveUnusedTranslations } from "./vite-plugin-remove-unused-translations.js";
25
- export * from "./TsNoCheckPlugin.js";
26
- export { default as VitePluginCopy } from "./vite-plugin-copy.js";
27
- export { default as VitePluginModifyExportsFiles } from "./vite-plugin-modify-exports-files.js";
28
- export { copyAssetsPlugin } from "./CopyAssetsPlugin.js";
29
- export type { CopyAssetEntry } from "./CopyAssetsPlugin.js";
30
- export { nodejsPolarsNativeFilePlugin } from "./NodejsPolarsNativeFilePlugin.js";
@@ -1,56 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { describe, expect, test } from "vitest";
16
- import { emptyExportsFiles } from "./vite-plugin-modify-exports-files.js";
17
-
18
- describe("emptyExportsFiles", () => {
19
- test("empties an assignment and keeps the code around it", () => {
20
- expect(
21
- emptyExportsFiles('a();\nexports2.FILES = ["a.js", "b.js"];\nb();'),
22
- ).toBe("a();\nexports2.FILES = [];\n\nb();");
23
- });
24
-
25
- test("empties every assignment, not just the first", () => {
26
- const out = emptyExportsFiles(
27
- 'exports2.FILES = ["a"];exports2.FILES = ["b"];',
28
- );
29
- expect(out).toBe("exports2.FILES = [];\nexports2.FILES = [];\n");
30
- });
31
-
32
- test("spans newlines, as a formatted listing does", () => {
33
- expect(emptyExportsFiles('exports2.FILES = [\n\t"a.js"\n];')).toBe(
34
- "exports2.FILES = [];\n",
35
- );
36
- });
37
-
38
- test("leaves code with no assignment untouched", () => {
39
- expect(emptyExportsFiles("const FILES = [];")).toBe("const FILES = [];");
40
- });
41
-
42
- test("leaves an unterminated assignment alone rather than truncating", () => {
43
- // Cutting to the end of the chunk here would delete real code.
44
- const code = 'exports2.FILES = ["a.js"';
45
- expect(emptyExportsFiles(code)).toBe(code);
46
- });
47
-
48
- test("stays linear on many unterminated assignments", () => {
49
- // The regex this replaced backtracked quadratically on exactly this
50
- // shape, which is what CodeQL flagged.
51
- const code = "exports2.FILES = [".repeat(20000);
52
- const started = performance.now();
53
- expect(emptyExportsFiles(code)).toBe(code);
54
- expect(performance.now() - started).toBeLessThan(1000);
55
- });
56
- });
@@ -1,365 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import fs from "node:fs";
16
- import os from "node:os";
17
- import path from "node:path";
18
- import type { Rollup } from "vite";
19
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
20
- import { tsNoCheckPlugin } from "./TsNoCheckPlugin.js";
21
- import VitePluginCopy from "./vite-plugin-copy.js";
22
- import VitePluginExternalizeObfuscatorDeadCode from "./vite-plugin-externalize-obfuscator-deadcode.js";
23
- import VitePluginFixAbsoluteImports from "./vite-plugin-fix-absolute-imports.js";
24
- import {
25
- flatten,
26
- unflatten,
27
- } from "./vite-plugin-remove-unused-translations.js";
28
- import VitePluginSourcemapExclude from "./vite-plugin-sourcemap-exclude.js";
29
-
30
- /**
31
- * Vite plugin hooks are declared as `hook | { handler }` objects, so they are
32
- * pulled out through a narrowing helper rather than being called directly.
33
- */
34
- const handlerOf = <T>(hook: T | { handler: T } | undefined): T => {
35
- if (!hook) {
36
- throw new Error("plugin does not define the hook");
37
- }
38
- return typeof hook === "function" ? hook : (hook as { handler: T }).handler;
39
- };
40
-
41
- describe("VitePluginSourcemapExclude", () => {
42
- const transformOf = (opts?: { excludeNodeModules?: boolean }) => {
43
- const plugin = VitePluginSourcemapExclude(opts);
44
- return handlerOf(plugin.transform) as (
45
- code: string,
46
- id: string,
47
- ) => { code: string; map: { mappings: string } } | undefined;
48
- };
49
-
50
- it("is named so it can be found in a plugin list", () => {
51
- expect(VitePluginSourcemapExclude().name).toBe("sourcemap-exclude");
52
- });
53
-
54
- it("blanks the sourcemap for node_modules when asked", () => {
55
- // The whole point is to stop Vite holding megabytes of vendor
56
- // sourcemaps in memory.
57
- const result = transformOf({ excludeNodeModules: true })(
58
- "code",
59
- "/repo/node_modules/zod/index.js",
60
- );
61
- expect(result).toEqual({ code: "code", map: { mappings: "" } });
62
- });
63
-
64
- it("leaves first-party code alone", () => {
65
- expect(
66
- transformOf({ excludeNodeModules: true })("code", "/repo/src/index.ts"),
67
- ).toBeUndefined();
68
- });
69
-
70
- it("does nothing without the option, or with no options at all", () => {
71
- expect(
72
- transformOf({})("code", "/repo/node_modules/zod/index.js"),
73
- ).toBeUndefined();
74
- expect(
75
- transformOf()("code", "/repo/node_modules/zod/index.js"),
76
- ).toBeUndefined();
77
- expect(
78
- transformOf({ excludeNodeModules: false })(
79
- "code",
80
- "/repo/node_modules/zod/index.js",
81
- ),
82
- ).toBeUndefined();
83
- });
84
-
85
- it("matches node_modules anywhere in the id, including nested copies", () => {
86
- expect(
87
- transformOf({ excludeNodeModules: true })(
88
- "code",
89
- "/repo/node_modules/a/node_modules/b/index.js",
90
- ),
91
- ).toBeDefined();
92
- });
93
- });
94
-
95
- describe("VitePluginFixAbsoluteImports", () => {
96
- const transform = handlerOf(VitePluginFixAbsoluteImports().transform) as (
97
- code: string,
98
- id: string,
99
- ) => { code: string };
100
-
101
- it("runs after other plugins have emitted their imports", () => {
102
- const plugin = VitePluginFixAbsoluteImports();
103
- expect(plugin.name).toBe("fix-absolute-imports");
104
- expect(plugin.enforce).toBe("post");
105
- });
106
-
107
- it("rewrites a deep builtin import back to the bare specifier", () => {
108
- // `from "node:fs/promises/index.js"` is not resolvable at runtime;
109
- // the bare builtin is.
110
- expect(transform('import x from "fs/promises/index.js";', "id").code).toBe(
111
- 'import x from "fs";',
112
- );
113
- });
114
-
115
- it("rewrites every occurrence in a file", () => {
116
- const code = 'from "path/posix/x.js";\nfrom "path/win32/y.js";';
117
- expect(transform(code, "id").code).toBe('from "path";\nfrom "path";');
118
- });
119
-
120
- it("leaves a bare builtin import untouched", () => {
121
- const code = 'import fs from "fs";';
122
- expect(transform(code, "id").code).toBe(code);
123
- });
124
-
125
- it("leaves third-party deep imports untouched", () => {
126
- const code = 'import x from "zod/lib/index.js";';
127
- expect(transform(code, "id").code).toBe(code);
128
- });
129
-
130
- it("returns empty code unchanged", () => {
131
- expect(transform("", "id").code).toBe("");
132
- });
133
- });
134
-
135
- describe("tsNoCheckPlugin", () => {
136
- const outputOptions = {} as Rollup.NormalizedOutputOptions;
137
-
138
- it("prefixes every emitted chunk with a ts-nocheck pragma", () => {
139
- // Consumers typecheck the bundled output; without this the generated
140
- // code fails their build.
141
- const bundle = {
142
- "index.js": {
143
- type: "chunk",
144
- code: "export const a = 1;",
145
- } as Rollup.OutputChunk,
146
- "other.js": {
147
- type: "chunk",
148
- code: "export const b = 2;",
149
- } as Rollup.OutputChunk,
150
- };
151
- tsNoCheckPlugin().generateBundle(
152
- outputOptions,
153
- bundle as Rollup.OutputBundle,
154
- );
155
-
156
- expect(bundle["index.js"].code).toBe("// @ts-nocheck\nexport const a = 1;");
157
- expect(bundle["other.js"].code).toBe("// @ts-nocheck\nexport const b = 2;");
158
- });
159
-
160
- it("leaves assets and empty chunks alone", () => {
161
- const bundle = {
162
- "style.css": { type: "asset", source: "body{}" } as Rollup.OutputAsset,
163
- "empty.js": { type: "chunk", code: "" } as Rollup.OutputChunk,
164
- };
165
- tsNoCheckPlugin().generateBundle(
166
- outputOptions,
167
- bundle as Rollup.OutputBundle,
168
- );
169
-
170
- expect(bundle["style.css"]).toEqual({ type: "asset", source: "body{}" });
171
- expect(bundle["empty.js"].code).toBe("");
172
- });
173
-
174
- it("handles an empty bundle", () => {
175
- expect(() =>
176
- tsNoCheckPlugin().generateBundle(outputOptions, {}),
177
- ).not.toThrow();
178
- });
179
-
180
- it("is named", () => {
181
- expect(tsNoCheckPlugin().name).toBe("vite-plugin-ts-nocheck");
182
- });
183
- });
184
-
185
- describe("VitePluginExternalizeObfuscatorDeadCode", () => {
186
- const resolveId = handlerOf(
187
- VitePluginExternalizeObfuscatorDeadCode().resolveId,
188
- ) as (source: string) => { id: string; external: boolean } | null;
189
-
190
- it("runs before resolution so rolldown never sees the specifier", () => {
191
- const plugin = VitePluginExternalizeObfuscatorDeadCode();
192
- expect(plugin.name).toBe("externalize-obfuscator-deadcode");
193
- expect(plugin.enforce).toBe("pre");
194
- });
195
-
196
- it("externalises each known dead-code specifier", () => {
197
- // Rolldown treats an unresolved import as a hard error, so missing one
198
- // of these breaks the obfuscated build outright.
199
- for (const source of [
200
- "../../package",
201
- "../redacted.js",
202
- "../utils/isStandaloneExecutable",
203
- "./resolve-local-redacted-path",
204
- "@redacted/components/package",
205
- "@redacted/enterprise-plugin",
206
- "@redacted/enterprise-plugin/package",
207
- ]) {
208
- expect(resolveId(source), source).toEqual({ id: source, external: true });
209
- }
210
- });
211
-
212
- it("defers every other specifier to the normal resolver", () => {
213
- for (const source of [
214
- "zod",
215
- "./real-module.js",
216
- "",
217
- "../../package.json",
218
- ]) {
219
- expect(resolveId(source), source).toBeNull();
220
- }
221
- });
222
-
223
- it("matches exactly, not by prefix", () => {
224
- expect(resolveId("@redacted/enterprise-plugin/extra")).toBeNull();
225
- });
226
- });
227
-
228
- describe("VitePluginCopy", () => {
229
- let root: string;
230
- let cwd: string;
231
-
232
- beforeEach(() => {
233
- root = fs.mkdtempSync(path.join(os.tmpdir(), "prosopo-copy-test-"));
234
- cwd = process.cwd();
235
- process.chdir(root);
236
- fs.mkdirSync(path.join(root, "src", "nested"), { recursive: true });
237
- fs.writeFileSync(path.join(root, "src", "a.txt"), "a");
238
- fs.writeFileSync(path.join(root, "src", "b.md"), "b");
239
- fs.writeFileSync(path.join(root, "src", "nested", "c.txt"), "c");
240
- });
241
-
242
- afterEach(() => {
243
- process.chdir(cwd);
244
- fs.rmSync(root, { recursive: true, force: true });
245
- });
246
-
247
- const run = async (options: Parameters<typeof VitePluginCopy>[0]) => {
248
- const plugin = VitePluginCopy(options);
249
- const closeBundle = handlerOf(plugin.closeBundle) as () => Promise<void>;
250
- await closeBundle();
251
- };
252
-
253
- it("copies matching files and creates the destination tree", async () => {
254
- await run({ srcDir: "src", destDir: "dist", include: ["**/*.txt"] });
255
-
256
- expect(fs.readFileSync(path.join(root, "dist", "a.txt"), "utf8")).toBe("a");
257
- expect(
258
- fs.readFileSync(path.join(root, "dist", "nested", "c.txt"), "utf8"),
259
- ).toBe("c");
260
- expect(fs.existsSync(path.join(root, "dist", "b.md"))).toBe(false);
261
- });
262
-
263
- it("honours the exclude globs", async () => {
264
- await run({
265
- srcDir: "src",
266
- destDir: "dist",
267
- include: ["**/*.txt"],
268
- exclude: ["nested/**"],
269
- });
270
-
271
- expect(fs.existsSync(path.join(root, "dist", "a.txt"))).toBe(true);
272
- expect(fs.existsSync(path.join(root, "dist", "nested"))).toBe(false);
273
- });
274
-
275
- it("copies nothing, and says so, when no include glob is given", async () => {
276
- // Copying the whole tree by default would silently ship sources.
277
- const info = vi.spyOn(console, "info").mockImplementation(() => undefined);
278
- await run({ srcDir: "src", destDir: "dist" });
279
-
280
- expect(fs.existsSync(path.join(root, "dist"))).toBe(false);
281
- expect(info).toHaveBeenCalledWith(
282
- "[copy-plugin] No include globs specified, nothing to copy.",
283
- );
284
- info.mockRestore();
285
- });
286
-
287
- it("treats an empty include list as no include list", async () => {
288
- await run({ srcDir: "src", destDir: "dist", include: [] });
289
- expect(fs.existsSync(path.join(root, "dist"))).toBe(false);
290
- });
291
-
292
- it("succeeds when nothing matches", async () => {
293
- await run({ srcDir: "src", destDir: "dist", include: ["**/*.nope"] });
294
- expect(fs.existsSync(path.join(root, "dist"))).toBe(false);
295
- });
296
-
297
- it("overwrites an existing destination file", async () => {
298
- fs.mkdirSync(path.join(root, "dist"), { recursive: true });
299
- fs.writeFileSync(path.join(root, "dist", "a.txt"), "stale");
300
- await run({ srcDir: "src", destDir: "dist", include: ["a.txt"] });
301
- expect(fs.readFileSync(path.join(root, "dist", "a.txt"), "utf8")).toBe("a");
302
- });
303
-
304
- it("is named", () => {
305
- expect(VitePluginCopy({ srcDir: "s", destDir: "d" }).name).toBe(
306
- "copy-plugin",
307
- );
308
- });
309
- });
310
-
311
- describe("translation flatten/unflatten", () => {
312
- it("flattens nested objects to dotted keys", () => {
313
- expect(flatten({ a: { b: { c: "x" } }, d: "y" })).toEqual({
314
- "a.b.c": "x",
315
- d: "y",
316
- });
317
- });
318
-
319
- it("returns an empty object for an empty input", () => {
320
- expect(flatten({})).toEqual({});
321
- });
322
-
323
- it("applies a prefix", () => {
324
- expect(flatten({ a: "x" }, "p.")).toEqual({ "p.a": "x" });
325
- });
326
-
327
- it("treats arrays as objects, keying by index", () => {
328
- expect(flatten({ a: ["x", "y"] })).toEqual({ "a.0": "x", "a.1": "y" });
329
- });
330
-
331
- it("round trips a nested object", () => {
332
- const original = { a: { b: "x" }, c: "y" };
333
- expect(unflatten(flatten(original))).toEqual(original);
334
- });
335
-
336
- it("rebuilds arrays when the next key is numeric", () => {
337
- // Locale files contain arrays of strings; turning them into objects
338
- // keyed "0", "1" would change the shape the app reads at runtime.
339
- expect(unflatten({ "a.0": "x", "a.1": "y" })).toEqual({ a: ["x", "y"] });
340
- });
341
-
342
- it("keeps object keys that merely look like words", () => {
343
- expect(unflatten({ "a.b": "x" })).toEqual({ a: { b: "x" } });
344
- });
345
-
346
- it("merges sibling keys under one parent", () => {
347
- expect(unflatten({ "a.b": "x", "a.c": "y" })).toEqual({
348
- a: { b: "x", c: "y" },
349
- });
350
- });
351
-
352
- it("handles a top-level key with no dots", () => {
353
- expect(unflatten({ a: "x" })).toEqual({ a: "x" });
354
- });
355
-
356
- it("returns an empty object for an empty input", () => {
357
- expect(unflatten({})).toEqual({});
358
- });
359
-
360
- it("preserves non-string leaf values", () => {
361
- expect(unflatten({ "a.b": 1, "a.c": true })).toEqual({
362
- a: { b: 1, c: true },
363
- });
364
- });
365
- });