@prosopo/config 3.4.2 → 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 (57) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +5 -8
  2. package/.turbo/turbo-build$colon$tsc.log +4 -7
  3. package/.turbo/turbo-build.log +6 -12
  4. package/CHANGELOG.md +13 -0
  5. package/LICENSE +201 -0
  6. package/dist/cjs/dependencies.cjs +86 -46
  7. package/dist/cjs/vite/vite.frontend.config.cjs +7 -0
  8. package/dist/cjs/webpack/webpack.config.cjs +8 -8
  9. package/dist/dependencies.d.ts.map +1 -1
  10. package/dist/dependencies.js +85 -44
  11. package/dist/dependencies.js.map +1 -1
  12. package/dist/getDependencies.test.js +151 -125
  13. package/dist/getDependencies.test.js.map +1 -1
  14. package/dist/vite/vite.frontend.config.d.ts.map +1 -1
  15. package/dist/vite/vite.frontend.config.js +8 -1
  16. package/dist/vite/vite.frontend.config.js.map +1 -1
  17. package/dist/webpack/webpack.config.d.ts +2 -0
  18. package/dist/webpack/webpack.config.d.ts.map +1 -1
  19. package/dist/webpack/webpack.config.js +8 -8
  20. package/dist/webpack/webpack.config.js.map +1 -1
  21. package/package.json +106 -103
  22. package/src/dependencies.test.ts +0 -249
  23. package/src/dependencies.ts +0 -259
  24. package/src/getDependencies.test.ts +0 -385
  25. package/src/index.ts +0 -16
  26. package/src/vite/CopyAssetsPlugin.ts +0 -49
  27. package/src/vite/NodejsPolarsDirnamePlugin.ts +0 -50
  28. package/src/vite/NodejsPolarsNativeFilePlugin.ts +0 -105
  29. package/src/vite/TsNoCheckPlugin.ts +0 -31
  30. package/src/vite/bundleConfigs.test.ts +0 -461
  31. package/src/vite/configs.test.ts +0 -223
  32. package/src/vite/index.ts +0 -30
  33. package/src/vite/modifyExportsFiles.test.ts +0 -56
  34. package/src/vite/plugins.test.ts +0 -365
  35. package/src/vite/testFileGlobs.test.ts +0 -162
  36. package/src/vite/testFileGlobs.ts +0 -57
  37. package/src/vite/translations.test.ts +0 -164
  38. package/src/vite/types.ts +0 -13
  39. package/src/vite/vite-plugin-close-and-copy.ts +0 -76
  40. package/src/vite/vite-plugin-copy.ts +0 -67
  41. package/src/vite/vite-plugin-externalize-obfuscator-deadcode.ts +0 -43
  42. package/src/vite/vite-plugin-fix-absolute-imports.ts +0 -40
  43. package/src/vite/vite-plugin-modify-exports-files.ts +0 -64
  44. package/src/vite/vite-plugin-remove-unused-translations.ts +0 -128
  45. package/src/vite/vite-plugin-sourcemap-exclude.ts +0 -36
  46. package/src/vite/vite.backend.config.ts +0 -172
  47. package/src/vite/vite.commonjs.config.ts +0 -98
  48. package/src/vite/vite.esm.config.ts +0 -99
  49. package/src/vite/vite.frontend.config.ts +0 -340
  50. package/src/vite/vite.test.config.ts +0 -115
  51. package/src/vite/vite.threads.test.config.ts +0 -100
  52. package/src/webpack/index.ts +0 -14
  53. package/src/webpack/webpack.config.ts +0 -159
  54. package/tsconfig.cjs.json +0 -9
  55. package/tsconfig.json +0 -17
  56. package/tsconfig.tsbuildinfo +0 -1
  57. package/tsconfig.types.json +0 -9
