@codefast/cli 0.3.5 → 0.3.12
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/README.md +66 -91
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/commands/arrange.d.ts +3 -0
- package/dist/commands/arrange.d.ts.map +1 -0
- package/dist/commands/arrange.js +114 -0
- package/dist/commands/mirror.d.ts +4 -0
- package/dist/commands/mirror.d.ts.map +1 -0
- package/dist/commands/mirror.js +63 -0
- package/dist/lib/arrange/analyze.d.ts +4 -0
- package/dist/lib/arrange/analyze.d.ts.map +1 -0
- package/dist/lib/arrange/analyze.js +105 -0
- package/dist/lib/arrange/ast/collectors-cn.d.ts +10 -0
- package/dist/lib/arrange/ast/collectors-cn.d.ts.map +1 -0
- package/dist/lib/arrange/ast/collectors-cn.js +84 -0
- package/dist/lib/arrange/ast/collectors-jsx.d.ts +4 -0
- package/dist/lib/arrange/ast/collectors-jsx.d.ts.map +1 -0
- package/dist/lib/arrange/ast/collectors-jsx.js +18 -0
- package/dist/lib/arrange/ast/collectors-tv.d.ts +13 -0
- package/dist/lib/arrange/ast/collectors-tv.d.ts.map +1 -0
- package/dist/lib/arrange/ast/collectors-tv.js +273 -0
- package/dist/lib/arrange/ast/targets.d.ts +8 -0
- package/dist/lib/arrange/ast/targets.d.ts.map +1 -0
- package/dist/lib/arrange/ast/targets.js +143 -0
- package/dist/lib/arrange/ast/utils.d.ts +36 -0
- package/dist/lib/arrange/ast/utils.d.ts.map +1 -0
- package/dist/lib/arrange/ast/utils.js +138 -0
- package/dist/lib/arrange/constants.d.ts +68 -0
- package/dist/lib/arrange/constants.d.ts.map +1 -0
- package/dist/lib/arrange/constants.js +179 -0
- package/dist/lib/arrange/errors.d.ts +14 -0
- package/dist/lib/arrange/errors.d.ts.map +1 -0
- package/dist/lib/arrange/errors.js +16 -0
- package/dist/lib/arrange/formatters.d.ts +16 -0
- package/dist/lib/arrange/formatters.d.ts.map +1 -0
- package/dist/lib/arrange/formatters.js +57 -0
- package/dist/lib/arrange/group-file.d.ts +4 -0
- package/dist/lib/arrange/group-file.d.ts.map +1 -0
- package/dist/lib/arrange/group-file.js +115 -0
- package/dist/lib/arrange/grouping.d.ts +41 -0
- package/dist/lib/arrange/grouping.d.ts.map +1 -0
- package/dist/lib/arrange/grouping.js +280 -0
- package/dist/lib/arrange/imports.d.ts +6 -0
- package/dist/lib/arrange/imports.d.ts.map +1 -0
- package/dist/lib/arrange/imports.js +74 -0
- package/dist/lib/arrange/report.d.ts +4 -0
- package/dist/lib/arrange/report.d.ts.map +1 -0
- package/dist/lib/arrange/report.js +37 -0
- package/dist/lib/arrange/run-target.d.ts +4 -0
- package/dist/lib/arrange/run-target.d.ts.map +1 -0
- package/dist/lib/arrange/run-target.js +28 -0
- package/dist/lib/arrange/tokenizer.d.ts +37 -0
- package/dist/lib/arrange/tokenizer.d.ts.map +1 -0
- package/dist/lib/arrange/tokenizer.js +390 -0
- package/dist/lib/arrange/types.d.ts +104 -0
- package/dist/lib/arrange/types.d.ts.map +1 -0
- package/dist/lib/arrange/types.js +7 -0
- package/dist/lib/arrange/walk.d.ts +4 -0
- package/dist/lib/arrange/walk.d.ts.map +1 -0
- package/dist/lib/arrange/walk.js +34 -0
- package/dist/lib/arrange.d.ts +41 -0
- package/dist/lib/arrange.d.ts.map +1 -0
- package/dist/lib/arrange.js +38 -0
- package/dist/lib/infra/fs-contract.d.ts +28 -0
- package/dist/lib/infra/fs-contract.d.ts.map +1 -0
- package/dist/lib/infra/fs-contract.js +1 -0
- package/dist/lib/infra/node-io.d.ts +4 -0
- package/dist/lib/infra/node-io.d.ts.map +1 -0
- package/dist/lib/infra/node-io.js +27 -0
- package/dist/lib/mirror/config.d.ts +7 -0
- package/dist/lib/mirror/config.d.ts.map +1 -0
- package/dist/lib/mirror/config.js +146 -0
- package/dist/lib/mirror/constants.d.ts +10 -0
- package/dist/lib/mirror/constants.d.ts.map +1 -0
- package/dist/lib/mirror/constants.js +13 -0
- package/dist/lib/mirror/engine.d.ts +12 -0
- package/dist/lib/mirror/engine.d.ts.map +1 -0
- package/dist/lib/mirror/engine.js +248 -0
- package/dist/lib/mirror/errors.d.ts +10 -0
- package/dist/lib/mirror/errors.d.ts.map +1 -0
- package/dist/lib/mirror/errors.js +12 -0
- package/dist/lib/mirror/package-filter.d.ts +8 -0
- package/dist/lib/mirror/package-filter.d.ts.map +1 -0
- package/dist/lib/mirror/package-filter.js +33 -0
- package/dist/lib/mirror/reporter.d.ts +24 -0
- package/dist/lib/mirror/reporter.d.ts.map +1 -0
- package/dist/lib/mirror/reporter.js +126 -0
- package/dist/lib/mirror/sync.d.ts +4 -0
- package/dist/lib/mirror/sync.d.ts.map +1 -0
- package/dist/lib/mirror/sync.js +135 -0
- package/dist/lib/mirror/types.d.ts +81 -0
- package/dist/lib/mirror/types.d.ts.map +1 -0
- package/dist/lib/mirror/types.js +1 -0
- package/dist/lib/mirror/update-pkg.d.ts +13 -0
- package/dist/lib/mirror/update-pkg.d.ts.map +1 -0
- package/dist/lib/mirror/update-pkg.js +39 -0
- package/dist/lib/mirror/workspace-packages.d.ts +26 -0
- package/dist/lib/mirror/workspace-packages.d.ts.map +1 -0
- package/dist/lib/mirror/workspace-packages.js +160 -0
- package/dist/lib/mirror.d.ts +7 -0
- package/dist/lib/mirror.d.ts.map +1 -0
- package/dist/lib/mirror.js +5 -0
- package/dist/lib/repo-root.d.ts +7 -0
- package/dist/lib/repo-root.d.ts.map +1 -0
- package/dist/lib/repo-root.js +23 -0
- package/dist/lib/shared/utils.d.ts +9 -0
- package/dist/lib/shared/utils.d.ts.map +1 -0
- package/dist/lib/shared/utils.js +12 -0
- package/dist/program.d.ts +4 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +38 -0
- package/package.json +53 -34
- package/CHANGELOG.md +0 -333
- package/dist/index.cjs +0 -1440
- package/dist/index.cjs.map +0 -1
- package/dist/index.js +0 -1406
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { StringNode, TailwindClassLiteral } from "#lib/arrange/types";
|
|
3
|
+
type StringNodeVisitor = (node: TailwindClassLiteral, sf: ts.SourceFile, cnCall?: ts.CallExpression) => void;
|
|
4
|
+
export declare function traverseTvObject(sf: ts.SourceFile, obj: ts.ObjectLiteralExpression, visitor: StringNodeVisitor, depth?: number, knownBindings?: Set<string>): void;
|
|
5
|
+
export declare function collectCnCallsInsideTv(sf: ts.SourceFile, obj: ts.ObjectLiteralExpression, knownBindings: Set<string>, depth?: number): ts.CallExpression[];
|
|
6
|
+
export declare function listAllCnCallsInsideTvInSourceFile(sf: ts.SourceFile, knownBindings: Set<string>): ts.CallExpression[];
|
|
7
|
+
export declare function makeStringNode(nodes: TailwindClassLiteral[], sf: ts.SourceFile, isTvContext: boolean, cnCall?: ts.CallExpression): StringNode;
|
|
8
|
+
export declare function slotClassString(item: StringNode): string;
|
|
9
|
+
export declare function emitTvSlot(lits: TailwindClassLiteral[], sf: ts.SourceFile, cnCall: ts.CallExpression | undefined, results: StringNode[], seenNodePos: Set<number>): void;
|
|
10
|
+
export declare function collectTvSlots(sf: ts.SourceFile, obj: ts.ObjectLiteralExpression, knownBindings: Set<string>, results: StringNode[], seenNodePos: Set<number>, depth?: number): void;
|
|
11
|
+
export declare function collectGroupableStringNodes(sf: ts.SourceFile): StringNode[];
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=collectors-tv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectors-tv.d.ts","sourceRoot":"","sources":["../../../../src/lib/arrange/ast/collectors-tv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAQ5B,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAQ3E,KAAK,iBAAiB,GAAG,CACvB,IAAI,EAAE,oBAAoB,EAC1B,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,MAAM,CAAC,EAAE,EAAE,CAAC,cAAc,KACvB,IAAI,CAAC;AAEV,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,GAAG,EAAE,EAAE,CAAC,uBAAuB,EAC/B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,SAAI,EACT,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,IAAI,CA+DN;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,GAAG,EAAE,EAAE,CAAC,uBAAuB,EAC/B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,EAC1B,KAAK,SAAI,GACR,EAAE,CAAC,cAAc,EAAE,CAqCrB;AAED,wBAAgB,kCAAkC,CAChD,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,EAAE,CAAC,cAAc,EAAE,CAarB;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,oBAAoB,EAAE,EAC7B,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,WAAW,EAAE,OAAO,EACpB,MAAM,CAAC,EAAE,EAAE,CAAC,cAAc,GACzB,UAAU,CAUZ;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAExD;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,oBAAoB,EAAE,EAC5B,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,MAAM,EAAE,EAAE,CAAC,cAAc,GAAG,SAAS,EACrC,OAAO,EAAE,UAAU,EAAE,EACrB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,IAAI,CASN;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,EAAE,CAAC,UAAU,EACjB,GAAG,EAAE,EAAE,CAAC,uBAAuB,EAC/B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,EAC1B,OAAO,EAAE,UAAU,EAAE,EACrB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,KAAK,SAAI,GACR,IAAI,CA+FN;AAED,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,UAAU,EAAE,CAiC3E"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { APPLY_MIN_TOKENS, MAX_OBJECT_DEPTH } from "#lib/arrange/constants";
|
|
3
|
+
import { CN_APPLY_LITERAL_WALK_OPTS, collectUnconditionalTailwindLiteralsFromCnArguments, forEachStringLiteralInClassExpression, isUnsafeLiteralForCnStyleApplySplit, } from "#lib/arrange/ast/collectors-cn";
|
|
4
|
+
import { tokenizeClassString } from "#lib/arrange/tokenizer";
|
|
5
|
+
import { buildKnownCnTvBindings, isCnOrTvIdentifier, propertyAssignmentNameText, } from "#lib/arrange/ast/utils";
|
|
6
|
+
export function traverseTvObject(sf, obj, visitor, depth = 0, knownBindings) {
|
|
7
|
+
if (depth > MAX_OBJECT_DEPTH)
|
|
8
|
+
return;
|
|
9
|
+
for (const prop of obj.properties) {
|
|
10
|
+
if (!ts.isPropertyAssignment(prop))
|
|
11
|
+
continue;
|
|
12
|
+
const init = prop.initializer;
|
|
13
|
+
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
14
|
+
visitor(init, sf, undefined);
|
|
15
|
+
}
|
|
16
|
+
else if (ts.isArrayLiteralExpression(init)) {
|
|
17
|
+
for (const el of init.elements) {
|
|
18
|
+
if (ts.isSpreadElement(el))
|
|
19
|
+
continue;
|
|
20
|
+
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) {
|
|
21
|
+
visitor(el, sf, undefined);
|
|
22
|
+
}
|
|
23
|
+
else if (ts.isCallExpression(el) &&
|
|
24
|
+
isCnOrTvIdentifier(el.expression, "cn", knownBindings)) {
|
|
25
|
+
for (const arg of el.arguments) {
|
|
26
|
+
forEachStringLiteralInClassExpression(arg, (lit) => {
|
|
27
|
+
visitor(lit, sf, el);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (ts.isObjectLiteralExpression(el)) {
|
|
32
|
+
for (const inner of el.properties) {
|
|
33
|
+
if (!ts.isPropertyAssignment(inner))
|
|
34
|
+
continue;
|
|
35
|
+
const propName = propertyAssignmentNameText(inner);
|
|
36
|
+
if (propName !== "className" && propName !== "class")
|
|
37
|
+
continue;
|
|
38
|
+
const innerInit = inner.initializer;
|
|
39
|
+
if (ts.isStringLiteral(innerInit) || ts.isNoSubstitutionTemplateLiteral(innerInit)) {
|
|
40
|
+
visitor(innerInit, sf, undefined);
|
|
41
|
+
}
|
|
42
|
+
else if (ts.isArrayLiteralExpression(innerInit)) {
|
|
43
|
+
for (const innerEl of innerInit.elements) {
|
|
44
|
+
if (ts.isSpreadElement(innerEl))
|
|
45
|
+
continue;
|
|
46
|
+
if (ts.isStringLiteral(innerEl) || ts.isNoSubstitutionTemplateLiteral(innerEl)) {
|
|
47
|
+
visitor(innerEl, sf, undefined);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (ts.isCallExpression(innerInit) &&
|
|
52
|
+
isCnOrTvIdentifier(innerInit.expression, "cn", knownBindings)) {
|
|
53
|
+
for (const arg of innerInit.arguments) {
|
|
54
|
+
forEachStringLiteralInClassExpression(arg, (lit) => {
|
|
55
|
+
visitor(lit, sf, innerInit);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (ts.isObjectLiteralExpression(init)) {
|
|
64
|
+
traverseTvObject(sf, init, visitor, depth + 1, knownBindings);
|
|
65
|
+
}
|
|
66
|
+
else if (ts.isCallExpression(init) &&
|
|
67
|
+
isCnOrTvIdentifier(init.expression, "cn", knownBindings)) {
|
|
68
|
+
for (const arg of init.arguments) {
|
|
69
|
+
forEachStringLiteralInClassExpression(arg, (lit) => {
|
|
70
|
+
visitor(lit, sf, init);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function collectCnCallsInsideTv(sf, obj, knownBindings, depth = 0) {
|
|
77
|
+
if (depth > MAX_OBJECT_DEPTH)
|
|
78
|
+
return [];
|
|
79
|
+
const calls = [];
|
|
80
|
+
for (const prop of obj.properties) {
|
|
81
|
+
if (!ts.isPropertyAssignment(prop))
|
|
82
|
+
continue;
|
|
83
|
+
const init = prop.initializer;
|
|
84
|
+
if (ts.isArrayLiteralExpression(init)) {
|
|
85
|
+
for (const el of init.elements) {
|
|
86
|
+
if (ts.isSpreadElement(el))
|
|
87
|
+
continue;
|
|
88
|
+
if (ts.isCallExpression(el) && isCnOrTvIdentifier(el.expression, "cn", knownBindings)) {
|
|
89
|
+
calls.push(el);
|
|
90
|
+
}
|
|
91
|
+
else if (ts.isObjectLiteralExpression(el)) {
|
|
92
|
+
for (const inner of el.properties) {
|
|
93
|
+
if (!ts.isPropertyAssignment(inner))
|
|
94
|
+
continue;
|
|
95
|
+
const propName = propertyAssignmentNameText(inner);
|
|
96
|
+
if (propName !== "className" && propName !== "class")
|
|
97
|
+
continue;
|
|
98
|
+
const innerInit = inner.initializer;
|
|
99
|
+
if (ts.isCallExpression(innerInit) &&
|
|
100
|
+
isCnOrTvIdentifier(innerInit.expression, "cn", knownBindings)) {
|
|
101
|
+
calls.push(innerInit);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (ts.isObjectLiteralExpression(init)) {
|
|
108
|
+
calls.push(...collectCnCallsInsideTv(sf, init, knownBindings, depth + 1));
|
|
109
|
+
}
|
|
110
|
+
else if (ts.isCallExpression(init) &&
|
|
111
|
+
isCnOrTvIdentifier(init.expression, "cn", knownBindings)) {
|
|
112
|
+
calls.push(init);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return calls;
|
|
116
|
+
}
|
|
117
|
+
export function listAllCnCallsInsideTvInSourceFile(sf, knownBindings) {
|
|
118
|
+
const calls = [];
|
|
119
|
+
const visit = (node) => {
|
|
120
|
+
if (ts.isCallExpression(node) && isCnOrTvIdentifier(node.expression, "tv", knownBindings)) {
|
|
121
|
+
const arg0 = node.arguments[0];
|
|
122
|
+
if (arg0 && ts.isObjectLiteralExpression(arg0)) {
|
|
123
|
+
calls.push(...collectCnCallsInsideTv(sf, arg0, knownBindings, 0));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
ts.forEachChild(node, visit);
|
|
127
|
+
};
|
|
128
|
+
visit(sf);
|
|
129
|
+
return calls;
|
|
130
|
+
}
|
|
131
|
+
export function makeStringNode(nodes, sf, isTvContext, cnCall) {
|
|
132
|
+
return {
|
|
133
|
+
nodes,
|
|
134
|
+
sf,
|
|
135
|
+
isTvContext,
|
|
136
|
+
cnCall,
|
|
137
|
+
get node() {
|
|
138
|
+
return this.nodes[0];
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function slotClassString(item) {
|
|
143
|
+
return item.nodes.map((n) => n.text).join(" ");
|
|
144
|
+
}
|
|
145
|
+
export function emitTvSlot(lits, sf, cnCall, results, seenNodePos) {
|
|
146
|
+
if (lits.length === 0)
|
|
147
|
+
return;
|
|
148
|
+
const firstPos = lits[0].getStart(sf);
|
|
149
|
+
if (seenNodePos.has(firstPos))
|
|
150
|
+
return;
|
|
151
|
+
seenNodePos.add(firstPos);
|
|
152
|
+
const totalTokens = lits.reduce((s, n) => s + tokenizeClassString(n.text).length, 0);
|
|
153
|
+
if (totalTokens < APPLY_MIN_TOKENS)
|
|
154
|
+
return;
|
|
155
|
+
results.push(makeStringNode(lits, sf, true, cnCall));
|
|
156
|
+
}
|
|
157
|
+
export function collectTvSlots(sf, obj, knownBindings, results, seenNodePos, depth = 0) {
|
|
158
|
+
if (depth > MAX_OBJECT_DEPTH)
|
|
159
|
+
return;
|
|
160
|
+
for (const prop of obj.properties) {
|
|
161
|
+
if (!ts.isPropertyAssignment(prop))
|
|
162
|
+
continue;
|
|
163
|
+
const init = prop.initializer;
|
|
164
|
+
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
165
|
+
emitTvSlot([init], sf, undefined, results, seenNodePos);
|
|
166
|
+
}
|
|
167
|
+
else if (ts.isArrayLiteralExpression(init)) {
|
|
168
|
+
const arrayPos = init.getStart(sf);
|
|
169
|
+
if (seenNodePos.has(arrayPos))
|
|
170
|
+
continue;
|
|
171
|
+
seenNodePos.add(arrayPos);
|
|
172
|
+
const staticLits = [];
|
|
173
|
+
for (const el of init.elements) {
|
|
174
|
+
if (ts.isSpreadElement(el))
|
|
175
|
+
continue;
|
|
176
|
+
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) {
|
|
177
|
+
staticLits.push(el);
|
|
178
|
+
}
|
|
179
|
+
else if (ts.isCallExpression(el) &&
|
|
180
|
+
isCnOrTvIdentifier(el.expression, "cn", knownBindings)) {
|
|
181
|
+
for (const arg of el.arguments) {
|
|
182
|
+
forEachStringLiteralInClassExpression(arg, (lit) => {
|
|
183
|
+
if (!isUnsafeLiteralForCnStyleApplySplit(lit))
|
|
184
|
+
staticLits.push(lit);
|
|
185
|
+
}, 0, CN_APPLY_LITERAL_WALK_OPTS);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (ts.isObjectLiteralExpression(el)) {
|
|
189
|
+
for (const inner of el.properties) {
|
|
190
|
+
if (!ts.isPropertyAssignment(inner))
|
|
191
|
+
continue;
|
|
192
|
+
const propName = propertyAssignmentNameText(inner);
|
|
193
|
+
if (propName !== "className" && propName !== "class")
|
|
194
|
+
continue;
|
|
195
|
+
const innerInit = inner.initializer;
|
|
196
|
+
if (ts.isStringLiteral(innerInit) || ts.isNoSubstitutionTemplateLiteral(innerInit)) {
|
|
197
|
+
emitTvSlot([innerInit], sf, undefined, results, seenNodePos);
|
|
198
|
+
}
|
|
199
|
+
else if (ts.isArrayLiteralExpression(innerInit)) {
|
|
200
|
+
const innerLits = [];
|
|
201
|
+
for (const innerEl of innerInit.elements) {
|
|
202
|
+
if (!ts.isSpreadElement(innerEl) &&
|
|
203
|
+
(ts.isStringLiteral(innerEl) || ts.isNoSubstitutionTemplateLiteral(innerEl))) {
|
|
204
|
+
innerLits.push(innerEl);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
emitTvSlot(innerLits, sf, undefined, results, seenNodePos);
|
|
208
|
+
}
|
|
209
|
+
else if (ts.isCallExpression(innerInit) &&
|
|
210
|
+
isCnOrTvIdentifier(innerInit.expression, "cn", knownBindings)) {
|
|
211
|
+
const cnLits = [];
|
|
212
|
+
for (const arg of innerInit.arguments) {
|
|
213
|
+
forEachStringLiteralInClassExpression(arg, (lit) => {
|
|
214
|
+
if (!isUnsafeLiteralForCnStyleApplySplit(lit))
|
|
215
|
+
cnLits.push(lit);
|
|
216
|
+
}, 0, CN_APPLY_LITERAL_WALK_OPTS);
|
|
217
|
+
}
|
|
218
|
+
emitTvSlot(cnLits, sf, innerInit, results, seenNodePos);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (staticLits.length > 0) {
|
|
224
|
+
emitTvSlot(staticLits, sf, undefined, results, seenNodePos);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (ts.isObjectLiteralExpression(init)) {
|
|
228
|
+
collectTvSlots(sf, init, knownBindings, results, seenNodePos, depth + 1);
|
|
229
|
+
}
|
|
230
|
+
else if (ts.isCallExpression(init) &&
|
|
231
|
+
isCnOrTvIdentifier(init.expression, "cn", knownBindings)) {
|
|
232
|
+
const cnLits = [];
|
|
233
|
+
for (const arg of init.arguments) {
|
|
234
|
+
forEachStringLiteralInClassExpression(arg, (lit) => {
|
|
235
|
+
if (!isUnsafeLiteralForCnStyleApplySplit(lit))
|
|
236
|
+
cnLits.push(lit);
|
|
237
|
+
}, 0, CN_APPLY_LITERAL_WALK_OPTS);
|
|
238
|
+
}
|
|
239
|
+
emitTvSlot(cnLits, sf, init, results, seenNodePos);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
export function collectGroupableStringNodes(sf) {
|
|
244
|
+
const results = [];
|
|
245
|
+
const seenNodePos = new Set();
|
|
246
|
+
const knownBindings = buildKnownCnTvBindings(sf);
|
|
247
|
+
const visit = (node) => {
|
|
248
|
+
if (ts.isCallExpression(node)) {
|
|
249
|
+
if (isCnOrTvIdentifier(node.expression, "cn", knownBindings)) {
|
|
250
|
+
const callPos = node.getStart(sf);
|
|
251
|
+
if (seenNodePos.has(callPos)) {
|
|
252
|
+
ts.forEachChild(node, visit);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
seenNodePos.add(callPos);
|
|
256
|
+
const staticLits = collectUnconditionalTailwindLiteralsFromCnArguments(node.arguments);
|
|
257
|
+
const totalTokens = staticLits.reduce((s, n) => s + tokenizeClassString(n.text).length, 0);
|
|
258
|
+
if (staticLits.length > 0 && totalTokens >= APPLY_MIN_TOKENS) {
|
|
259
|
+
results.push(makeStringNode(staticLits, sf, false, node));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (isCnOrTvIdentifier(node.expression, "tv", knownBindings)) {
|
|
263
|
+
const arg0 = node.arguments[0];
|
|
264
|
+
if (arg0 && ts.isObjectLiteralExpression(arg0)) {
|
|
265
|
+
collectTvSlots(sf, arg0, knownBindings, results, seenNodePos);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
ts.forEachChild(node, visit);
|
|
270
|
+
};
|
|
271
|
+
visit(sf);
|
|
272
|
+
return results;
|
|
273
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { GroupTarget, PlannedGroupEdit, StringNode } from "#lib/arrange/types";
|
|
3
|
+
export declare function targetReplaceStart(target: GroupTarget): number;
|
|
4
|
+
export declare function collectLongJsxClassNameTargets(sf: ts.SourceFile): GroupTarget[];
|
|
5
|
+
export declare function collectGroupTargets(sf: ts.SourceFile, filePath: string): GroupTarget[];
|
|
6
|
+
export declare function formatCnCallReplacement(item: StringNode, sourceText: string, withClassName: boolean): string;
|
|
7
|
+
export declare function planGroupEditForTarget(target: GroupTarget, textAfterUnwrap: string, withClassName: boolean): PlannedGroupEdit | undefined;
|
|
8
|
+
//# sourceMappingURL=targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../../../../src/lib/arrange/ast/targets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAY5B,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAOpF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAO9D;AAED,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,WAAW,EAAE,CAkB/E;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAItF;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,OAAO,GACrB,MAAM,CA2CR;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,OAAO,GACrB,gBAAgB,GAAG,SAAS,CAyE9B"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { APPLY_MIN_TOKENS } from "#lib/arrange/constants";
|
|
3
|
+
import { areCnTailwindPartitionsEquivalent, suggestCnGroups, summarizeGroupBucketLabels, } from "#lib/arrange/grouping";
|
|
4
|
+
import { escapeTsStringLiteralContent, formatArray, formatJsxCnAttributeValue, } from "#lib/arrange/formatters";
|
|
5
|
+
import { tokenizeClassString } from "#lib/arrange/tokenizer";
|
|
6
|
+
import { isUnsafeLiteralForCnStyleApplySplit } from "#lib/arrange/ast/collectors-cn";
|
|
7
|
+
import { jsxClassNameStaticLiteral } from "#lib/arrange/ast/collectors-jsx";
|
|
8
|
+
import { collectGroupableStringNodes, slotClassString } from "#lib/arrange/ast/collectors-tv";
|
|
9
|
+
import { indentOfLineContaining } from "#lib/arrange/ast/utils";
|
|
10
|
+
export function targetReplaceStart(target) {
|
|
11
|
+
if (target.kind === "cnArg") {
|
|
12
|
+
return target.item.cnCall
|
|
13
|
+
? target.item.cnCall.getStart(target.item.sf)
|
|
14
|
+
: target.item.node.getStart(target.item.sf);
|
|
15
|
+
}
|
|
16
|
+
return target.valueNode.getStart(target.sf);
|
|
17
|
+
}
|
|
18
|
+
export function collectLongJsxClassNameTargets(sf) {
|
|
19
|
+
const results = [];
|
|
20
|
+
const visit = (node) => {
|
|
21
|
+
if (ts.isJsxAttribute(node)) {
|
|
22
|
+
const parsed = jsxClassNameStaticLiteral(node);
|
|
23
|
+
if (parsed && tokenizeClassString(parsed.lit.text).length >= APPLY_MIN_TOKENS) {
|
|
24
|
+
results.push({
|
|
25
|
+
kind: "jsxClassName",
|
|
26
|
+
sf,
|
|
27
|
+
lit: parsed.lit,
|
|
28
|
+
valueNode: parsed.valueNode,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
ts.forEachChild(node, visit);
|
|
33
|
+
};
|
|
34
|
+
visit(sf);
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
export function collectGroupTargets(sf, filePath) {
|
|
38
|
+
const cnPart = collectGroupableStringNodes(sf).map((item) => ({ kind: "cnArg", item }));
|
|
39
|
+
if (!filePath.endsWith(".tsx"))
|
|
40
|
+
return cnPart;
|
|
41
|
+
return [...cnPart, ...collectLongJsxClassNameTargets(sf)];
|
|
42
|
+
}
|
|
43
|
+
export function formatCnCallReplacement(item, sourceText, withClassName) {
|
|
44
|
+
const call = item.cnCall;
|
|
45
|
+
const sf = item.sf;
|
|
46
|
+
const baseIndent = indentOfLineContaining(sourceText, call.getStart(sf));
|
|
47
|
+
const argIndent = `${baseIndent} `;
|
|
48
|
+
const dynamicArgTexts = [];
|
|
49
|
+
for (const arg of call.arguments) {
|
|
50
|
+
const isSimpleStatic = (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg)) &&
|
|
51
|
+
!isUnsafeLiteralForCnStyleApplySplit(arg);
|
|
52
|
+
if (!isSimpleStatic) {
|
|
53
|
+
dynamicArgTexts.push(sourceText.slice(arg.getStart(sf), arg.getEnd()));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const pool = slotClassString(item);
|
|
57
|
+
const groups = pool.trim() ? suggestCnGroups(pool) : [];
|
|
58
|
+
const allArgs = [];
|
|
59
|
+
if (groups.length > 1) {
|
|
60
|
+
for (const group of groups) {
|
|
61
|
+
allArgs.push(`${argIndent}"${escapeTsStringLiteralContent(group)}"`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (groups.length === 1) {
|
|
65
|
+
allArgs.push(`${argIndent}"${escapeTsStringLiteralContent(groups[0])}"`);
|
|
66
|
+
}
|
|
67
|
+
for (const dyn of dynamicArgTexts) {
|
|
68
|
+
allArgs.push(`${argIndent}${dyn}`);
|
|
69
|
+
}
|
|
70
|
+
if (withClassName) {
|
|
71
|
+
allArgs.push(`${argIndent}className`);
|
|
72
|
+
}
|
|
73
|
+
// Keep formatter behavior explicit: multiline cn() calls with multiple args use trailing comma.
|
|
74
|
+
const commaAfterLastArg = allArgs.length > 1;
|
|
75
|
+
const argLines = allArgs.map((argLine, lineIndex) => lineIndex < allArgs.length - 1 || commaAfterLastArg ? `${argLine},` : `${argLine}`);
|
|
76
|
+
return `cn(\n${argLines.join("\n")}\n${baseIndent})`;
|
|
77
|
+
}
|
|
78
|
+
export function planGroupEditForTarget(target, textAfterUnwrap, withClassName) {
|
|
79
|
+
if (target.kind === "jsxClassName") {
|
|
80
|
+
const groups = suggestCnGroups(target.lit.text);
|
|
81
|
+
if (groups.length <= 1)
|
|
82
|
+
return undefined;
|
|
83
|
+
const start = target.valueNode.getStart(target.sf);
|
|
84
|
+
const end = target.valueNode.getEnd();
|
|
85
|
+
const replacement = formatJsxCnAttributeValue(groups, textAfterUnwrap, start);
|
|
86
|
+
return {
|
|
87
|
+
start,
|
|
88
|
+
end,
|
|
89
|
+
replacement,
|
|
90
|
+
bucketSummary: summarizeGroupBucketLabels(groups),
|
|
91
|
+
jsxCn: true,
|
|
92
|
+
lineSf: target.sf,
|
|
93
|
+
reportNode: target.lit,
|
|
94
|
+
label: "JSX className",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const pool = slotClassString(target.item);
|
|
98
|
+
const groups = suggestCnGroups(pool);
|
|
99
|
+
if (groups.length <= 1)
|
|
100
|
+
return undefined;
|
|
101
|
+
if (areCnTailwindPartitionsEquivalent(target.item.nodes.map((node) => node.text), groups)) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
if (!target.item.cnCall) {
|
|
105
|
+
const firstNode = target.item.node;
|
|
106
|
+
const parentArray = target.item.nodes.length > 1 && ts.isArrayLiteralExpression(firstNode.parent)
|
|
107
|
+
? firstNode.parent
|
|
108
|
+
: null;
|
|
109
|
+
const start = parentArray
|
|
110
|
+
? parentArray.getStart(target.item.sf)
|
|
111
|
+
: firstNode.getStart(target.item.sf);
|
|
112
|
+
const end = parentArray ? parentArray.getEnd() : firstNode.getEnd();
|
|
113
|
+
const baseIndent = indentOfLineContaining(textAfterUnwrap, start);
|
|
114
|
+
const replacement = formatArray(groups)
|
|
115
|
+
.split("\n")
|
|
116
|
+
.map((line, lineIndex) => (lineIndex === 0 ? line : `${baseIndent}${line}`))
|
|
117
|
+
.join("\n");
|
|
118
|
+
return {
|
|
119
|
+
start,
|
|
120
|
+
end,
|
|
121
|
+
replacement,
|
|
122
|
+
bucketSummary: summarizeGroupBucketLabels(groups),
|
|
123
|
+
jsxCn: false,
|
|
124
|
+
lineSf: target.item.sf,
|
|
125
|
+
reportNode: firstNode,
|
|
126
|
+
label: target.item.isTvContext ? "tv" : "cn",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const call = target.item.cnCall;
|
|
130
|
+
const start = call.getStart(target.item.sf);
|
|
131
|
+
const end = call.getEnd();
|
|
132
|
+
const replacement = formatCnCallReplacement(target.item, textAfterUnwrap, withClassName);
|
|
133
|
+
return {
|
|
134
|
+
start,
|
|
135
|
+
end,
|
|
136
|
+
replacement,
|
|
137
|
+
bucketSummary: summarizeGroupBucketLabels(groups),
|
|
138
|
+
jsxCn: false,
|
|
139
|
+
lineSf: target.item.sf,
|
|
140
|
+
reportNode: target.item.node,
|
|
141
|
+
label: target.item.isTvContext ? "tv" : "cn",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
/** Known module specifiers that export `cn` / `tv`. */
|
|
3
|
+
export declare const KNOWN_CN_TV_MODULES: Set<string>;
|
|
4
|
+
/**
|
|
5
|
+
* True for typical shadcn-style re-exports: `./utils`, `@/lib/utils`, `…/utils/…`,
|
|
6
|
+
* or a dedicated `cn.ts` / `cn.tsx` module.
|
|
7
|
+
*/
|
|
8
|
+
export declare function moduleLooksLikeCnTvReexport(mod: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Build a set of local binding names that are imported from a known cn/tv
|
|
11
|
+
* module in `sf`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildKnownCnTvBindings(sf: ts.SourceFile): Set<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Returns true when `expr` is an identifier whose name matches `name` AND
|
|
16
|
+
* that name is listed in `knownBindings` from a recognized import.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isCnOrTvIdentifier(expr: ts.Expression, name: "cn" | "tv", knownBindings?: Set<string>): boolean;
|
|
19
|
+
export declare function propertyAssignmentNameText(prop: ts.PropertyAssignment): string | undefined;
|
|
20
|
+
export declare function lineOf(sf: ts.SourceFile, node: ts.Node): number;
|
|
21
|
+
/**
|
|
22
|
+
* Returns only the leading whitespace (tabs / spaces) of the line containing
|
|
23
|
+
* `pos`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function indentOfLineContaining(source: string, pos: number): string;
|
|
26
|
+
export declare function applyEditsDescending(sourceText: string, edits: ReadonlyArray<{
|
|
27
|
+
start: number;
|
|
28
|
+
end: number;
|
|
29
|
+
replacement: string;
|
|
30
|
+
}>): string;
|
|
31
|
+
/**
|
|
32
|
+
* Replace `cn(...)` nested in `tv({...})` with a plain string (one arg) or a string
|
|
33
|
+
* array (multiple args). Returns undefined when there are zero arguments.
|
|
34
|
+
*/
|
|
35
|
+
export declare function unwrapCnInsideTvCallReplacement(call: ts.CallExpression, sourceText: string, sf: ts.SourceFile): string | undefined;
|
|
36
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/lib/arrange/ast/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,aAU9B,CAAC;AAEH;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMhE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,CAqBrE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,EAAE,CAAC,UAAU,EACnB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,aAAa,GAAE,GAAG,CAAC,MAAM,CAAwB,GAChD,OAAO,CAOT;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,GAAG,MAAM,GAAG,SAAS,CAI1F;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,CAE/D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAoB1E;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GACxE,MAAM,CAOR;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,EAAE,CAAC,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,EAAE,CAAC,UAAU,GAChB,MAAM,GAAG,SAAS,CAoBpB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { EMPTY_CN_TV_BINDINGS } from "#lib/arrange/constants";
|
|
3
|
+
/** Known module specifiers that export `cn` / `tv`. */
|
|
4
|
+
export const KNOWN_CN_TV_MODULES = new Set([
|
|
5
|
+
"@codefast/tailwind-variants",
|
|
6
|
+
"clsx",
|
|
7
|
+
"class-variance-authority",
|
|
8
|
+
// Backward compatibility only: keep detection for existing user code that still imports
|
|
9
|
+
// from "tailwind-variants"; new code should prefer "@codefast/tailwind-variants".
|
|
10
|
+
"tailwind-variants",
|
|
11
|
+
"#lib/utils",
|
|
12
|
+
"~/lib/utils",
|
|
13
|
+
"@/lib/utils",
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* True for typical shadcn-style re-exports: `./utils`, `@/lib/utils`, `…/utils/…`,
|
|
17
|
+
* or a dedicated `cn.ts` / `cn.tsx` module.
|
|
18
|
+
*/
|
|
19
|
+
export function moduleLooksLikeCnTvReexport(mod) {
|
|
20
|
+
const norm = mod.replace(/\\/g, "/");
|
|
21
|
+
if (/(?:^|[./])utils(?:\/|$)/.test(norm))
|
|
22
|
+
return true;
|
|
23
|
+
if (/\/utils\//.test(norm) || /\/utils$/.test(norm))
|
|
24
|
+
return true;
|
|
25
|
+
if (/(?:^|\/)cn\.tsx?$/.test(norm))
|
|
26
|
+
return true;
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build a set of local binding names that are imported from a known cn/tv
|
|
31
|
+
* module in `sf`.
|
|
32
|
+
*/
|
|
33
|
+
export function buildKnownCnTvBindings(sf) {
|
|
34
|
+
const bindings = new Set();
|
|
35
|
+
for (const stmt of sf.statements) {
|
|
36
|
+
if (!ts.isImportDeclaration(stmt) || !stmt.importClause)
|
|
37
|
+
continue;
|
|
38
|
+
if (stmt.importClause.isTypeOnly)
|
|
39
|
+
continue;
|
|
40
|
+
const spec = stmt.moduleSpecifier;
|
|
41
|
+
if (!ts.isStringLiteral(spec))
|
|
42
|
+
continue;
|
|
43
|
+
const mod = spec.text;
|
|
44
|
+
const isKnown = KNOWN_CN_TV_MODULES.has(mod);
|
|
45
|
+
if (!isKnown && !moduleLooksLikeCnTvReexport(mod))
|
|
46
|
+
continue;
|
|
47
|
+
const clause = stmt.importClause;
|
|
48
|
+
if (clause.name)
|
|
49
|
+
bindings.add(clause.name.text);
|
|
50
|
+
if (!clause.namedBindings)
|
|
51
|
+
continue;
|
|
52
|
+
if (ts.isNamedImports(clause.namedBindings)) {
|
|
53
|
+
for (const el of clause.namedBindings.elements)
|
|
54
|
+
bindings.add(el.name.text);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
bindings.add(clause.namedBindings.name.text);
|
|
58
|
+
}
|
|
59
|
+
return bindings;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns true when `expr` is an identifier whose name matches `name` AND
|
|
63
|
+
* that name is listed in `knownBindings` from a recognized import.
|
|
64
|
+
*/
|
|
65
|
+
export function isCnOrTvIdentifier(expr, name, knownBindings = EMPTY_CN_TV_BINDINGS) {
|
|
66
|
+
if (knownBindings.size === 0)
|
|
67
|
+
return false;
|
|
68
|
+
if (ts.isIdentifier(expr) && expr.text === name)
|
|
69
|
+
return knownBindings.has(expr.text);
|
|
70
|
+
if (ts.isPropertyAccessExpression(expr) && expr.name.text === name) {
|
|
71
|
+
return ts.isIdentifier(expr.expression) && knownBindings.has(expr.expression.text);
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
export function propertyAssignmentNameText(prop) {
|
|
76
|
+
if (ts.isIdentifier(prop.name))
|
|
77
|
+
return prop.name.text;
|
|
78
|
+
if (ts.isStringLiteral(prop.name))
|
|
79
|
+
return prop.name.text;
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
export function lineOf(sf, node) {
|
|
83
|
+
return sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns only the leading whitespace (tabs / spaces) of the line containing
|
|
87
|
+
* `pos`.
|
|
88
|
+
*/
|
|
89
|
+
export function indentOfLineContaining(source, pos) {
|
|
90
|
+
const searchPos = Math.max(0, Math.min(pos, source.length));
|
|
91
|
+
const prevLineBreak = Math.max(source.lastIndexOf("\n", searchPos - 1), source.lastIndexOf("\r", searchPos - 1));
|
|
92
|
+
const lineStart = prevLineBreak === -1 ? 0 : prevLineBreak + 1;
|
|
93
|
+
let lineEnd = source.length;
|
|
94
|
+
for (let i = lineStart; i < source.length; i++) {
|
|
95
|
+
const ch = source[i];
|
|
96
|
+
if (ch === "\n" || ch === "\r") {
|
|
97
|
+
lineEnd = i;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const line = source.slice(lineStart, lineEnd);
|
|
102
|
+
const indentMatch = /^[\t ]*/.exec(line);
|
|
103
|
+
return indentMatch?.[0] ?? "";
|
|
104
|
+
}
|
|
105
|
+
export function applyEditsDescending(sourceText, edits) {
|
|
106
|
+
const sorted = [...edits].sort((editA, editB) => editB.start - editA.start);
|
|
107
|
+
let out = sourceText;
|
|
108
|
+
for (const edit of sorted) {
|
|
109
|
+
out = out.slice(0, edit.start) + edit.replacement + out.slice(edit.end);
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Replace `cn(...)` nested in `tv({...})` with a plain string (one arg) or a string
|
|
115
|
+
* array (multiple args). Returns undefined when there are zero arguments.
|
|
116
|
+
*/
|
|
117
|
+
export function unwrapCnInsideTvCallReplacement(call, sourceText, sf) {
|
|
118
|
+
const args = call.arguments;
|
|
119
|
+
if (args.length === 0)
|
|
120
|
+
return undefined;
|
|
121
|
+
const baseIndent = indentOfLineContaining(sourceText, call.getStart(sf));
|
|
122
|
+
const innerIndent = `${baseIndent} `;
|
|
123
|
+
if (args.length === 1) {
|
|
124
|
+
const firstArg = args[0];
|
|
125
|
+
return sourceText.slice(firstArg.getStart(sf), firstArg.getEnd());
|
|
126
|
+
}
|
|
127
|
+
const lines = ["["];
|
|
128
|
+
for (let i = 0; i < args.length; i++) {
|
|
129
|
+
const arg = args[i];
|
|
130
|
+
const piece = sourceText.slice(arg.getStart(sf), arg.getEnd());
|
|
131
|
+
// Trailing comma on every element — intentional (Prettier-compatible style;
|
|
132
|
+
// keeps array diffs clean when arguments are later added or removed).
|
|
133
|
+
const comma = i < args.length - 1 || args.length > 1 ? "," : "";
|
|
134
|
+
lines.push(`${innerIndent}${piece}${comma}`);
|
|
135
|
+
}
|
|
136
|
+
lines.push(`${baseIndent}]`);
|
|
137
|
+
return lines.join("\n");
|
|
138
|
+
}
|