trix-ui 0.2.14 → 0.3.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.
- package/dist/commands/add/analysis.d.ts +4 -4
- package/dist/commands/add/analysis.js +56 -56
- package/dist/commands/add/command.d.ts +2 -2
- package/dist/commands/add/command.js +205 -202
- package/dist/commands/add/command.js.map +1 -1
- package/dist/commands/add/config.d.ts +2 -2
- package/dist/commands/add/config.js +11 -11
- package/dist/commands/add/install.d.ts +27 -27
- package/dist/commands/add/install.js +80 -80
- package/dist/commands/add/package-manager.d.ts +1 -1
- package/dist/commands/add/package-manager.js +4 -4
- package/dist/commands/add/project-files.d.ts +2 -2
- package/dist/commands/add/project-files.js +17 -17
- package/dist/commands/add/prompts.d.ts +3 -3
- package/dist/commands/add/prompts.js +28 -28
- package/dist/commands/add/registry.d.ts +4 -4
- package/dist/commands/add/registry.js +6 -6
- package/dist/commands/add/types.d.ts +33 -33
- package/dist/commands/add/types.js +1 -1
- package/dist/commands/add/ui.d.ts +4 -4
- package/dist/commands/add/ui.js +55 -55
- package/dist/commands/add/validation.d.ts +3 -3
- package/dist/commands/add/validation.js +30 -30
- package/dist/commands/add-collection.d.ts +1 -1
- package/dist/commands/add-collection.js +1 -1
- package/dist/commands/add-composite.d.ts +2 -2
- package/dist/commands/add-composite.js +205 -201
- package/dist/commands/add-composite.js.map +1 -1
- package/dist/commands/add-section.d.ts +2 -2
- package/dist/commands/add-section.js +205 -201
- package/dist/commands/add-section.js.map +1 -1
- package/dist/commands/add-wrapper.d.ts +2 -2
- package/dist/commands/add-wrapper.js +205 -201
- package/dist/commands/add-wrapper.js.map +1 -1
- package/dist/commands/add.d.ts +1 -1
- package/dist/commands/add.js +1 -1
- package/dist/commands/build.d.ts +2 -2
- package/dist/commands/build.js +104 -104
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -2
- package/dist/commands/doctor.js +67 -67
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init/command.d.ts +8 -2
- package/dist/commands/init/command.js +186 -114
- package/dist/commands/init/command.js.map +1 -1
- package/dist/commands/init/config.d.ts +9 -2
- package/dist/commands/init/config.js +45 -25
- package/dist/commands/init/config.js.map +1 -1
- package/dist/commands/init/constants.d.ts +3 -3
- package/dist/commands/init/constants.js +105 -105
- package/dist/commands/init/dependencies.d.ts +22 -5
- package/dist/commands/init/dependencies.js +86 -52
- package/dist/commands/init/dependencies.js.map +1 -1
- package/dist/commands/init/filesystem.d.ts +7 -1
- package/dist/commands/init/filesystem.js +16 -10
- package/dist/commands/init/filesystem.js.map +1 -1
- package/dist/commands/init/lockfile.d.ts +1 -1
- package/dist/commands/init/lockfile.js +1 -1
- package/dist/commands/init/package-json.d.ts +17 -6
- package/dist/commands/init/package-json.js +28 -18
- package/dist/commands/init/package-json.js.map +1 -1
- package/dist/commands/init/project.d.ts +15 -3
- package/dist/commands/init/project.js +120 -97
- package/dist/commands/init/project.js.map +1 -1
- package/dist/commands/init/tailwind.d.ts +14 -3
- package/dist/commands/init/tailwind.js +141 -33
- package/dist/commands/init/tailwind.js.map +1 -1
- package/dist/commands/init/templates.d.ts +15 -3
- package/dist/commands/init/templates.js +27 -15
- package/dist/commands/init/templates.js.map +1 -1
- package/dist/commands/init/tsconfig.d.ts +14 -2
- package/dist/commands/init/tsconfig.js +561 -273
- package/dist/commands/init/tsconfig.js.map +1 -1
- package/dist/commands/init/types.d.ts +34 -33
- package/dist/commands/init/types.js +1 -1
- package/dist/commands/init/ui.d.ts +12 -3
- package/dist/commands/init/ui.js +45 -33
- package/dist/commands/init/ui.js.map +1 -1
- package/dist/commands/init/vite.d.ts +1 -1
- package/dist/commands/init/vite.js +17 -27
- package/dist/commands/init/vite.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.js +69 -62
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/remove.d.ts +2 -2
- package/dist/commands/remove.js +95 -93
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/shared/add-collection.d.ts +50 -50
- package/dist/commands/shared/add-collection.js +206 -206
- package/dist/commands/shared/list-entries.d.ts +6 -6
- package/dist/commands/shared/list-entries.js +12 -12
- package/dist/commands/shared/name-utils.d.ts +1 -1
- package/dist/commands/shared/name-utils.js +13 -13
- package/dist/commands/shared/remove-entries.d.ts +16 -16
- package/dist/commands/shared/remove-entries.js +41 -41
- package/dist/icons/index.d.ts +1 -1
- package/dist/icons/index.js +1 -1
- package/dist/icons/libraries.d.ts +37 -37
- package/dist/icons/libraries.js +34 -34
- package/dist/index.d.ts +1 -2
- package/dist/index.js +29 -30
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +45 -45
- package/dist/lib/config.js +97 -97
- package/dist/lib/fs.d.ts +82 -76
- package/dist/lib/fs.js +299 -302
- package/dist/lib/fs.js.map +1 -1
- package/dist/lib/highlighter.d.ts +6 -6
- package/dist/lib/highlighter.js +7 -7
- package/dist/lib/install.d.ts +19 -19
- package/dist/lib/install.js +101 -55
- package/dist/lib/install.js.map +1 -1
- package/dist/lib/lockfile.d.ts +63 -63
- package/dist/lib/lockfile.js +179 -173
- package/dist/lib/lockfile.js.map +1 -1
- package/dist/lib/logger.d.ts +8 -8
- package/dist/lib/logger.js +41 -41
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/paths.d.ts +14 -14
- package/dist/lib/paths.js +41 -31
- package/dist/lib/paths.js.map +1 -1
- package/dist/lib/registry.d.ts +35 -35
- package/dist/lib/registry.js +180 -180
- package/dist/schema/index.d.ts +1128 -1128
- package/dist/schema/index.js +237 -238
- package/dist/schema/index.js.map +1 -1
- package/dist/styles/create-style-map.d.ts +4 -4
- package/dist/styles/create-style-map.js +68 -68
- package/dist/styles/transform-style-map.d.ts +3 -3
- package/dist/styles/transform-style-map.js +428 -428
- package/dist/styles/transform.d.ts +10 -10
- package/dist/styles/transform.js +15 -15
- package/dist/utils/index.d.ts +6 -6
- package/dist/utils/index.js +5 -5
- package/dist/utils/transformers/transform-icons.d.ts +2 -2
- package/dist/utils/transformers/transform-icons.js +164 -164
- package/dist/utils/transformers/transform-menu.d.ts +2 -2
- package/dist/utils/transformers/transform-menu.js +39 -39
- package/dist/utils/transformers/transform-render.d.ts +2 -2
- package/dist/utils/transformers/transform-render.js +97 -97
- package/dist/utils/transformers/types.d.ts +14 -14
- package/dist/utils/transformers/types.js +1 -1
- package/package.json +69 -69
- package/dist/__tests__/contracts/registry.test.d.ts +0 -1
- package/dist/__tests__/contracts/registry.test.js +0 -42
- package/dist/__tests__/contracts/registry.test.js.map +0 -1
- package/dist/__tests__/e2e/cli.test.d.ts +0 -1
- package/dist/__tests__/e2e/cli.test.js +0 -67
- package/dist/__tests__/e2e/cli.test.js.map +0 -1
- package/dist/__tests__/helpers/fs.d.ts +0 -5
- package/dist/__tests__/helpers/fs.js +0 -26
- package/dist/__tests__/helpers/fs.js.map +0 -1
- package/dist/__tests__/integration/commands.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/commands.integration.test.js +0 -184
- package/dist/__tests__/integration/commands.integration.test.js.map +0 -1
- package/dist/commands/__tests__/add-composite.test.d.ts +0 -2
- package/dist/commands/__tests__/add-composite.test.js +0 -171
- package/dist/commands/__tests__/add-composite.test.js.map +0 -1
- package/dist/commands/__tests__/add-entry.mocks.d.ts +0 -23
- package/dist/commands/__tests__/add-entry.mocks.js +0 -64
- package/dist/commands/__tests__/add-entry.mocks.js.map +0 -1
- package/dist/commands/__tests__/add-section.test.d.ts +0 -2
- package/dist/commands/__tests__/add-section.test.js +0 -191
- package/dist/commands/__tests__/add-section.test.js.map +0 -1
- package/dist/commands/__tests__/add-wrapper.test.d.ts +0 -2
- package/dist/commands/__tests__/add-wrapper.test.js +0 -171
- package/dist/commands/__tests__/add-wrapper.test.js.map +0 -1
- package/dist/commands/__tests__/cli-mocks.d.ts +0 -1
- package/dist/commands/__tests__/cli-mocks.js +0 -24
- package/dist/commands/__tests__/cli-mocks.js.map +0 -1
- package/dist/commands/__tests__/doctor.mocks.d.ts +0 -6
- package/dist/commands/__tests__/doctor.mocks.js +0 -20
- package/dist/commands/__tests__/doctor.mocks.js.map +0 -1
- package/dist/commands/__tests__/doctor.test.d.ts +0 -2
- package/dist/commands/__tests__/doctor.test.js +0 -80
- package/dist/commands/__tests__/doctor.test.js.map +0 -1
- package/dist/commands/__tests__/list.mocks.d.ts +0 -8
- package/dist/commands/__tests__/list.mocks.js +0 -20
- package/dist/commands/__tests__/list.mocks.js.map +0 -1
- package/dist/commands/__tests__/list.test.d.ts +0 -2
- package/dist/commands/__tests__/list.test.js +0 -60
- package/dist/commands/__tests__/list.test.js.map +0 -1
- package/dist/commands/__tests__/remove.mocks.d.ts +0 -9
- package/dist/commands/__tests__/remove.mocks.js +0 -26
- package/dist/commands/__tests__/remove.mocks.js.map +0 -1
- package/dist/commands/__tests__/remove.test.d.ts +0 -2
- package/dist/commands/__tests__/remove.test.js +0 -116
- package/dist/commands/__tests__/remove.test.js.map +0 -1
- package/dist/commands/add/__tests__/__mocks__/cli-mocks.d.ts +0 -1
- package/dist/commands/add/__tests__/__mocks__/cli-mocks.js +0 -21
- package/dist/commands/add/__tests__/__mocks__/cli-mocks.js.map +0 -1
- package/dist/commands/add/__tests__/add.mocks.d.ts +0 -19
- package/dist/commands/add/__tests__/add.mocks.js +0 -60
- package/dist/commands/add/__tests__/add.mocks.js.map +0 -1
- package/dist/commands/add/__tests__/add.test.d.ts +0 -2
- package/dist/commands/add/__tests__/add.test.js +0 -141
- package/dist/commands/add/__tests__/add.test.js.map +0 -1
- package/dist/commands/init/__tests__/init.mocks.d.ts +0 -24
- package/dist/commands/init/__tests__/init.mocks.js +0 -84
- package/dist/commands/init/__tests__/init.mocks.js.map +0 -1
- package/dist/commands/init/__tests__/init.test.d.ts +0 -2
- package/dist/commands/init/__tests__/init.test.js +0 -283
- package/dist/commands/init/__tests__/init.test.js.map +0 -1
- package/dist/commands/init/__tests__/tailwind.test.d.ts +0 -1
- package/dist/commands/init/__tests__/tailwind.test.js +0 -56
- package/dist/commands/init/__tests__/tailwind.test.js.map +0 -1
- package/dist/commands/init/__tests__/tsconfig.test.d.ts +0 -1
- package/dist/commands/init/__tests__/tsconfig.test.js +0 -108
- package/dist/commands/init/__tests__/tsconfig.test.js.map +0 -1
- package/dist/commands/init/__tests__/vite.test.d.ts +0 -1
- package/dist/commands/init/__tests__/vite.test.js +0 -66
- package/dist/commands/init/__tests__/vite.test.js.map +0 -1
- package/dist/commands/list-sections.d.ts +0 -2
- package/dist/commands/list-sections.js +0 -20
- package/dist/commands/list-sections.js.map +0 -1
- package/dist/commands/list-wrappers.d.ts +0 -2
- package/dist/commands/list-wrappers.js +0 -20
- package/dist/commands/list-wrappers.js.map +0 -1
- package/dist/commands/remove-section.d.ts +0 -2
- package/dist/commands/remove-section.js +0 -37
- package/dist/commands/remove-section.js.map +0 -1
- package/dist/commands/remove-wrapper.d.ts +0 -2
- package/dist/commands/remove-wrapper.js +0 -37
- package/dist/commands/remove-wrapper.js.map +0 -1
- package/dist/lib/__tests__/config.test.d.ts +0 -1
- package/dist/lib/__tests__/config.test.js +0 -49
- package/dist/lib/__tests__/config.test.js.map +0 -1
- package/dist/lib/__tests__/install.test.d.ts +0 -1
- package/dist/lib/__tests__/install.test.js +0 -149
- package/dist/lib/__tests__/install.test.js.map +0 -1
- package/dist/lib/__tests__/lockfile.test.d.ts +0 -1
- package/dist/lib/__tests__/lockfile.test.js +0 -89
- package/dist/lib/__tests__/lockfile.test.js.map +0 -1
- package/dist/lib/__tests__/paths.test.d.ts +0 -1
- package/dist/lib/__tests__/paths.test.js +0 -39
- package/dist/lib/__tests__/paths.test.js.map +0 -1
- package/dist/lib/__tests__/registry.test.d.ts +0 -1
- package/dist/lib/__tests__/registry.test.js +0 -76
- package/dist/lib/__tests__/registry.test.js.map +0 -1
|
@@ -1,429 +1,429 @@
|
|
|
1
|
-
import { Node, } from "ts-morph";
|
|
2
|
-
/**
|
|
3
|
-
* Classes that should never be removed during transformation.
|
|
4
|
-
* These are typically used as CSS selectors or for other purposes
|
|
5
|
-
* that require the class name to remain in the code.
|
|
6
|
-
*/
|
|
7
|
-
const ALLOWLIST = new Set(["cn-menu-target"]);
|
|
8
|
-
function isStringLiteralLike(node) {
|
|
9
|
-
return (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node));
|
|
10
|
-
}
|
|
11
|
-
export const transformStyleMap = async ({ sourceFile, styleMap, }) => {
|
|
12
|
-
const matchedClasses = new Set();
|
|
13
|
-
applyToCvaCalls(sourceFile, styleMap, matchedClasses);
|
|
14
|
-
applyToClassNameAttributes(sourceFile, styleMap, matchedClasses);
|
|
15
|
-
applyToMergePropsCalls(sourceFile, styleMap, matchedClasses);
|
|
16
|
-
return sourceFile;
|
|
17
|
-
};
|
|
18
|
-
function applyStyleToCvaString(stringNode, styleMap, matchedClasses) {
|
|
19
|
-
const stringValue = stringNode.getLiteralText();
|
|
20
|
-
const cnClasses = extractCnClasses(stringValue);
|
|
21
|
-
if (cnClasses.length === 0) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
// Process all cn-* classes, not just the first one
|
|
25
|
-
const unmatchedClasses = cnClasses.filter((cnClass) => !matchedClasses.has(cnClass));
|
|
26
|
-
if (unmatchedClasses.length === 0) {
|
|
27
|
-
// All classes already matched, just clean up non-allowlisted ones
|
|
28
|
-
const updated = removeCnClasses(stringValue);
|
|
29
|
-
stringNode.setLiteralValue(updated);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const tailwindClassesToApply = unmatchedClasses
|
|
33
|
-
.map((cnClass) => styleMap[cnClass])
|
|
34
|
-
.filter((classes) => Boolean(classes));
|
|
35
|
-
if (tailwindClassesToApply.length > 0) {
|
|
36
|
-
const mergedClasses = tailwindClassesToApply.join(" ");
|
|
37
|
-
const updated = removeCnClasses(mergeClasses(mergedClasses, stringValue));
|
|
38
|
-
stringNode.setLiteralValue(updated);
|
|
39
|
-
unmatchedClasses.forEach((cnClass) => matchedClasses.add(cnClass));
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
// No styles to apply, but still need to clean up non-allowlisted classes
|
|
43
|
-
const updated = removeCnClasses(stringValue);
|
|
44
|
-
stringNode.setLiteralValue(updated);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function applyToCvaCalls(sourceFile, styleMap, matchedClasses) {
|
|
48
|
-
sourceFile.forEachDescendant((node) => {
|
|
49
|
-
if (!Node.isCallExpression(node)) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const expression = node.getExpression();
|
|
53
|
-
if (!Node.isIdentifier(expression) || expression.getText() !== "cva") {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const baseArg = node.getArguments()[0];
|
|
57
|
-
if (Node.isStringLiteral(baseArg)) {
|
|
58
|
-
applyStyleToCvaString(baseArg, styleMap, matchedClasses);
|
|
59
|
-
}
|
|
60
|
-
const configArg = node.getArguments()[1];
|
|
61
|
-
if (!configArg || !Node.isObjectLiteralExpression(configArg)) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const variantsProp = configArg
|
|
65
|
-
.getProperties()
|
|
66
|
-
.find((prop) => Node.isPropertyAssignment(prop) &&
|
|
67
|
-
Node.isIdentifier(prop.getNameNode()) &&
|
|
68
|
-
prop.getNameNode().getText() === "variants");
|
|
69
|
-
if (!variantsProp || !Node.isPropertyAssignment(variantsProp)) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const variantsObj = variantsProp.getInitializer();
|
|
73
|
-
if (!variantsObj || !Node.isObjectLiteralExpression(variantsObj)) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
variantsObj.getProperties().forEach((typeProp) => {
|
|
77
|
-
if (!Node.isPropertyAssignment(typeProp)) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const typeObj = typeProp.getInitializer();
|
|
81
|
-
if (!typeObj || !Node.isObjectLiteralExpression(typeObj)) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
typeObj.getProperties().forEach((variantProp) => {
|
|
85
|
-
if (!Node.isPropertyAssignment(variantProp)) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const variantValue = variantProp.getInitializer();
|
|
89
|
-
if (variantValue && Node.isStringLiteral(variantValue)) {
|
|
90
|
-
applyStyleToCvaString(variantValue, styleMap, matchedClasses);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
function applyToClassNameAttributes(sourceFile, styleMap, matchedClasses) {
|
|
97
|
-
sourceFile.forEachDescendant((node) => {
|
|
98
|
-
if (!Node.isJsxAttribute(node) ||
|
|
99
|
-
node.getNameNode().getText() !== "className") {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const initializer = node.getInitializer();
|
|
103
|
-
if (!initializer) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const cnClasses = extractCnClassesFromAttribute(initializer);
|
|
107
|
-
if (cnClasses.length === 0) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const jsxElement = node.getParent()?.getParent();
|
|
111
|
-
if (!jsxElement ||
|
|
112
|
-
(!Node.isJsxOpeningElement(jsxElement) &&
|
|
113
|
-
!Node.isJsxSelfClosingElement(jsxElement))) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
const unmatchedClasses = cnClasses.filter((cnClass) => !matchedClasses.has(cnClass));
|
|
117
|
-
if (unmatchedClasses.length === 0) {
|
|
118
|
-
// Even if all classes are already matched, we still need to clean them up
|
|
119
|
-
cleanCnClassesFromAttribute(initializer);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const tailwindClassesToApply = unmatchedClasses
|
|
123
|
-
.map((cnClass) => styleMap[cnClass])
|
|
124
|
-
.filter((classes) => Boolean(classes));
|
|
125
|
-
if (tailwindClassesToApply.length > 0) {
|
|
126
|
-
const mergedClasses = tailwindClassesToApply.join(" ");
|
|
127
|
-
applyClassesToElement(jsxElement, mergedClasses);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
cleanCnClassesFromAttribute(initializer);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
function extractCnClassesFromAttribute(initializer) {
|
|
135
|
-
const classes = [];
|
|
136
|
-
if (isStringLiteralLike(initializer)) {
|
|
137
|
-
return extractCnClasses(initializer.getLiteralText());
|
|
138
|
-
}
|
|
139
|
-
if (!Node.isJsxExpression(initializer)) {
|
|
140
|
-
return classes;
|
|
141
|
-
}
|
|
142
|
-
const expression = initializer.getExpression();
|
|
143
|
-
if (!expression) {
|
|
144
|
-
return classes;
|
|
145
|
-
}
|
|
146
|
-
if (isStringLiteralLike(expression)) {
|
|
147
|
-
return extractCnClasses(expression.getLiteralText());
|
|
148
|
-
}
|
|
149
|
-
if (Node.isCallExpression(expression) && isCnCall(expression)) {
|
|
150
|
-
for (const argument of expression.getArguments()) {
|
|
151
|
-
if (isStringLiteralLike(argument)) {
|
|
152
|
-
classes.push(...extractCnClasses(argument.getLiteralText()));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return classes;
|
|
157
|
-
}
|
|
158
|
-
function cleanCnClassesFromAttribute(initializer) {
|
|
159
|
-
if (isStringLiteralLike(initializer)) {
|
|
160
|
-
const cleaned = removeCnClasses(initializer.getLiteralText());
|
|
161
|
-
initializer.setLiteralValue(cleaned);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
if (!Node.isJsxExpression(initializer)) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const expression = initializer.getExpression();
|
|
168
|
-
if (!expression) {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
if (isStringLiteralLike(expression)) {
|
|
172
|
-
const cleaned = removeCnClasses(expression.getLiteralText());
|
|
173
|
-
expression.setLiteralValue(cleaned);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (Node.isCallExpression(expression) && isCnCall(expression)) {
|
|
177
|
-
for (const argument of expression.getArguments()) {
|
|
178
|
-
if (isStringLiteralLike(argument)) {
|
|
179
|
-
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
180
|
-
argument.setLiteralValue(cleaned);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
removeEmptyArgumentsFromCnCall(expression);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
function extractCnClasses(str) {
|
|
187
|
-
const matches = str.matchAll(/\bcn-[\w-]+\b/g);
|
|
188
|
-
return Array.from(matches, (match) => match[0]);
|
|
189
|
-
}
|
|
190
|
-
function extractCnClass(str) {
|
|
191
|
-
const classes = extractCnClasses(str);
|
|
192
|
-
return classes[0] ?? null;
|
|
193
|
-
}
|
|
194
|
-
function removeCnClasses(str) {
|
|
195
|
-
return str
|
|
196
|
-
.replace(/\bcn-[\w-]+\b/g, (match) => {
|
|
197
|
-
// Preserve allowlisted classes
|
|
198
|
-
if (ALLOWLIST.has(match)) {
|
|
199
|
-
return match;
|
|
200
|
-
}
|
|
201
|
-
return "";
|
|
202
|
-
})
|
|
203
|
-
.replace(/\s+/g, " ")
|
|
204
|
-
.trim();
|
|
205
|
-
}
|
|
206
|
-
function removeEmptyArgumentsFromCnCall(callExpression) {
|
|
207
|
-
if (!isCnCall(callExpression)) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const args = callExpression.getArguments();
|
|
211
|
-
const nonEmptyArgs = args.filter((arg) => {
|
|
212
|
-
if (isStringLiteralLike(arg)) {
|
|
213
|
-
const text = arg.getLiteralText().trim();
|
|
214
|
-
return text !== "";
|
|
215
|
-
}
|
|
216
|
-
return true;
|
|
217
|
-
});
|
|
218
|
-
if (nonEmptyArgs.length !== args.length) {
|
|
219
|
-
const argTexts = nonEmptyArgs.map((arg) => arg.getText());
|
|
220
|
-
const parent = callExpression.getParent();
|
|
221
|
-
if (parent && Node.isJsxExpression(parent)) {
|
|
222
|
-
parent.replaceWithText(`{cn(${argTexts.join(", ")})}`);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
callExpression.replaceWithText(`cn(${argTexts.join(", ")})`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
function applyClassesToElement(element, tailwindClasses) {
|
|
230
|
-
if (!Node.isJsxOpeningElement(element) &&
|
|
231
|
-
!Node.isJsxSelfClosingElement(element)) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const attribute = element
|
|
235
|
-
.getAttributes()
|
|
236
|
-
.find((attr) => Node.isJsxAttribute(attr) &&
|
|
237
|
-
attr.getNameNode().getText() === "className");
|
|
238
|
-
if (!attribute || !Node.isJsxAttribute(attribute)) {
|
|
239
|
-
element.addAttribute({
|
|
240
|
-
name: "className",
|
|
241
|
-
initializer: `{cn(${JSON.stringify(tailwindClasses)})}`,
|
|
242
|
-
});
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const initializer = attribute.getInitializer();
|
|
246
|
-
if (!initializer) {
|
|
247
|
-
attribute.setInitializer(`{cn(${JSON.stringify(tailwindClasses)})}`);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
if (isStringLiteralLike(initializer)) {
|
|
251
|
-
const existing = initializer.getLiteralText();
|
|
252
|
-
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
253
|
-
initializer.setLiteralValue(updated);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
if (!Node.isJsxExpression(initializer)) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const expression = initializer.getExpression();
|
|
260
|
-
if (!expression) {
|
|
261
|
-
attribute.setInitializer(`{cn(${JSON.stringify(tailwindClasses)})}`);
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
if (isStringLiteralLike(expression)) {
|
|
265
|
-
const existing = expression.getLiteralText();
|
|
266
|
-
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
267
|
-
expression.setLiteralValue(updated);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (Node.isCallExpression(expression) && isCnCall(expression)) {
|
|
271
|
-
const firstArg = expression.getArguments()[0];
|
|
272
|
-
if (isStringLiteralLike(firstArg)) {
|
|
273
|
-
const existing = firstArg.getLiteralText();
|
|
274
|
-
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
275
|
-
firstArg.setLiteralValue(updated);
|
|
276
|
-
for (let i = 1; i < expression.getArguments().length; i++) {
|
|
277
|
-
const arg = expression.getArguments()[i];
|
|
278
|
-
if (isStringLiteralLike(arg)) {
|
|
279
|
-
const argText = arg.getLiteralText();
|
|
280
|
-
const cleaned = removeCnClasses(argText);
|
|
281
|
-
if (cleaned !== argText) {
|
|
282
|
-
arg.setLiteralValue(cleaned);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
removeEmptyArgumentsFromCnCall(expression);
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
const argumentTexts = expression
|
|
290
|
-
.getArguments()
|
|
291
|
-
.map((argument) => {
|
|
292
|
-
if (isStringLiteralLike(argument)) {
|
|
293
|
-
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
294
|
-
return cleaned ? JSON.stringify(cleaned) : null;
|
|
295
|
-
}
|
|
296
|
-
return argument.getText();
|
|
297
|
-
})
|
|
298
|
-
.filter((arg) => arg !== null);
|
|
299
|
-
const updatedArguments = [JSON.stringify(tailwindClasses), ...argumentTexts];
|
|
300
|
-
attribute.setInitializer(`{cn(${updatedArguments.join(", ")})}`);
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
attribute.setInitializer(`{cn(${JSON.stringify(tailwindClasses)}, ${expression.getText()})}`);
|
|
304
|
-
}
|
|
305
|
-
function mergeClasses(newClasses, existing) {
|
|
306
|
-
const existingParts = existing.split(/\s+/).filter(Boolean);
|
|
307
|
-
const newParts = newClasses.split(/\s+/).filter(Boolean);
|
|
308
|
-
const combined = [...newParts, ...existingParts];
|
|
309
|
-
return combined.join(" ").trim();
|
|
310
|
-
}
|
|
311
|
-
function isCnCall(call) {
|
|
312
|
-
const expression = call.getExpression();
|
|
313
|
-
return Node.isIdentifier(expression) && expression.getText() === "cn";
|
|
314
|
-
}
|
|
315
|
-
function applyToMergePropsCalls(sourceFile, styleMap, matchedClasses) {
|
|
316
|
-
sourceFile.forEachDescendant((node) => {
|
|
317
|
-
if (!Node.isCallExpression(node)) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
const expression = node.getExpression();
|
|
321
|
-
if (!Node.isIdentifier(expression) ||
|
|
322
|
-
expression.getText() !== "mergeProps") {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
// Look for object literals in mergeProps arguments
|
|
326
|
-
for (const arg of node.getArguments()) {
|
|
327
|
-
if (!Node.isObjectLiteralExpression(arg)) {
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
// Find className property in the object literal
|
|
331
|
-
const classNameProp = arg
|
|
332
|
-
.getProperties()
|
|
333
|
-
.find((prop) => Node.isPropertyAssignment(prop) &&
|
|
334
|
-
Node.isIdentifier(prop.getNameNode()) &&
|
|
335
|
-
prop.getNameNode().getText() === "className");
|
|
336
|
-
if (!classNameProp || !Node.isPropertyAssignment(classNameProp)) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
const classNameInitializer = classNameProp.getInitializer();
|
|
340
|
-
if (!classNameInitializer) {
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
// Handle cn() calls in className
|
|
344
|
-
if (Node.isCallExpression(classNameInitializer) &&
|
|
345
|
-
isCnCall(classNameInitializer)) {
|
|
346
|
-
const cnClasses = extractCnClassesFromCnCall(classNameInitializer);
|
|
347
|
-
if (cnClasses.length === 0) {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
const unmatchedClasses = cnClasses.filter((cnClass) => !matchedClasses.has(cnClass));
|
|
351
|
-
if (unmatchedClasses.length === 0) {
|
|
352
|
-
// Clean up cn-* classes even if already matched
|
|
353
|
-
cleanCnClassesFromCnCall(classNameInitializer);
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
const tailwindClassesToApply = unmatchedClasses
|
|
357
|
-
.map((cnClass) => styleMap[cnClass])
|
|
358
|
-
.filter((classes) => Boolean(classes));
|
|
359
|
-
if (tailwindClassesToApply.length > 0) {
|
|
360
|
-
const mergedClasses = tailwindClassesToApply.join(" ");
|
|
361
|
-
applyClassesToCnCall(classNameInitializer, mergedClasses, matchedClasses, unmatchedClasses);
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
cleanCnClassesFromCnCall(classNameInitializer);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
function extractCnClassesFromCnCall(cnCall) {
|
|
371
|
-
const classes = [];
|
|
372
|
-
for (const argument of cnCall.getArguments()) {
|
|
373
|
-
if (isStringLiteralLike(argument)) {
|
|
374
|
-
classes.push(...extractCnClasses(argument.getLiteralText()));
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return classes;
|
|
378
|
-
}
|
|
379
|
-
function cleanCnClassesFromCnCall(cnCall) {
|
|
380
|
-
for (const argument of cnCall.getArguments()) {
|
|
381
|
-
if (isStringLiteralLike(argument)) {
|
|
382
|
-
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
383
|
-
argument.setLiteralValue(cleaned);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
removeEmptyArgumentsFromCnCall(cnCall);
|
|
387
|
-
}
|
|
388
|
-
function applyClassesToCnCall(cnCall, tailwindClasses, matchedClasses, unmatchedClasses) {
|
|
389
|
-
const firstArg = cnCall.getArguments()[0];
|
|
390
|
-
if (isStringLiteralLike(firstArg)) {
|
|
391
|
-
const existing = firstArg.getLiteralText();
|
|
392
|
-
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
393
|
-
firstArg.setLiteralValue(updated);
|
|
394
|
-
// Mark classes as matched
|
|
395
|
-
unmatchedClasses.forEach((cnClass) => matchedClasses.add(cnClass));
|
|
396
|
-
// Clean up cn-* classes from remaining arguments
|
|
397
|
-
for (let i = 1; i < cnCall.getArguments().length; i++) {
|
|
398
|
-
const arg = cnCall.getArguments()[i];
|
|
399
|
-
if (isStringLiteralLike(arg)) {
|
|
400
|
-
const argText = arg.getLiteralText();
|
|
401
|
-
const cleaned = removeCnClasses(argText);
|
|
402
|
-
if (cleaned !== argText) {
|
|
403
|
-
arg.setLiteralValue(cleaned);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
removeEmptyArgumentsFromCnCall(cnCall);
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
// If first arg is not a string literal, prepend tailwind classes
|
|
411
|
-
const argumentTexts = cnCall
|
|
412
|
-
.getArguments()
|
|
413
|
-
.map((argument) => {
|
|
414
|
-
if (isStringLiteralLike(argument)) {
|
|
415
|
-
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
416
|
-
return cleaned ? JSON.stringify(cleaned) : null;
|
|
417
|
-
}
|
|
418
|
-
return argument.getText();
|
|
419
|
-
})
|
|
420
|
-
.filter((arg) => arg !== null);
|
|
421
|
-
const updatedArguments = [JSON.stringify(tailwindClasses), ...argumentTexts];
|
|
422
|
-
// Mark classes as matched
|
|
423
|
-
unmatchedClasses.forEach((cnClass) => matchedClasses.add(cnClass));
|
|
424
|
-
const parent = cnCall.getParent();
|
|
425
|
-
if (parent) {
|
|
426
|
-
cnCall.replaceWithText(`cn(${updatedArguments.join(", ")})`);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
1
|
+
import { Node, } from "ts-morph";
|
|
2
|
+
/**
|
|
3
|
+
* Classes that should never be removed during transformation.
|
|
4
|
+
* These are typically used as CSS selectors or for other purposes
|
|
5
|
+
* that require the class name to remain in the code.
|
|
6
|
+
*/
|
|
7
|
+
const ALLOWLIST = new Set(["cn-menu-target"]);
|
|
8
|
+
function isStringLiteralLike(node) {
|
|
9
|
+
return (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node));
|
|
10
|
+
}
|
|
11
|
+
export const transformStyleMap = async ({ sourceFile, styleMap, }) => {
|
|
12
|
+
const matchedClasses = new Set();
|
|
13
|
+
applyToCvaCalls(sourceFile, styleMap, matchedClasses);
|
|
14
|
+
applyToClassNameAttributes(sourceFile, styleMap, matchedClasses);
|
|
15
|
+
applyToMergePropsCalls(sourceFile, styleMap, matchedClasses);
|
|
16
|
+
return sourceFile;
|
|
17
|
+
};
|
|
18
|
+
function applyStyleToCvaString(stringNode, styleMap, matchedClasses) {
|
|
19
|
+
const stringValue = stringNode.getLiteralText();
|
|
20
|
+
const cnClasses = extractCnClasses(stringValue);
|
|
21
|
+
if (cnClasses.length === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Process all cn-* classes, not just the first one
|
|
25
|
+
const unmatchedClasses = cnClasses.filter((cnClass) => !matchedClasses.has(cnClass));
|
|
26
|
+
if (unmatchedClasses.length === 0) {
|
|
27
|
+
// All classes already matched, just clean up non-allowlisted ones
|
|
28
|
+
const updated = removeCnClasses(stringValue);
|
|
29
|
+
stringNode.setLiteralValue(updated);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const tailwindClassesToApply = unmatchedClasses
|
|
33
|
+
.map((cnClass) => styleMap[cnClass])
|
|
34
|
+
.filter((classes) => Boolean(classes));
|
|
35
|
+
if (tailwindClassesToApply.length > 0) {
|
|
36
|
+
const mergedClasses = tailwindClassesToApply.join(" ");
|
|
37
|
+
const updated = removeCnClasses(mergeClasses(mergedClasses, stringValue));
|
|
38
|
+
stringNode.setLiteralValue(updated);
|
|
39
|
+
unmatchedClasses.forEach((cnClass) => matchedClasses.add(cnClass));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// No styles to apply, but still need to clean up non-allowlisted classes
|
|
43
|
+
const updated = removeCnClasses(stringValue);
|
|
44
|
+
stringNode.setLiteralValue(updated);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function applyToCvaCalls(sourceFile, styleMap, matchedClasses) {
|
|
48
|
+
sourceFile.forEachDescendant((node) => {
|
|
49
|
+
if (!Node.isCallExpression(node)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const expression = node.getExpression();
|
|
53
|
+
if (!Node.isIdentifier(expression) || expression.getText() !== "cva") {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const baseArg = node.getArguments()[0];
|
|
57
|
+
if (Node.isStringLiteral(baseArg)) {
|
|
58
|
+
applyStyleToCvaString(baseArg, styleMap, matchedClasses);
|
|
59
|
+
}
|
|
60
|
+
const configArg = node.getArguments()[1];
|
|
61
|
+
if (!configArg || !Node.isObjectLiteralExpression(configArg)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const variantsProp = configArg
|
|
65
|
+
.getProperties()
|
|
66
|
+
.find((prop) => Node.isPropertyAssignment(prop) &&
|
|
67
|
+
Node.isIdentifier(prop.getNameNode()) &&
|
|
68
|
+
prop.getNameNode().getText() === "variants");
|
|
69
|
+
if (!variantsProp || !Node.isPropertyAssignment(variantsProp)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const variantsObj = variantsProp.getInitializer();
|
|
73
|
+
if (!variantsObj || !Node.isObjectLiteralExpression(variantsObj)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
variantsObj.getProperties().forEach((typeProp) => {
|
|
77
|
+
if (!Node.isPropertyAssignment(typeProp)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const typeObj = typeProp.getInitializer();
|
|
81
|
+
if (!typeObj || !Node.isObjectLiteralExpression(typeObj)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
typeObj.getProperties().forEach((variantProp) => {
|
|
85
|
+
if (!Node.isPropertyAssignment(variantProp)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const variantValue = variantProp.getInitializer();
|
|
89
|
+
if (variantValue && Node.isStringLiteral(variantValue)) {
|
|
90
|
+
applyStyleToCvaString(variantValue, styleMap, matchedClasses);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function applyToClassNameAttributes(sourceFile, styleMap, matchedClasses) {
|
|
97
|
+
sourceFile.forEachDescendant((node) => {
|
|
98
|
+
if (!Node.isJsxAttribute(node) ||
|
|
99
|
+
node.getNameNode().getText() !== "className") {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const initializer = node.getInitializer();
|
|
103
|
+
if (!initializer) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const cnClasses = extractCnClassesFromAttribute(initializer);
|
|
107
|
+
if (cnClasses.length === 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const jsxElement = node.getParent()?.getParent();
|
|
111
|
+
if (!jsxElement ||
|
|
112
|
+
(!Node.isJsxOpeningElement(jsxElement) &&
|
|
113
|
+
!Node.isJsxSelfClosingElement(jsxElement))) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const unmatchedClasses = cnClasses.filter((cnClass) => !matchedClasses.has(cnClass));
|
|
117
|
+
if (unmatchedClasses.length === 0) {
|
|
118
|
+
// Even if all classes are already matched, we still need to clean them up
|
|
119
|
+
cleanCnClassesFromAttribute(initializer);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const tailwindClassesToApply = unmatchedClasses
|
|
123
|
+
.map((cnClass) => styleMap[cnClass])
|
|
124
|
+
.filter((classes) => Boolean(classes));
|
|
125
|
+
if (tailwindClassesToApply.length > 0) {
|
|
126
|
+
const mergedClasses = tailwindClassesToApply.join(" ");
|
|
127
|
+
applyClassesToElement(jsxElement, mergedClasses);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
cleanCnClassesFromAttribute(initializer);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function extractCnClassesFromAttribute(initializer) {
|
|
135
|
+
const classes = [];
|
|
136
|
+
if (isStringLiteralLike(initializer)) {
|
|
137
|
+
return extractCnClasses(initializer.getLiteralText());
|
|
138
|
+
}
|
|
139
|
+
if (!Node.isJsxExpression(initializer)) {
|
|
140
|
+
return classes;
|
|
141
|
+
}
|
|
142
|
+
const expression = initializer.getExpression();
|
|
143
|
+
if (!expression) {
|
|
144
|
+
return classes;
|
|
145
|
+
}
|
|
146
|
+
if (isStringLiteralLike(expression)) {
|
|
147
|
+
return extractCnClasses(expression.getLiteralText());
|
|
148
|
+
}
|
|
149
|
+
if (Node.isCallExpression(expression) && isCnCall(expression)) {
|
|
150
|
+
for (const argument of expression.getArguments()) {
|
|
151
|
+
if (isStringLiteralLike(argument)) {
|
|
152
|
+
classes.push(...extractCnClasses(argument.getLiteralText()));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return classes;
|
|
157
|
+
}
|
|
158
|
+
function cleanCnClassesFromAttribute(initializer) {
|
|
159
|
+
if (isStringLiteralLike(initializer)) {
|
|
160
|
+
const cleaned = removeCnClasses(initializer.getLiteralText());
|
|
161
|
+
initializer.setLiteralValue(cleaned);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!Node.isJsxExpression(initializer)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const expression = initializer.getExpression();
|
|
168
|
+
if (!expression) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (isStringLiteralLike(expression)) {
|
|
172
|
+
const cleaned = removeCnClasses(expression.getLiteralText());
|
|
173
|
+
expression.setLiteralValue(cleaned);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (Node.isCallExpression(expression) && isCnCall(expression)) {
|
|
177
|
+
for (const argument of expression.getArguments()) {
|
|
178
|
+
if (isStringLiteralLike(argument)) {
|
|
179
|
+
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
180
|
+
argument.setLiteralValue(cleaned);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
removeEmptyArgumentsFromCnCall(expression);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function extractCnClasses(str) {
|
|
187
|
+
const matches = str.matchAll(/\bcn-[\w-]+\b/g);
|
|
188
|
+
return Array.from(matches, (match) => match[0]);
|
|
189
|
+
}
|
|
190
|
+
function extractCnClass(str) {
|
|
191
|
+
const classes = extractCnClasses(str);
|
|
192
|
+
return classes[0] ?? null;
|
|
193
|
+
}
|
|
194
|
+
function removeCnClasses(str) {
|
|
195
|
+
return str
|
|
196
|
+
.replace(/\bcn-[\w-]+\b/g, (match) => {
|
|
197
|
+
// Preserve allowlisted classes
|
|
198
|
+
if (ALLOWLIST.has(match)) {
|
|
199
|
+
return match;
|
|
200
|
+
}
|
|
201
|
+
return "";
|
|
202
|
+
})
|
|
203
|
+
.replace(/\s+/g, " ")
|
|
204
|
+
.trim();
|
|
205
|
+
}
|
|
206
|
+
function removeEmptyArgumentsFromCnCall(callExpression) {
|
|
207
|
+
if (!isCnCall(callExpression)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const args = callExpression.getArguments();
|
|
211
|
+
const nonEmptyArgs = args.filter((arg) => {
|
|
212
|
+
if (isStringLiteralLike(arg)) {
|
|
213
|
+
const text = arg.getLiteralText().trim();
|
|
214
|
+
return text !== "";
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
});
|
|
218
|
+
if (nonEmptyArgs.length !== args.length) {
|
|
219
|
+
const argTexts = nonEmptyArgs.map((arg) => arg.getText());
|
|
220
|
+
const parent = callExpression.getParent();
|
|
221
|
+
if (parent && Node.isJsxExpression(parent)) {
|
|
222
|
+
parent.replaceWithText(`{cn(${argTexts.join(", ")})}`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
callExpression.replaceWithText(`cn(${argTexts.join(", ")})`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function applyClassesToElement(element, tailwindClasses) {
|
|
230
|
+
if (!Node.isJsxOpeningElement(element) &&
|
|
231
|
+
!Node.isJsxSelfClosingElement(element)) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const attribute = element
|
|
235
|
+
.getAttributes()
|
|
236
|
+
.find((attr) => Node.isJsxAttribute(attr) &&
|
|
237
|
+
attr.getNameNode().getText() === "className");
|
|
238
|
+
if (!attribute || !Node.isJsxAttribute(attribute)) {
|
|
239
|
+
element.addAttribute({
|
|
240
|
+
name: "className",
|
|
241
|
+
initializer: `{cn(${JSON.stringify(tailwindClasses)})}`,
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const initializer = attribute.getInitializer();
|
|
246
|
+
if (!initializer) {
|
|
247
|
+
attribute.setInitializer(`{cn(${JSON.stringify(tailwindClasses)})}`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (isStringLiteralLike(initializer)) {
|
|
251
|
+
const existing = initializer.getLiteralText();
|
|
252
|
+
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
253
|
+
initializer.setLiteralValue(updated);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (!Node.isJsxExpression(initializer)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const expression = initializer.getExpression();
|
|
260
|
+
if (!expression) {
|
|
261
|
+
attribute.setInitializer(`{cn(${JSON.stringify(tailwindClasses)})}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (isStringLiteralLike(expression)) {
|
|
265
|
+
const existing = expression.getLiteralText();
|
|
266
|
+
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
267
|
+
expression.setLiteralValue(updated);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (Node.isCallExpression(expression) && isCnCall(expression)) {
|
|
271
|
+
const firstArg = expression.getArguments()[0];
|
|
272
|
+
if (isStringLiteralLike(firstArg)) {
|
|
273
|
+
const existing = firstArg.getLiteralText();
|
|
274
|
+
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
275
|
+
firstArg.setLiteralValue(updated);
|
|
276
|
+
for (let i = 1; i < expression.getArguments().length; i++) {
|
|
277
|
+
const arg = expression.getArguments()[i];
|
|
278
|
+
if (isStringLiteralLike(arg)) {
|
|
279
|
+
const argText = arg.getLiteralText();
|
|
280
|
+
const cleaned = removeCnClasses(argText);
|
|
281
|
+
if (cleaned !== argText) {
|
|
282
|
+
arg.setLiteralValue(cleaned);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
removeEmptyArgumentsFromCnCall(expression);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const argumentTexts = expression
|
|
290
|
+
.getArguments()
|
|
291
|
+
.map((argument) => {
|
|
292
|
+
if (isStringLiteralLike(argument)) {
|
|
293
|
+
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
294
|
+
return cleaned ? JSON.stringify(cleaned) : null;
|
|
295
|
+
}
|
|
296
|
+
return argument.getText();
|
|
297
|
+
})
|
|
298
|
+
.filter((arg) => arg !== null);
|
|
299
|
+
const updatedArguments = [JSON.stringify(tailwindClasses), ...argumentTexts];
|
|
300
|
+
attribute.setInitializer(`{cn(${updatedArguments.join(", ")})}`);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
attribute.setInitializer(`{cn(${JSON.stringify(tailwindClasses)}, ${expression.getText()})}`);
|
|
304
|
+
}
|
|
305
|
+
function mergeClasses(newClasses, existing) {
|
|
306
|
+
const existingParts = existing.split(/\s+/).filter(Boolean);
|
|
307
|
+
const newParts = newClasses.split(/\s+/).filter(Boolean);
|
|
308
|
+
const combined = [...newParts, ...existingParts];
|
|
309
|
+
return combined.join(" ").trim();
|
|
310
|
+
}
|
|
311
|
+
function isCnCall(call) {
|
|
312
|
+
const expression = call.getExpression();
|
|
313
|
+
return Node.isIdentifier(expression) && expression.getText() === "cn";
|
|
314
|
+
}
|
|
315
|
+
function applyToMergePropsCalls(sourceFile, styleMap, matchedClasses) {
|
|
316
|
+
sourceFile.forEachDescendant((node) => {
|
|
317
|
+
if (!Node.isCallExpression(node)) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const expression = node.getExpression();
|
|
321
|
+
if (!Node.isIdentifier(expression) ||
|
|
322
|
+
expression.getText() !== "mergeProps") {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// Look for object literals in mergeProps arguments
|
|
326
|
+
for (const arg of node.getArguments()) {
|
|
327
|
+
if (!Node.isObjectLiteralExpression(arg)) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
// Find className property in the object literal
|
|
331
|
+
const classNameProp = arg
|
|
332
|
+
.getProperties()
|
|
333
|
+
.find((prop) => Node.isPropertyAssignment(prop) &&
|
|
334
|
+
Node.isIdentifier(prop.getNameNode()) &&
|
|
335
|
+
prop.getNameNode().getText() === "className");
|
|
336
|
+
if (!classNameProp || !Node.isPropertyAssignment(classNameProp)) {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const classNameInitializer = classNameProp.getInitializer();
|
|
340
|
+
if (!classNameInitializer) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
// Handle cn() calls in className
|
|
344
|
+
if (Node.isCallExpression(classNameInitializer) &&
|
|
345
|
+
isCnCall(classNameInitializer)) {
|
|
346
|
+
const cnClasses = extractCnClassesFromCnCall(classNameInitializer);
|
|
347
|
+
if (cnClasses.length === 0) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
const unmatchedClasses = cnClasses.filter((cnClass) => !matchedClasses.has(cnClass));
|
|
351
|
+
if (unmatchedClasses.length === 0) {
|
|
352
|
+
// Clean up cn-* classes even if already matched
|
|
353
|
+
cleanCnClassesFromCnCall(classNameInitializer);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const tailwindClassesToApply = unmatchedClasses
|
|
357
|
+
.map((cnClass) => styleMap[cnClass])
|
|
358
|
+
.filter((classes) => Boolean(classes));
|
|
359
|
+
if (tailwindClassesToApply.length > 0) {
|
|
360
|
+
const mergedClasses = tailwindClassesToApply.join(" ");
|
|
361
|
+
applyClassesToCnCall(classNameInitializer, mergedClasses, matchedClasses, unmatchedClasses);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
cleanCnClassesFromCnCall(classNameInitializer);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function extractCnClassesFromCnCall(cnCall) {
|
|
371
|
+
const classes = [];
|
|
372
|
+
for (const argument of cnCall.getArguments()) {
|
|
373
|
+
if (isStringLiteralLike(argument)) {
|
|
374
|
+
classes.push(...extractCnClasses(argument.getLiteralText()));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return classes;
|
|
378
|
+
}
|
|
379
|
+
function cleanCnClassesFromCnCall(cnCall) {
|
|
380
|
+
for (const argument of cnCall.getArguments()) {
|
|
381
|
+
if (isStringLiteralLike(argument)) {
|
|
382
|
+
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
383
|
+
argument.setLiteralValue(cleaned);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
removeEmptyArgumentsFromCnCall(cnCall);
|
|
387
|
+
}
|
|
388
|
+
function applyClassesToCnCall(cnCall, tailwindClasses, matchedClasses, unmatchedClasses) {
|
|
389
|
+
const firstArg = cnCall.getArguments()[0];
|
|
390
|
+
if (isStringLiteralLike(firstArg)) {
|
|
391
|
+
const existing = firstArg.getLiteralText();
|
|
392
|
+
const updated = removeCnClasses(mergeClasses(tailwindClasses, existing));
|
|
393
|
+
firstArg.setLiteralValue(updated);
|
|
394
|
+
// Mark classes as matched
|
|
395
|
+
unmatchedClasses.forEach((cnClass) => matchedClasses.add(cnClass));
|
|
396
|
+
// Clean up cn-* classes from remaining arguments
|
|
397
|
+
for (let i = 1; i < cnCall.getArguments().length; i++) {
|
|
398
|
+
const arg = cnCall.getArguments()[i];
|
|
399
|
+
if (isStringLiteralLike(arg)) {
|
|
400
|
+
const argText = arg.getLiteralText();
|
|
401
|
+
const cleaned = removeCnClasses(argText);
|
|
402
|
+
if (cleaned !== argText) {
|
|
403
|
+
arg.setLiteralValue(cleaned);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
removeEmptyArgumentsFromCnCall(cnCall);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
// If first arg is not a string literal, prepend tailwind classes
|
|
411
|
+
const argumentTexts = cnCall
|
|
412
|
+
.getArguments()
|
|
413
|
+
.map((argument) => {
|
|
414
|
+
if (isStringLiteralLike(argument)) {
|
|
415
|
+
const cleaned = removeCnClasses(argument.getLiteralText());
|
|
416
|
+
return cleaned ? JSON.stringify(cleaned) : null;
|
|
417
|
+
}
|
|
418
|
+
return argument.getText();
|
|
419
|
+
})
|
|
420
|
+
.filter((arg) => arg !== null);
|
|
421
|
+
const updatedArguments = [JSON.stringify(tailwindClasses), ...argumentTexts];
|
|
422
|
+
// Mark classes as matched
|
|
423
|
+
unmatchedClasses.forEach((cnClass) => matchedClasses.add(cnClass));
|
|
424
|
+
const parent = cnCall.getParent();
|
|
425
|
+
if (parent) {
|
|
426
|
+
cnCall.replaceWithText(`cn(${updatedArguments.join(", ")})`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
429
|
//# sourceMappingURL=transform-style-map.js.map
|