@@ -1,223 +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 every test file when no filter is set", () => {
85
- asPackage();
86
- const config = ViteTestConfig();
87
- expect(config_include(config)).toBe(
88
- "src/**/*.@(test|spec).@(mts|cts|mjs|cjs|js|ts|tsx|jsx)",
89
- );
90
- expect(config.test?.exclude).toEqual(["**/node_modules/**", "**/dist/**"]);
91
- });
92
-
93
- it("filters by excluding the other known test types", () => {
94
- // Which files that actually selects is proven against vitest's own
95
- // collection in testFileGlobs.test.ts.
96
- asPackage();
97
- process.env.TEST_TYPE = "unit";
98
- expect(ViteTestConfig().test?.exclude).toContain(
99
- "src/**/*.@(integration).@(test|spec).@(mts|cts|mjs|cjs|js|ts|tsx|jsx)",
100
- );
101
- });
102
-
103
- it("scopes coverage to the local src when run from a package", () => {
104
- // Workspaces outside `packages/` were reporting 0/0 coverage because
105
- // the repo-root globs only match `packages/*/src/**`.
106
- asPackage();
107
- const config = ViteTestConfig();
108
- // Vitest's coverage type is a discriminated union on `provider`; the
109
- // custom-provider variant doesn't declare `include`/`exclude` even
110
- // though they're valid at runtime for every provider. Cast to the v8
111
- // variant (which does declare them) for assertion purposes only.
112
- const coverage = config.test?.coverage as
113
- | { include?: unknown; exclude?: unknown }
114
- | undefined;
115
- expect(coverage?.include).toContain("src/**/*.ts");
116
- expect(coverage?.exclude).toContain("src/**/*.test.ts");
117
- });
118
-
119
- it("falls back to repo-wide globs when there is no src directory", () => {
120
- asRepoRoot();
121
- const config = ViteTestConfig();
122
- const coverage = config.test?.coverage as
123
- | { include?: unknown; exclude?: unknown }
124
- | undefined;
125
- expect(coverage?.include).toEqual([
126
- "packages/*/src/**",
127
- "captcha/packages/*/src/**",
128
- ]);
129
- expect(coverage?.exclude).toContain("**/node_modules/**");
130
- });
131
-
132
- it("adds the tsconfig-paths plugin only when given a tsconfig", () => {
133
- asPackage();
134
- const withoutPaths = ViteTestConfig().plugins ?? [];
135
- const tsConfigPath = path.join(root, "tsconfig.json");
136
- fs.writeFileSync(tsConfigPath, "{}");
137
- const withPaths = ViteTestConfig(tsConfigPath).plugins ?? [];
138
- expect(withPaths.length).toBe(withoutPaths.length + 1);
139
- });
140
-
141
- it("always excludes node_modules and dist from the test run", () => {
142
- asPackage();
143
- expect(ViteTestConfig().test?.exclude).toEqual([
144
- "**/node_modules/**",
145
- "**/dist/**",
146
- ]);
147
- });
148
-
149
- it("disables sourcemaps and minification, which only cost time here", () => {
150
- asPackage();
151
- const config = ViteTestConfig();
152
- expect(config.build?.minify).toBe(false);
153
- expect(config.build?.sourcemap).toBe(false);
154
- });
155
- });
156
-
157
- describe("ViteThreadsTestConfig", () => {
158
- it("differs from the forks config only in its pool", () => {
159
- asPackage();
160
- expect(ViteThreadsTestConfig().test?.pool).toBe("threads");
161
- expect(ViteTestConfig().test?.pool).toBe("forks");
162
- });
163
-
164
- it("keeps coverage and typechecking on", () => {
165
- asPackage();
166
- const config = ViteThreadsTestConfig();
167
- expect(config.test?.coverage?.enabled).toBe(true);
168
- expect(config.test?.typecheck?.enabled).toBe(true);
169
- });
170
- });
171
-
172
- describe("nodejsPolarsDirnamePlugin", () => {
173
- const plugin = nodejsPolarsDirnamePlugin();
174
-
175
- it("claims only the native polars entry point", () => {
176
- expect(plugin.resolveId(NATIVE_POLARS, undefined, resolveOptions)).toBe(
177
- NATIVE_POLARS,
178
- );
179
- expect(plugin.resolveId("zod", undefined, resolveOptions)).toBeNull();
180
- expect(
181
- plugin.resolveId("nodejs-polars/bin/other.js", undefined, resolveOptions),
182
- ).toBeNull();
183
- });
184
-
185
- it("rewrites __dirname to a path derived from import.meta.url", () => {
186
- // The bundled output is a single ESM file, where `__dirname` does not
187
- // exist; polars uses it to locate its native binary.
188
- const code = "const a = __dirname; const b = __dirname;";
189
- const transformed = plugin.transform(code, NATIVE_POLARS);
190
- expect(transformed).not.toContain("__dirname");
191
- expect(transformed).toContain("import.meta.url");
192
- expect(transformed.match(/import\.meta\.url/g)).toHaveLength(2);
193
- });
194
-
195
- it("leaves every other module untouched", () => {
196
- const code = "const a = __dirname;";
197
- expect(plugin.transform(code, "/repo/src/index.ts")).toBe(code);
198
- });
199
-
200
- it("is named", () => {
201
- expect(plugin.name).toBe("nodejs-polars-dirname-plugin");
202
- });
203
- });
204
-
205
- /** The include glob is stored as a single-element array. */
206
- const config_include = (config: ReturnType<typeof ViteTestConfig>): string => {
207
- const include = config.test?.include;
208
- if (!include || !include[0]) {
209
- throw new Error("config has no test include glob");
210
- }
211
- return include[0];
212
- };
213
-
214
- describe("coverage excludes", () => {
215
- it("drops type-test files, which are compiled but never executed", () => {
216
- // `src/**/*.d.ts` does not match `*.test-d.ts`, so without this every
217
- // package reported its type tests as 0%-covered source.
218
- asPackage();
219
- const exclude = ViteTestConfig().test?.coverage?.exclude ?? [];
220
- expect(exclude).toContain("src/**/*.test-d.ts");
221
- expect(exclude).toContain("src/**/*.test-d.tsx");
222
- });
223
- });
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
- });