@codefast/cli 0.3.13-canary.0 → 0.3.13-canary.1
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/bin.mjs +1 -1
- package/dist/commands/arrange.mjs +106 -1
- package/dist/commands/mirror.mjs +65 -1
- package/dist/commands/tag.mjs +57 -1
- package/dist/lib/arrange/application/analyze.mjs +91 -1
- package/dist/lib/arrange/application/group-file.mjs +112 -1
- package/dist/lib/arrange/application/run-target.mjs +64 -1
- package/dist/lib/arrange/domain/ast/ast-helpers.mjs +114 -1
- package/dist/lib/arrange/domain/ast/collectors-cn.mjs +60 -1
- package/dist/lib/arrange/domain/ast/collectors-jsx.mjs +19 -1
- package/dist/lib/arrange/domain/ast/collectors-tv.mjs +169 -1
- package/dist/lib/arrange/domain/ast/targets.mjs +105 -1
- package/dist/lib/arrange/domain/constants.mjs +168 -1
- package/dist/lib/arrange/domain/errors.mjs +18 -1
- package/dist/lib/arrange/domain/grouping.mjs +234 -1
- package/dist/lib/arrange/domain/imports.mjs +53 -1
- package/dist/lib/arrange/domain/tokenizer.mjs +215 -1
- package/dist/lib/arrange/index.mjs +14 -15
- package/dist/lib/arrange/infra/walk.mjs +32 -1
- package/dist/lib/arrange/presentation/formatters.mjs +51 -1
- package/dist/lib/arrange/presentation/report.mjs +23 -1
- package/dist/lib/config/domain/schema.mjs +24 -1
- package/dist/lib/config/index.mjs +2 -3
- package/dist/lib/config/infra/loader.mjs +77 -1
- package/dist/lib/infra/caught-unknown-message.mjs +19 -1
- package/dist/lib/infra/config-reporter.mjs +9 -1
- package/dist/lib/infra/node-io.mjs +29 -1
- package/dist/lib/infra/workspace/repo-root.mjs +21 -1
- package/dist/lib/mirror/application/engine.mjs +247 -1
- package/dist/lib/mirror/application/sync.mjs +161 -1
- package/dist/lib/mirror/domain/constants.mjs +11 -1
- package/dist/lib/mirror/domain/errors.mjs +14 -1
- package/dist/lib/mirror/index.mjs +1 -2
- package/dist/lib/mirror/infra/dirent-list.mjs +13 -1
- package/dist/lib/mirror/infra/package-filter.mjs +25 -1
- package/dist/lib/mirror/infra/path-normalizer.mjs +6 -1
- package/dist/lib/mirror/infra/update-pkg.mjs +104 -1
- package/dist/lib/mirror/infra/workspace-packages.mjs +158 -1
- package/dist/lib/mirror/presentation/reporter.mjs +111 -2
- package/dist/lib/tag/application/engine.mjs +286 -1
- package/dist/lib/tag/index.mjs +3 -4
- package/dist/lib/tag/infra/target-resolver.mjs +169 -1
- package/dist/lib/tag/presentation/colors.mjs +11 -1
- package/dist/lib/tag/presentation/tag-presenter.mjs +62 -1
- package/dist/program.mjs +33 -1
- package/package.json +2 -2
- package/dist/analyze-DvtJ3j9m.mjs +0 -92
- package/dist/arrange-Bbh62DrU.mjs +0 -107
- package/dist/arrange-BqP2DI3w.mjs +0 -8
- package/dist/ast-helpers-MDRYuIAk.mjs +0 -115
- package/dist/caught-unknown-message-CRhkDWO7.mjs +0 -20
- package/dist/collectors-cn-BS8TTQ8O.mjs +0 -61
- package/dist/collectors-jsx-D9tL2ZPM.mjs +0 -20
- package/dist/collectors-tv-D2PXZaRt.mjs +0 -170
- package/dist/colors-BjdkCfGK.mjs +0 -12
- package/dist/commands/mirror.test.mjs +0 -48
- package/dist/config-DVia_f9a.mjs +0 -3
- package/dist/config-reporter-SCcWDZc1.mjs +0 -10
- package/dist/constants-AtPhF-Ct.mjs +0 -169
- package/dist/constants-Bq5wdyLC.mjs +0 -8
- package/dist/dirent-list-BSDeRG5i.mjs +0 -14
- package/dist/engine-CcWEHSCq.mjs +0 -287
- package/dist/engine-DRTdkc5u.mjs +0 -236
- package/dist/errors-CFH4w6ic.mjs +0 -15
- package/dist/errors-CpU1ogfA.mjs +0 -19
- package/dist/formatters-DGE8jJ53.mjs +0 -52
- package/dist/group-file-ClW7XdD6.mjs +0 -113
- package/dist/grouping-c1grpCQT.mjs +0 -235
- package/dist/imports-DSVq1xhB.mjs +0 -54
- package/dist/lib/arrange/application/analyze.test.mjs +0 -171
- package/dist/lib/arrange/application/group-file.test.mjs +0 -273
- package/dist/lib/arrange/application/run-target.test.mjs +0 -97
- package/dist/lib/arrange/domain/ast/ast-helpers.test.mjs +0 -148
- package/dist/lib/arrange/domain/ast/collectors-cn.test.mjs +0 -58
- package/dist/lib/arrange/domain/ast/collectors-jsx.test.mjs +0 -20
- package/dist/lib/arrange/domain/ast/collectors-tv.test.mjs +0 -145
- package/dist/lib/arrange/domain/ast/targets.test.mjs +0 -50
- package/dist/lib/arrange/domain/grouping.test.mjs +0 -186
- package/dist/lib/arrange/domain/imports.test.mjs +0 -48
- package/dist/lib/arrange/domain/tokenizer.test.mjs +0 -165
- package/dist/lib/arrange/infra/walk.test.mjs +0 -50
- package/dist/lib/arrange/presentation/formatters.test.mjs +0 -73
- package/dist/lib/config/infra/loader.test.mjs +0 -116
- package/dist/lib/infra/workspace/repo-root.test.mjs +0 -45
- package/dist/lib/mirror/application/sync.test.mjs +0 -482
- package/dist/lib/mirror/infra/dirent-list.test.mjs +0 -18
- package/dist/lib/mirror/infra/package-filter.test.mjs +0 -32
- package/dist/lib/mirror/infra/workspace-packages.test.mjs +0 -167
- package/dist/lib/tag/application/engine.test.mjs +0 -159
- package/dist/lib/tag/presentation/tag-presenter.test.mjs +0 -29
- package/dist/loader-DpN-zl5g.mjs +0 -78
- package/dist/mirror-CbGiLXKT.mjs +0 -2
- package/dist/mirror-DqJE3ejL.mjs +0 -66
- package/dist/node-io-DFRbHO6m.mjs +0 -30
- package/dist/package-filter-C_8ryqvz.mjs +0 -26
- package/dist/path-normalizer-D4ceBO3l.mjs +0 -7
- package/dist/program-DR-H4tH3.mjs +0 -34
- package/dist/repo-root-HmtIEm6J.mjs +0 -22
- package/dist/report-DYxvw34T.mjs +0 -24
- package/dist/reporter-CwIe_tAM.mjs +0 -108
- package/dist/run-target-CJWrqNT4.mjs +0 -65
- package/dist/schema-Or8RAARd.mjs +0 -25
- package/dist/sync-e1Sp-shi.mjs +0 -157
- package/dist/tag-Cd9JujU5.mjs +0 -2
- package/dist/tag-hEKVZ_-7.mjs +0 -58
- package/dist/tag-presenter-Cs66UBsH.mjs +0 -63
- package/dist/target-resolver-CR3FXMTy.mjs +0 -170
- package/dist/targets-pCTslnFh.mjs +0 -106
- package/dist/tokenizer-CeZx73Zu.mjs +0 -216
- package/dist/update-pkg-CnGfjWBy.mjs +0 -37
- package/dist/walk-DjgQn66n.mjs +0 -33
- package/dist/workspace-packages-K2NYTHJq.mjs +0 -159
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { a as isCnOrTvIdentifier, i as indentOfLineContaining, l as unwrapCnInsideTvCallReplacement, n as applyEditsDescending, o as lineOf, r as buildKnownCnTvBindings, s as moduleLooksLikeCnTvReexport, t as KNOWN_CN_TV_MODULES } from "../../../../ast-helpers-MDRYuIAk.mjs";
|
|
2
|
-
import "../../../../arrange-BqP2DI3w.mjs";
|
|
3
|
-
import ts from "typescript";
|
|
4
|
-
//#region src/lib/arrange/domain/ast/ast-helpers.test.ts
|
|
5
|
-
function parseCallee(source) {
|
|
6
|
-
const stmt = ts.createSourceFile("x.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS).statements[0];
|
|
7
|
-
if (!ts.isExpressionStatement(stmt)) throw new Error("expected expression statement");
|
|
8
|
-
if (!ts.isCallExpression(stmt.expression)) throw new Error("expected call expression");
|
|
9
|
-
return stmt.expression.expression;
|
|
10
|
-
}
|
|
11
|
-
describe("indentOfLineContaining", () => {
|
|
12
|
-
it("handles CRLF files", () => {
|
|
13
|
-
const src = "a\r\n b\r\n c";
|
|
14
|
-
expect(indentOfLineContaining(src, src.indexOf("b"))).toBe(" ");
|
|
15
|
-
});
|
|
16
|
-
it("handles CR-only line endings", () => {
|
|
17
|
-
const src = "a\r b\rc";
|
|
18
|
-
expect(indentOfLineContaining(src, src.indexOf("b"))).toBe(" ");
|
|
19
|
-
});
|
|
20
|
-
it("supports pos at start and end of source", () => {
|
|
21
|
-
const src = "x\n y\n";
|
|
22
|
-
expect(indentOfLineContaining(src, 0)).toBe("");
|
|
23
|
-
expect(indentOfLineContaining(src, 6)).toBe("");
|
|
24
|
-
});
|
|
25
|
-
it("returns mixed tab/space indentation", () => {
|
|
26
|
-
const src = " line";
|
|
27
|
-
expect(indentOfLineContaining(src, src.indexOf("line"))).toBe(" ");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
describe("applyEditsDescending", () => {
|
|
31
|
-
it("returns original text for empty edits", () => {
|
|
32
|
-
expect(applyEditsDescending("abc", [])).toBe("abc");
|
|
33
|
-
});
|
|
34
|
-
it("applies multiple edits at start/middle/end", () => {
|
|
35
|
-
const out = applyEditsDescending("abcdef", [
|
|
36
|
-
{
|
|
37
|
-
start: 0,
|
|
38
|
-
end: 1,
|
|
39
|
-
replacement: "A"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
start: 2,
|
|
43
|
-
end: 4,
|
|
44
|
-
replacement: "CD"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
start: 5,
|
|
48
|
-
end: 6,
|
|
49
|
-
replacement: "F"
|
|
50
|
-
}
|
|
51
|
-
]);
|
|
52
|
-
expect(out).toBe("AbCDeF");
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe("buildKnownCnTvBindings", () => {
|
|
56
|
-
it("collects default, named alias, and namespace bindings", () => {
|
|
57
|
-
const sf = ts.createSourceFile("x.ts", ["import tvDefault, { cn as cx } from \"@codefast/tailwind-variants\";", "import * as tw from \"@codefast/tailwind-variants\";"].join("\n"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
58
|
-
expect(buildKnownCnTvBindings(sf)).toEqual(new Set([
|
|
59
|
-
"tvDefault",
|
|
60
|
-
"cx",
|
|
61
|
-
"tw"
|
|
62
|
-
]));
|
|
63
|
-
});
|
|
64
|
-
it("ignores type-only and unknown-module imports", () => {
|
|
65
|
-
const sf = ts.createSourceFile("x.ts", ["import type { cn } from \"@codefast/tailwind-variants\";", "import { cn } from \"lodash\";"].join("\n"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
66
|
-
expect(buildKnownCnTvBindings(sf)).toEqual(/* @__PURE__ */ new Set());
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe("known cn/tv module helpers", () => {
|
|
70
|
-
it("includes canonical shadcn-style module specifiers", () => {
|
|
71
|
-
expect(KNOWN_CN_TV_MODULES.has("#lib/utils")).toBe(true);
|
|
72
|
-
expect(KNOWN_CN_TV_MODULES.has("@codefast/tailwind-variants")).toBe(true);
|
|
73
|
-
expect(KNOWN_CN_TV_MODULES.has("tailwind-variants")).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
it.each([
|
|
76
|
-
["./utils", true],
|
|
77
|
-
["pkg/utils/cn", true],
|
|
78
|
-
["./cn.ts", true],
|
|
79
|
-
["lodash", false]
|
|
80
|
-
])("moduleLooksLikeCnTvReexport(%s) -> %s", (mod, expected) => {
|
|
81
|
-
expect(moduleLooksLikeCnTvReexport(mod)).toBe(expected);
|
|
82
|
-
});
|
|
83
|
-
it("accepts legacy tailwind-variants import bindings (backward compat)", () => {
|
|
84
|
-
const sf = ts.createSourceFile("x.ts", ["import tvDefault, { cn as cx } from \"tailwind-variants\";", "import * as tw from \"tailwind-variants\";"].join("\n"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
85
|
-
expect(buildKnownCnTvBindings(sf)).toEqual(new Set([
|
|
86
|
-
"tvDefault",
|
|
87
|
-
"cx",
|
|
88
|
-
"tw"
|
|
89
|
-
]));
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
describe("isCnOrTvIdentifier", () => {
|
|
93
|
-
const known = new Set([
|
|
94
|
-
"cn",
|
|
95
|
-
"tv",
|
|
96
|
-
"tw"
|
|
97
|
-
]);
|
|
98
|
-
it("matches identifier calls", () => {
|
|
99
|
-
expect(isCnOrTvIdentifier(parseCallee("cn('a');"), "cn", known)).toBe(true);
|
|
100
|
-
expect(isCnOrTvIdentifier(parseCallee("tv({});"), "tv", known)).toBe(true);
|
|
101
|
-
});
|
|
102
|
-
it("matches property access with known namespace binding", () => {
|
|
103
|
-
expect(isCnOrTvIdentifier(parseCallee("tw.cn('a');"), "cn", known)).toBe(true);
|
|
104
|
-
expect(isCnOrTvIdentifier(parseCallee("tw.tv({});"), "tv", known)).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
it("returns false for unknown namespace and non-identifier base", () => {
|
|
107
|
-
expect(isCnOrTvIdentifier(parseCallee("utils2.cn('a');"), "cn", known)).toBe(false);
|
|
108
|
-
expect(isCnOrTvIdentifier(parseCallee("getNs().cn('a');"), "cn", known)).toBe(false);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
describe("unwrapCnInsideTvCallReplacement", () => {
|
|
112
|
-
it("unwraps single arg to argument source", () => {
|
|
113
|
-
const src = "cn(\"flex gap-2\")";
|
|
114
|
-
const sf = ts.createSourceFile("x.ts", src, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
115
|
-
const expr = sf.statements[0].expression;
|
|
116
|
-
expect(unwrapCnInsideTvCallReplacement(expr, src, sf)).toBe("\"flex gap-2\"");
|
|
117
|
-
});
|
|
118
|
-
it("unwraps multiple args to multiline array", () => {
|
|
119
|
-
const src = "cn(\"flex gap-2\", \"text-sm\")";
|
|
120
|
-
const sf = ts.createSourceFile("x.ts", src, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
121
|
-
const expr = sf.statements[0].expression;
|
|
122
|
-
expect(unwrapCnInsideTvCallReplacement(expr, src, sf)).toBe([
|
|
123
|
-
"[",
|
|
124
|
-
" \"flex gap-2\",",
|
|
125
|
-
" \"text-sm\",",
|
|
126
|
-
"]"
|
|
127
|
-
].join("\n"));
|
|
128
|
-
});
|
|
129
|
-
it("returns undefined for zero args", () => {
|
|
130
|
-
const sf = ts.createSourceFile("x.ts", "cn()", ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
131
|
-
const expr = sf.statements[0].expression;
|
|
132
|
-
expect(unwrapCnInsideTvCallReplacement(expr, "cn()", sf)).toBeUndefined();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
describe("lineOf", () => {
|
|
136
|
-
it("returns 1-based line number for node start", () => {
|
|
137
|
-
const src = [
|
|
138
|
-
"const a = 1;",
|
|
139
|
-
"const b = 2;",
|
|
140
|
-
"const c = 3;"
|
|
141
|
-
].join("\n");
|
|
142
|
-
const sf = ts.createSourceFile("x.ts", src, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
143
|
-
const stmt = sf.statements[1];
|
|
144
|
-
expect(lineOf(sf, stmt)).toBe(2);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
//#endregion
|
|
148
|
-
export {};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import "../../../../constants-AtPhF-Ct.mjs";
|
|
2
|
-
import { a as mergeCnUnconditionalLiteralPoolForTest, r as forEachStringLiteralInClassExpression } from "../../../../collectors-cn-BS8TTQ8O.mjs";
|
|
3
|
-
import "../../../../arrange-BqP2DI3w.mjs";
|
|
4
|
-
import ts from "typescript";
|
|
5
|
-
//#region src/lib/arrange/domain/ast/collectors-cn.test.ts
|
|
6
|
-
describe("forEachStringLiteralInClassExpression", () => {
|
|
7
|
-
function literalsFromArgSnippet(snippet, walk) {
|
|
8
|
-
const arg0 = ts.createSourceFile("x.ts", `cn(${snippet});`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS).statements[0].expression.arguments[0];
|
|
9
|
-
const out = [];
|
|
10
|
-
forEachStringLiteralInClassExpression(arg0, (n) => out.push(n.text), 0, walk);
|
|
11
|
-
return out;
|
|
12
|
-
}
|
|
13
|
-
it("collects both branches of conditionals by default", () => {
|
|
14
|
-
expect(literalsFromArgSnippet("x ? \"a\" : \"b\"")).toEqual(["a", "b"]);
|
|
15
|
-
});
|
|
16
|
-
it("skips conditional branches when configured", () => {
|
|
17
|
-
expect(literalsFromArgSnippet("x ? \"a\" : \"b\"", { descendIntoConditional: false })).toEqual([]);
|
|
18
|
-
});
|
|
19
|
-
it("walks nested arrays and assertions", () => {
|
|
20
|
-
expect(literalsFromArgSnippet("[\"p\", ((\"q\" as const))]")).toEqual(["p", "q"]);
|
|
21
|
-
});
|
|
22
|
-
it("documents that traversal stops safely when class expression depth exceeds the configured limit", () => {
|
|
23
|
-
let expr = "\"deep-token\"";
|
|
24
|
-
for (let i = 0; i < 14; i += 1) expr = `[${expr}]`;
|
|
25
|
-
const arg0 = ts.createSourceFile("x.ts", `cn(${expr});`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS).statements[0].expression.arguments[0];
|
|
26
|
-
const out = [];
|
|
27
|
-
expect(() => {
|
|
28
|
-
forEachStringLiteralInClassExpression(arg0, (n) => out.push(n.text), 0);
|
|
29
|
-
}).not.toThrow();
|
|
30
|
-
expect(out).toEqual([]);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe("mergeCnUnconditionalLiteralPoolForTest", () => {
|
|
34
|
-
it("does not merge classes from mutually exclusive ternary branches", () => {
|
|
35
|
-
const pool = mergeCnUnconditionalLiteralPoolForTest("\"flex flex-1 justify-between leading-none\", nestLabel ? \"items-end\" : \"items-center\"");
|
|
36
|
-
expect(pool).toBe("flex flex-1 justify-between leading-none");
|
|
37
|
-
});
|
|
38
|
-
it("collects only unconditional literals", () => {
|
|
39
|
-
expect(mergeCnUnconditionalLiteralPoolForTest("\"a b\", cond ? \"c\" : \"d\"")).toBe("a b");
|
|
40
|
-
});
|
|
41
|
-
it("supports aliased callee names", () => {
|
|
42
|
-
expect(mergeCnUnconditionalLiteralPoolForTest("\"a b\", cond ? \"c\" : \"d\"", { callee: "cx" })).toBe("a b");
|
|
43
|
-
});
|
|
44
|
-
it("returns empty when every arg is ternary", () => {
|
|
45
|
-
expect(mergeCnUnconditionalLiteralPoolForTest("x ? \"a\" : \"b\"")).toBe("");
|
|
46
|
-
});
|
|
47
|
-
it("skips cn([...]) array arg literals for apply pool", () => {
|
|
48
|
-
expect(mergeCnUnconditionalLiteralPoolForTest("[\"foo\", nest ? \"a\" : \"b\"]")).toBe("");
|
|
49
|
-
});
|
|
50
|
-
it("collects parenthesized string literal arg", () => {
|
|
51
|
-
expect(mergeCnUnconditionalLiteralPoolForTest("((\"flex gap-2\" as const))")).toBe("flex gap-2");
|
|
52
|
-
});
|
|
53
|
-
it("collects no-substitution template literal arg", () => {
|
|
54
|
-
expect(mergeCnUnconditionalLiteralPoolForTest("`flex gap-2`")).toBe("flex gap-2");
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
//#endregion
|
|
58
|
-
export {};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { t as jsxClassNameStaticLiteral } from "../../../../collectors-jsx-D9tL2ZPM.mjs";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
//#region src/lib/arrange/domain/ast/collectors-jsx.test.ts
|
|
4
|
-
function firstJsxClassAttr(source) {
|
|
5
|
-
const attr = ts.createSourceFile("x.tsx", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX).statements[0].declarationList.declarations[0].initializer.attributes.properties[0];
|
|
6
|
-
if (!attr || !ts.isJsxAttribute(attr)) throw new Error("expected jsx attribute");
|
|
7
|
-
return attr;
|
|
8
|
-
}
|
|
9
|
-
describe("jsxClassNameStaticLiteral", () => {
|
|
10
|
-
it("extracts direct string literal className", () => {
|
|
11
|
-
const result = jsxClassNameStaticLiteral(firstJsxClassAttr("const node = <div className=\"flex gap-2\" />;"));
|
|
12
|
-
expect(result?.lit.text).toBe("flex gap-2");
|
|
13
|
-
});
|
|
14
|
-
it("extracts className string inside JSX expression", () => {
|
|
15
|
-
const result = jsxClassNameStaticLiteral(firstJsxClassAttr("const node = <div className={\"flex gap-2\"} />;"));
|
|
16
|
-
expect(result?.lit.text).toBe("flex gap-2");
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
//#endregion
|
|
20
|
-
export {};
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import "../../../../constants-AtPhF-Ct.mjs";
|
|
2
|
-
import { r as buildKnownCnTvBindings } from "../../../../ast-helpers-MDRYuIAk.mjs";
|
|
3
|
-
import { a as listAllCnCallsInsideTvInSourceFile, c as traverseTvObject, n as collectGroupableStringNodes, s as slotClassString, t as collectCnCallsInsideTv } from "../../../../collectors-tv-D2PXZaRt.mjs";
|
|
4
|
-
import "../../../../arrange-BqP2DI3w.mjs";
|
|
5
|
-
import ts from "typescript";
|
|
6
|
-
//#region src/lib/arrange/domain/ast/collectors-tv.test.ts
|
|
7
|
-
describe("collectGroupableStringNodes", () => {
|
|
8
|
-
it("collects cn/tv static class slots eligible for grouping", () => {
|
|
9
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
10
|
-
export const styles = tv({
|
|
11
|
-
base: cn("flex gap-2 text-sm rounded-md border px-3"),
|
|
12
|
-
});
|
|
13
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
14
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
15
|
-
expect(slotClassString(nodes[0])).toContain("flex gap-2");
|
|
16
|
-
});
|
|
17
|
-
it("collects tv array slots without cn()", () => {
|
|
18
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { tv } from "@codefast/tailwind-variants";
|
|
19
|
-
export const styles = tv({
|
|
20
|
-
base: ["flex gap-2 text-sm rounded-md border px-3", "shadow-xs"],
|
|
21
|
-
});
|
|
22
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
23
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
24
|
-
expect(nodes.some((stringNode) => slotClassString(stringNode).includes("shadow-xs"))).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
it("collects compoundVariants className string slots", () => {
|
|
27
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { tv } from "@codefast/tailwind-variants";
|
|
28
|
-
export const styles = tv({
|
|
29
|
-
compoundVariants: [{ className: "flex gap-2 text-sm rounded-md border px-3" }],
|
|
30
|
-
});
|
|
31
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
32
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
33
|
-
});
|
|
34
|
-
it("collects compoundVariants class array with mixed strings and cn()", () => {
|
|
35
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
36
|
-
export const styles = tv({
|
|
37
|
-
compoundVariants: [{ class: ["py-1", cn("flex gap-2 text-sm rounded-md border px-3")] }],
|
|
38
|
-
});
|
|
39
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
40
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
41
|
-
expect(nodes.some((stringNode) => slotClassString(stringNode).includes("flex gap-2"))).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
it("collects direct cn(...) entries inside tv arrays and ignores unsafe nested array literals", () => {
|
|
44
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
45
|
-
export const styles = tv({
|
|
46
|
-
base: [cn(["skip-a", "skip-b"]), cn("flex gap-2 text-sm rounded-md border px-3")],
|
|
47
|
-
});
|
|
48
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
49
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
50
|
-
expect(nodes.some((stringNode) => slotClassString(stringNode).includes("flex gap-2 text-sm"))).toBe(true);
|
|
51
|
-
expect(nodes.some((stringNode) => slotClassString(stringNode).includes("skip-a"))).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
it("collects compoundVariants class set directly to cn(...) call", () => {
|
|
54
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
55
|
-
export const styles = tv({
|
|
56
|
-
compoundVariants: [{ class: cn("flex gap-2 text-sm rounded-md border px-3") }],
|
|
57
|
-
});
|
|
58
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
59
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
60
|
-
expect(nodes.some((stringNode) => slotClassString(stringNode).includes("rounded-md border px-3"))).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
it("returns empty when file has no cn/tv candidates", () => {
|
|
63
|
-
const sf = ts.createSourceFile("x.ts", "export const only = 1;", ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
64
|
-
expect(collectGroupableStringNodes(sf)).toEqual([]);
|
|
65
|
-
});
|
|
66
|
-
it("documents that tv object traversal stops safely when nesting exceeds the configured depth limit", () => {
|
|
67
|
-
let nestedObject = "{ className: \"flex gap-2 text-sm rounded-md border px-3\" }";
|
|
68
|
-
for (let i = 0; i < 14; i += 1) nestedObject = `{ layer${i}: ${nestedObject} }`;
|
|
69
|
-
const source = `import { tv } from "@codefast/tailwind-variants";
|
|
70
|
-
export const styles = tv(${nestedObject});
|
|
71
|
-
`;
|
|
72
|
-
const sf = ts.createSourceFile("x.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
73
|
-
expect(() => collectGroupableStringNodes(sf)).not.toThrow();
|
|
74
|
-
expect(collectGroupableStringNodes(sf)).toEqual([]);
|
|
75
|
-
});
|
|
76
|
-
it("accepts legacy tailwind-variants import for tv slot collection (backward compat)", () => {
|
|
77
|
-
const nodes = collectGroupableStringNodes(ts.createSourceFile("x.ts", `import { cn, tv } from "tailwind-variants";
|
|
78
|
-
export const styles = tv({
|
|
79
|
-
base: cn("flex gap-2 text-sm rounded-md border px-3"),
|
|
80
|
-
});
|
|
81
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
|
|
82
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
83
|
-
expect(slotClassString(nodes[0])).toContain("flex gap-2");
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe("tv collector helpers", () => {
|
|
87
|
-
it("traverseTvObject visits nested className/class string literals and cn call literals", () => {
|
|
88
|
-
const sf = ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
89
|
-
const cfg = {
|
|
90
|
-
base: [{ className: ["flex gap-2", "text-sm"] }, { class: cn("rounded-md border", "px-3") }],
|
|
91
|
-
variants: {
|
|
92
|
-
intent: { primary: { className: "font-medium shadow-xs" } },
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
tv(cfg);
|
|
96
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
97
|
-
const knownBindings = buildKnownCnTvBindings(sf);
|
|
98
|
-
const obj = sf.statements[1].declarationList.declarations[0].initializer;
|
|
99
|
-
const visited = [];
|
|
100
|
-
traverseTvObject(sf, obj, (classLiteral) => {
|
|
101
|
-
visited.push(classLiteral.text);
|
|
102
|
-
}, 0, knownBindings);
|
|
103
|
-
expect(visited).toEqual(expect.arrayContaining([
|
|
104
|
-
"flex gap-2",
|
|
105
|
-
"text-sm",
|
|
106
|
-
"rounded-md border",
|
|
107
|
-
"px-3",
|
|
108
|
-
"font-medium shadow-xs"
|
|
109
|
-
]));
|
|
110
|
-
});
|
|
111
|
-
it("collectCnCallsInsideTv finds cn calls from arrays, class fields, and nested objects", () => {
|
|
112
|
-
const sf = ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
113
|
-
tv({
|
|
114
|
-
base: [cn("flex gap-2"), { class: cn("text-sm rounded-md") }],
|
|
115
|
-
variants: {
|
|
116
|
-
intent: {
|
|
117
|
-
primary: { className: cn("border px-3") },
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
122
|
-
const knownBindings = buildKnownCnTvBindings(sf);
|
|
123
|
-
const tvObj = sf.statements[1].expression.arguments[0];
|
|
124
|
-
const calls = collectCnCallsInsideTv(sf, tvObj, knownBindings, 0);
|
|
125
|
-
expect(calls).toHaveLength(3);
|
|
126
|
-
expect(calls.map((call) => call.getText(sf))).toEqual(expect.arrayContaining([
|
|
127
|
-
"cn(\"flex gap-2\")",
|
|
128
|
-
"cn(\"text-sm rounded-md\")",
|
|
129
|
-
"cn(\"border px-3\")"
|
|
130
|
-
]));
|
|
131
|
-
});
|
|
132
|
-
it("listAllCnCallsInsideTvInSourceFile collects cn calls only from tv() object arguments", () => {
|
|
133
|
-
const sf = ts.createSourceFile("x.ts", `import { cn, tv } from "@codefast/tailwind-variants";
|
|
134
|
-
cn("outside call should not be counted");
|
|
135
|
-
tv({
|
|
136
|
-
base: [cn("inside-tv one"), { className: cn("inside-tv two") }],
|
|
137
|
-
});
|
|
138
|
-
`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
139
|
-
const calls = listAllCnCallsInsideTvInSourceFile(sf, buildKnownCnTvBindings(sf));
|
|
140
|
-
expect(calls).toHaveLength(2);
|
|
141
|
-
expect(calls.map((call) => call.getText(sf))).toEqual(expect.arrayContaining(["cn(\"inside-tv one\")", "cn(\"inside-tv two\")"]));
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
//#endregion
|
|
145
|
-
export {};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { n as collectGroupableStringNodes } from "../../../../collectors-tv-D2PXZaRt.mjs";
|
|
2
|
-
import { i as planGroupEditForTarget, r as formatCnCallReplacement, t as collectGroupTargets } from "../../../../targets-pCTslnFh.mjs";
|
|
3
|
-
import ts from "typescript";
|
|
4
|
-
//#region src/lib/arrange/domain/ast/targets.test.ts
|
|
5
|
-
describe("formatCnCallReplacement", () => {
|
|
6
|
-
it("keeps multiline trailing comma behavior explicit for multi-arg cn calls", () => {
|
|
7
|
-
const source = `import { cn } from "@codefast/tailwind-variants";
|
|
8
|
-
export function Row(className: string) {
|
|
9
|
-
return cn("flex gap-2 text-sm rounded-md border px-3", className);
|
|
10
|
-
}
|
|
11
|
-
`;
|
|
12
|
-
const stringNode = collectGroupableStringNodes(ts.createSourceFile("x.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS))[0];
|
|
13
|
-
const out = formatCnCallReplacement(stringNode, source, false);
|
|
14
|
-
expect(out).toContain("className,");
|
|
15
|
-
expect(out).toContain("\"flex gap-2\"");
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe("collectGroupTargets + planGroupEditForTarget", () => {
|
|
19
|
-
it("collects JSX className targets in tsx files", () => {
|
|
20
|
-
const targets = collectGroupTargets(ts.createSourceFile("x.tsx", `export function C() {
|
|
21
|
-
return <div className="flex items-center gap-2 px-4 py-2 text-sm rounded-md border bg-card" />;
|
|
22
|
-
}`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX), "x.tsx");
|
|
23
|
-
expect(targets.some((groupTarget) => groupTarget.kind === "jsxClassName")).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
it("plans undefined when groups length <= 1", () => {
|
|
26
|
-
const source = `import { cn } from "@codefast/tailwind-variants"; cn("flex gap-2");`;
|
|
27
|
-
const cnTarget = collectGroupTargets(ts.createSourceFile("x.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS), "x.ts").find((groupTarget) => groupTarget.kind === "cnArg");
|
|
28
|
-
expect(cnTarget).toBeDefined();
|
|
29
|
-
const plan = planGroupEditForTarget(cnTarget, source, false);
|
|
30
|
-
expect(plan).toBeUndefined();
|
|
31
|
-
});
|
|
32
|
-
it("includes trailing className when withClassName=true", () => {
|
|
33
|
-
const source = `import { cn } from "@codefast/tailwind-variants";
|
|
34
|
-
export function C(className: string) {
|
|
35
|
-
return cn("flex gap-2 text-sm rounded-md border px-3 font-medium", className);
|
|
36
|
-
}`;
|
|
37
|
-
const plan = planGroupEditForTarget(collectGroupTargets(ts.createSourceFile("x.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS), "x.ts").find((groupTarget) => groupTarget.kind === "cnArg"), source, true);
|
|
38
|
-
expect(plan?.replacement).toContain("className");
|
|
39
|
-
});
|
|
40
|
-
it("accepts legacy tailwind-variants import when planning cn target edits (backward compat)", () => {
|
|
41
|
-
const source = `import { cn } from "tailwind-variants";
|
|
42
|
-
export function C(className: string) {
|
|
43
|
-
return cn("flex gap-2 text-sm rounded-md border px-3 font-medium", className);
|
|
44
|
-
}`;
|
|
45
|
-
const plan = planGroupEditForTarget(collectGroupTargets(ts.createSourceFile("x.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS), "x.ts").find((groupTarget) => groupTarget.kind === "cnArg"), source, true);
|
|
46
|
-
expect(plan?.replacement).toContain("className");
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
//#endregion
|
|
50
|
-
export {};
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { a as suggestCnGroups, i as mergeSingletons, n as capGroups, o as summarizeGroupBucketLabels, r as mergeEaseTimingIntoFollowingAnimatedState, t as areCnTailwindPartitionsEquivalent } from "../../../grouping-c1grpCQT.mjs";
|
|
2
|
-
import "../../../arrange-BqP2DI3w.mjs";
|
|
3
|
-
//#region src/lib/arrange/domain/grouping.test.ts
|
|
4
|
-
const CHECKBOX_GROUP_ITEM_INPUT = "peer flex size-4 shrink-0 items-center justify-center rounded-sm border border-input text-primary-foreground shadow-xs outline-hidden transition hover:not-disabled:not-aria-checked:border-ring/60 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:opacity-50 aria-checked:border-primary aria-checked:bg-primary focus-visible:aria-checked:ring-primary/20 aria-invalid:border-destructive aria-invalid:ring-destructive/20 hover:not-disabled:not-aria-checked:aria-invalid:border-destructive/60 aria-checked:aria-invalid:bg-destructive dark:bg-input/30 dark:focus-visible:aria-checked:ring-primary/40 dark:aria-invalid:ring-destructive/40";
|
|
5
|
-
const CHECKBOX_GROUP_ITEM_GROUPS = [
|
|
6
|
-
"peer flex items-center justify-center size-4 shrink-0",
|
|
7
|
-
"rounded-sm border border-input shadow-xs outline-hidden",
|
|
8
|
-
"text-primary-foreground",
|
|
9
|
-
"transition",
|
|
10
|
-
"hover:not-disabled:not-aria-checked:border-ring/60",
|
|
11
|
-
"focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
12
|
-
"disabled:opacity-50",
|
|
13
|
-
"aria-checked:border-primary aria-checked:bg-primary",
|
|
14
|
-
"focus-visible:aria-checked:ring-primary/20",
|
|
15
|
-
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
16
|
-
"hover:not-disabled:not-aria-checked:aria-invalid:border-destructive/60",
|
|
17
|
-
"aria-checked:aria-invalid:bg-destructive",
|
|
18
|
-
"dark:bg-input/30",
|
|
19
|
-
"dark:focus-visible:aria-checked:ring-primary/40",
|
|
20
|
-
"dark:aria-invalid:ring-destructive/40"
|
|
21
|
-
];
|
|
22
|
-
function sortedTokens(classString) {
|
|
23
|
-
return classString.trim().split(/\s+/).filter(Boolean).sort();
|
|
24
|
-
}
|
|
25
|
-
describe("suggestCnGroups", () => {
|
|
26
|
-
it.each([
|
|
27
|
-
["", []],
|
|
28
|
-
[" ", []],
|
|
29
|
-
["\n ", []],
|
|
30
|
-
["flex gap-2", ["flex gap-2"]],
|
|
31
|
-
["flex gap-2 text-sm", ["flex gap-2 text-sm"]],
|
|
32
|
-
["w-4 h-4 rounded-md", ["w-4 h-4", "rounded-md"]],
|
|
33
|
-
["transition outline-hidden", ["outline-hidden", "transition"]],
|
|
34
|
-
["text-sm transition outline-hidden", [
|
|
35
|
-
"outline-hidden",
|
|
36
|
-
"text-sm",
|
|
37
|
-
"transition"
|
|
38
|
-
]],
|
|
39
|
-
["ease-ui data-open:animate-in data-open:fade-in-0", ["ease-ui data-open:animate-in data-open:fade-in-0"]],
|
|
40
|
-
["fixed inset-0 overflow-auto", ["fixed inset-0", "overflow-auto"]],
|
|
41
|
-
["fixed inset-0 grid overflow-auto", ["fixed inset-0 grid overflow-auto"]],
|
|
42
|
-
["sm:text-sm", ["sm:text-sm"]],
|
|
43
|
-
["group-hover:bg-red-500", ["group-hover:bg-red-500"]],
|
|
44
|
-
["not-disabled:opacity-50", ["not-disabled:opacity-50"]],
|
|
45
|
-
["focus:bg-accent focus:text-accent-foreground aria-disabled:opacity-50", ["focus:bg-accent focus:text-accent-foreground", "aria-disabled:opacity-50"]],
|
|
46
|
-
["grid", ["grid"]],
|
|
47
|
-
["@3xl:flex @3xl:gap-4 p-4", ["p-4", "@3xl:flex @3xl:gap-4"]],
|
|
48
|
-
["bg-white has-checked:bg-indigo-50", ["bg-white", "has-checked:bg-indigo-50"]],
|
|
49
|
-
["flex nth-3:underline", ["flex", "nth-3:underline"]],
|
|
50
|
-
["p-2 pointer-coarse:p-4", ["p-2", "pointer-coarse:p-4"]],
|
|
51
|
-
["opacity-100 inert:opacity-50", ["opacity-100", "inert:opacity-50"]],
|
|
52
|
-
["flex in-[.popover]:opacity-100", ["flex", "in-[.popover]:opacity-100"]],
|
|
53
|
-
["flex *:rounded **:text-sm", [
|
|
54
|
-
"flex",
|
|
55
|
-
"*:rounded",
|
|
56
|
-
"**:text-sm"
|
|
57
|
-
]],
|
|
58
|
-
["-gap-4 flex", ["-gap-4 flex"]],
|
|
59
|
-
["-space-x-4 flex", ["-space-x-4 flex"]],
|
|
60
|
-
["grid -gap-4 p-4", ["grid -gap-4", "p-4"]],
|
|
61
|
-
["-tracking-widest text-sm", ["-tracking-widest text-sm"]],
|
|
62
|
-
["-z-10 relative", ["-z-10 relative"]],
|
|
63
|
-
["-scale-95 w-full", ["w-full -scale-95"]],
|
|
64
|
-
["data-[vaul-drawer-direction=bottom]:a data-[vaul-drawer-direction=left]:b data-[vaul-drawer-direction=right]:c", [
|
|
65
|
-
"data-[vaul-drawer-direction=bottom]:a",
|
|
66
|
-
"data-[vaul-drawer-direction=left]:b",
|
|
67
|
-
"data-[vaul-drawer-direction=right]:c"
|
|
68
|
-
]],
|
|
69
|
-
["data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none", ["data-[range-end=true]:rounded-md", "data-[range-middle=true]:rounded-none"]]
|
|
70
|
-
])("golden groups `%s`", (input, expected) => {
|
|
71
|
-
expect(suggestCnGroups(input)).toEqual(expected);
|
|
72
|
-
expect(sortedTokens(input)).toEqual(sortedTokens(expected.join(" ")));
|
|
73
|
-
});
|
|
74
|
-
it("groups command-menu style rows deterministically", () => {
|
|
75
|
-
const input = "group/cmd relative flex items-center gap-x-2 px-2 py-1.5 rounded-sm text-sm cursor-default outline-hidden select-none";
|
|
76
|
-
const expected = [
|
|
77
|
-
"group/cmd relative flex items-center gap-x-2",
|
|
78
|
-
"px-2 py-1.5",
|
|
79
|
-
"rounded-sm outline-hidden",
|
|
80
|
-
"text-sm",
|
|
81
|
-
"cursor-default select-none"
|
|
82
|
-
];
|
|
83
|
-
const got = suggestCnGroups(input);
|
|
84
|
-
expect(got).toEqual(expected);
|
|
85
|
-
expect(sortedTokens(input)).toEqual(sortedTokens(got.join(" ")));
|
|
86
|
-
});
|
|
87
|
-
it("keeps checkbox-style partition stable", () => {
|
|
88
|
-
const got = suggestCnGroups(CHECKBOX_GROUP_ITEM_INPUT);
|
|
89
|
-
expect(got).toEqual([...CHECKBOX_GROUP_ITEM_GROUPS]);
|
|
90
|
-
expect(sortedTokens(CHECKBOX_GROUP_ITEM_INPUT)).toEqual(sortedTokens(got.join(" ")));
|
|
91
|
-
});
|
|
92
|
-
it("keeps many state keys without dropping tokens", () => {
|
|
93
|
-
const input = "flex gap-2 text-sm rounded-md border px-3 py-2 bg-card outline-hidden transition " + [
|
|
94
|
-
"hover:a",
|
|
95
|
-
"focus:b",
|
|
96
|
-
"active:c",
|
|
97
|
-
"disabled:d",
|
|
98
|
-
"checked:e",
|
|
99
|
-
"focus-visible:f",
|
|
100
|
-
"aria-invalid:g",
|
|
101
|
-
"data-open:h",
|
|
102
|
-
"dark:i",
|
|
103
|
-
"sm:j",
|
|
104
|
-
"md:k",
|
|
105
|
-
"group-hover:l",
|
|
106
|
-
"peer-focus:m",
|
|
107
|
-
"inert:n"
|
|
108
|
-
].join(" ");
|
|
109
|
-
const groups = suggestCnGroups(input);
|
|
110
|
-
expect(groups.length).toBeGreaterThan(8);
|
|
111
|
-
expect(sortedTokens(input)).toEqual(sortedTokens(groups.join(" ")));
|
|
112
|
-
});
|
|
113
|
-
it("splits in-data variant state chunks from base utilities", () => {
|
|
114
|
-
const got = suggestCnGroups("rounded-md bg-muted in-data-[slot=tooltip-content]:bg-background/20 font-sans text-xs font-medium text-muted-foreground in-data-[slot=tooltip-content]:text-background");
|
|
115
|
-
expect(got.some((c) => c.includes("bg-muted") && !c.includes("in-data-"))).toBe(true);
|
|
116
|
-
expect(got.some((c) => c.includes("in-data-[slot=tooltip-content]:bg-background/20"))).toBe(true);
|
|
117
|
-
});
|
|
118
|
-
it("splits arbitrary parent selectors from base chunk", () => {
|
|
119
|
-
const got = suggestCnGroups("flex justify-center aspect-video text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-layer]:outline-hidden");
|
|
120
|
-
expect(got[0]).not.toContain("[&");
|
|
121
|
-
expect(got.some((c) => c.includes("[&_.recharts-layer]"))).toBe(true);
|
|
122
|
-
});
|
|
123
|
-
it("merges shadcn [&_svg] utilities and :not size guard into one selector chunk", () => {
|
|
124
|
-
const input = "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-6";
|
|
125
|
-
expect(suggestCnGroups(input)).toEqual([input]);
|
|
126
|
-
});
|
|
127
|
-
it("keeps cap-groups headroom behavior for bg + outline-hidden", () => {
|
|
128
|
-
const groups = suggestCnGroups("flex items-center justify-center bg-border outline-hidden focus-visible:bg-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-[orientation=horizontal]:h-px aria-[orientation=vertical]:w-px");
|
|
129
|
-
expect(groups.some((chunk) => /\bbg-border\b/.test(chunk) && /\boutline-hidden\b/.test(chunk))).toBe(true);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
describe("areCnTailwindPartitionsEquivalent", () => {
|
|
133
|
-
it("returns true for same partitions with different order", () => {
|
|
134
|
-
expect(areCnTailwindPartitionsEquivalent(["b a", "d c"], ["a b", "c d"])).toBe(true);
|
|
135
|
-
});
|
|
136
|
-
it("returns false for different partitions", () => {
|
|
137
|
-
expect(areCnTailwindPartitionsEquivalent(["a", "b c"], ["a b", "c"])).toBe(false);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
describe("mergeEaseTimingIntoFollowingAnimatedState", () => {
|
|
141
|
-
it("merges ease-only chunk into later animated state chunk", () => {
|
|
142
|
-
expect(mergeEaseTimingIntoFollowingAnimatedState(["ease-ui", "data-open:animate-in data-open:fade-in-0"])).toEqual(["ease-ui data-open:animate-in data-open:fade-in-0"]);
|
|
143
|
-
});
|
|
144
|
-
it("skips intermediate state chunks to merge with later animate", () => {
|
|
145
|
-
expect(mergeEaseTimingIntoFollowingAnimatedState([
|
|
146
|
-
"base",
|
|
147
|
-
"ease-ui",
|
|
148
|
-
"sm:grid-rows-1 sm:p-4",
|
|
149
|
-
"data-open:animate-in"
|
|
150
|
-
])).toEqual([
|
|
151
|
-
"base",
|
|
152
|
-
"sm:grid-rows-1 sm:p-4",
|
|
153
|
-
"ease-ui data-open:animate-in"
|
|
154
|
-
]);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
describe("mergeSingletons", () => {
|
|
158
|
-
it("merges singleton into compatible forward neighbor", () => {
|
|
159
|
-
expect(mergeSingletons(["bg-card", "text-white font-medium"])).toEqual(["bg-card text-white font-medium"]);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
describe("capGroups", () => {
|
|
163
|
-
it("caps with compatible adjacent merges only", () => {
|
|
164
|
-
const out = capGroups([
|
|
165
|
-
"fixed inset-0",
|
|
166
|
-
"grid",
|
|
167
|
-
"overflow-auto",
|
|
168
|
-
"hover:bg-accent",
|
|
169
|
-
"focus:ring-2"
|
|
170
|
-
], 3);
|
|
171
|
-
expect(out.length).toBeGreaterThanOrEqual(3);
|
|
172
|
-
expect(out.join(" ")).toContain("fixed");
|
|
173
|
-
expect(out.join(" ")).toContain("overflow-auto");
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
describe("summarizeGroupBucketLabels", () => {
|
|
177
|
-
it("returns bucket labels for single-bucket chunks", () => {
|
|
178
|
-
expect(summarizeGroupBucketLabels(["flex gap-2", "text-sm"])).toEqual(["layout", "typography"]);
|
|
179
|
-
});
|
|
180
|
-
it("returns mixed labels for multi-bucket chunks", () => {
|
|
181
|
-
const labels = summarizeGroupBucketLabels(["bg-card text-sm"]);
|
|
182
|
-
expect(labels[0]).toBe("mixed:background+typography");
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
//#endregion
|
|
186
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { n as ensureCnImport, t as cnModuleSpecifierForFile } from "../../../imports-DSVq1xhB.mjs";
|
|
2
|
-
import "../../../arrange-BqP2DI3w.mjs";
|
|
3
|
-
//#region src/lib/arrange/domain/imports.test.ts
|
|
4
|
-
describe("cnModuleSpecifierForFile", () => {
|
|
5
|
-
it("documents that default module specifier is stable across file paths", () => {
|
|
6
|
-
expect(cnModuleSpecifierForFile("/repo/packages/ui/src/components/a.tsx")).toBe("@codefast/tailwind-variants");
|
|
7
|
-
expect(cnModuleSpecifierForFile("/repo/packages/cli/src/lib/a.ts")).toBe("@codefast/tailwind-variants");
|
|
8
|
-
});
|
|
9
|
-
it("documents that explicit override always takes precedence", () => {
|
|
10
|
-
expect(cnModuleSpecifierForFile("/repo/packages/ui/src/components/a.tsx", "clsx")).toBe("clsx");
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
describe("ensureCnImport", () => {
|
|
14
|
-
it("does not change source when cn is imported as default binding", () => {
|
|
15
|
-
const source = "import cn from \"@codefast/tailwind-variants\";\nconst x = 1;\n";
|
|
16
|
-
expect(ensureCnImport(source, "/repo/x.ts")).toBe(source);
|
|
17
|
-
});
|
|
18
|
-
it("does not change source when cn already imported", () => {
|
|
19
|
-
const source = "import { cn, tv } from \"@codefast/tailwind-variants\";\nconst x = 1;\n";
|
|
20
|
-
expect(ensureCnImport(source, "/repo/x.ts")).toBe(source);
|
|
21
|
-
});
|
|
22
|
-
it("injects cn into an existing named import from matching module", () => {
|
|
23
|
-
const out = ensureCnImport("import { tv } from \"clsx\";\nconst x = 1;\n", "/repo/x.ts", "clsx");
|
|
24
|
-
expect(out).toContain("import { cn, tv } from \"clsx\";");
|
|
25
|
-
});
|
|
26
|
-
it("inserts after use client directive", () => {
|
|
27
|
-
const out = ensureCnImport("\"use client\";\n\nexport const x = 1;\n", "/repo/x.tsx");
|
|
28
|
-
expect(out.startsWith("\"use client\";\n\nimport { cn }")).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
it("inserts before first import when no matching import exists", () => {
|
|
31
|
-
const out = ensureCnImport("import { tv } from \"clsx\";\nconst x = 1;\n", "/repo/x.ts");
|
|
32
|
-
expect(out.indexOf("import { cn } from \"@codefast/tailwind-variants\";")).toBe(0);
|
|
33
|
-
});
|
|
34
|
-
it("inserts at file start when there are no imports", () => {
|
|
35
|
-
const out = ensureCnImport("const x = 1;\n", "/repo/x.ts");
|
|
36
|
-
expect(out.startsWith("import { cn } from \"@codefast/tailwind-variants\";\n")).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
it("respects module override when adding new import", () => {
|
|
39
|
-
const out = ensureCnImport("const x = 1;\n", "/repo/x.ts", "clsx");
|
|
40
|
-
expect(out.startsWith("import { cn } from \"clsx\";\n")).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
it("keeps existing legacy tailwind-variants import unchanged (backward compat)", () => {
|
|
43
|
-
const source = "import { cn, tv } from \"tailwind-variants\";\nconst x = 1;\n";
|
|
44
|
-
expect(ensureCnImport(source, "/repo/x.ts")).toBe(source);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
//#endregion
|
|
48
|
-
export {};
|