vite-plugin-css-module-types 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,130 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ normalizeFolderPath,
4
+ normalizeFolderList,
5
+ isInFolders,
6
+ shouldProcessFile,
7
+ } from "../lib/path-filter.ts";
8
+
9
+ // ─── normalizeFolderPath ────────────────────────────────────────────────────
10
+
11
+ describe("normalizeFolderPath", () => {
12
+ it("returns the path unchanged when already normalized", () => {
13
+ expect(normalizeFolderPath("apps/app/src")).toBe("apps/app/src");
14
+ });
15
+
16
+ it("replaces backslashes with forward slashes", () => {
17
+ expect(normalizeFolderPath("apps\\app\\src")).toBe("apps/app/src");
18
+ });
19
+
20
+ it('strips leading "./"', () => {
21
+ expect(normalizeFolderPath("./apps/app")).toBe("apps/app");
22
+ });
23
+
24
+ it('strips leading "/"', () => {
25
+ expect(normalizeFolderPath("/apps/app")).toBe("apps/app");
26
+ });
27
+
28
+ it("strips trailing slashes", () => {
29
+ expect(normalizeFolderPath("apps/app/")).toBe("apps/app");
30
+ expect(normalizeFolderPath("apps/app///")).toBe("apps/app");
31
+ });
32
+
33
+ it("handles combined edge cases", () => {
34
+ expect(normalizeFolderPath(".\\apps\\app\\")).toBe("apps/app");
35
+ });
36
+
37
+ it("returns empty string for root-like paths", () => {
38
+ expect(normalizeFolderPath("./")).toBe("");
39
+ expect(normalizeFolderPath("/")).toBe("");
40
+ });
41
+ });
42
+
43
+ // ─── normalizeFolderList ────────────────────────────────────────────────────
44
+
45
+ describe("normalizeFolderList", () => {
46
+ it("returns empty array for undefined", () => {
47
+ expect(normalizeFolderList(undefined)).toEqual([]);
48
+ });
49
+
50
+ it("returns empty array for empty string", () => {
51
+ expect(normalizeFolderList("")).toEqual([]);
52
+ });
53
+
54
+ it("wraps a single string into a normalized array", () => {
55
+ expect(normalizeFolderList("./apps/app/")).toEqual(["apps/app"]);
56
+ });
57
+
58
+ it("normalizes each item in an array", () => {
59
+ expect(normalizeFolderList(["./apps/app/", "apps\\shared"])).toEqual([
60
+ "apps/app",
61
+ "apps/shared",
62
+ ]);
63
+ });
64
+
65
+ it("filters out empty results after normalization", () => {
66
+ expect(normalizeFolderList(["./", "apps/app"])).toEqual(["apps/app"]);
67
+ });
68
+ });
69
+
70
+ // ─── isInFolders ────────────────────────────────────────────────────────────
71
+
72
+ describe("isInFolders", () => {
73
+ it("returns false for empty folders array", () => {
74
+ expect(isInFolders("apps/app/src/file.css", [])).toBe(false);
75
+ });
76
+
77
+ it("returns true when file is inside a matching folder", () => {
78
+ expect(isInFolders("apps/app/src/file.css", ["apps/app/src"])).toBe(true);
79
+ });
80
+
81
+ it("returns true when file path matches the folder exactly", () => {
82
+ expect(isInFolders("apps/app/src", ["apps/app/src"])).toBe(true);
83
+ });
84
+
85
+ it("returns false when file is outside all folders", () => {
86
+ expect(isInFolders("apps/admin/src/file.css", ["apps/app/src"])).toBe(false);
87
+ });
88
+
89
+ it("returns true when matching any of multiple folders", () => {
90
+ expect(isInFolders("apps/shared/src/ui/button.css", ["apps/app/src", "apps/shared/src"])).toBe(
91
+ true,
92
+ );
93
+ });
94
+
95
+ it("does not match partial folder names", () => {
96
+ expect(isInFolders("apps/app/src-legacy/file.css", ["apps/app/src"])).toBe(false);
97
+ });
98
+
99
+ it("normalizes backslashes in the file path", () => {
100
+ expect(isInFolders("apps\\app\\src\\file.css", ["apps/app/src"])).toBe(true);
101
+ });
102
+ });
103
+
104
+ // ─── shouldProcessFile ──────────────────────────────────────────────────────
105
+
106
+ describe("shouldProcessFile", () => {
107
+ it("includes all files when include list is empty", () => {
108
+ expect(shouldProcessFile("any/path/file.css", [], [])).toBe(true);
109
+ });
110
+
111
+ it("includes files matching include folders", () => {
112
+ expect(shouldProcessFile("src/ui/file.css", ["src"], [])).toBe(true);
113
+ });
114
+
115
+ it("excludes files not matching include folders", () => {
116
+ expect(shouldProcessFile("other/file.css", ["src"], [])).toBe(false);
117
+ });
118
+
119
+ it("respects exclude over include", () => {
120
+ expect(shouldProcessFile("src/legacy/file.css", ["src"], ["src/legacy"])).toBe(false);
121
+ });
122
+
123
+ it("allows files in include but not in exclude", () => {
124
+ expect(shouldProcessFile("src/features/file.css", ["src"], ["src/legacy"])).toBe(true);
125
+ });
126
+
127
+ it("excludes even when include is empty (all included)", () => {
128
+ expect(shouldProcessFile("vendor/lib.css", [], ["vendor"])).toBe(false);
129
+ });
130
+ });
@@ -0,0 +1,101 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { patchViteModuleCode, evaluateModuleExports } from "../lib/transform.ts";
3
+
4
+ // ─── patchViteModuleCode ────────────────────────────────────────────────────
5
+
6
+ describe("patchViteModuleCode", () => {
7
+ it("comments out import statements", () => {
8
+ const code = 'import { updateStyle } from "/@vite/client"';
9
+ const result = patchViteModuleCode(code);
10
+ expect(result).toBe('// import { updateStyle } from "/@vite/client"');
11
+ });
12
+
13
+ it("comments out __vite__updateStyle calls", () => {
14
+ const code = '__vite__updateStyle("id", css)';
15
+ const result = patchViteModuleCode(code);
16
+ expect(result).toBe('// __vite__updateStyle("id", css)');
17
+ });
18
+
19
+ it("comments out import.meta.hot references", () => {
20
+ const code = "import.meta.hot.accept()";
21
+ const result = patchViteModuleCode(code);
22
+ expect(result).toBe("// import.meta.hot.accept()");
23
+ });
24
+
25
+ it("removes entire if(import.meta.hot) blocks", () => {
26
+ const code = ["const x = 1;", "if (import.meta.hot) {", " import.meta.hot.accept()", "}"].join(
27
+ "\n",
28
+ );
29
+ const result = patchViteModuleCode(code);
30
+ expect(result).toContain("const x = 1;");
31
+ expect(result).not.toContain("import.meta.hot");
32
+ });
33
+
34
+ it("re-exports __vite__css as an export", () => {
35
+ const code = 'const __vite__css = ".button { }"';
36
+ const result = patchViteModuleCode(code);
37
+ expect(result).toBe('export const __vite__css = ".button { }"');
38
+ });
39
+
40
+ it("applies all patches together on realistic Vite output", () => {
41
+ const viteOutput = [
42
+ 'import { updateStyle, removeStyle } from "/@vite/client"',
43
+ 'const __vite__css = ".btn_hash { color: red; }"',
44
+ '__vite__updateStyle("some-id", __vite__css)',
45
+ 'export default { "btn": "btn_hash" }',
46
+ "if (import.meta.hot) {",
47
+ " import.meta.hot.accept()",
48
+ "}",
49
+ ].join("\n");
50
+
51
+ const patched = patchViteModuleCode(viteOutput);
52
+
53
+ expect(patched).toContain("// import { updateStyle");
54
+ expect(patched).toContain("export const __vite__css");
55
+ expect(patched).toContain("// __vite__updateStyle");
56
+ expect(patched).not.toContain("import.meta.hot");
57
+ expect(patched).toContain("export default");
58
+ });
59
+ });
60
+
61
+ // ─── evaluateModuleExports ──────────────────────────────────────────────────
62
+
63
+ describe("evaluateModuleExports", () => {
64
+ it("extracts class mapping and CSS text from patched code", async () => {
65
+ const patchedCode = [
66
+ 'export const __vite__css = ".btn_hash123 { color: red; }";',
67
+ 'export default { "btn": "btn_hash123" };',
68
+ ].join("\n");
69
+
70
+ const result = await evaluateModuleExports(patchedCode);
71
+ expect(result.classMapping).toEqual({ btn: "btn_hash123" });
72
+ expect(result.cssText).toBe(".btn_hash123 { color: red; }");
73
+ });
74
+
75
+ it("returns empty classMapping when no default export", async () => {
76
+ const patchedCode = 'export const __vite__css = ".x { }";';
77
+ const result = await evaluateModuleExports(patchedCode);
78
+ expect(result.classMapping).toEqual({});
79
+ expect(result.cssText).toBe(".x { }");
80
+ });
81
+
82
+ it("returns empty cssText when __vite__css is not exported", async () => {
83
+ const patchedCode = 'export default { "a": "a_hash" };';
84
+ const result = await evaluateModuleExports(patchedCode);
85
+ expect(result.classMapping).toEqual({ a: "a_hash" });
86
+ expect(result.cssText).toBe("");
87
+ });
88
+
89
+ it("handles multiple class exports", async () => {
90
+ const patchedCode = [
91
+ 'export const __vite__css = ".a_h1 {} .b_h2 {}";',
92
+ 'export default { "my-class": "a_h1", "other": "b_h2" };',
93
+ ].join("\n");
94
+
95
+ const result = await evaluateModuleExports(patchedCode);
96
+ expect(result.classMapping).toEqual({
97
+ "my-class": "a_h1",
98
+ other: "b_h2",
99
+ });
100
+ });
101
+ });