@csszyx/cli 0.9.0 → 0.9.2
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 +3 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +720 -120
- package/package.json +17 -16
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs$1, { existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
3
|
import cac from 'cac';
|
|
3
|
-
import
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import path__default, { resolve, dirname } from 'node:path';
|
|
4
6
|
import fs from 'fs-extra';
|
|
5
7
|
import ora from 'ora';
|
|
6
8
|
import pc from 'picocolors';
|
|
7
|
-
import fs$1, { existsSync, writeFileSync } from 'node:fs';
|
|
8
9
|
import { mkdir } from 'node:fs/promises';
|
|
9
10
|
import { pathToFileURL } from 'node:url';
|
|
10
11
|
import resolveConfig from 'tailwindcss/resolveConfig.js';
|
|
@@ -13,8 +14,11 @@ import prompts from 'prompts';
|
|
|
13
14
|
import readline from 'node:readline';
|
|
14
15
|
import fg from 'fast-glob';
|
|
15
16
|
import { parse } from '@babel/parser';
|
|
16
|
-
import _traverse from '@babel/traverse';
|
|
17
17
|
import * as t from '@babel/types';
|
|
18
|
+
import { runNextPrebuild } from '@csszyx/unplugin/next-prebuild';
|
|
19
|
+
import { NextSafelistWatcher } from '@csszyx/unplugin/next-watcher';
|
|
20
|
+
import { watch } from 'chokidar';
|
|
21
|
+
import { Minimatch } from 'minimatch';
|
|
18
22
|
|
|
19
23
|
const colors = {
|
|
20
24
|
success: pc.green,
|
|
@@ -141,19 +145,19 @@ async function collectStats(cwd) {
|
|
|
141
145
|
mangledCSS: 0
|
|
142
146
|
}
|
|
143
147
|
};
|
|
144
|
-
const distDir =
|
|
148
|
+
const distDir = path__default.join(cwd, "dist");
|
|
145
149
|
if (!fs.existsSync(distDir)) {
|
|
146
150
|
return stats;
|
|
147
151
|
}
|
|
148
152
|
const htmlFiles = fs.readdirSync(distDir, { recursive: true }).filter((f) => String(f).endsWith(".html"));
|
|
149
153
|
const cssFiles = fs.readdirSync(distDir, { recursive: true }).filter((f) => String(f).endsWith(".css"));
|
|
150
154
|
if (htmlFiles.length > 0) {
|
|
151
|
-
const htmlContent = fs.readFileSync(
|
|
155
|
+
const htmlContent = fs.readFileSync(path__default.join(distDir, String(htmlFiles[0])), "utf-8");
|
|
152
156
|
stats.bundleSavings.mangledHTML = Buffer.byteLength(htmlContent);
|
|
153
157
|
stats.bundleSavings.originalHTML = Math.round(stats.bundleSavings.mangledHTML * 1.67);
|
|
154
158
|
}
|
|
155
159
|
if (cssFiles.length > 0) {
|
|
156
|
-
const cssContent = fs.readFileSync(
|
|
160
|
+
const cssContent = fs.readFileSync(path__default.join(distDir, String(cssFiles[0])), "utf-8");
|
|
157
161
|
stats.bundleSavings.mangledCSS = Buffer.byteLength(cssContent);
|
|
158
162
|
stats.bundleSavings.originalCSS = Math.round(stats.bundleSavings.mangledCSS * 1.71);
|
|
159
163
|
}
|
|
@@ -174,14 +178,14 @@ function formatBytes(bytes) {
|
|
|
174
178
|
|
|
175
179
|
function detectFramework(cwd) {
|
|
176
180
|
try {
|
|
177
|
-
const pkgPath =
|
|
181
|
+
const pkgPath = path__default.join(cwd, "package.json");
|
|
178
182
|
if (!fs.existsSync(pkgPath)) {
|
|
179
183
|
return "unknown";
|
|
180
184
|
}
|
|
181
185
|
const pkg = fs.readJSONSync(pkgPath);
|
|
182
186
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
183
187
|
if (deps.next) {
|
|
184
|
-
const hasAppDir = fs.existsSync(
|
|
188
|
+
const hasAppDir = fs.existsSync(path__default.join(cwd, "app"));
|
|
185
189
|
return hasAppDir ? "nextjs-app" : "nextjs-pages";
|
|
186
190
|
}
|
|
187
191
|
if (deps.nuxt) {
|
|
@@ -210,20 +214,20 @@ function detectFramework(cwd) {
|
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
function detectPackageManager(cwd) {
|
|
213
|
-
if (fs.existsSync(
|
|
217
|
+
if (fs.existsSync(path__default.join(cwd, "pnpm-lock.yaml"))) {
|
|
214
218
|
return "pnpm";
|
|
215
219
|
}
|
|
216
|
-
if (fs.existsSync(
|
|
220
|
+
if (fs.existsSync(path__default.join(cwd, "yarn.lock"))) {
|
|
217
221
|
return "yarn";
|
|
218
222
|
}
|
|
219
|
-
if (fs.existsSync(
|
|
223
|
+
if (fs.existsSync(path__default.join(cwd, "bun.lockb"))) {
|
|
220
224
|
return "bun";
|
|
221
225
|
}
|
|
222
226
|
return "npm";
|
|
223
227
|
}
|
|
224
228
|
function hasTailwindInstalled(cwd) {
|
|
225
229
|
try {
|
|
226
|
-
const pkgPath =
|
|
230
|
+
const pkgPath = path__default.join(cwd, "package.json");
|
|
227
231
|
if (!fs.existsSync(pkgPath)) {
|
|
228
232
|
return false;
|
|
229
233
|
}
|
|
@@ -235,7 +239,7 @@ function hasTailwindInstalled(cwd) {
|
|
|
235
239
|
}
|
|
236
240
|
}
|
|
237
241
|
function hasTypeScript(cwd) {
|
|
238
|
-
return fs.existsSync(
|
|
242
|
+
return fs.existsSync(path__default.join(cwd, "tsconfig.json")) || fs.existsSync(path__default.join(cwd, "jsconfig.json"));
|
|
239
243
|
}
|
|
240
244
|
function getProjectInfo(cwd = process.cwd()) {
|
|
241
245
|
return {
|
|
@@ -267,7 +271,7 @@ async function doctor(options = {}) {
|
|
|
267
271
|
printHeader("csszyx Doctor");
|
|
268
272
|
let issueCount = 0;
|
|
269
273
|
printSection("\u{1F4CB} Configuration Health");
|
|
270
|
-
const hasConfig = fs.existsSync(
|
|
274
|
+
const hasConfig = fs.existsSync(path__default.join(cwd, "csszyx.config.ts")) || fs.existsSync(path__default.join(cwd, "csszyx.config.js"));
|
|
271
275
|
if (hasConfig) {
|
|
272
276
|
printSuccess("csszyx configuration found");
|
|
273
277
|
} else {
|
|
@@ -284,7 +288,7 @@ async function doctor(options = {}) {
|
|
|
284
288
|
}
|
|
285
289
|
printSection("\u{1F4E6} Package Versions");
|
|
286
290
|
try {
|
|
287
|
-
const pkgPath =
|
|
291
|
+
const pkgPath = path__default.join(cwd, "package.json");
|
|
288
292
|
const pkg = fs.readJSONSync(pkgPath);
|
|
289
293
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
290
294
|
if (deps.csszyx) {
|
|
@@ -298,12 +302,12 @@ async function doctor(options = {}) {
|
|
|
298
302
|
issueCount++;
|
|
299
303
|
}
|
|
300
304
|
printSection("\u{1F528} Build Output");
|
|
301
|
-
const distDir =
|
|
305
|
+
const distDir = path__default.join(cwd, "dist");
|
|
302
306
|
if (fs.existsSync(distDir)) {
|
|
303
307
|
const htmlFiles = fs.readdirSync(distDir, { recursive: true }).filter((f) => String(f).endsWith(".html"));
|
|
304
308
|
if (htmlFiles.length > 0) {
|
|
305
309
|
printSuccess(`Found ${htmlFiles.length} HTML file(s)`);
|
|
306
|
-
const htmlContent = fs.readFileSync(
|
|
310
|
+
const htmlContent = fs.readFileSync(path__default.join(distDir, String(htmlFiles[0])), "utf-8");
|
|
307
311
|
if (htmlContent.includes("data-sz-checksum")) {
|
|
308
312
|
printSuccess("Checksum injection working");
|
|
309
313
|
} else {
|
|
@@ -1196,6 +1200,16 @@ async function generateTypes(options = {}) {
|
|
|
1196
1200
|
}
|
|
1197
1201
|
|
|
1198
1202
|
const VITE_FRAMEWORKS = /* @__PURE__ */ new Set(["vite-react", "vite-vue", "vite-svelte"]);
|
|
1203
|
+
async function readFileOrNull(filePath) {
|
|
1204
|
+
try {
|
|
1205
|
+
return await fs.readFile(filePath, "utf8");
|
|
1206
|
+
} catch (err) {
|
|
1207
|
+
if (err?.code === "ENOENT") {
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
throw err;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1199
1213
|
const NEXTJS_FRAMEWORKS = /* @__PURE__ */ new Set(["nextjs-app", "nextjs-pages"]);
|
|
1200
1214
|
const CSS_ENTRY_CANDIDATES = [
|
|
1201
1215
|
"src/index.css",
|
|
@@ -1273,7 +1287,7 @@ async function init(options = {}) {
|
|
|
1273
1287
|
const spin2 = spinner.start("Creating config files...");
|
|
1274
1288
|
try {
|
|
1275
1289
|
const configContent = generateConfigFile(config);
|
|
1276
|
-
const configPath =
|
|
1290
|
+
const configPath = path__default.join(
|
|
1277
1291
|
cwd,
|
|
1278
1292
|
projectInfo.hasTypeScript ? "csszyx.config.ts" : "csszyx.config.js"
|
|
1279
1293
|
);
|
|
@@ -1283,11 +1297,11 @@ async function init(options = {}) {
|
|
|
1283
1297
|
}
|
|
1284
1298
|
await injectPlugin(cwd, projectInfo.framework);
|
|
1285
1299
|
if (config.setupGitignore) {
|
|
1286
|
-
const gitignorePath =
|
|
1300
|
+
const gitignorePath = path__default.join(cwd, ".gitignore");
|
|
1287
1301
|
const ignoreEntry = "\n# csszyx generated theme types\n.csszyx\n";
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
if (!
|
|
1302
|
+
const existing = await readFileOrNull(gitignorePath);
|
|
1303
|
+
if (existing !== null) {
|
|
1304
|
+
if (!existing.includes(".csszyx")) {
|
|
1291
1305
|
await fs.appendFile(gitignorePath, ignoreEntry);
|
|
1292
1306
|
}
|
|
1293
1307
|
} else {
|
|
@@ -1312,32 +1326,35 @@ async function init(options = {}) {
|
|
|
1312
1326
|
}
|
|
1313
1327
|
async function setupTailwindCss(cwd, framework) {
|
|
1314
1328
|
let cssPath;
|
|
1329
|
+
let content = null;
|
|
1315
1330
|
for (const candidate of CSS_ENTRY_CANDIDATES) {
|
|
1316
|
-
const full =
|
|
1317
|
-
|
|
1331
|
+
const full = path__default.join(cwd, candidate);
|
|
1332
|
+
const existing = await readFileOrNull(full);
|
|
1333
|
+
if (existing !== null) {
|
|
1318
1334
|
cssPath = full;
|
|
1335
|
+
content = existing;
|
|
1319
1336
|
break;
|
|
1320
1337
|
}
|
|
1321
1338
|
}
|
|
1322
|
-
if (!cssPath) {
|
|
1323
|
-
cssPath =
|
|
1324
|
-
await fs.ensureDir(
|
|
1339
|
+
if (!cssPath || content === null) {
|
|
1340
|
+
cssPath = path__default.join(cwd, "src/index.css");
|
|
1341
|
+
await fs.ensureDir(path__default.dirname(cssPath));
|
|
1325
1342
|
await fs.writeFile(cssPath, '@import "tailwindcss";\n');
|
|
1326
|
-
printInfo(`Created ${
|
|
1343
|
+
printInfo(`Created ${path__default.relative(cwd, cssPath)} with Tailwind v4 import`);
|
|
1327
1344
|
return;
|
|
1328
1345
|
}
|
|
1329
|
-
const content = await fs.readFile(cssPath, "utf8");
|
|
1330
1346
|
if (!content.includes('@import "tailwindcss"') && !content.includes("@import 'tailwindcss'")) {
|
|
1331
1347
|
await fs.writeFile(cssPath, `@import "tailwindcss";
|
|
1332
1348
|
|
|
1333
1349
|
${content}`);
|
|
1334
|
-
printInfo(`Added Tailwind v4 import to ${
|
|
1350
|
+
printInfo(`Added Tailwind v4 import to ${path__default.relative(cwd, cssPath)}`);
|
|
1335
1351
|
}
|
|
1336
1352
|
if (NEXTJS_FRAMEWORKS.has(framework)) {
|
|
1337
|
-
const postcssMjs =
|
|
1338
|
-
const postcssJs =
|
|
1339
|
-
const postcssTs =
|
|
1340
|
-
|
|
1353
|
+
const postcssMjs = path__default.join(cwd, "postcss.config.mjs");
|
|
1354
|
+
const postcssJs = path__default.join(cwd, "postcss.config.js");
|
|
1355
|
+
const postcssTs = path__default.join(cwd, "postcss.config.ts");
|
|
1356
|
+
const hasExisting = await readFileOrNull(postcssMjs) !== null || await readFileOrNull(postcssJs) !== null || await readFileOrNull(postcssTs) !== null;
|
|
1357
|
+
if (!hasExisting) {
|
|
1341
1358
|
await fs.writeFile(postcssMjs, generatePostcssConfig());
|
|
1342
1359
|
printInfo("Created postcss.config.mjs for Tailwind v4");
|
|
1343
1360
|
}
|
|
@@ -1363,22 +1380,24 @@ async function injectPlugin(cwd, framework) {
|
|
|
1363
1380
|
}
|
|
1364
1381
|
async function injectVitePlugin(cwd) {
|
|
1365
1382
|
const candidates = [
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1383
|
+
path__default.join(cwd, "vite.config.ts"),
|
|
1384
|
+
path__default.join(cwd, "vite.config.js"),
|
|
1385
|
+
path__default.join(cwd, "vite.config.mts"),
|
|
1386
|
+
path__default.join(cwd, "vite.config.mjs")
|
|
1370
1387
|
];
|
|
1371
1388
|
let configPath;
|
|
1389
|
+
let content = null;
|
|
1372
1390
|
for (const c of candidates) {
|
|
1373
|
-
|
|
1391
|
+
const existing = await readFileOrNull(c);
|
|
1392
|
+
if (existing !== null) {
|
|
1374
1393
|
configPath = c;
|
|
1394
|
+
content = existing;
|
|
1375
1395
|
break;
|
|
1376
1396
|
}
|
|
1377
1397
|
}
|
|
1378
|
-
if (!configPath) {
|
|
1398
|
+
if (!configPath || content === null) {
|
|
1379
1399
|
return false;
|
|
1380
1400
|
}
|
|
1381
|
-
let content = await fs.readFile(configPath, "utf8");
|
|
1382
1401
|
if (content.includes("csszyx")) {
|
|
1383
1402
|
return true;
|
|
1384
1403
|
}
|
|
@@ -1402,46 +1421,48 @@ ${importBlock}${content.slice(insertAt)}`;
|
|
|
1402
1421
|
content = content.slice(0, pluginsInsertAt) + `
|
|
1403
1422
|
...csszyx(), // csszyx MUST come before tailwindcss${twEntry}` + content.slice(pluginsInsertAt);
|
|
1404
1423
|
await fs.writeFile(configPath, content);
|
|
1405
|
-
printInfo(`Injected csszyx plugin into ${
|
|
1424
|
+
printInfo(`Injected csszyx plugin into ${path__default.basename(configPath)}`);
|
|
1406
1425
|
return true;
|
|
1407
1426
|
}
|
|
1408
1427
|
async function injectNextPlugin(cwd) {
|
|
1409
1428
|
const candidates = [
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1429
|
+
path__default.join(cwd, "next.config.ts"),
|
|
1430
|
+
path__default.join(cwd, "next.config.mjs"),
|
|
1431
|
+
path__default.join(cwd, "next.config.js")
|
|
1413
1432
|
];
|
|
1414
1433
|
let configPath;
|
|
1434
|
+
let content = null;
|
|
1415
1435
|
for (const c of candidates) {
|
|
1416
|
-
|
|
1436
|
+
const existing = await readFileOrNull(c);
|
|
1437
|
+
if (existing !== null) {
|
|
1417
1438
|
configPath = c;
|
|
1439
|
+
content = existing;
|
|
1418
1440
|
break;
|
|
1419
1441
|
}
|
|
1420
1442
|
}
|
|
1421
1443
|
if (!configPath) {
|
|
1422
|
-
configPath =
|
|
1444
|
+
configPath = path__default.join(cwd, "next.config.js");
|
|
1423
1445
|
await fs.writeFile(configPath, generateNextConfig());
|
|
1424
1446
|
printInfo("Created next.config.js with csszyx plugin");
|
|
1425
1447
|
return true;
|
|
1426
1448
|
}
|
|
1427
|
-
|
|
1428
|
-
if (content.includes("csszyx")) {
|
|
1449
|
+
if (content?.includes("csszyx")) {
|
|
1429
1450
|
return true;
|
|
1430
1451
|
}
|
|
1431
1452
|
return false;
|
|
1432
1453
|
}
|
|
1433
1454
|
async function setupTsconfig(cwd) {
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1455
|
+
const primary = path__default.join(cwd, "tsconfig.json");
|
|
1456
|
+
const viteTsConfig = path__default.join(cwd, "tsconfig.app.json");
|
|
1457
|
+
let tsconfigPath = primary;
|
|
1458
|
+
let content = await readFileOrNull(tsconfigPath);
|
|
1459
|
+
if (content === null) {
|
|
1460
|
+
tsconfigPath = viteTsConfig;
|
|
1461
|
+
content = await readFileOrNull(tsconfigPath);
|
|
1462
|
+
}
|
|
1463
|
+
if (content === null) {
|
|
1442
1464
|
return;
|
|
1443
1465
|
}
|
|
1444
|
-
let content = await fs.readFile(tsconfigPath, "utf8");
|
|
1445
1466
|
if (content.includes(".csszyx")) {
|
|
1446
1467
|
return;
|
|
1447
1468
|
}
|
|
@@ -1590,7 +1611,7 @@ function formatValue(value, indent) {
|
|
|
1590
1611
|
const items = value.map((v) => formatValue(v, indent));
|
|
1591
1612
|
return `[${items.join(", ")}]`;
|
|
1592
1613
|
}
|
|
1593
|
-
if (typeof value === "object"
|
|
1614
|
+
if (typeof value === "object") {
|
|
1594
1615
|
if (isColorOpacityObj(value)) {
|
|
1595
1616
|
const parts = [`color: '${escapeString(String(value.color))}'`];
|
|
1596
1617
|
if (typeof value.op === "number") {
|
|
@@ -1934,6 +1955,11 @@ const REVERSE_BOOLEAN_MAP = {
|
|
|
1934
1955
|
isolate: "isolate",
|
|
1935
1956
|
ordinal: "ordinal",
|
|
1936
1957
|
"slashed-zero": "slashedZero",
|
|
1958
|
+
// Bare `transition` (common transition property) and the `group`/`peer`
|
|
1959
|
+
// marker classes round-trip through the compiler as boolean sugar.
|
|
1960
|
+
transition: "transition",
|
|
1961
|
+
group: "group",
|
|
1962
|
+
peer: "peer",
|
|
1937
1963
|
// Divide/Space reverse
|
|
1938
1964
|
"divide-x-reverse": "divideXReverse",
|
|
1939
1965
|
"divide-y-reverse": "divideYReverse",
|
|
@@ -2351,7 +2377,7 @@ const KNOWN_SIMPLE_VARIANTS = /* @__PURE__ */ new Set([
|
|
|
2351
2377
|
"pointer-none"
|
|
2352
2378
|
]);
|
|
2353
2379
|
|
|
2354
|
-
function parseClass(cls) {
|
|
2380
|
+
function parseClass(cls, options = {}) {
|
|
2355
2381
|
let important = false;
|
|
2356
2382
|
let input = cls;
|
|
2357
2383
|
if (input.endsWith("!")) {
|
|
@@ -2364,7 +2390,7 @@ function parseClass(cls) {
|
|
|
2364
2390
|
negative = true;
|
|
2365
2391
|
negInput = input.slice(1);
|
|
2366
2392
|
}
|
|
2367
|
-
const boolResult = tryBooleanMatch(input);
|
|
2393
|
+
const boolResult = tryBooleanMatch(input, options);
|
|
2368
2394
|
if (boolResult) {
|
|
2369
2395
|
return applyImportant(boolResult, important);
|
|
2370
2396
|
}
|
|
@@ -2380,10 +2406,7 @@ function parseClass(cls) {
|
|
|
2380
2406
|
continue;
|
|
2381
2407
|
}
|
|
2382
2408
|
if (REVERSE_BOOLEAN_MAP[source]) {
|
|
2383
|
-
return applyImportant(
|
|
2384
|
-
{ prop: REVERSE_BOOLEAN_MAP[source], value: true },
|
|
2385
|
-
important
|
|
2386
|
-
);
|
|
2409
|
+
return applyImportant(booleanClassToParsed(source, options), important);
|
|
2387
2410
|
}
|
|
2388
2411
|
if (prefix === "divide-x" || prefix === "divide-y") {
|
|
2389
2412
|
return applyImportant({ prop, value: true }, important);
|
|
@@ -2422,7 +2445,7 @@ function parseClass(cls) {
|
|
|
2422
2445
|
}
|
|
2423
2446
|
}
|
|
2424
2447
|
}
|
|
2425
|
-
const displayResult = tryDisplay(input);
|
|
2448
|
+
const displayResult = tryDisplay(input, options);
|
|
2426
2449
|
if (displayResult) {
|
|
2427
2450
|
return applyImportant(displayResult, important);
|
|
2428
2451
|
}
|
|
@@ -2456,17 +2479,17 @@ function applyImportant(result, important) {
|
|
|
2456
2479
|
}
|
|
2457
2480
|
return result;
|
|
2458
2481
|
}
|
|
2459
|
-
function tryBooleanMatch(cls) {
|
|
2482
|
+
function tryBooleanMatch(cls, options) {
|
|
2460
2483
|
if (BOOLEAN_VALUE_MAP[cls]) {
|
|
2461
2484
|
const { prop, value } = BOOLEAN_VALUE_MAP[cls];
|
|
2462
2485
|
return { prop, value };
|
|
2463
2486
|
}
|
|
2464
2487
|
if (REVERSE_BOOLEAN_MAP[cls]) {
|
|
2465
|
-
return
|
|
2488
|
+
return booleanClassToParsed(cls, options);
|
|
2466
2489
|
}
|
|
2467
2490
|
return null;
|
|
2468
2491
|
}
|
|
2469
|
-
function tryDisplay(cls) {
|
|
2492
|
+
function tryDisplay(cls, options) {
|
|
2470
2493
|
const displayValues = /* @__PURE__ */ new Set([
|
|
2471
2494
|
"block",
|
|
2472
2495
|
"inline",
|
|
@@ -2484,10 +2507,33 @@ function tryDisplay(cls) {
|
|
|
2484
2507
|
"list-item"
|
|
2485
2508
|
]);
|
|
2486
2509
|
if (displayValues.has(cls)) {
|
|
2487
|
-
return REVERSE_BOOLEAN_MAP[cls] ?
|
|
2510
|
+
return REVERSE_BOOLEAN_MAP[cls] ? booleanClassToParsed(cls, options) : null;
|
|
2488
2511
|
}
|
|
2489
2512
|
return null;
|
|
2490
2513
|
}
|
|
2514
|
+
function booleanClassToParsed(cls, options) {
|
|
2515
|
+
const displayValue = DISPLAY_CLASS_VALUES[cls];
|
|
2516
|
+
if (options.display === "canonical" && displayValue) {
|
|
2517
|
+
return { prop: "display", value: displayValue, cssProperty: "display" };
|
|
2518
|
+
}
|
|
2519
|
+
return { prop: REVERSE_BOOLEAN_MAP[cls], value: true };
|
|
2520
|
+
}
|
|
2521
|
+
const DISPLAY_CLASS_VALUES = {
|
|
2522
|
+
block: "block",
|
|
2523
|
+
inline: "inline",
|
|
2524
|
+
"inline-block": "inline-block",
|
|
2525
|
+
flex: "flex",
|
|
2526
|
+
"inline-flex": "inline-flex",
|
|
2527
|
+
grid: "grid",
|
|
2528
|
+
"inline-grid": "inline-grid",
|
|
2529
|
+
hidden: "none",
|
|
2530
|
+
contents: "contents",
|
|
2531
|
+
table: "table",
|
|
2532
|
+
"table-row": "table-row",
|
|
2533
|
+
"table-cell": "table-cell",
|
|
2534
|
+
"flow-root": "flow-root",
|
|
2535
|
+
"list-item": "list-item"
|
|
2536
|
+
};
|
|
2491
2537
|
function tryGradient(cls, negative) {
|
|
2492
2538
|
let input = cls;
|
|
2493
2539
|
let type = null;
|
|
@@ -3133,6 +3179,8 @@ function normalizeVariantKey(variant) {
|
|
|
3133
3179
|
const TODO_KEEP = "sz:keep";
|
|
3134
3180
|
const TODO_REMOVE = "sz:remove";
|
|
3135
3181
|
const TODO_PENDING = "sz:todo";
|
|
3182
|
+
const MAX_TOKEN_CACHE_SIZE = 4096;
|
|
3183
|
+
const parsedTokenCache = /* @__PURE__ */ new Map();
|
|
3136
3184
|
function resolveCustomMapEntry(token, customMap, resolveString) {
|
|
3137
3185
|
if (!(token in customMap)) {
|
|
3138
3186
|
return null;
|
|
@@ -3164,6 +3212,8 @@ function classNameToSzObject(className, customMap) {
|
|
|
3164
3212
|
const szObject = {};
|
|
3165
3213
|
const unrecognized = [];
|
|
3166
3214
|
const keepInClassName = [];
|
|
3215
|
+
const seenCssPropertiesByPath = /* @__PURE__ */ new Map();
|
|
3216
|
+
const conflictedCssPropertiesByPath = /* @__PURE__ */ new Map();
|
|
3167
3217
|
for (const token of tokens) {
|
|
3168
3218
|
if (customMap && token in customMap) {
|
|
3169
3219
|
const entry = resolveCustomMapEntry(
|
|
@@ -3201,21 +3251,79 @@ function classNameToSzObject(className, customMap) {
|
|
|
3201
3251
|
}
|
|
3202
3252
|
}
|
|
3203
3253
|
}
|
|
3204
|
-
const
|
|
3205
|
-
|
|
3206
|
-
if (!parsed) {
|
|
3254
|
+
const parsedToken = parseClassTokenCached(token);
|
|
3255
|
+
if (!parsedToken) {
|
|
3207
3256
|
unrecognized.push(token);
|
|
3208
3257
|
continue;
|
|
3209
3258
|
}
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
keyPath.push(...vk);
|
|
3259
|
+
if (isCssPropertyConflicted(conflictedCssPropertiesByPath, parsedToken)) {
|
|
3260
|
+
unrecognized.push(token);
|
|
3261
|
+
continue;
|
|
3214
3262
|
}
|
|
3215
|
-
|
|
3263
|
+
const conflict = findCssPropertyConflict(seenCssPropertiesByPath, parsedToken, token);
|
|
3264
|
+
if (conflict) {
|
|
3265
|
+
rememberCssPropertyConflict(conflictedCssPropertiesByPath, parsedToken);
|
|
3266
|
+
unrecognized.push(conflict, token);
|
|
3267
|
+
removeNestedValue(szObject, parsedToken.keyPath, parsedToken.prop);
|
|
3268
|
+
continue;
|
|
3269
|
+
}
|
|
3270
|
+
rememberCssProperty(seenCssPropertiesByPath, parsedToken, token);
|
|
3271
|
+
setNestedValue(
|
|
3272
|
+
szObject,
|
|
3273
|
+
parsedToken.keyPath,
|
|
3274
|
+
parsedToken.prop,
|
|
3275
|
+
cloneParsedValue(parsedToken.value)
|
|
3276
|
+
);
|
|
3216
3277
|
}
|
|
3217
3278
|
return { szObject, unrecognized, keepInClassName };
|
|
3218
3279
|
}
|
|
3280
|
+
function parseClassTokenCached(token) {
|
|
3281
|
+
if (parsedTokenCache.has(token)) {
|
|
3282
|
+
return parsedTokenCache.get(token) ?? null;
|
|
3283
|
+
}
|
|
3284
|
+
const parsed = parseClassToken(token);
|
|
3285
|
+
rememberParsedToken(token, parsed);
|
|
3286
|
+
return parsed;
|
|
3287
|
+
}
|
|
3288
|
+
function parseClassToken(token) {
|
|
3289
|
+
const { variantParts, baseClass } = extractVariants(token);
|
|
3290
|
+
const parsed = parseClass(baseClass, { display: "canonical" });
|
|
3291
|
+
if (!parsed) {
|
|
3292
|
+
return null;
|
|
3293
|
+
}
|
|
3294
|
+
const keyPath = [];
|
|
3295
|
+
for (const variant of variantParts) {
|
|
3296
|
+
keyPath.push(...mapVariant(variant));
|
|
3297
|
+
}
|
|
3298
|
+
return {
|
|
3299
|
+
keyPath,
|
|
3300
|
+
prop: parsed.prop,
|
|
3301
|
+
value: parsed.value,
|
|
3302
|
+
cssProperty: parsed.cssProperty
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
function rememberParsedToken(token, parsed) {
|
|
3306
|
+
if (parsedTokenCache.size >= MAX_TOKEN_CACHE_SIZE) {
|
|
3307
|
+
const oldest = parsedTokenCache.keys().next().value;
|
|
3308
|
+
if (oldest !== void 0) {
|
|
3309
|
+
parsedTokenCache.delete(oldest);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
parsedTokenCache.set(token, parsed);
|
|
3313
|
+
}
|
|
3314
|
+
function cloneParsedValue(value) {
|
|
3315
|
+
if (Array.isArray(value)) {
|
|
3316
|
+
return value.map(cloneParsedValue);
|
|
3317
|
+
}
|
|
3318
|
+
if (value && typeof value === "object") {
|
|
3319
|
+
const clone = {};
|
|
3320
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
3321
|
+
clone[key] = cloneParsedValue(nested);
|
|
3322
|
+
}
|
|
3323
|
+
return clone;
|
|
3324
|
+
}
|
|
3325
|
+
return value;
|
|
3326
|
+
}
|
|
3219
3327
|
function setNestedValue(obj, keyPath, prop, value) {
|
|
3220
3328
|
let current = obj;
|
|
3221
3329
|
for (const key of keyPath) {
|
|
@@ -3226,6 +3334,64 @@ function setNestedValue(obj, keyPath, prop, value) {
|
|
|
3226
3334
|
}
|
|
3227
3335
|
current[prop] = value;
|
|
3228
3336
|
}
|
|
3337
|
+
function findCssPropertyConflict(seen, parsed, token) {
|
|
3338
|
+
if (!parsed.cssProperty) {
|
|
3339
|
+
return null;
|
|
3340
|
+
}
|
|
3341
|
+
const scope = parsed.keyPath.join("\0");
|
|
3342
|
+
const previous = seen.get(scope)?.get(parsed.cssProperty);
|
|
3343
|
+
return previous && previous !== token ? previous : null;
|
|
3344
|
+
}
|
|
3345
|
+
function isCssPropertyConflicted(conflicted, parsed) {
|
|
3346
|
+
if (!parsed.cssProperty) {
|
|
3347
|
+
return false;
|
|
3348
|
+
}
|
|
3349
|
+
return conflicted.get(parsed.keyPath.join("\0"))?.has(parsed.cssProperty) === true;
|
|
3350
|
+
}
|
|
3351
|
+
function rememberCssPropertyConflict(conflicted, parsed) {
|
|
3352
|
+
if (!parsed.cssProperty) {
|
|
3353
|
+
return;
|
|
3354
|
+
}
|
|
3355
|
+
const scope = parsed.keyPath.join("\0");
|
|
3356
|
+
let properties = conflicted.get(scope);
|
|
3357
|
+
if (!properties) {
|
|
3358
|
+
properties = /* @__PURE__ */ new Set();
|
|
3359
|
+
conflicted.set(scope, properties);
|
|
3360
|
+
}
|
|
3361
|
+
properties.add(parsed.cssProperty);
|
|
3362
|
+
}
|
|
3363
|
+
function rememberCssProperty(seen, parsed, token) {
|
|
3364
|
+
if (!parsed.cssProperty) {
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
const scope = parsed.keyPath.join("\0");
|
|
3368
|
+
let properties = seen.get(scope);
|
|
3369
|
+
if (!properties) {
|
|
3370
|
+
properties = /* @__PURE__ */ new Map();
|
|
3371
|
+
seen.set(scope, properties);
|
|
3372
|
+
}
|
|
3373
|
+
properties.set(parsed.cssProperty, token);
|
|
3374
|
+
}
|
|
3375
|
+
function removeNestedValue(obj, keyPath, prop) {
|
|
3376
|
+
let current = obj;
|
|
3377
|
+
const parents = [];
|
|
3378
|
+
for (const key of keyPath) {
|
|
3379
|
+
const next = current[key];
|
|
3380
|
+
if (!next || typeof next !== "object" || Array.isArray(next)) {
|
|
3381
|
+
return;
|
|
3382
|
+
}
|
|
3383
|
+
parents.push([current, key]);
|
|
3384
|
+
current = next;
|
|
3385
|
+
}
|
|
3386
|
+
delete current[prop];
|
|
3387
|
+
for (let index = parents.length - 1; index >= 0; index--) {
|
|
3388
|
+
const [parent, key] = parents[index];
|
|
3389
|
+
const child = parent[key];
|
|
3390
|
+
if (child && typeof child === "object" && Object.keys(child).length === 0) {
|
|
3391
|
+
delete parent[key];
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3229
3395
|
|
|
3230
3396
|
const CLSX_LIKE_NAMES = /* @__PURE__ */ new Set(["clsx", "cn", "cx", "twMerge", "classNames", "classnames"]);
|
|
3231
3397
|
function isClsxLikeName(name) {
|
|
@@ -3532,7 +3698,7 @@ function isExpression(node, t) {
|
|
|
3532
3698
|
return t.isExpression(node);
|
|
3533
3699
|
}
|
|
3534
3700
|
|
|
3535
|
-
const
|
|
3701
|
+
const VISITOR_KEYS = t.VISITOR_KEYS;
|
|
3536
3702
|
function injectTodoComment(unrecognized, parent, options, replacements) {
|
|
3537
3703
|
if (!options.injectTodos || unrecognized.length === 0) {
|
|
3538
3704
|
return;
|
|
@@ -3548,16 +3714,64 @@ function injectTodoComment(unrecognized, parent, options, replacements) {
|
|
|
3548
3714
|
`
|
|
3549
3715
|
});
|
|
3550
3716
|
}
|
|
3717
|
+
function walkAst(node, visitors, ancestors = []) {
|
|
3718
|
+
if (t.isImportDeclaration(node)) {
|
|
3719
|
+
visitors.ImportDeclaration?.(node);
|
|
3720
|
+
} else if (t.isCallExpression(node)) {
|
|
3721
|
+
visitors.CallExpression?.(node, ancestors);
|
|
3722
|
+
} else if (t.isJSXAttribute(node)) {
|
|
3723
|
+
visitors.JSXAttribute?.(node, ancestors[ancestors.length - 1] ?? null);
|
|
3724
|
+
}
|
|
3725
|
+
const keys = VISITOR_KEYS[node.type];
|
|
3726
|
+
if (!keys) {
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
ancestors.push(node);
|
|
3730
|
+
for (const key of keys) {
|
|
3731
|
+
const child = node[key];
|
|
3732
|
+
if (Array.isArray(child)) {
|
|
3733
|
+
for (const item of child) {
|
|
3734
|
+
if (isAstNode(item)) {
|
|
3735
|
+
walkAst(item, visitors, ancestors);
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
} else if (isAstNode(child)) {
|
|
3739
|
+
walkAst(child, visitors, ancestors);
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
ancestors.pop();
|
|
3743
|
+
}
|
|
3744
|
+
function isAstNode(value) {
|
|
3745
|
+
return Boolean(value && typeof value === "object" && "type" in value);
|
|
3746
|
+
}
|
|
3747
|
+
function isClassNameJsxAttribute(node) {
|
|
3748
|
+
return t.isJSXAttribute(node) && t.isJSXIdentifier(node.name) && node.name.name === "className";
|
|
3749
|
+
}
|
|
3551
3750
|
function transformSource(source, filePath, options = {}) {
|
|
3552
3751
|
const warnings = [];
|
|
3553
3752
|
let classNamesTransformed = 0;
|
|
3554
3753
|
let classNamesSkipped = 0;
|
|
3754
|
+
let classNamesSkippedComponent = 0;
|
|
3555
3755
|
const classesUnrecognized = [];
|
|
3556
3756
|
const replacements = [];
|
|
3557
3757
|
const clsxImportNames = /* @__PURE__ */ new Set();
|
|
3558
3758
|
let clsxUsedOutsideClassName = false;
|
|
3559
3759
|
const clsxCallsitesMigrated = /* @__PURE__ */ new Set();
|
|
3560
3760
|
let hasCvaImport = false;
|
|
3761
|
+
if (source.indexOf("className") === -1 && source.indexOf("cva") === -1) {
|
|
3762
|
+
return {
|
|
3763
|
+
code: source,
|
|
3764
|
+
changed: false,
|
|
3765
|
+
warnings: [],
|
|
3766
|
+
stats: {
|
|
3767
|
+
classNamesTransformed: 0,
|
|
3768
|
+
classNamesSkipped: 0,
|
|
3769
|
+
classNamesSkippedComponent: 0,
|
|
3770
|
+
classesUnrecognized: []
|
|
3771
|
+
},
|
|
3772
|
+
potentiallyUnusedImports: []
|
|
3773
|
+
};
|
|
3774
|
+
}
|
|
3561
3775
|
let ast;
|
|
3562
3776
|
try {
|
|
3563
3777
|
ast = parse(source, {
|
|
@@ -3571,50 +3785,49 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3571
3785
|
code: source,
|
|
3572
3786
|
changed: false,
|
|
3573
3787
|
warnings: [`Parse error in ${filePath}: ${msg}`],
|
|
3574
|
-
stats: {
|
|
3788
|
+
stats: {
|
|
3789
|
+
classNamesTransformed: 0,
|
|
3790
|
+
classNamesSkipped: 0,
|
|
3791
|
+
classNamesSkippedComponent: 0,
|
|
3792
|
+
classesUnrecognized: []
|
|
3793
|
+
},
|
|
3575
3794
|
potentiallyUnusedImports: []
|
|
3576
3795
|
};
|
|
3577
3796
|
}
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
const src = path.node.source.value;
|
|
3797
|
+
walkAst(ast, {
|
|
3798
|
+
ImportDeclaration(node) {
|
|
3799
|
+
const src = node.source.value;
|
|
3582
3800
|
const clsxPackages = ["clsx", "clsx/lite", "classnames", "tailwind-merge"];
|
|
3583
3801
|
const isClsxPkg = clsxPackages.some((p) => src === p || src.startsWith(`${p}/`));
|
|
3584
3802
|
const cvaPkgs = ["cva", "class-variance-authority"];
|
|
3585
3803
|
if (cvaPkgs.some((p) => src === p || src.startsWith(`${p}/`))) {
|
|
3586
3804
|
hasCvaImport = true;
|
|
3587
3805
|
}
|
|
3588
|
-
for (const spec of
|
|
3806
|
+
for (const spec of node.specifiers) {
|
|
3589
3807
|
const localName = spec.local.name;
|
|
3590
3808
|
if (isClsxPkg || isClsxLikeName(localName)) {
|
|
3591
3809
|
clsxImportNames.add(localName);
|
|
3592
3810
|
}
|
|
3593
3811
|
}
|
|
3594
3812
|
},
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
const inClassName = path.findParent(
|
|
3599
|
-
(p) => t.isJSXAttribute(p.node) && t.isJSXIdentifier(p.node.name) && p.node.name.name === "className"
|
|
3600
|
-
);
|
|
3813
|
+
CallExpression(node, ancestors) {
|
|
3814
|
+
if (t.isIdentifier(node.callee) && clsxImportNames.has(node.callee.name)) {
|
|
3815
|
+
const inClassName = ancestors.some(isClassNameJsxAttribute);
|
|
3601
3816
|
if (!inClassName) {
|
|
3602
3817
|
clsxUsedOutsideClassName = true;
|
|
3603
3818
|
}
|
|
3604
3819
|
}
|
|
3605
3820
|
},
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
const attrName = path.node.name;
|
|
3821
|
+
JSXAttribute(node, parent) {
|
|
3822
|
+
const attrName = node.name;
|
|
3609
3823
|
if (!t.isJSXIdentifier(attrName) || attrName.name !== "className") {
|
|
3610
3824
|
return;
|
|
3611
3825
|
}
|
|
3612
|
-
const parent = path.parent;
|
|
3613
3826
|
if (t.isJSXOpeningElement(parent)) {
|
|
3614
3827
|
const elementName = parent.name;
|
|
3615
3828
|
const isCapitalized = t.isJSXIdentifier(elementName) && /^[A-Z]/.test(elementName.name) || t.isJSXMemberExpression(elementName);
|
|
3616
3829
|
if (isCapitalized) {
|
|
3617
|
-
|
|
3830
|
+
classNamesSkippedComponent++;
|
|
3618
3831
|
return;
|
|
3619
3832
|
}
|
|
3620
3833
|
}
|
|
@@ -3627,9 +3840,9 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3627
3840
|
return;
|
|
3628
3841
|
}
|
|
3629
3842
|
}
|
|
3630
|
-
const value =
|
|
3631
|
-
const attrStart =
|
|
3632
|
-
const attrEnd =
|
|
3843
|
+
const value = node.value;
|
|
3844
|
+
const attrStart = node.start;
|
|
3845
|
+
const attrEnd = node.end;
|
|
3633
3846
|
if (attrStart === null || attrStart === void 0 || attrEnd === null || attrEnd === void 0) {
|
|
3634
3847
|
return;
|
|
3635
3848
|
}
|
|
@@ -3639,7 +3852,7 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3639
3852
|
replacements.push({ start: attrStart, end: attrEnd, text: result.replacement });
|
|
3640
3853
|
classNamesTransformed++;
|
|
3641
3854
|
classesUnrecognized.push(...result.unrecognized);
|
|
3642
|
-
injectTodoComment(result.unrecognized,
|
|
3855
|
+
injectTodoComment(result.unrecognized, parent, options, replacements);
|
|
3643
3856
|
} else {
|
|
3644
3857
|
classNamesSkipped++;
|
|
3645
3858
|
}
|
|
@@ -3657,7 +3870,7 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3657
3870
|
});
|
|
3658
3871
|
classNamesTransformed++;
|
|
3659
3872
|
classesUnrecognized.push(...result.unrecognized);
|
|
3660
|
-
injectTodoComment(result.unrecognized,
|
|
3873
|
+
injectTodoComment(result.unrecognized, parent, options, replacements);
|
|
3661
3874
|
} else {
|
|
3662
3875
|
classNamesSkipped++;
|
|
3663
3876
|
}
|
|
@@ -3676,8 +3889,9 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3676
3889
|
} else {
|
|
3677
3890
|
classNamesSkipped++;
|
|
3678
3891
|
warnings.push(...result.warnings.map((w) => `[${filePath}] ${w}`));
|
|
3892
|
+
classesUnrecognized.push(...result.unrecognized);
|
|
3679
3893
|
}
|
|
3680
|
-
injectTodoComment(result.unrecognized,
|
|
3894
|
+
injectTodoComment(result.unrecognized, parent, options, replacements);
|
|
3681
3895
|
return;
|
|
3682
3896
|
}
|
|
3683
3897
|
if (t.isCallExpression(expr) && t.isIdentifier(expr.callee) && isClsxLikeName(expr.callee.name)) {
|
|
@@ -3696,8 +3910,9 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3696
3910
|
} else {
|
|
3697
3911
|
classNamesSkipped++;
|
|
3698
3912
|
warnings.push(...result.warnings.map((w) => `[${filePath}] ${w}`));
|
|
3913
|
+
classesUnrecognized.push(...result.unrecognized);
|
|
3699
3914
|
}
|
|
3700
|
-
injectTodoComment(result.unrecognized,
|
|
3915
|
+
injectTodoComment(result.unrecognized, parent, options, replacements);
|
|
3701
3916
|
return;
|
|
3702
3917
|
}
|
|
3703
3918
|
if (t.isConditionalExpression(expr)) {
|
|
@@ -3713,8 +3928,9 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3713
3928
|
} else {
|
|
3714
3929
|
classNamesSkipped++;
|
|
3715
3930
|
warnings.push(...result.warnings.map((w) => `[${filePath}] ${w}`));
|
|
3931
|
+
classesUnrecognized.push(...result.unrecognized);
|
|
3716
3932
|
}
|
|
3717
|
-
injectTodoComment(result.unrecognized,
|
|
3933
|
+
injectTodoComment(result.unrecognized, parent, options, replacements);
|
|
3718
3934
|
return;
|
|
3719
3935
|
}
|
|
3720
3936
|
if (t.isLogicalExpression(expr) && expr.operator === "&&") {
|
|
@@ -3730,8 +3946,9 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3730
3946
|
} else {
|
|
3731
3947
|
classNamesSkipped++;
|
|
3732
3948
|
warnings.push(...result.warnings.map((w) => `[${filePath}] ${w}`));
|
|
3949
|
+
classesUnrecognized.push(...result.unrecognized);
|
|
3733
3950
|
}
|
|
3734
|
-
injectTodoComment(result.unrecognized,
|
|
3951
|
+
injectTodoComment(result.unrecognized, parent, options, replacements);
|
|
3735
3952
|
return;
|
|
3736
3953
|
}
|
|
3737
3954
|
classNamesSkipped++;
|
|
@@ -3763,7 +3980,12 @@ function transformSource(source, filePath, options = {}) {
|
|
|
3763
3980
|
code: output,
|
|
3764
3981
|
changed: replacements.length > 0,
|
|
3765
3982
|
warnings,
|
|
3766
|
-
stats: {
|
|
3983
|
+
stats: {
|
|
3984
|
+
classNamesTransformed,
|
|
3985
|
+
classNamesSkipped,
|
|
3986
|
+
classNamesSkippedComponent,
|
|
3987
|
+
classesUnrecognized
|
|
3988
|
+
},
|
|
3767
3989
|
potentiallyUnusedImports
|
|
3768
3990
|
};
|
|
3769
3991
|
}
|
|
@@ -3805,6 +4027,7 @@ function transformHtmlSourceSimple(source, filePath, options = {}) {
|
|
|
3805
4027
|
const warnings = [];
|
|
3806
4028
|
let classNamesTransformed = 0;
|
|
3807
4029
|
let classNamesSkipped = 0;
|
|
4030
|
+
const classNamesSkippedComponent = 0;
|
|
3808
4031
|
const classesUnrecognized = [];
|
|
3809
4032
|
let changed = false;
|
|
3810
4033
|
let output = source.replace(/\bclass="([^"]*)"/g, (match, classStr) => {
|
|
@@ -3852,7 +4075,12 @@ function transformHtmlSourceSimple(source, filePath, options = {}) {
|
|
|
3852
4075
|
code: output,
|
|
3853
4076
|
changed,
|
|
3854
4077
|
warnings,
|
|
3855
|
-
stats: {
|
|
4078
|
+
stats: {
|
|
4079
|
+
classNamesTransformed,
|
|
4080
|
+
classNamesSkipped,
|
|
4081
|
+
classNamesSkippedComponent,
|
|
4082
|
+
classesUnrecognized
|
|
4083
|
+
},
|
|
3856
4084
|
potentiallyUnusedImports: []
|
|
3857
4085
|
};
|
|
3858
4086
|
}
|
|
@@ -3860,9 +4088,9 @@ function transformHtmlSourceSimple(source, filePath, options = {}) {
|
|
|
3860
4088
|
function createLogFile(cwd) {
|
|
3861
4089
|
const now = /* @__PURE__ */ new Date();
|
|
3862
4090
|
const ts = now.toISOString().slice(0, 19).replace("T", "_").replace(/:/g, "-");
|
|
3863
|
-
const logDir =
|
|
4091
|
+
const logDir = path__default.join(cwd, ".csszyx", "logs");
|
|
3864
4092
|
fs$1.mkdirSync(logDir, { recursive: true });
|
|
3865
|
-
const filePath =
|
|
4093
|
+
const filePath = path__default.join(logDir, `migrate-${ts}.log`);
|
|
3866
4094
|
const lines = [`csszyx migrate \u2014 ${now.toISOString()}`, ""];
|
|
3867
4095
|
return {
|
|
3868
4096
|
filePath,
|
|
@@ -3873,7 +4101,7 @@ function createLogFile(cwd) {
|
|
|
3873
4101
|
}
|
|
3874
4102
|
function isGitignored(cwd, pattern) {
|
|
3875
4103
|
try {
|
|
3876
|
-
const content = fs$1.readFileSync(
|
|
4104
|
+
const content = fs$1.readFileSync(path__default.join(cwd, ".gitignore"), "utf-8");
|
|
3877
4105
|
return content.split("\n").some((l) => {
|
|
3878
4106
|
const t = l.trim();
|
|
3879
4107
|
return t === pattern || t === `${pattern}/` || t === `/${pattern}`;
|
|
@@ -3916,7 +4144,7 @@ async function migrate(options = {}) {
|
|
|
3916
4144
|
}
|
|
3917
4145
|
if (resolveTodosPath) {
|
|
3918
4146
|
try {
|
|
3919
|
-
const absolutePath =
|
|
4147
|
+
const absolutePath = path__default.resolve(cwd, resolveTodosPath);
|
|
3920
4148
|
const content = fs$1.readFileSync(absolutePath, "utf-8");
|
|
3921
4149
|
customMap = JSON.parse(content);
|
|
3922
4150
|
printInfo(`Loaded resolution map from ${resolveTodosPath}`);
|
|
@@ -3972,6 +4200,7 @@ async function migrate(options = {}) {
|
|
|
3972
4200
|
}
|
|
3973
4201
|
let totalTransformed = 0;
|
|
3974
4202
|
let totalSkipped = 0;
|
|
4203
|
+
let totalSkippedComponent = 0;
|
|
3975
4204
|
let totalFiles = 0;
|
|
3976
4205
|
const allUnrecognized = [];
|
|
3977
4206
|
const allWarnings = [];
|
|
@@ -4003,16 +4232,17 @@ async function migrate(options = {}) {
|
|
|
4003
4232
|
totalFiles++;
|
|
4004
4233
|
totalTransformed += result.stats.classNamesTransformed;
|
|
4005
4234
|
totalSkipped += result.stats.classNamesSkipped;
|
|
4235
|
+
totalSkippedComponent += result.stats.classNamesSkippedComponent;
|
|
4006
4236
|
allUnrecognized.push(...result.stats.classesUnrecognized);
|
|
4007
4237
|
if (result.potentiallyUnusedImports.length > 0) {
|
|
4008
|
-
const rel2 =
|
|
4238
|
+
const rel2 = path__default.relative(cwd, filePath);
|
|
4009
4239
|
unusedImportFiles.push({ file: rel2, imports: result.potentiallyUnusedImports });
|
|
4010
4240
|
}
|
|
4011
4241
|
if (!dryRun) {
|
|
4012
4242
|
try {
|
|
4013
4243
|
fs$1.writeFileSync(filePath, result.code, "utf-8");
|
|
4014
4244
|
} catch (err) {
|
|
4015
|
-
const rel2 =
|
|
4245
|
+
const rel2 = path__default.relative(cwd, filePath);
|
|
4016
4246
|
printWarn(
|
|
4017
4247
|
`Could not write ${rel2}: ${err instanceof Error ? err.message : String(err)}`
|
|
4018
4248
|
);
|
|
@@ -4020,7 +4250,7 @@ async function migrate(options = {}) {
|
|
|
4020
4250
|
continue;
|
|
4021
4251
|
}
|
|
4022
4252
|
}
|
|
4023
|
-
const rel =
|
|
4253
|
+
const rel = path__default.relative(cwd, filePath);
|
|
4024
4254
|
if (dryRun) {
|
|
4025
4255
|
printInfo(` ${rel}: ${result.stats.classNamesTransformed} className(s) \u2192 sz`);
|
|
4026
4256
|
log.writeLine(` ${rel}: ${result.stats.classNamesTransformed} className(s) \u2192 sz`);
|
|
@@ -4039,6 +4269,10 @@ async function migrate(options = {}) {
|
|
|
4039
4269
|
printWarn(`classNames skipped (dynamic): ${totalSkipped}`);
|
|
4040
4270
|
log.writeLine(`classNames skipped (dynamic): ${totalSkipped}`);
|
|
4041
4271
|
}
|
|
4272
|
+
if (totalSkippedComponent > 0) {
|
|
4273
|
+
printWarn(`classNames kept on components (no sz support): ${totalSkippedComponent}`);
|
|
4274
|
+
log.writeLine(`classNames kept on components (no sz support): ${totalSkippedComponent}`);
|
|
4275
|
+
}
|
|
4042
4276
|
if (allUnrecognized.length > 0) {
|
|
4043
4277
|
const unique = [...new Set(allUnrecognized)];
|
|
4044
4278
|
printWarn(
|
|
@@ -4061,7 +4295,7 @@ async function migrate(options = {}) {
|
|
|
4061
4295
|
}
|
|
4062
4296
|
}
|
|
4063
4297
|
if (audit) {
|
|
4064
|
-
const todoPath =
|
|
4298
|
+
const todoPath = path__default.join(cwd, ".csszyx-todo.json");
|
|
4065
4299
|
const unique = [...new Set(allUnrecognized)];
|
|
4066
4300
|
console.info();
|
|
4067
4301
|
if (unique.length === 0) {
|
|
@@ -4078,19 +4312,19 @@ async function migrate(options = {}) {
|
|
|
4078
4312
|
fs$1.writeFileSync(todoPath, JSON.stringify(todoObj, null, 2));
|
|
4079
4313
|
} catch (err) {
|
|
4080
4314
|
printWarn(
|
|
4081
|
-
`Could not write ${
|
|
4315
|
+
`Could not write ${path__default.relative(cwd, todoPath)}: ${err instanceof Error ? err.message : String(err)}`
|
|
4082
4316
|
);
|
|
4083
4317
|
log.flush();
|
|
4084
4318
|
return;
|
|
4085
4319
|
}
|
|
4086
4320
|
printSuccess(
|
|
4087
|
-
`Audit complete. Exported ${unique.length} unrecognized classes to ${
|
|
4321
|
+
`Audit complete. Exported ${unique.length} unrecognized classes to ${path__default.relative(cwd, todoPath)}.`
|
|
4088
4322
|
);
|
|
4089
4323
|
printInfo(
|
|
4090
4324
|
"Edit this file to map custom classes, then run: npx @csszyx/cli migrate --resolve-todos .csszyx-todo.json"
|
|
4091
4325
|
);
|
|
4092
4326
|
log.writeLine(
|
|
4093
|
-
`Audit: ${unique.length} unrecognized classes written to ${
|
|
4327
|
+
`Audit: ${unique.length} unrecognized classes written to ${path__default.relative(cwd, todoPath)}`
|
|
4094
4328
|
);
|
|
4095
4329
|
}
|
|
4096
4330
|
}
|
|
@@ -4115,13 +4349,363 @@ async function migrate(options = {}) {
|
|
|
4115
4349
|
}
|
|
4116
4350
|
try {
|
|
4117
4351
|
log.flush();
|
|
4118
|
-
printInfo(`Migration log saved to ${
|
|
4352
|
+
printInfo(`Migration log saved to ${path__default.relative(cwd, log.filePath)}`);
|
|
4119
4353
|
} catch {
|
|
4120
4354
|
}
|
|
4121
4355
|
}
|
|
4122
4356
|
|
|
4357
|
+
const DEFAULT_NEXT_SOURCE_PATTERN = "{app,pages,src}/**/*.{ts,tsx,js,jsx,mjs,cjs}";
|
|
4358
|
+
const DEFAULT_NEXT_SOURCE_IGNORE = [
|
|
4359
|
+
"node_modules/**",
|
|
4360
|
+
".git/**",
|
|
4361
|
+
".next/**",
|
|
4362
|
+
".next-turbo-*/**",
|
|
4363
|
+
".csszyx/**",
|
|
4364
|
+
"dist/**",
|
|
4365
|
+
"build/**"
|
|
4366
|
+
];
|
|
4367
|
+
|
|
4368
|
+
async function nextPrebuild(options = {}) {
|
|
4369
|
+
const cwd = path__default.resolve(options.cwd ?? process.cwd());
|
|
4370
|
+
const root = path__default.resolve(options.root ?? cwd);
|
|
4371
|
+
const pattern = options.pattern ?? DEFAULT_NEXT_SOURCE_PATTERN;
|
|
4372
|
+
try {
|
|
4373
|
+
const mode = normalizeMode(options.mode);
|
|
4374
|
+
const parserMode = normalizeParserMode$1(options.parserMode);
|
|
4375
|
+
const matches = await fg(pattern, {
|
|
4376
|
+
cwd: root,
|
|
4377
|
+
absolute: true,
|
|
4378
|
+
ignore: [...DEFAULT_NEXT_SOURCE_IGNORE, ...options.extraIgnore ?? []],
|
|
4379
|
+
dot: false,
|
|
4380
|
+
onlyFiles: true
|
|
4381
|
+
});
|
|
4382
|
+
if (matches.length === 0) {
|
|
4383
|
+
const message = `No source files matched pattern \`${pattern}\` under ${root}.`;
|
|
4384
|
+
if (options.json) {
|
|
4385
|
+
console.log(
|
|
4386
|
+
JSON.stringify(
|
|
4387
|
+
{ ok: false, reason: "no-files-matched", root, pattern, mode },
|
|
4388
|
+
null,
|
|
4389
|
+
2
|
|
4390
|
+
)
|
|
4391
|
+
);
|
|
4392
|
+
} else {
|
|
4393
|
+
console.error(`${colors.error(icons.error)} ${message}`);
|
|
4394
|
+
}
|
|
4395
|
+
return 1;
|
|
4396
|
+
}
|
|
4397
|
+
const result = runNextPrebuild({
|
|
4398
|
+
files: matches,
|
|
4399
|
+
explicitRoot: root,
|
|
4400
|
+
cwd,
|
|
4401
|
+
mode,
|
|
4402
|
+
parserMode,
|
|
4403
|
+
safelistOutputFile: options.outputFile,
|
|
4404
|
+
cacheDir: options.cacheDir,
|
|
4405
|
+
config: { mangleVars: false }
|
|
4406
|
+
// Versions intentionally omitted: runNextPrebuild's package.json
|
|
4407
|
+
// fallback reads the real installed @csszyx/unplugin and
|
|
4408
|
+
// @csszyx/compiler versions so the manifest's generation identity
|
|
4409
|
+
// tracks the engine that actually runs the transform.
|
|
4410
|
+
});
|
|
4411
|
+
if (options.json) {
|
|
4412
|
+
console.log(
|
|
4413
|
+
JSON.stringify(
|
|
4414
|
+
{
|
|
4415
|
+
ok: true,
|
|
4416
|
+
root,
|
|
4417
|
+
mode,
|
|
4418
|
+
scannedCount: result.scannedCount,
|
|
4419
|
+
transformedCount: result.transformedCount,
|
|
4420
|
+
skippedMissingCount: result.skippedMissingCount,
|
|
4421
|
+
sourceCount: result.sourceCount,
|
|
4422
|
+
classCount: result.classCount,
|
|
4423
|
+
manifestPath: result.manifestPath,
|
|
4424
|
+
safelistOutputPath: result.safelistOutputPath
|
|
4425
|
+
},
|
|
4426
|
+
null,
|
|
4427
|
+
2
|
|
4428
|
+
)
|
|
4429
|
+
);
|
|
4430
|
+
} else {
|
|
4431
|
+
console.log(`${colors.success(icons.success)} csszyx next prebuild done`);
|
|
4432
|
+
console.log(` root: ${root}`);
|
|
4433
|
+
console.log(` mode: ${mode}`);
|
|
4434
|
+
console.log(` scanned: ${result.scannedCount}`);
|
|
4435
|
+
console.log(` transformed: ${result.transformedCount}`);
|
|
4436
|
+
console.log(` skipped: ${result.skippedMissingCount}`);
|
|
4437
|
+
console.log(` sources: ${result.sourceCount}`);
|
|
4438
|
+
console.log(` classes: ${result.classCount}`);
|
|
4439
|
+
console.log(` safelist: ${result.safelistOutputPath}`);
|
|
4440
|
+
console.log(` manifest: ${result.manifestPath}`);
|
|
4441
|
+
}
|
|
4442
|
+
return 0;
|
|
4443
|
+
} catch (error) {
|
|
4444
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4445
|
+
if (options.json) {
|
|
4446
|
+
console.log(JSON.stringify({ ok: false, reason: message }, null, 2));
|
|
4447
|
+
} else {
|
|
4448
|
+
console.error(`${colors.error(icons.error)} ${message}`);
|
|
4449
|
+
}
|
|
4450
|
+
return 1;
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
function normalizeMode(mode) {
|
|
4454
|
+
if (mode === void 0) {
|
|
4455
|
+
return "production";
|
|
4456
|
+
}
|
|
4457
|
+
if (mode === "development" || mode === "production") {
|
|
4458
|
+
return mode;
|
|
4459
|
+
}
|
|
4460
|
+
throw new Error(`Invalid --mode "${mode}". Expected "development" or "production".`);
|
|
4461
|
+
}
|
|
4462
|
+
function normalizeParserMode$1(parserMode) {
|
|
4463
|
+
if (parserMode === void 0) {
|
|
4464
|
+
return void 0;
|
|
4465
|
+
}
|
|
4466
|
+
if (parserMode === "rust" || parserMode === "oxc" || parserMode === "babel") {
|
|
4467
|
+
return parserMode;
|
|
4468
|
+
}
|
|
4469
|
+
throw new Error(`Invalid --parser-mode "${parserMode}". Expected "rust", "oxc", or "babel".`);
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
const SOURCE_EXTENSION = /\.[cm]?[jt]sx?$/i;
|
|
4473
|
+
async function startNextWatch(options = {}, dependencies = {}) {
|
|
4474
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
4475
|
+
const root = path.resolve(options.root ?? cwd);
|
|
4476
|
+
const pattern = options.pattern ?? DEFAULT_NEXT_SOURCE_PATTERN;
|
|
4477
|
+
const ignore = [...DEFAULT_NEXT_SOURCE_IGNORE, ...options.extraIgnore ?? []];
|
|
4478
|
+
const parserMode = normalizeParserMode(options.parserMode);
|
|
4479
|
+
const debounceMs = normalizeDebounceMs(options.debounceMs);
|
|
4480
|
+
const files = await fg(pattern, {
|
|
4481
|
+
cwd: root,
|
|
4482
|
+
absolute: true,
|
|
4483
|
+
ignore,
|
|
4484
|
+
dot: false,
|
|
4485
|
+
onlyFiles: true
|
|
4486
|
+
});
|
|
4487
|
+
if (files.length === 0) {
|
|
4488
|
+
throw new Error(`No source files matched pattern \`${pattern}\` under ${root}.`);
|
|
4489
|
+
}
|
|
4490
|
+
const prebuild = runNextPrebuild({
|
|
4491
|
+
files,
|
|
4492
|
+
explicitRoot: root,
|
|
4493
|
+
cwd,
|
|
4494
|
+
mode: "development",
|
|
4495
|
+
parserMode,
|
|
4496
|
+
safelistOutputFile: options.outputFile,
|
|
4497
|
+
cacheDir: options.cacheDir,
|
|
4498
|
+
config: { mangleVars: false }
|
|
4499
|
+
});
|
|
4500
|
+
let resolveFailure = () => {
|
|
4501
|
+
};
|
|
4502
|
+
let failed = false;
|
|
4503
|
+
const failure = new Promise((resolve) => {
|
|
4504
|
+
resolveFailure = resolve;
|
|
4505
|
+
});
|
|
4506
|
+
const reportFailure = (error) => {
|
|
4507
|
+
if (failed) {
|
|
4508
|
+
return;
|
|
4509
|
+
}
|
|
4510
|
+
failed = true;
|
|
4511
|
+
resolveFailure(error instanceof Error ? error : new Error(String(error)));
|
|
4512
|
+
};
|
|
4513
|
+
const controller = new NextSafelistWatcher({
|
|
4514
|
+
context: prebuild.context,
|
|
4515
|
+
debounceMs,
|
|
4516
|
+
onError: reportFailure
|
|
4517
|
+
});
|
|
4518
|
+
const watchFactory = dependencies.watch ?? watch;
|
|
4519
|
+
const fsWatcher = watchFactory(root, {
|
|
4520
|
+
ignoreInitial: true,
|
|
4521
|
+
persistent: true,
|
|
4522
|
+
atomic: true,
|
|
4523
|
+
awaitWriteFinish: {
|
|
4524
|
+
stabilityThreshold: 25,
|
|
4525
|
+
pollInterval: 10
|
|
4526
|
+
},
|
|
4527
|
+
ignored: createIgnoredMatcher(root, prebuild.context.safelist.shardsDir, ignore)
|
|
4528
|
+
});
|
|
4529
|
+
fsWatcher.on("all", (event, filePath) => {
|
|
4530
|
+
const absolutePath = path.resolve(filePath);
|
|
4531
|
+
if (event === "add" || event === "change" || event === "unlink") {
|
|
4532
|
+
if (controller.notify(event, absolutePath) || event !== "unlink" || !SOURCE_EXTENSION.test(absolutePath)) {
|
|
4533
|
+
return;
|
|
4534
|
+
}
|
|
4535
|
+
controller.notifySourceRemoval(absolutePath);
|
|
4536
|
+
}
|
|
4537
|
+
});
|
|
4538
|
+
fsWatcher.on("error", reportFailure);
|
|
4539
|
+
try {
|
|
4540
|
+
await waitForWatcherReady(fsWatcher);
|
|
4541
|
+
controller.start();
|
|
4542
|
+
} catch (error) {
|
|
4543
|
+
await fsWatcher.close();
|
|
4544
|
+
controller.close();
|
|
4545
|
+
throw error;
|
|
4546
|
+
}
|
|
4547
|
+
let closed = false;
|
|
4548
|
+
return {
|
|
4549
|
+
root,
|
|
4550
|
+
sourcePattern: pattern,
|
|
4551
|
+
safelistOutputPath: prebuild.safelistOutputPath,
|
|
4552
|
+
manifestPath: prebuild.manifestPath,
|
|
4553
|
+
failure,
|
|
4554
|
+
close: async () => {
|
|
4555
|
+
if (closed) {
|
|
4556
|
+
return;
|
|
4557
|
+
}
|
|
4558
|
+
closed = true;
|
|
4559
|
+
await fsWatcher.close();
|
|
4560
|
+
controller.close();
|
|
4561
|
+
}
|
|
4562
|
+
};
|
|
4563
|
+
}
|
|
4564
|
+
async function nextWatch(options = {}) {
|
|
4565
|
+
let session;
|
|
4566
|
+
let exitCode = 0;
|
|
4567
|
+
try {
|
|
4568
|
+
session = await startNextWatch(options);
|
|
4569
|
+
if (!options.silent) {
|
|
4570
|
+
console.log(`${colors.success(icons.success)} csszyx next watch ready`);
|
|
4571
|
+
console.log(` root: ${session.root}`);
|
|
4572
|
+
console.log(` pattern: ${session.sourcePattern}`);
|
|
4573
|
+
console.log(` safelist: ${session.safelistOutputPath}`);
|
|
4574
|
+
console.log(` manifest: ${session.manifestPath}`);
|
|
4575
|
+
}
|
|
4576
|
+
const outcome = await waitForShutdown(session.failure);
|
|
4577
|
+
if (outcome) {
|
|
4578
|
+
console.error(`${colors.error(icons.error)} ${outcome.message}`);
|
|
4579
|
+
exitCode = 1;
|
|
4580
|
+
}
|
|
4581
|
+
} catch (error) {
|
|
4582
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4583
|
+
console.error(`${colors.error(icons.error)} ${message}`);
|
|
4584
|
+
exitCode = 1;
|
|
4585
|
+
}
|
|
4586
|
+
try {
|
|
4587
|
+
await session?.close();
|
|
4588
|
+
} catch (error) {
|
|
4589
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4590
|
+
console.error(`${colors.error(icons.error)} Failed to close Next watcher: ${message}`);
|
|
4591
|
+
exitCode = 1;
|
|
4592
|
+
}
|
|
4593
|
+
return exitCode;
|
|
4594
|
+
}
|
|
4595
|
+
function waitForWatcherReady(watcher) {
|
|
4596
|
+
return new Promise((resolve, reject) => {
|
|
4597
|
+
const onReady = () => {
|
|
4598
|
+
watcher.off("error", onStartupError);
|
|
4599
|
+
resolve();
|
|
4600
|
+
};
|
|
4601
|
+
const onStartupError = (error) => {
|
|
4602
|
+
watcher.off("ready", onReady);
|
|
4603
|
+
reject(error);
|
|
4604
|
+
};
|
|
4605
|
+
watcher.once("ready", onReady);
|
|
4606
|
+
watcher.once("error", onStartupError);
|
|
4607
|
+
});
|
|
4608
|
+
}
|
|
4609
|
+
function waitForShutdown(failure) {
|
|
4610
|
+
return new Promise((resolve) => {
|
|
4611
|
+
const cleanup = () => {
|
|
4612
|
+
process.off("SIGINT", onSignal);
|
|
4613
|
+
process.off("SIGTERM", onSignal);
|
|
4614
|
+
};
|
|
4615
|
+
const onSignal = () => {
|
|
4616
|
+
cleanup();
|
|
4617
|
+
resolve(void 0);
|
|
4618
|
+
};
|
|
4619
|
+
process.once("SIGINT", onSignal);
|
|
4620
|
+
process.once("SIGTERM", onSignal);
|
|
4621
|
+
failure.then((error) => {
|
|
4622
|
+
cleanup();
|
|
4623
|
+
resolve(error);
|
|
4624
|
+
});
|
|
4625
|
+
});
|
|
4626
|
+
}
|
|
4627
|
+
function createIgnoredMatcher(root, shardsDir, ignore) {
|
|
4628
|
+
const normalizedShardsDir = path.resolve(shardsDir);
|
|
4629
|
+
const matchers = ignore.flatMap((pattern) => {
|
|
4630
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
4631
|
+
const variants = normalized.endsWith("/**") ? [normalized, normalized.slice(0, -3)] : [normalized];
|
|
4632
|
+
return variants.map((variant) => new Minimatch(variant, { dot: true }));
|
|
4633
|
+
});
|
|
4634
|
+
return (candidate) => {
|
|
4635
|
+
const absolute = path.resolve(candidate);
|
|
4636
|
+
const relativeToShards = path.relative(absolute, normalizedShardsDir);
|
|
4637
|
+
if (absolute === normalizedShardsDir || absolute.startsWith(`${normalizedShardsDir}${path.sep}`) || relativeToShards !== ".." && !relativeToShards.startsWith(`..${path.sep}`) && !path.isAbsolute(relativeToShards)) {
|
|
4638
|
+
return false;
|
|
4639
|
+
}
|
|
4640
|
+
const relative = path.relative(root, absolute).replace(/\\/g, "/");
|
|
4641
|
+
if (!relative || relative.startsWith("../") || path.isAbsolute(relative)) {
|
|
4642
|
+
return false;
|
|
4643
|
+
}
|
|
4644
|
+
return matchers.some((matcher) => matcher.match(relative));
|
|
4645
|
+
};
|
|
4646
|
+
}
|
|
4647
|
+
function normalizeParserMode(parserMode) {
|
|
4648
|
+
if (parserMode === void 0) {
|
|
4649
|
+
return void 0;
|
|
4650
|
+
}
|
|
4651
|
+
if (parserMode === "rust" || parserMode === "oxc" || parserMode === "babel") {
|
|
4652
|
+
return parserMode;
|
|
4653
|
+
}
|
|
4654
|
+
throw new Error(`Invalid --parser-mode "${parserMode}". Expected "rust", "oxc", or "babel".`);
|
|
4655
|
+
}
|
|
4656
|
+
function normalizeDebounceMs(debounceMs) {
|
|
4657
|
+
if (debounceMs === void 0) {
|
|
4658
|
+
return void 0;
|
|
4659
|
+
}
|
|
4660
|
+
const parsed = typeof debounceMs === "number" ? debounceMs : Number(debounceMs);
|
|
4661
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 6e4) {
|
|
4662
|
+
throw new Error("Invalid --debounce-ms. Expected an integer between 0 and 60000.");
|
|
4663
|
+
}
|
|
4664
|
+
return parsed;
|
|
4665
|
+
}
|
|
4666
|
+
|
|
4123
4667
|
const cli = cac("csszyx");
|
|
4124
|
-
|
|
4668
|
+
normalizeNextCommandAlias(process.argv);
|
|
4669
|
+
function readCliVersion() {
|
|
4670
|
+
try {
|
|
4671
|
+
const manifest = JSON.parse(
|
|
4672
|
+
readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
4673
|
+
);
|
|
4674
|
+
return typeof manifest.version === "string" ? manifest.version : "0.0.0";
|
|
4675
|
+
} catch {
|
|
4676
|
+
return "0.0.0";
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
const VERSION = readCliVersion();
|
|
4680
|
+
async function runNextPrebuildCommand(pattern, options) {
|
|
4681
|
+
const code = await nextPrebuild({
|
|
4682
|
+
cwd: options.cwd,
|
|
4683
|
+
root: options.root,
|
|
4684
|
+
mode: options.mode,
|
|
4685
|
+
parserMode: options.parserMode,
|
|
4686
|
+
outputFile: options.outputFile,
|
|
4687
|
+
cacheDir: options.cacheDir,
|
|
4688
|
+
pattern,
|
|
4689
|
+
extraIgnore: options.ignore ? String(options.ignore).split(",") : void 0,
|
|
4690
|
+
json: options.json
|
|
4691
|
+
});
|
|
4692
|
+
if (code !== 0) {
|
|
4693
|
+
process.exit(code);
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
async function runNextWatchCommand(pattern, options) {
|
|
4697
|
+
const code = await nextWatch({
|
|
4698
|
+
cwd: options.cwd,
|
|
4699
|
+
root: options.root,
|
|
4700
|
+
parserMode: options.parserMode,
|
|
4701
|
+
outputFile: options.outputFile,
|
|
4702
|
+
cacheDir: options.cacheDir,
|
|
4703
|
+
pattern,
|
|
4704
|
+
extraIgnore: options.ignore ? String(options.ignore).split(",") : void 0,
|
|
4705
|
+
debounceMs: options.debounceMs
|
|
4706
|
+
});
|
|
4707
|
+
process.exitCode = code;
|
|
4708
|
+
}
|
|
4125
4709
|
cli.command("init", "Setup csszyx in your project").option("--framework <name>", "Specify framework").option("--yes", "Skip prompts (use defaults)").option("--cwd <dir>", "Current working directory").action(async (options) => {
|
|
4126
4710
|
await init({
|
|
4127
4711
|
framework: options.framework,
|
|
@@ -4170,11 +4754,27 @@ cli.command("migrate [dir]", "Convert Tailwind className to sz prop").option("--
|
|
|
4170
4754
|
resolveTodos: options.resolveTodos
|
|
4171
4755
|
});
|
|
4172
4756
|
});
|
|
4757
|
+
cli.command(
|
|
4758
|
+
"next-prebuild [pattern]",
|
|
4759
|
+
"Seed the Next.js Turbopack csszyx safelist and generation manifest"
|
|
4760
|
+
).option("--root <dir>", "Next app root (defaults to cwd)").option("--cwd <dir>", "Current working directory").option("--mode <mode>", "development | production (default: production)").option("--parser-mode <mode>", "rust | oxc | babel (default: rust)").option(
|
|
4761
|
+
"--output-file <path>",
|
|
4762
|
+
"Tailwind @source safelist output (default: csszyx-classes.html)"
|
|
4763
|
+
).option("--cache-dir <dir>", "Cache directory relative to root (default: .csszyx/cache)").option("--ignore <patterns>", "Extra glob patterns to ignore (comma-separated)").option("--json", "Emit a single JSON result instead of formatted text").action(runNextPrebuildCommand);
|
|
4764
|
+
cli.command("next-watch [pattern]", "Maintain the Next.js Turbopack csszyx safelist").option("--root <dir>", "Next app root (defaults to cwd)").option("--cwd <dir>", "Current working directory").option("--parser-mode <mode>", "rust | oxc | babel (default: rust)").option(
|
|
4765
|
+
"--output-file <path>",
|
|
4766
|
+
"Tailwind @source safelist output (default: csszyx-classes.html)"
|
|
4767
|
+
).option("--cache-dir <dir>", "Cache directory relative to root (default: .csszyx/cache)").option("--ignore <patterns>", "Extra glob patterns to ignore (comma-separated)").option("--debounce-ms <ms>", "Safelist materialization debounce (default: 50)").action(runNextWatchCommand);
|
|
4173
4768
|
cli.command("").action(() => {
|
|
4174
4769
|
cli.outputHelp();
|
|
4175
4770
|
});
|
|
4176
4771
|
cli.help();
|
|
4177
4772
|
cli.version(VERSION);
|
|
4178
4773
|
cli.parse();
|
|
4774
|
+
function normalizeNextCommandAlias(argv) {
|
|
4775
|
+
if (argv[2] === "next" && (argv[3] === "prebuild" || argv[3] === "watch")) {
|
|
4776
|
+
argv.splice(2, 2, `next-${argv[3]}`);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4179
4779
|
|
|
4180
4780
|
export { classNameToSzObject, extractScreenKeys, extractSpacingKeys, findConfigFile, flattenColors, generateAndWriteTypes, generateTypeDeclarations, generateTypes, transformSource as migrateSource, scanTailwindConfig, writeDeclarationFile };
|