@nifrajs/cli 3.1.0 → 3.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/README.md +10 -0
- package/dist/assure-hydration.d.ts.map +1 -1
- package/dist/assure-hydration.js +6 -1
- package/dist/assure-hydration.js.map +1 -1
- package/dist/assure.d.ts.map +1 -1
- package/dist/assure.js +3 -2
- package/dist/assure.js.map +1 -1
- package/dist/check-diagnostics.d.ts +165 -0
- package/dist/check-diagnostics.d.ts.map +1 -0
- package/dist/check-diagnostics.js +1009 -0
- package/dist/check-diagnostics.js.map +1 -0
- package/dist/check-scan.d.ts +245 -0
- package/dist/check-scan.d.ts.map +1 -0
- package/dist/check-scan.js +1599 -0
- package/dist/check-scan.js.map +1 -0
- package/dist/check.d.ts +4 -324
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +8 -2366
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +247 -10
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.d.ts +21 -1
- package/dist/command-catalog.d.ts.map +1 -1
- package/dist/command-catalog.js +122 -0
- package/dist/command-catalog.js.map +1 -1
- package/dist/env-file.js +5 -5
- package/dist/env-file.js.map +1 -1
- package/dist/frontend-guidance.d.ts +57 -0
- package/dist/frontend-guidance.d.ts.map +1 -0
- package/dist/frontend-guidance.js +195 -0
- package/dist/frontend-guidance.js.map +1 -0
- package/dist/levels-tool.d.ts.map +1 -1
- package/dist/levels-tool.js +6 -0
- package/dist/levels-tool.js.map +1 -1
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +4 -1
- package/dist/load.js.map +1 -1
- package/dist/mcp-context.d.ts +48 -0
- package/dist/mcp-context.d.ts.map +1 -0
- package/dist/mcp-context.js +215 -0
- package/dist/mcp-context.js.map +1 -0
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +31 -0
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-exec.d.ts +74 -0
- package/dist/mcp-exec.d.ts.map +1 -0
- package/dist/mcp-exec.js +844 -0
- package/dist/mcp-exec.js.map +1 -0
- package/dist/mcp-http.js +1 -1
- package/dist/mcp-io.d.ts +26 -0
- package/dist/mcp-io.d.ts.map +1 -0
- package/dist/mcp-io.js +100 -0
- package/dist/mcp-io.js.map +1 -0
- package/dist/mcp-reflect.d.ts +16 -0
- package/dist/mcp-reflect.d.ts.map +1 -0
- package/dist/mcp-reflect.js +101 -0
- package/dist/mcp-reflect.js.map +1 -0
- package/dist/mcp-root.d.ts +7 -0
- package/dist/mcp-root.d.ts.map +1 -1
- package/dist/mcp-root.js +51 -3
- package/dist/mcp-root.js.map +1 -1
- package/dist/mcp.d.ts +10 -92
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +13 -1232
- package/dist/mcp.js.map +1 -1
- package/dist/openapi-tool.d.ts +5 -10
- package/dist/openapi-tool.d.ts.map +1 -1
- package/dist/openapi-tool.js +21 -2
- package/dist/openapi-tool.js.map +1 -1
- package/dist/openapi-types.d.ts +21 -0
- package/dist/openapi-types.d.ts.map +1 -0
- package/dist/openapi-types.js +214 -0
- package/dist/openapi-types.js.map +1 -0
- package/dist/port.d.ts.map +1 -1
- package/dist/port.js +2 -1
- package/dist/port.js.map +1 -1
- package/dist/project-facts.d.ts +2 -1
- package/dist/project-facts.d.ts.map +1 -1
- package/dist/project-facts.js.map +1 -1
- package/dist/release-verification.d.ts +4 -1
- package/dist/release-verification.d.ts.map +1 -1
- package/dist/release-verification.js +6 -138
- package/dist/release-verification.js.map +1 -1
- package/dist/rules/codes.d.ts +4 -0
- package/dist/rules/codes.d.ts.map +1 -1
- package/dist/rules/codes.js +4 -0
- package/dist/rules/codes.js.map +1 -1
- package/dist/rules/islands.d.ts +4 -0
- package/dist/rules/islands.d.ts.map +1 -0
- package/dist/rules/islands.js +155 -0
- package/dist/rules/islands.js.map +1 -0
- package/dist/rules/nano.d.ts +6 -0
- package/dist/rules/nano.d.ts.map +1 -0
- package/dist/rules/nano.js +264 -0
- package/dist/rules/nano.js.map +1 -0
- package/dist/rules/security.d.ts.map +1 -1
- package/dist/rules/security.js +38 -4
- package/dist/rules/security.js.map +1 -1
- package/dist/scaffold.d.ts +12 -6
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +149 -10
- package/dist/scaffold.js.map +1 -1
- package/dist/search-terms.d.ts.map +1 -1
- package/dist/search-terms.js +5 -3
- package/dist/search-terms.js.map +1 -1
- package/dist/stylex-migrate.d.ts +38 -0
- package/dist/stylex-migrate.d.ts.map +1 -0
- package/dist/stylex-migrate.js +1052 -0
- package/dist/stylex-migrate.js.map +1 -0
- package/dist/sync-manifest.d.ts.map +1 -1
- package/dist/sync-manifest.js +2 -1
- package/dist/sync-manifest.js.map +1 -1
- package/dist/verification-plan.d.ts +24 -0
- package/dist/verification-plan.d.ts.map +1 -0
- package/dist/verification-plan.js +116 -0
- package/dist/verification-plan.js.map +1 -0
- package/dist/work-graph.d.ts.map +1 -1
- package/dist/work-graph.js +6 -3
- package/dist/work-graph.js.map +1 -1
- package/docs/examples.json +46 -4
- package/docs/llms-full.txt +1346 -28
- package/docs/types.json +4185 -87
- package/package.json +9 -9
- package/src/assure-hydration.ts +6 -1
- package/src/assure.ts +4 -2
- package/src/check-diagnostics.ts +1219 -0
- package/src/check-scan.ts +1825 -0
- package/src/check.ts +47 -2752
- package/src/cli.ts +252 -10
- package/src/command-catalog.ts +155 -0
- package/src/env-file.ts +5 -5
- package/src/frontend-guidance.ts +259 -0
- package/src/levels-tool.ts +6 -0
- package/src/load.ts +4 -1
- package/src/mcp-context.ts +266 -0
- package/src/mcp-docs-tools.ts +37 -0
- package/src/mcp-exec.ts +1040 -0
- package/src/mcp-http.ts +1 -1
- package/src/mcp-io.ts +118 -0
- package/src/mcp-reflect.ts +140 -0
- package/src/mcp-root.ts +62 -4
- package/src/mcp.ts +61 -1511
- package/src/openapi-tool.ts +32 -2
- package/src/openapi-types.ts +261 -0
- package/src/port.ts +2 -1
- package/src/project-facts.ts +2 -4
- package/src/release-verification.ts +22 -164
- package/src/rules/codes.ts +4 -0
- package/src/rules/islands.ts +171 -0
- package/src/rules/nano.ts +295 -0
- package/src/rules/security.ts +40 -4
- package/src/scaffold.ts +169 -9
- package/src/search-terms.ts +7 -3
- package/src/stylex-migrate.ts +1139 -0
- package/src/sync-manifest.ts +2 -1
- package/src/verification-plan.ts +242 -0
- package/src/work-graph.ts +6 -3
|
@@ -0,0 +1,1139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe, source-preserving Tailwind utility migration for StyleX.
|
|
3
|
+
*
|
|
4
|
+
* This is intentionally a bounded codemod rather than a Tailwind interpreter. Static utilities with
|
|
5
|
+
* a direct StyleX equivalent are rewritten; dynamic className expressions, parent-dependent variants,
|
|
6
|
+
* arbitrary values, and utilities outside the table are left untouched and reported.
|
|
7
|
+
*/
|
|
8
|
+
import { readdir, readFile, writeFile } from "node:fs/promises"
|
|
9
|
+
import { relative, resolve } from "node:path"
|
|
10
|
+
|
|
11
|
+
export interface MigrationIssue {
|
|
12
|
+
readonly file: string
|
|
13
|
+
readonly line: number
|
|
14
|
+
readonly token: string
|
|
15
|
+
readonly reason: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SourceMigration {
|
|
19
|
+
readonly source: string
|
|
20
|
+
readonly changed: boolean
|
|
21
|
+
readonly replacements: number
|
|
22
|
+
readonly issues: readonly MigrationIssue[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface StylexMigrationOptions {
|
|
26
|
+
readonly write?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StylexMigrationFile {
|
|
30
|
+
readonly file: string
|
|
31
|
+
readonly changed: boolean
|
|
32
|
+
readonly replacements: number
|
|
33
|
+
readonly written: boolean
|
|
34
|
+
readonly issues: readonly MigrationIssue[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface StylexMigrationResult {
|
|
38
|
+
readonly ok: boolean
|
|
39
|
+
readonly from: "tailwind"
|
|
40
|
+
readonly to: "stylex"
|
|
41
|
+
readonly write: boolean
|
|
42
|
+
readonly scanned: number
|
|
43
|
+
readonly changed: readonly string[]
|
|
44
|
+
readonly written: readonly string[]
|
|
45
|
+
readonly issues: readonly MigrationIssue[]
|
|
46
|
+
readonly files: readonly StylexMigrationFile[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface StyleObject {
|
|
50
|
+
readonly [key: string]: string | number | StyleObject
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface UtilityConversion {
|
|
54
|
+
readonly style?: StyleObject
|
|
55
|
+
readonly reason?: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface ClassAttribute {
|
|
59
|
+
readonly start: number
|
|
60
|
+
readonly end: number
|
|
61
|
+
readonly value?: string
|
|
62
|
+
readonly expression?: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const SOURCE_FILE = /\.[cm]?[jt]sx?$/
|
|
66
|
+
const SKIP_DIRECTORIES = new Set([
|
|
67
|
+
".git",
|
|
68
|
+
".next",
|
|
69
|
+
".nuxt",
|
|
70
|
+
".planning",
|
|
71
|
+
".turbo",
|
|
72
|
+
"build",
|
|
73
|
+
"coverage",
|
|
74
|
+
"dist",
|
|
75
|
+
"node_modules",
|
|
76
|
+
"out",
|
|
77
|
+
"public",
|
|
78
|
+
"vendor",
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
const BREAKPOINTS: Readonly<Record<string, string>> = {
|
|
82
|
+
sm: "640px",
|
|
83
|
+
md: "768px",
|
|
84
|
+
lg: "1024px",
|
|
85
|
+
xl: "1280px",
|
|
86
|
+
"2xl": "1536px",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const SPACING: Readonly<Record<string, string | number>> = {
|
|
90
|
+
"0": 0,
|
|
91
|
+
px: "1px",
|
|
92
|
+
"0.5": "0.125rem",
|
|
93
|
+
"1": "0.25rem",
|
|
94
|
+
"1.5": "0.375rem",
|
|
95
|
+
"2": "0.5rem",
|
|
96
|
+
"2.5": "0.625rem",
|
|
97
|
+
"3": "0.75rem",
|
|
98
|
+
"3.5": "0.875rem",
|
|
99
|
+
"4": "1rem",
|
|
100
|
+
"5": "1.25rem",
|
|
101
|
+
"6": "1.5rem",
|
|
102
|
+
"7": "1.75rem",
|
|
103
|
+
"8": "2rem",
|
|
104
|
+
"9": "2.25rem",
|
|
105
|
+
"10": "2.5rem",
|
|
106
|
+
"11": "2.75rem",
|
|
107
|
+
"12": "3rem",
|
|
108
|
+
"14": "3.5rem",
|
|
109
|
+
"16": "4rem",
|
|
110
|
+
"20": "5rem",
|
|
111
|
+
"24": "6rem",
|
|
112
|
+
"28": "7rem",
|
|
113
|
+
"32": "8rem",
|
|
114
|
+
"36": "9rem",
|
|
115
|
+
"40": "10rem",
|
|
116
|
+
"44": "11rem",
|
|
117
|
+
"48": "12rem",
|
|
118
|
+
"52": "13rem",
|
|
119
|
+
"56": "14rem",
|
|
120
|
+
"60": "15rem",
|
|
121
|
+
"64": "16rem",
|
|
122
|
+
"72": "18rem",
|
|
123
|
+
"80": "20rem",
|
|
124
|
+
"96": "24rem",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const FRACTIONS: Readonly<Record<string, string>> = {
|
|
128
|
+
"1/2": "50%",
|
|
129
|
+
"1/3": "33.333333%",
|
|
130
|
+
"2/3": "66.666667%",
|
|
131
|
+
"1/4": "25%",
|
|
132
|
+
"2/4": "50%",
|
|
133
|
+
"3/4": "75%",
|
|
134
|
+
"1/5": "20%",
|
|
135
|
+
"2/5": "40%",
|
|
136
|
+
"3/5": "60%",
|
|
137
|
+
"4/5": "80%",
|
|
138
|
+
"1/6": "16.666667%",
|
|
139
|
+
"2/6": "33.333333%",
|
|
140
|
+
"3/6": "50%",
|
|
141
|
+
"4/6": "66.666667%",
|
|
142
|
+
"5/6": "83.333333%",
|
|
143
|
+
"1/12": "8.333333%",
|
|
144
|
+
"2/12": "16.666667%",
|
|
145
|
+
"3/12": "25%",
|
|
146
|
+
"4/12": "33.333333%",
|
|
147
|
+
"5/12": "41.666667%",
|
|
148
|
+
"6/12": "50%",
|
|
149
|
+
"7/12": "58.333333%",
|
|
150
|
+
"8/12": "66.666667%",
|
|
151
|
+
"9/12": "75%",
|
|
152
|
+
"10/12": "83.333333%",
|
|
153
|
+
"11/12": "91.666667%",
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const COLORS: Readonly<Record<string, string>> = {
|
|
157
|
+
transparent: "transparent",
|
|
158
|
+
current: "currentColor",
|
|
159
|
+
inherit: "inherit",
|
|
160
|
+
black: "#000000",
|
|
161
|
+
white: "#ffffff",
|
|
162
|
+
"slate-50": "#f8fafc",
|
|
163
|
+
"slate-100": "#f1f5f9",
|
|
164
|
+
"slate-200": "#e2e8f0",
|
|
165
|
+
"slate-300": "#cbd5e1",
|
|
166
|
+
"slate-400": "#94a3b8",
|
|
167
|
+
"slate-500": "#64748b",
|
|
168
|
+
"slate-600": "#475569",
|
|
169
|
+
"slate-700": "#334155",
|
|
170
|
+
"slate-800": "#1e293b",
|
|
171
|
+
"slate-900": "#0f172a",
|
|
172
|
+
"slate-950": "#020617",
|
|
173
|
+
"gray-50": "#f9fafb",
|
|
174
|
+
"gray-100": "#f3f4f6",
|
|
175
|
+
"gray-200": "#e5e7eb",
|
|
176
|
+
"gray-300": "#d1d5db",
|
|
177
|
+
"gray-400": "#9ca3af",
|
|
178
|
+
"gray-500": "#6b7280",
|
|
179
|
+
"gray-600": "#4b5563",
|
|
180
|
+
"gray-700": "#374151",
|
|
181
|
+
"gray-800": "#1f2937",
|
|
182
|
+
"gray-900": "#111827",
|
|
183
|
+
"gray-950": "#030712",
|
|
184
|
+
"red-50": "#fef2f2",
|
|
185
|
+
"red-100": "#fee2e2",
|
|
186
|
+
"red-200": "#fecaca",
|
|
187
|
+
"red-300": "#fca5a5",
|
|
188
|
+
"red-400": "#f87171",
|
|
189
|
+
"red-500": "#ef4444",
|
|
190
|
+
"red-600": "#dc2626",
|
|
191
|
+
"red-700": "#b91c1c",
|
|
192
|
+
"red-800": "#991b1b",
|
|
193
|
+
"red-900": "#7f1d1d",
|
|
194
|
+
"red-950": "#450a0a",
|
|
195
|
+
"orange-50": "#fff7ed",
|
|
196
|
+
"orange-100": "#ffedd5",
|
|
197
|
+
"orange-200": "#fed7aa",
|
|
198
|
+
"orange-300": "#fdba74",
|
|
199
|
+
"orange-400": "#fb923c",
|
|
200
|
+
"orange-500": "#f97316",
|
|
201
|
+
"orange-600": "#ea580c",
|
|
202
|
+
"orange-700": "#c2410c",
|
|
203
|
+
"orange-800": "#9a3412",
|
|
204
|
+
"orange-900": "#7c2d12",
|
|
205
|
+
"orange-950": "#431407",
|
|
206
|
+
"amber-50": "#fffbeb",
|
|
207
|
+
"amber-100": "#fef3c7",
|
|
208
|
+
"amber-200": "#fde68a",
|
|
209
|
+
"amber-300": "#fcd34d",
|
|
210
|
+
"amber-400": "#fbbf24",
|
|
211
|
+
"amber-500": "#f59e0b",
|
|
212
|
+
"amber-600": "#d97706",
|
|
213
|
+
"amber-700": "#b45309",
|
|
214
|
+
"amber-800": "#92400e",
|
|
215
|
+
"amber-900": "#78350f",
|
|
216
|
+
"amber-950": "#451a03",
|
|
217
|
+
"yellow-50": "#fefce8",
|
|
218
|
+
"yellow-100": "#fef9c3",
|
|
219
|
+
"yellow-200": "#fef08a",
|
|
220
|
+
"yellow-300": "#fde047",
|
|
221
|
+
"yellow-400": "#facc15",
|
|
222
|
+
"yellow-500": "#eab308",
|
|
223
|
+
"yellow-600": "#ca8a04",
|
|
224
|
+
"yellow-700": "#a16207",
|
|
225
|
+
"yellow-800": "#854d0e",
|
|
226
|
+
"yellow-900": "#713f12",
|
|
227
|
+
"yellow-950": "#422006",
|
|
228
|
+
"green-50": "#f0fdf4",
|
|
229
|
+
"green-100": "#dcfce7",
|
|
230
|
+
"green-200": "#bbf7d0",
|
|
231
|
+
"green-300": "#86efac",
|
|
232
|
+
"green-400": "#4ade80",
|
|
233
|
+
"green-500": "#22c55e",
|
|
234
|
+
"green-600": "#16a34a",
|
|
235
|
+
"green-700": "#15803d",
|
|
236
|
+
"green-800": "#166534",
|
|
237
|
+
"green-900": "#14532d",
|
|
238
|
+
"green-950": "#052e16",
|
|
239
|
+
"emerald-50": "#ecfdf5",
|
|
240
|
+
"emerald-100": "#d1fae5",
|
|
241
|
+
"emerald-200": "#a7f3d0",
|
|
242
|
+
"emerald-300": "#6ee7b7",
|
|
243
|
+
"emerald-400": "#34d399",
|
|
244
|
+
"emerald-500": "#10b981",
|
|
245
|
+
"emerald-600": "#059669",
|
|
246
|
+
"emerald-700": "#047857",
|
|
247
|
+
"emerald-800": "#065f46",
|
|
248
|
+
"emerald-900": "#064e3b",
|
|
249
|
+
"emerald-950": "#022c22",
|
|
250
|
+
"teal-50": "#f0fdfa",
|
|
251
|
+
"teal-100": "#ccfbf1",
|
|
252
|
+
"teal-200": "#99f6e4",
|
|
253
|
+
"teal-300": "#5eead4",
|
|
254
|
+
"teal-400": "#2dd4bf",
|
|
255
|
+
"teal-500": "#14b8a6",
|
|
256
|
+
"teal-600": "#0d9488",
|
|
257
|
+
"teal-700": "#0f766e",
|
|
258
|
+
"teal-800": "#115e59",
|
|
259
|
+
"teal-900": "#134e4a",
|
|
260
|
+
"teal-950": "#042f2e",
|
|
261
|
+
"cyan-50": "#ecfeff",
|
|
262
|
+
"cyan-100": "#cffafe",
|
|
263
|
+
"cyan-200": "#a5f3fc",
|
|
264
|
+
"cyan-300": "#67e8f9",
|
|
265
|
+
"cyan-400": "#22d3ee",
|
|
266
|
+
"cyan-500": "#06b6d4",
|
|
267
|
+
"cyan-600": "#0891b2",
|
|
268
|
+
"cyan-700": "#0e7490",
|
|
269
|
+
"cyan-800": "#155e75",
|
|
270
|
+
"cyan-900": "#164e63",
|
|
271
|
+
"cyan-950": "#083344",
|
|
272
|
+
"blue-50": "#eff6ff",
|
|
273
|
+
"blue-100": "#dbeafe",
|
|
274
|
+
"blue-200": "#bfdbfe",
|
|
275
|
+
"blue-300": "#93c5fd",
|
|
276
|
+
"blue-400": "#60a5fa",
|
|
277
|
+
"blue-500": "#3b82f6",
|
|
278
|
+
"blue-600": "#2563eb",
|
|
279
|
+
"blue-700": "#1d4ed8",
|
|
280
|
+
"blue-800": "#1e40af",
|
|
281
|
+
"blue-900": "#1e3a8a",
|
|
282
|
+
"blue-950": "#172554",
|
|
283
|
+
"indigo-50": "#eef2ff",
|
|
284
|
+
"indigo-100": "#e0e7ff",
|
|
285
|
+
"indigo-200": "#c7d2fe",
|
|
286
|
+
"indigo-300": "#a5b4fc",
|
|
287
|
+
"indigo-400": "#818cf8",
|
|
288
|
+
"indigo-500": "#6366f1",
|
|
289
|
+
"indigo-600": "#4f46e5",
|
|
290
|
+
"indigo-700": "#4338ca",
|
|
291
|
+
"indigo-800": "#3730a3",
|
|
292
|
+
"indigo-900": "#312e81",
|
|
293
|
+
"indigo-950": "#1e1b4b",
|
|
294
|
+
"violet-50": "#f5f3ff",
|
|
295
|
+
"violet-100": "#ede9fe",
|
|
296
|
+
"violet-200": "#ddd6fe",
|
|
297
|
+
"violet-300": "#c4b5fd",
|
|
298
|
+
"violet-400": "#a78bfa",
|
|
299
|
+
"violet-500": "#8b5cf6",
|
|
300
|
+
"violet-600": "#7c3aed",
|
|
301
|
+
"violet-700": "#6d28d9",
|
|
302
|
+
"violet-800": "#5b21b6",
|
|
303
|
+
"violet-900": "#4c1d95",
|
|
304
|
+
"violet-950": "#2e1065",
|
|
305
|
+
"purple-50": "#faf5ff",
|
|
306
|
+
"purple-100": "#f3e8ff",
|
|
307
|
+
"purple-200": "#e9d5ff",
|
|
308
|
+
"purple-300": "#d8b4fe",
|
|
309
|
+
"purple-400": "#c084fc",
|
|
310
|
+
"purple-500": "#a855f7",
|
|
311
|
+
"purple-600": "#9333ea",
|
|
312
|
+
"purple-700": "#7e22ce",
|
|
313
|
+
"purple-800": "#6b21a8",
|
|
314
|
+
"purple-900": "#581c87",
|
|
315
|
+
"purple-950": "#3b0764",
|
|
316
|
+
"pink-50": "#fdf2f8",
|
|
317
|
+
"pink-100": "#fce7f3",
|
|
318
|
+
"pink-200": "#fbcfe8",
|
|
319
|
+
"pink-300": "#f9a8d4",
|
|
320
|
+
"pink-400": "#f472b6",
|
|
321
|
+
"pink-500": "#ec4899",
|
|
322
|
+
"pink-600": "#db2777",
|
|
323
|
+
"pink-700": "#be185d",
|
|
324
|
+
"pink-800": "#9d174d",
|
|
325
|
+
"pink-900": "#831843",
|
|
326
|
+
"pink-950": "#500724",
|
|
327
|
+
"rose-50": "#fff1f2",
|
|
328
|
+
"rose-100": "#ffe4e6",
|
|
329
|
+
"rose-200": "#fecdd3",
|
|
330
|
+
"rose-300": "#fda4af",
|
|
331
|
+
"rose-400": "#fb7185",
|
|
332
|
+
"rose-500": "#f43f5e",
|
|
333
|
+
"rose-600": "#e11d48",
|
|
334
|
+
"rose-700": "#be123c",
|
|
335
|
+
"rose-800": "#9f1239",
|
|
336
|
+
"rose-900": "#881337",
|
|
337
|
+
"rose-950": "#4c0519",
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const STATIC_STYLES: Readonly<Record<string, StyleObject>> = {
|
|
341
|
+
block: { display: "block" },
|
|
342
|
+
"inline-block": { display: "inline-block" },
|
|
343
|
+
inline: { display: "inline" },
|
|
344
|
+
flex: { display: "flex" },
|
|
345
|
+
"inline-flex": { display: "inline-flex" },
|
|
346
|
+
grid: { display: "grid" },
|
|
347
|
+
"inline-grid": { display: "inline-grid" },
|
|
348
|
+
contents: { display: "contents" },
|
|
349
|
+
"flow-root": { display: "flow-root" },
|
|
350
|
+
hidden: { display: "none" },
|
|
351
|
+
static: { position: "static" },
|
|
352
|
+
fixed: { position: "fixed" },
|
|
353
|
+
absolute: { position: "absolute" },
|
|
354
|
+
relative: { position: "relative" },
|
|
355
|
+
sticky: { position: "sticky" },
|
|
356
|
+
"flex-row": { flexDirection: "row" },
|
|
357
|
+
"flex-row-reverse": { flexDirection: "row-reverse" },
|
|
358
|
+
"flex-col": { flexDirection: "column" },
|
|
359
|
+
"flex-col-reverse": { flexDirection: "column-reverse" },
|
|
360
|
+
"flex-wrap": { flexWrap: "wrap" },
|
|
361
|
+
"flex-wrap-reverse": { flexWrap: "wrap-reverse" },
|
|
362
|
+
"flex-nowrap": { flexWrap: "nowrap" },
|
|
363
|
+
"flex-1": { flex: "1 1 0%" },
|
|
364
|
+
"flex-auto": { flex: "1 1 auto" },
|
|
365
|
+
"flex-initial": { flex: "0 1 auto" },
|
|
366
|
+
"flex-none": { flex: "none" },
|
|
367
|
+
grow: { flexGrow: 1 },
|
|
368
|
+
"grow-0": { flexGrow: 0 },
|
|
369
|
+
shrink: { flexShrink: 1 },
|
|
370
|
+
"shrink-0": { flexShrink: 0 },
|
|
371
|
+
"justify-normal": { justifyContent: "normal" },
|
|
372
|
+
"justify-start": { justifyContent: "flex-start" },
|
|
373
|
+
"justify-end": { justifyContent: "flex-end" },
|
|
374
|
+
"justify-center": { justifyContent: "center" },
|
|
375
|
+
"justify-between": { justifyContent: "space-between" },
|
|
376
|
+
"justify-around": { justifyContent: "space-around" },
|
|
377
|
+
"justify-evenly": { justifyContent: "space-evenly" },
|
|
378
|
+
"justify-stretch": { justifyContent: "stretch" },
|
|
379
|
+
"items-start": { alignItems: "flex-start" },
|
|
380
|
+
"items-end": { alignItems: "flex-end" },
|
|
381
|
+
"items-center": { alignItems: "center" },
|
|
382
|
+
"items-baseline": { alignItems: "baseline" },
|
|
383
|
+
"items-stretch": { alignItems: "stretch" },
|
|
384
|
+
"self-auto": { alignSelf: "auto" },
|
|
385
|
+
"self-start": { alignSelf: "flex-start" },
|
|
386
|
+
"self-end": { alignSelf: "flex-end" },
|
|
387
|
+
"self-center": { alignSelf: "center" },
|
|
388
|
+
"self-stretch": { alignSelf: "stretch" },
|
|
389
|
+
"place-items-start": { placeItems: "start" },
|
|
390
|
+
"place-items-end": { placeItems: "end" },
|
|
391
|
+
"place-items-center": { placeItems: "center" },
|
|
392
|
+
"place-items-stretch": { placeItems: "stretch" },
|
|
393
|
+
"place-content-start": { placeContent: "start" },
|
|
394
|
+
"place-content-end": { placeContent: "end" },
|
|
395
|
+
"place-content-center": { placeContent: "center" },
|
|
396
|
+
"place-content-between": { placeContent: "space-between" },
|
|
397
|
+
"place-content-around": { placeContent: "space-around" },
|
|
398
|
+
"place-content-evenly": { placeContent: "space-evenly" },
|
|
399
|
+
"place-content-stretch": { placeContent: "stretch" },
|
|
400
|
+
"text-left": { textAlign: "left" },
|
|
401
|
+
"text-center": { textAlign: "center" },
|
|
402
|
+
"text-right": { textAlign: "right" },
|
|
403
|
+
"text-justify": { textAlign: "justify" },
|
|
404
|
+
"text-start": { textAlign: "start" },
|
|
405
|
+
"text-end": { textAlign: "end" },
|
|
406
|
+
italic: { fontStyle: "italic" },
|
|
407
|
+
"not-italic": { fontStyle: "normal" },
|
|
408
|
+
uppercase: { textTransform: "uppercase" },
|
|
409
|
+
lowercase: { textTransform: "lowercase" },
|
|
410
|
+
capitalize: { textTransform: "capitalize" },
|
|
411
|
+
"normal-case": { textTransform: "none" },
|
|
412
|
+
underline: { textDecorationLine: "underline" },
|
|
413
|
+
overline: { textDecorationLine: "overline" },
|
|
414
|
+
"line-through": { textDecorationLine: "line-through" },
|
|
415
|
+
"no-underline": { textDecorationLine: "none" },
|
|
416
|
+
"whitespace-normal": { whiteSpace: "normal" },
|
|
417
|
+
"whitespace-nowrap": { whiteSpace: "nowrap" },
|
|
418
|
+
"whitespace-pre": { whiteSpace: "pre" },
|
|
419
|
+
"whitespace-pre-line": { whiteSpace: "pre-line" },
|
|
420
|
+
"whitespace-pre-wrap": { whiteSpace: "pre-wrap" },
|
|
421
|
+
"break-normal": { overflowWrap: "normal", wordBreak: "normal" },
|
|
422
|
+
"break-words": { overflowWrap: "break-word" },
|
|
423
|
+
"break-all": { wordBreak: "break-all" },
|
|
424
|
+
truncate: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
425
|
+
"overflow-auto": { overflow: "auto" },
|
|
426
|
+
"overflow-hidden": { overflow: "hidden" },
|
|
427
|
+
"overflow-clip": { overflow: "clip" },
|
|
428
|
+
"overflow-visible": { overflow: "visible" },
|
|
429
|
+
"overflow-scroll": { overflow: "scroll" },
|
|
430
|
+
"object-contain": { objectFit: "contain" },
|
|
431
|
+
"object-cover": { objectFit: "cover" },
|
|
432
|
+
"object-fill": { objectFit: "fill" },
|
|
433
|
+
"object-none": { objectFit: "none" },
|
|
434
|
+
"object-scale-down": { objectFit: "scale-down" },
|
|
435
|
+
"bg-fixed": { backgroundAttachment: "fixed" },
|
|
436
|
+
"bg-local": { backgroundAttachment: "local" },
|
|
437
|
+
"bg-scroll": { backgroundAttachment: "scroll" },
|
|
438
|
+
"bg-cover": { backgroundSize: "cover" },
|
|
439
|
+
"bg-contain": { backgroundSize: "contain" },
|
|
440
|
+
"bg-center": { backgroundPosition: "center" },
|
|
441
|
+
"bg-top": { backgroundPosition: "top" },
|
|
442
|
+
"bg-right": { backgroundPosition: "right" },
|
|
443
|
+
"bg-bottom": { backgroundPosition: "bottom" },
|
|
444
|
+
"bg-left": { backgroundPosition: "left" },
|
|
445
|
+
"border-solid": { borderStyle: "solid" },
|
|
446
|
+
"border-dashed": { borderStyle: "dashed" },
|
|
447
|
+
"border-dotted": { borderStyle: "dotted" },
|
|
448
|
+
"border-double": { borderStyle: "double" },
|
|
449
|
+
"border-hidden": { borderStyle: "hidden" },
|
|
450
|
+
"border-none": { borderStyle: "none" },
|
|
451
|
+
"rounded-none": { borderRadius: "0px" },
|
|
452
|
+
rounded: { borderRadius: "0.25rem" },
|
|
453
|
+
"rounded-sm": { borderRadius: "0.125rem" },
|
|
454
|
+
"rounded-md": { borderRadius: "0.375rem" },
|
|
455
|
+
"rounded-lg": { borderRadius: "0.5rem" },
|
|
456
|
+
"rounded-xl": { borderRadius: "0.75rem" },
|
|
457
|
+
"rounded-2xl": { borderRadius: "1rem" },
|
|
458
|
+
"rounded-3xl": { borderRadius: "1.5rem" },
|
|
459
|
+
"rounded-full": { borderRadius: "9999px" },
|
|
460
|
+
"shadow-sm": { boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)" },
|
|
461
|
+
shadow: { boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)" },
|
|
462
|
+
"shadow-md": { boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)" },
|
|
463
|
+
"shadow-lg": { boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)" },
|
|
464
|
+
"shadow-xl": { boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)" },
|
|
465
|
+
"shadow-2xl": { boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.25)" },
|
|
466
|
+
"shadow-inner": { boxShadow: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)" },
|
|
467
|
+
"shadow-none": { boxShadow: "0 0 #0000" },
|
|
468
|
+
"transition-none": { transitionProperty: "none" },
|
|
469
|
+
"transition-all": {
|
|
470
|
+
transitionProperty: "all",
|
|
471
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
472
|
+
transitionDuration: "150ms",
|
|
473
|
+
},
|
|
474
|
+
"transition-colors": {
|
|
475
|
+
transitionProperty:
|
|
476
|
+
"color, background-color, border-color, text-decoration-color, fill, stroke",
|
|
477
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
478
|
+
transitionDuration: "150ms",
|
|
479
|
+
},
|
|
480
|
+
"transition-opacity": {
|
|
481
|
+
transitionProperty: "opacity",
|
|
482
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
483
|
+
transitionDuration: "150ms",
|
|
484
|
+
},
|
|
485
|
+
"transition-shadow": {
|
|
486
|
+
transitionProperty: "box-shadow",
|
|
487
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
488
|
+
transitionDuration: "150ms",
|
|
489
|
+
},
|
|
490
|
+
"transition-transform": {
|
|
491
|
+
transitionProperty: "transform",
|
|
492
|
+
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
493
|
+
transitionDuration: "150ms",
|
|
494
|
+
},
|
|
495
|
+
"cursor-auto": { cursor: "auto" },
|
|
496
|
+
"cursor-default": { cursor: "default" },
|
|
497
|
+
"cursor-pointer": { cursor: "pointer" },
|
|
498
|
+
"cursor-wait": { cursor: "wait" },
|
|
499
|
+
"cursor-text": { cursor: "text" },
|
|
500
|
+
"cursor-move": { cursor: "move" },
|
|
501
|
+
"cursor-not-allowed": { cursor: "not-allowed" },
|
|
502
|
+
"select-none": { userSelect: "none" },
|
|
503
|
+
"select-text": { userSelect: "text" },
|
|
504
|
+
"select-all": { userSelect: "all" },
|
|
505
|
+
"select-auto": { userSelect: "auto" },
|
|
506
|
+
visible: { visibility: "visible" },
|
|
507
|
+
invisible: { visibility: "hidden" },
|
|
508
|
+
collapse: { visibility: "collapse" },
|
|
509
|
+
"appearance-none": { appearance: "none" },
|
|
510
|
+
"pointer-events-none": { pointerEvents: "none" },
|
|
511
|
+
"pointer-events-auto": { pointerEvents: "auto" },
|
|
512
|
+
"resize-none": { resize: "none" },
|
|
513
|
+
"resize-y": { resize: "vertical" },
|
|
514
|
+
"resize-x": { resize: "horizontal" },
|
|
515
|
+
resize: { resize: "both" },
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const FONT_SIZES: Readonly<Record<string, readonly [string, string]>> = {
|
|
519
|
+
xs: ["0.75rem", "1rem"],
|
|
520
|
+
sm: ["0.875rem", "1.25rem"],
|
|
521
|
+
base: ["1rem", "1.5rem"],
|
|
522
|
+
lg: ["1.125rem", "1.75rem"],
|
|
523
|
+
xl: ["1.25rem", "1.75rem"],
|
|
524
|
+
"2xl": ["1.5rem", "2rem"],
|
|
525
|
+
"3xl": ["1.875rem", "2.25rem"],
|
|
526
|
+
"4xl": ["2.25rem", "2.5rem"],
|
|
527
|
+
"5xl": ["3rem", "1"],
|
|
528
|
+
"6xl": ["3.75rem", "1"],
|
|
529
|
+
"7xl": ["4.5rem", "1"],
|
|
530
|
+
"8xl": ["6rem", "1"],
|
|
531
|
+
"9xl": ["8rem", "1"],
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const FONT_WEIGHTS: Readonly<Record<string, number>> = {
|
|
535
|
+
thin: 100,
|
|
536
|
+
extralight: 200,
|
|
537
|
+
light: 300,
|
|
538
|
+
normal: 400,
|
|
539
|
+
medium: 500,
|
|
540
|
+
semibold: 600,
|
|
541
|
+
bold: 700,
|
|
542
|
+
extrabold: 800,
|
|
543
|
+
black: 900,
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const LINE_HEIGHTS: Readonly<Record<string, string>> = {
|
|
547
|
+
none: "1",
|
|
548
|
+
tight: "1.25",
|
|
549
|
+
snug: "1.375",
|
|
550
|
+
normal: "1.5",
|
|
551
|
+
relaxed: "1.625",
|
|
552
|
+
loose: "2",
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const MAX_WIDTHS: Readonly<Record<string, string>> = {
|
|
556
|
+
none: "none",
|
|
557
|
+
0: "0rem",
|
|
558
|
+
xs: "20rem",
|
|
559
|
+
sm: "24rem",
|
|
560
|
+
md: "28rem",
|
|
561
|
+
lg: "32rem",
|
|
562
|
+
xl: "36rem",
|
|
563
|
+
"2xl": "42rem",
|
|
564
|
+
"3xl": "48rem",
|
|
565
|
+
"4xl": "56rem",
|
|
566
|
+
"5xl": "64rem",
|
|
567
|
+
"6xl": "72rem",
|
|
568
|
+
"7xl": "80rem",
|
|
569
|
+
full: "100%",
|
|
570
|
+
min: "min-content",
|
|
571
|
+
max: "max-content",
|
|
572
|
+
fit: "fit-content",
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const PSEUDO_VARIANTS: Readonly<Record<string, string>> = {
|
|
576
|
+
hover: ":hover",
|
|
577
|
+
focus: ":focus",
|
|
578
|
+
"focus-visible": ":focus-visible",
|
|
579
|
+
active: ":active",
|
|
580
|
+
visited: ":visited",
|
|
581
|
+
target: ":target",
|
|
582
|
+
enabled: ":enabled",
|
|
583
|
+
disabled: ":disabled",
|
|
584
|
+
checked: ":checked",
|
|
585
|
+
indeterminate: ":indeterminate",
|
|
586
|
+
default: ":default",
|
|
587
|
+
required: ":required",
|
|
588
|
+
valid: ":valid",
|
|
589
|
+
invalid: ":invalid",
|
|
590
|
+
placeholder: "::placeholder",
|
|
591
|
+
"placeholder-shown": ":placeholder-shown",
|
|
592
|
+
autofill: ":autofill",
|
|
593
|
+
"read-only": ":read-only",
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const CLASS_ATTRIBUTE =
|
|
597
|
+
/\bclassName\s*=\s*(?:"([^"]*)"|'([^']*)'|\{\s*(?:"([^"]*)"|'([^']*)')\s*\}|\{([\s\S]*?)\})/g
|
|
598
|
+
|
|
599
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
600
|
+
typeof value === "object" && value !== null
|
|
601
|
+
|
|
602
|
+
const cloneStyle = (style: StyleObject): StyleObject => {
|
|
603
|
+
const result: Record<string, string | number | StyleObject> = {}
|
|
604
|
+
for (const [key, value] of Object.entries(style))
|
|
605
|
+
result[key] = isRecord(value) ? cloneStyle(value as StyleObject) : (value as string | number)
|
|
606
|
+
return result
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const withVariants = (style: StyleObject, variants: readonly string[]): UtilityConversion => {
|
|
610
|
+
let result = cloneStyle(style)
|
|
611
|
+
for (const variant of [...variants].reverse()) {
|
|
612
|
+
const breakpoint = BREAKPOINTS[variant]
|
|
613
|
+
if (breakpoint !== undefined) {
|
|
614
|
+
result = { [`@media (min-width: ${breakpoint})`]: result }
|
|
615
|
+
continue
|
|
616
|
+
}
|
|
617
|
+
const pseudo = PSEUDO_VARIANTS[variant]
|
|
618
|
+
if (pseudo !== undefined) {
|
|
619
|
+
result = { [pseudo]: result }
|
|
620
|
+
continue
|
|
621
|
+
}
|
|
622
|
+
if (variant === "motion-safe") {
|
|
623
|
+
result = { "@media (prefers-reduced-motion: no-preference)": result }
|
|
624
|
+
continue
|
|
625
|
+
}
|
|
626
|
+
if (variant === "motion-reduce") {
|
|
627
|
+
result = { "@media (prefers-reduced-motion: reduce)": result }
|
|
628
|
+
continue
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
reason: `variant '${variant}:' depends on parent state, arbitrary selectors, or runtime configuration`,
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return { style: result }
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const negativeValue = (value: string | number, negative: boolean): string | number => {
|
|
638
|
+
if (!negative) return value
|
|
639
|
+
if (typeof value === "number") return -value
|
|
640
|
+
if (value === "0") return 0
|
|
641
|
+
return value.startsWith("-") ? value.slice(1) : `-${value}`
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const spacingValue = (value: string, negative: boolean): string | number | undefined => {
|
|
645
|
+
if (value === "auto") return negative ? undefined : "auto"
|
|
646
|
+
const spacing = SPACING[value]
|
|
647
|
+
return spacing === undefined ? undefined : negativeValue(spacing, negative)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const dimensionValue = (value: string, axis: "w" | "h"): string | number | undefined => {
|
|
651
|
+
if (value === "auto") return "auto"
|
|
652
|
+
if (value === "full") return "100%"
|
|
653
|
+
if (value === "screen") return axis === "h" ? "100vh" : "100vw"
|
|
654
|
+
if (value === "svw") return "100svw"
|
|
655
|
+
if (value === "lvw") return "100lvw"
|
|
656
|
+
if (value === "dvw") return "100dvw"
|
|
657
|
+
if (value === "svh") return "100svh"
|
|
658
|
+
if (value === "lvh") return "100lvh"
|
|
659
|
+
if (value === "dvh") return "100dvh"
|
|
660
|
+
if (value === "min") return "min-content"
|
|
661
|
+
if (value === "max") return "max-content"
|
|
662
|
+
if (value === "fit") return "fit-content"
|
|
663
|
+
return FRACTIONS[value] ?? SPACING[value]
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const colorValue = (value: string): string | undefined => COLORS[value]
|
|
667
|
+
|
|
668
|
+
const directionalProperty = (
|
|
669
|
+
prefix: string,
|
|
670
|
+
axis: string | undefined,
|
|
671
|
+
): readonly string[] | undefined => {
|
|
672
|
+
const properties: Readonly<Record<string, readonly string[]>> = {
|
|
673
|
+
p: ["padding"],
|
|
674
|
+
px: ["paddingInline"],
|
|
675
|
+
py: ["paddingBlock"],
|
|
676
|
+
pt: ["paddingTop"],
|
|
677
|
+
pr: ["paddingRight"],
|
|
678
|
+
pb: ["paddingBottom"],
|
|
679
|
+
pl: ["paddingLeft"],
|
|
680
|
+
ps: ["paddingInlineStart"],
|
|
681
|
+
pe: ["paddingInlineEnd"],
|
|
682
|
+
m: ["margin"],
|
|
683
|
+
mx: ["marginInline"],
|
|
684
|
+
my: ["marginBlock"],
|
|
685
|
+
mt: ["marginTop"],
|
|
686
|
+
mr: ["marginRight"],
|
|
687
|
+
mb: ["marginBottom"],
|
|
688
|
+
ml: ["marginLeft"],
|
|
689
|
+
ms: ["marginInlineStart"],
|
|
690
|
+
me: ["marginInlineEnd"],
|
|
691
|
+
inset: ["inset"],
|
|
692
|
+
"inset-x": ["insetInline"],
|
|
693
|
+
"inset-y": ["insetBlock"],
|
|
694
|
+
start: ["insetInlineStart"],
|
|
695
|
+
end: ["insetInlineEnd"],
|
|
696
|
+
top: ["top"],
|
|
697
|
+
right: ["right"],
|
|
698
|
+
bottom: ["bottom"],
|
|
699
|
+
left: ["left"],
|
|
700
|
+
}
|
|
701
|
+
if (axis === undefined) return properties[prefix]
|
|
702
|
+
return properties[`${prefix}-${axis}`]
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const convertBase = (token: string, negative: boolean): UtilityConversion => {
|
|
706
|
+
const staticStyle = STATIC_STYLES[token]
|
|
707
|
+
if (staticStyle !== undefined) {
|
|
708
|
+
if (negative) return { reason: `negative utility '${token}' has no safe StyleX equivalent` }
|
|
709
|
+
return { style: staticStyle }
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const spacing =
|
|
713
|
+
/^(p|px|py|pt|pr|pb|pl|ps|pe|m|mx|my|mt|mr|mb|ml|ms|me|inset|start|end|top|right|bottom|left)(?:-(.+))$/.exec(
|
|
714
|
+
token,
|
|
715
|
+
)
|
|
716
|
+
if (spacing !== null) {
|
|
717
|
+
const value = spacingValue(spacing[2] as string, negative)
|
|
718
|
+
const properties = directionalProperty(spacing[1] as string, undefined)
|
|
719
|
+
if (value !== undefined && properties !== undefined) {
|
|
720
|
+
return { style: Object.fromEntries(properties.map((property) => [property, value])) }
|
|
721
|
+
}
|
|
722
|
+
return { reason: `spacing value '${spacing[2]}' is not in the safe static scale` }
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const gap = /^(gap|gap-x|gap-y)-(.+)$/.exec(token)
|
|
726
|
+
if (gap !== null) {
|
|
727
|
+
const value = spacingValue(gap[2] as string, negative)
|
|
728
|
+
if (value !== undefined) {
|
|
729
|
+
const property = gap[1] === "gap-x" ? "columnGap" : gap[1] === "gap-y" ? "rowGap" : "gap"
|
|
730
|
+
return { style: { [property]: value } }
|
|
731
|
+
}
|
|
732
|
+
return { reason: `gap value '${gap[2]}' is not in the safe static scale` }
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const size = /^(w|h|min-w|min-h|max-w|max-h)-(.+)$/.exec(token)
|
|
736
|
+
if (size !== null) {
|
|
737
|
+
const rawValue = size[2] as string
|
|
738
|
+
const axis = size[1]?.endsWith("h") ? "h" : "w"
|
|
739
|
+
const value = size[1] === "max-w" ? MAX_WIDTHS[rawValue] : dimensionValue(rawValue, axis)
|
|
740
|
+
if (value !== undefined) {
|
|
741
|
+
const property =
|
|
742
|
+
size[1] === "w"
|
|
743
|
+
? "width"
|
|
744
|
+
: size[1] === "h"
|
|
745
|
+
? "height"
|
|
746
|
+
: size[1] === "min-w"
|
|
747
|
+
? "minWidth"
|
|
748
|
+
: size[1] === "min-h"
|
|
749
|
+
? "minHeight"
|
|
750
|
+
: size[1] === "max-w"
|
|
751
|
+
? "maxWidth"
|
|
752
|
+
: "maxHeight"
|
|
753
|
+
return { style: { [property]: negativeValue(value, negative) } }
|
|
754
|
+
}
|
|
755
|
+
return { reason: `size value '${rawValue}' is not in the safe static scale` }
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const inset = /^(inset|inset-x|inset-y|start|end|top|right|bottom|left)-(.+)$/.exec(token)
|
|
759
|
+
if (inset !== null) {
|
|
760
|
+
const value = spacingValue(inset[2] as string, negative)
|
|
761
|
+
const properties = directionalProperty(inset[1] as string, undefined)
|
|
762
|
+
if (value !== undefined && properties !== undefined)
|
|
763
|
+
return { style: Object.fromEntries(properties.map((property) => [property, value])) }
|
|
764
|
+
return { reason: `inset value '${inset[2]}' is not in the safe static scale` }
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const zIndex = /^z-(.+)$/.exec(token)
|
|
768
|
+
if (zIndex !== null && (zIndex[1] === "auto" || /^\d+$/.test(zIndex[1] as string)))
|
|
769
|
+
return { style: { zIndex: zIndex[1] === "auto" ? "auto" : Number(zIndex[1]) } }
|
|
770
|
+
|
|
771
|
+
const order = /^order-(.+)$/.exec(token)
|
|
772
|
+
if (order !== null) {
|
|
773
|
+
const value = { first: -9999, last: 9999, none: 0 }[order[1] as "first" | "last" | "none"]
|
|
774
|
+
if (value !== undefined) return { style: { order: value } }
|
|
775
|
+
if (/^\d+$/.test(order[1] as string)) return { style: { order: Number(order[1]) } }
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const textSize = /^text-(.+)$/.exec(token)
|
|
779
|
+
if (textSize !== null) {
|
|
780
|
+
const size = FONT_SIZES[textSize[1] as string]
|
|
781
|
+
if (size !== undefined) return { style: { fontSize: size[0], lineHeight: size[1] } }
|
|
782
|
+
const color = colorValue(textSize[1] as string)
|
|
783
|
+
if (color !== undefined) return { style: { color } }
|
|
784
|
+
return {
|
|
785
|
+
reason: `text utility '${textSize[1]}' is not in the safe static typography/color set`,
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const background = /^bg-(.+)$/.exec(token)
|
|
790
|
+
if (background !== null) {
|
|
791
|
+
const color = colorValue(background[1] as string)
|
|
792
|
+
if (color !== undefined) return { style: { backgroundColor: color } }
|
|
793
|
+
return { reason: `background utility '${background[1]}' is not in the safe static color set` }
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const font = /^font-(.+)$/.exec(token)
|
|
797
|
+
if (font !== null) {
|
|
798
|
+
const weight = FONT_WEIGHTS[font[1] as string]
|
|
799
|
+
if (weight !== undefined) return { style: { fontWeight: weight } }
|
|
800
|
+
if (font[1] === "sans") return { style: { fontFamily: "ui-sans-serif, system-ui, sans-serif" } }
|
|
801
|
+
if (font[1] === "serif") return { style: { fontFamily: "ui-serif, Georgia, serif" } }
|
|
802
|
+
if (font[1] === "mono")
|
|
803
|
+
return { style: { fontFamily: "ui-monospace, SFMono-Regular, monospace" } }
|
|
804
|
+
return { reason: `font utility '${font[1]}' is not in the safe static typography set` }
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const leading = /^leading-(.+)$/.exec(token)
|
|
808
|
+
if (leading !== null) {
|
|
809
|
+
const value = LINE_HEIGHTS[leading[1] as string] ?? spacingValue(leading[1] as string, false)
|
|
810
|
+
if (value !== undefined) return { style: { lineHeight: value } }
|
|
811
|
+
return { reason: `line-height value '${leading[1]}' is not in the safe static scale` }
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const tracking = /^tracking-(.+)$/.exec(token)
|
|
815
|
+
if (tracking !== null) {
|
|
816
|
+
const values: Readonly<Record<string, string>> = {
|
|
817
|
+
tighter: "-0.05em",
|
|
818
|
+
tight: "-0.025em",
|
|
819
|
+
normal: "0em",
|
|
820
|
+
wide: "0.025em",
|
|
821
|
+
wider: "0.05em",
|
|
822
|
+
widest: "0.1em",
|
|
823
|
+
}
|
|
824
|
+
const value = values[tracking[1] as string]
|
|
825
|
+
if (value !== undefined) return { style: { letterSpacing: value } }
|
|
826
|
+
return { reason: `letter-spacing value '${tracking[1]}' is not in the safe static set` }
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const opacity = /^opacity-(\d+)$/.exec(token)
|
|
830
|
+
if (opacity !== null && Number(opacity[1]) <= 100)
|
|
831
|
+
return { style: { opacity: Number(opacity[1]) / 100 } }
|
|
832
|
+
|
|
833
|
+
const borderWidth = /^(border(?:-[trblxyse])?)(?:-(0|2|4|8))?$/.exec(token)
|
|
834
|
+
if (borderWidth !== null) {
|
|
835
|
+
const value = borderWidth[2] === undefined ? "1px" : `${borderWidth[2]}px`
|
|
836
|
+
const properties: Readonly<Record<string, string>> = {
|
|
837
|
+
border: "borderWidth",
|
|
838
|
+
"border-x": "borderInlineWidth",
|
|
839
|
+
"border-y": "borderBlockWidth",
|
|
840
|
+
"border-t": "borderTopWidth",
|
|
841
|
+
"border-r": "borderRightWidth",
|
|
842
|
+
"border-b": "borderBottomWidth",
|
|
843
|
+
"border-l": "borderLeftWidth",
|
|
844
|
+
"border-s": "borderInlineStartWidth",
|
|
845
|
+
"border-e": "borderInlineEndWidth",
|
|
846
|
+
}
|
|
847
|
+
const property = properties[borderWidth[1] as string]
|
|
848
|
+
if (property === undefined)
|
|
849
|
+
return { reason: `border utility '${token}' has no safe StyleX property mapping` }
|
|
850
|
+
return { style: { [property]: value } }
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const borderColor = /^(border(?:-[trblxyse])?)-(.+)$/.exec(token)
|
|
854
|
+
if (borderColor !== null) {
|
|
855
|
+
const color = colorValue(borderColor[2] as string)
|
|
856
|
+
if (color !== undefined) {
|
|
857
|
+
const properties: Readonly<Record<string, string>> = {
|
|
858
|
+
border: "borderColor",
|
|
859
|
+
"border-x": "borderInlineColor",
|
|
860
|
+
"border-y": "borderBlockColor",
|
|
861
|
+
"border-t": "borderTopColor",
|
|
862
|
+
"border-r": "borderRightColor",
|
|
863
|
+
"border-b": "borderBottomColor",
|
|
864
|
+
"border-l": "borderLeftColor",
|
|
865
|
+
"border-s": "borderInlineStartColor",
|
|
866
|
+
"border-e": "borderInlineEndColor",
|
|
867
|
+
}
|
|
868
|
+
const property = properties[borderColor[1] as string]
|
|
869
|
+
if (property === undefined)
|
|
870
|
+
return { reason: `border utility '${token}' has no safe StyleX property mapping` }
|
|
871
|
+
return { style: { [property]: color } }
|
|
872
|
+
}
|
|
873
|
+
return { reason: `border color '${borderColor[2]}' is not in the safe static color set` }
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
const rounded = /^rounded(?:-([trblse]{1,2}))?(?:-(none|sm|md|lg|xl|2xl|3xl|full))?$/.exec(token)
|
|
877
|
+
if (rounded !== null) {
|
|
878
|
+
const sizes: Readonly<Record<string, string>> = {
|
|
879
|
+
none: "0px",
|
|
880
|
+
sm: "0.125rem",
|
|
881
|
+
md: "0.375rem",
|
|
882
|
+
lg: "0.5rem",
|
|
883
|
+
xl: "0.75rem",
|
|
884
|
+
"2xl": "1rem",
|
|
885
|
+
"3xl": "1.5rem",
|
|
886
|
+
full: "9999px",
|
|
887
|
+
}
|
|
888
|
+
const value = sizes[rounded[2] ?? "md"] ?? (rounded[2] === undefined ? "0.25rem" : undefined)
|
|
889
|
+
if (value !== undefined) {
|
|
890
|
+
const side = rounded[1]
|
|
891
|
+
if (side === undefined) return { style: { borderRadius: value } }
|
|
892
|
+
const properties: Readonly<Record<string, readonly string[]>> = {
|
|
893
|
+
t: ["borderTopLeftRadius", "borderTopRightRadius"],
|
|
894
|
+
r: ["borderTopRightRadius", "borderBottomRightRadius"],
|
|
895
|
+
b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
|
|
896
|
+
l: ["borderTopLeftRadius", "borderBottomLeftRadius"],
|
|
897
|
+
s: ["borderStartStartRadius", "borderEndStartRadius"],
|
|
898
|
+
e: ["borderStartEndRadius", "borderEndEndRadius"],
|
|
899
|
+
}
|
|
900
|
+
const first = properties[side[0] as string]
|
|
901
|
+
const second = properties[side[1] as string]
|
|
902
|
+
if (first !== undefined && second !== undefined)
|
|
903
|
+
return {
|
|
904
|
+
style: Object.fromEntries([...first, ...second].map((property) => [property, value])),
|
|
905
|
+
}
|
|
906
|
+
if (first !== undefined)
|
|
907
|
+
return { style: Object.fromEntries(first.map((property) => [property, value])) }
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
return { reason: "utility is outside the safe static Tailwind-to-StyleX subset" }
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const convertToken = (token: string): UtilityConversion => {
|
|
915
|
+
const parts = token.split(":")
|
|
916
|
+
const base = parts.pop() ?? ""
|
|
917
|
+
const negative = base.startsWith("-")
|
|
918
|
+
const normalized = negative ? base.slice(1) : base
|
|
919
|
+
const conversion = convertBase(normalized, negative)
|
|
920
|
+
if (conversion.style === undefined) return conversion
|
|
921
|
+
return withVariants(conversion.style, parts)
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const lineOf = (source: string, offset: number): number => {
|
|
925
|
+
let line = 1
|
|
926
|
+
for (let index = 0; index < offset; index += 1) if (source[index] === "\n") line += 1
|
|
927
|
+
return line
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const isLikelyJsxAttribute = (source: string, offset: number): boolean => {
|
|
931
|
+
const tagStart = source.lastIndexOf("<", offset)
|
|
932
|
+
const tagEnd = source.lastIndexOf(">", offset)
|
|
933
|
+
return tagStart > tagEnd && !source.slice(tagStart, offset).includes("<!--")
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
const classAttributes = (source: string): readonly ClassAttribute[] => {
|
|
937
|
+
const attributes: ClassAttribute[] = []
|
|
938
|
+
for (const match of source.matchAll(CLASS_ATTRIBUTE)) {
|
|
939
|
+
const start = match.index ?? 0
|
|
940
|
+
if (!isLikelyJsxAttribute(source, start)) continue
|
|
941
|
+
const end = start + match[0].length
|
|
942
|
+
const value = match[1] ?? match[2] ?? match[3] ?? match[4]
|
|
943
|
+
attributes.push(
|
|
944
|
+
value === undefined ? { start, end, expression: match[5] ?? "" } : { start, end, value },
|
|
945
|
+
)
|
|
946
|
+
}
|
|
947
|
+
return attributes
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const safeIdentifier = (value: string): string => {
|
|
951
|
+
const normalized = value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^\d/, "_$&")
|
|
952
|
+
return normalized.length === 0 ? "utility" : normalized
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const uniqueIdentifier = (source: string, base: string): string => {
|
|
956
|
+
const names = new Set(
|
|
957
|
+
[...source.matchAll(/\b(?:const|let|var|function|class|import)\s+([A-Za-z_$][\w$]*)/g)].map(
|
|
958
|
+
(match) => match[1],
|
|
959
|
+
),
|
|
960
|
+
)
|
|
961
|
+
let candidate = base
|
|
962
|
+
let suffix = 2
|
|
963
|
+
while (names.has(candidate)) candidate = `${base}${suffix++}`
|
|
964
|
+
return candidate
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const existingStylexImport = (source: string): string | undefined => {
|
|
968
|
+
const match =
|
|
969
|
+
/import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s+["'](?:@stylexjs\/stylex|stylex)["']/.exec(
|
|
970
|
+
source,
|
|
971
|
+
)
|
|
972
|
+
return match?.[1]
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const insertionOffset = (source: string): number => {
|
|
976
|
+
let offset = source.startsWith("#!") ? source.indexOf("\n") + 1 || source.length : 0
|
|
977
|
+
const lines = source.slice(offset).split(/(?<=\n)/)
|
|
978
|
+
let consumed = 0
|
|
979
|
+
let inImport = false
|
|
980
|
+
for (const line of lines) {
|
|
981
|
+
const trimmed = line.trim()
|
|
982
|
+
const directive = /^(['"])(?:use client|use server)\1;?$/.test(trimmed)
|
|
983
|
+
if (inImport || trimmed.startsWith("import ") || trimmed.startsWith("import{")) {
|
|
984
|
+
inImport = !/(?:from\s+["'][^"']+["']|import\s+["'][^"']+["'])\s*;?$/.test(trimmed)
|
|
985
|
+
consumed += line.length
|
|
986
|
+
continue
|
|
987
|
+
}
|
|
988
|
+
if (directive || trimmed === "") {
|
|
989
|
+
consumed += line.length
|
|
990
|
+
continue
|
|
991
|
+
}
|
|
992
|
+
break
|
|
993
|
+
}
|
|
994
|
+
offset += consumed
|
|
995
|
+
return offset
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/** Transform one source file. The returned source is unchanged when every match is unsupported. */
|
|
999
|
+
export function migrateTailwindSource(source: string, file: string): SourceMigration {
|
|
1000
|
+
const attributes = classAttributes(source)
|
|
1001
|
+
if (attributes.length === 0) return { source, changed: false, replacements: 0, issues: [] }
|
|
1002
|
+
|
|
1003
|
+
const replacements: { start: number; end: number; text: string }[] = []
|
|
1004
|
+
const issues: MigrationIssue[] = []
|
|
1005
|
+
const styles = new Map<string, StyleObject>()
|
|
1006
|
+
const tokenToKey = new Map<string, string>()
|
|
1007
|
+
|
|
1008
|
+
for (const attribute of attributes) {
|
|
1009
|
+
if (attribute.value === undefined) {
|
|
1010
|
+
issues.push({
|
|
1011
|
+
file,
|
|
1012
|
+
line: lineOf(source, attribute.start),
|
|
1013
|
+
token: attribute.expression?.trim() || "className expression",
|
|
1014
|
+
reason: "dynamic className expressions are left for a manual StyleX props composition",
|
|
1015
|
+
})
|
|
1016
|
+
continue
|
|
1017
|
+
}
|
|
1018
|
+
const tokens = attribute.value.trim().split(/\s+/).filter(Boolean)
|
|
1019
|
+
if (tokens.length === 0) continue
|
|
1020
|
+
const converted: { token: string; style: StyleObject }[] = []
|
|
1021
|
+
let unsupported = false
|
|
1022
|
+
for (const token of tokens) {
|
|
1023
|
+
const conversion = convertToken(token)
|
|
1024
|
+
if (conversion.style === undefined) {
|
|
1025
|
+
issues.push({
|
|
1026
|
+
file,
|
|
1027
|
+
line: lineOf(source, attribute.start),
|
|
1028
|
+
token,
|
|
1029
|
+
reason: conversion.reason ?? "utility could not be converted safely",
|
|
1030
|
+
})
|
|
1031
|
+
unsupported = true
|
|
1032
|
+
} else converted.push({ token, style: conversion.style })
|
|
1033
|
+
}
|
|
1034
|
+
if (unsupported) continue
|
|
1035
|
+
|
|
1036
|
+
const refs: string[] = []
|
|
1037
|
+
for (const { token, style } of converted) {
|
|
1038
|
+
let key = tokenToKey.get(token)
|
|
1039
|
+
if (key === undefined) {
|
|
1040
|
+
const base = `tw_${safeIdentifier(token)}`
|
|
1041
|
+
key = base
|
|
1042
|
+
let suffix = 2
|
|
1043
|
+
while (styles.has(key)) key = `${base}_${suffix++}`
|
|
1044
|
+
tokenToKey.set(token, key)
|
|
1045
|
+
styles.set(key, style)
|
|
1046
|
+
}
|
|
1047
|
+
refs.push(`${stylesNamePlaceholder}.${key}`)
|
|
1048
|
+
}
|
|
1049
|
+
replacements.push({
|
|
1050
|
+
start: attribute.start,
|
|
1051
|
+
end: attribute.end,
|
|
1052
|
+
text: `{...${stylexNamePlaceholder}.props(${refs.join(", ")})}`,
|
|
1053
|
+
})
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
if (replacements.length === 0) return { source, changed: false, replacements: 0, issues }
|
|
1057
|
+
|
|
1058
|
+
const stylexName = existingStylexImport(source) ?? uniqueIdentifier(source, "nifraStylex")
|
|
1059
|
+
const stylesName = uniqueIdentifier(source, "nifraStyles")
|
|
1060
|
+
const rewritten = [...replacements]
|
|
1061
|
+
.sort((left, right) => right.start - left.start)
|
|
1062
|
+
.reduce(
|
|
1063
|
+
(current, replacement) =>
|
|
1064
|
+
`${current.slice(0, replacement.start)}${replacement.text
|
|
1065
|
+
.replaceAll(stylexNamePlaceholder, stylexName)
|
|
1066
|
+
.replaceAll(stylesNamePlaceholder, stylesName)}${current.slice(replacement.end)}`,
|
|
1067
|
+
source,
|
|
1068
|
+
)
|
|
1069
|
+
const needsImport = existingStylexImport(source) === undefined
|
|
1070
|
+
const generated = `${needsImport ? `import * as ${stylexName} from "${STYLEX_SPECIFIER}"\n\n` : ""}const ${stylesName} = ${stylexName}.create(${JSON.stringify(Object.fromEntries(styles), null, 2)})\n\n`
|
|
1071
|
+
const offset = insertionOffset(rewritten)
|
|
1072
|
+
const withGenerated = `${rewritten.slice(0, offset)}${generated}${rewritten.slice(offset)}`
|
|
1073
|
+
return { source: withGenerated, changed: true, replacements: replacements.length, issues }
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
const stylexNamePlaceholder = "__NIFRA_STYLEX__"
|
|
1077
|
+
const stylesNamePlaceholder = "__NIFRA_STYLES__"
|
|
1078
|
+
const STYLEX_SPECIFIER = "@stylexjs/" + "stylex"
|
|
1079
|
+
|
|
1080
|
+
async function sourceFiles(root: string): Promise<readonly string[]> {
|
|
1081
|
+
const files: string[] = []
|
|
1082
|
+
const visit = async (directory: string): Promise<void> => {
|
|
1083
|
+
const entries = await readdir(directory, { withFileTypes: true })
|
|
1084
|
+
for (const entry of entries) {
|
|
1085
|
+
if (entry.name.startsWith(".") && entry.isDirectory()) continue
|
|
1086
|
+
if (entry.isDirectory()) {
|
|
1087
|
+
if (!SKIP_DIRECTORIES.has(entry.name)) await visit(resolve(directory, entry.name))
|
|
1088
|
+
continue
|
|
1089
|
+
}
|
|
1090
|
+
if (entry.isFile() && SOURCE_FILE.test(entry.name)) files.push(resolve(directory, entry.name))
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
await visit(root)
|
|
1094
|
+
return files.sort()
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/** Scan a project and optionally write only the statically safe transformations. */
|
|
1098
|
+
export async function migrateTailwindToStylex(
|
|
1099
|
+
root: string,
|
|
1100
|
+
options: StylexMigrationOptions = {},
|
|
1101
|
+
): Promise<StylexMigrationResult> {
|
|
1102
|
+
const write = options.write === true
|
|
1103
|
+
const files: StylexMigrationFile[] = []
|
|
1104
|
+
const issues: MigrationIssue[] = []
|
|
1105
|
+
const changed: string[] = []
|
|
1106
|
+
const written: string[] = []
|
|
1107
|
+
const paths = await sourceFiles(root)
|
|
1108
|
+
for (const path of paths) {
|
|
1109
|
+
const file = relative(root, path).replaceAll("\\", "/")
|
|
1110
|
+
const source = await readFile(path, "utf8")
|
|
1111
|
+
const migration = migrateTailwindSource(source, file)
|
|
1112
|
+
if (!migration.changed && migration.issues.length === 0) continue
|
|
1113
|
+
const willWrite = write && migration.changed
|
|
1114
|
+
if (migration.changed) changed.push(file)
|
|
1115
|
+
if (willWrite) {
|
|
1116
|
+
await writeFile(path, migration.source, "utf8")
|
|
1117
|
+
written.push(file)
|
|
1118
|
+
}
|
|
1119
|
+
issues.push(...migration.issues)
|
|
1120
|
+
files.push({
|
|
1121
|
+
file,
|
|
1122
|
+
changed: migration.changed,
|
|
1123
|
+
replacements: migration.replacements,
|
|
1124
|
+
written: willWrite,
|
|
1125
|
+
issues: migration.issues,
|
|
1126
|
+
})
|
|
1127
|
+
}
|
|
1128
|
+
return {
|
|
1129
|
+
ok: issues.length === 0,
|
|
1130
|
+
from: "tailwind",
|
|
1131
|
+
to: "stylex",
|
|
1132
|
+
write,
|
|
1133
|
+
scanned: paths.length,
|
|
1134
|
+
changed,
|
|
1135
|
+
written,
|
|
1136
|
+
issues,
|
|
1137
|
+
files,
|
|
1138
|
+
}
|
|
1139
|
+
}
|