tailwind-unwind 0.3.0 → 0.4.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 +97 -252
- package/dist/{chunk-4GXMK3NB.js → chunk-UXXIEFP4.js} +351 -101
- package/dist/chunk-UXXIEFP4.js.map +1 -0
- package/dist/cli/index.js +60 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +71 -27
- package/dist/index.js +11 -1
- package/package.json +3 -2
- package/dist/chunk-4GXMK3NB.js.map +0 -1
|
@@ -35,28 +35,28 @@ var KNOWN_COMMAND_KEYS = /* @__PURE__ */ new Set([
|
|
|
35
35
|
function isRecord(value) {
|
|
36
36
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
37
|
}
|
|
38
|
-
function assertPositiveNumber(value,
|
|
38
|
+
function assertPositiveNumber(value, path9, errors) {
|
|
39
39
|
if (value === void 0) {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
43
|
-
errors.push(`${
|
|
43
|
+
errors.push(`${path9} must be a positive number`);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
function assertBoolean(value,
|
|
46
|
+
function assertBoolean(value, path9, errors) {
|
|
47
47
|
if (value === void 0) {
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
if (typeof value !== "boolean") {
|
|
51
|
-
errors.push(`${
|
|
51
|
+
errors.push(`${path9} must be a boolean`);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
function assertStringArray(value,
|
|
54
|
+
function assertStringArray(value, path9, errors) {
|
|
55
55
|
if (value === void 0) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
if (!Array.isArray(value) || !value.every((item) => typeof item === "string" && item.length > 0)) {
|
|
59
|
-
errors.push(`${
|
|
59
|
+
errors.push(`${path9} must be an array of non-empty strings`);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
function validateCommandSection(value, section, errors) {
|
|
@@ -169,6 +169,7 @@ import fs from "fs/promises";
|
|
|
169
169
|
import path from "path";
|
|
170
170
|
import { pathToFileURL } from "url";
|
|
171
171
|
var CONFIG_FILENAMES = [
|
|
172
|
+
"tailwind-unwind.config.ts",
|
|
172
173
|
"tailwind-unwind.config.js",
|
|
173
174
|
"tailwind-unwind.config.mjs",
|
|
174
175
|
"tailwind-unwind.config.cjs",
|
|
@@ -263,7 +264,7 @@ function normalizeLoadedConfig(raw) {
|
|
|
263
264
|
}
|
|
264
265
|
function mergeCommandConfig(command, fileConfig) {
|
|
265
266
|
const { analyze, generate: generate2, apply, ...root } = fileConfig;
|
|
266
|
-
const commandSection = command === "analyze" ? analyze : command === "generate" ? generate2 : apply;
|
|
267
|
+
const commandSection = command === "analyze" ? analyze : command === "generate" ? generate2 : command === "apply" ? apply : void 0;
|
|
267
268
|
return {
|
|
268
269
|
...root,
|
|
269
270
|
...commandSection
|
|
@@ -323,6 +324,11 @@ async function resolveConfigFile(explicitPath, searchRoots) {
|
|
|
323
324
|
return null;
|
|
324
325
|
}
|
|
325
326
|
async function importConfigModule(configPath) {
|
|
327
|
+
if (configPath.endsWith(".ts")) {
|
|
328
|
+
const { createJiti } = await import("jiti");
|
|
329
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
330
|
+
return jiti(configPath);
|
|
331
|
+
}
|
|
326
332
|
const moduleUrl = pathToFileURL(configPath).href;
|
|
327
333
|
const imported = await import(moduleUrl);
|
|
328
334
|
return imported;
|
|
@@ -1048,8 +1054,8 @@ var traverse = resolveTraverse(babelTraverse);
|
|
|
1048
1054
|
function collectVariantRegistry(ast) {
|
|
1049
1055
|
const registry = /* @__PURE__ */ new Map();
|
|
1050
1056
|
traverse(ast, {
|
|
1051
|
-
VariableDeclarator(
|
|
1052
|
-
registerVariantDeclarator(
|
|
1057
|
+
VariableDeclarator(path9) {
|
|
1058
|
+
registerVariantDeclarator(path9.node, registry);
|
|
1053
1059
|
}
|
|
1054
1060
|
});
|
|
1055
1061
|
return registry;
|
|
@@ -1286,8 +1292,8 @@ function resolveTraverse2(module) {
|
|
|
1286
1292
|
throw new Error("Failed to load @babel/traverse");
|
|
1287
1293
|
}
|
|
1288
1294
|
var traverse2 = resolveTraverse2(babelTraverse2);
|
|
1289
|
-
function isJSXElementWithClassAttribute(
|
|
1290
|
-
const opening =
|
|
1295
|
+
function isJSXElementWithClassAttribute(path9) {
|
|
1296
|
+
const opening = path9.node.openingElement;
|
|
1291
1297
|
return opening.attributes.some(
|
|
1292
1298
|
(attr) => attr.type === "JSXAttribute" && isClassAttribute(attr)
|
|
1293
1299
|
);
|
|
@@ -1297,11 +1303,11 @@ function collectExtractionsFromAst(ast, filePath) {
|
|
|
1297
1303
|
const warnings = [];
|
|
1298
1304
|
const variantRegistry = collectVariantRegistry(ast);
|
|
1299
1305
|
traverse2(ast, {
|
|
1300
|
-
JSXElement(
|
|
1301
|
-
if (!isJSXElementWithClassAttribute(
|
|
1306
|
+
JSXElement(path9) {
|
|
1307
|
+
if (!isJSXElementWithClassAttribute(path9)) {
|
|
1302
1308
|
return;
|
|
1303
1309
|
}
|
|
1304
|
-
const opening =
|
|
1310
|
+
const opening = path9.node.openingElement;
|
|
1305
1311
|
for (const attr of opening.attributes) {
|
|
1306
1312
|
if (attr.type !== "JSXAttribute") continue;
|
|
1307
1313
|
const extraction = extractFromJSXAttribute(attr, variantRegistry);
|
|
@@ -1352,23 +1358,95 @@ var IGNORE_PATTERNS = IGNORED_DIRECTORIES.map(
|
|
|
1352
1358
|
(dir) => `**/${dir}/**`
|
|
1353
1359
|
);
|
|
1354
1360
|
|
|
1361
|
+
// src/scanner/gitChanged.ts
|
|
1362
|
+
import { execFile } from "child_process";
|
|
1363
|
+
import path2 from "path";
|
|
1364
|
+
import { promisify } from "util";
|
|
1365
|
+
var execFileAsync = promisify(execFile);
|
|
1366
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
|
|
1367
|
+
function isSourceFile(filePath) {
|
|
1368
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
1369
|
+
return SOURCE_EXTENSIONS.has(ext);
|
|
1370
|
+
}
|
|
1371
|
+
function isIgnoredPath(filePath) {
|
|
1372
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
1373
|
+
return IGNORE_PATTERNS.some((pattern) => {
|
|
1374
|
+
const dir = pattern.replace("/**", "").replace("**/", "");
|
|
1375
|
+
return normalized.includes(`/${dir}/`);
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
async function runGit(cwd, args) {
|
|
1379
|
+
try {
|
|
1380
|
+
const { stdout } = await execFileAsync("git", args, { cwd });
|
|
1381
|
+
return stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
1382
|
+
} catch {
|
|
1383
|
+
return [];
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
function resolveAbsoluteFiles(files, rootPath) {
|
|
1387
|
+
const absoluteRoot = path2.resolve(rootPath);
|
|
1388
|
+
return [...new Set(
|
|
1389
|
+
files.map((file) => path2.resolve(absoluteRoot, file)).filter((file) => file.startsWith(absoluteRoot)).filter(isSourceFile).filter((file) => !isIgnoredPath(path2.relative(absoluteRoot, file)))
|
|
1390
|
+
)].sort();
|
|
1391
|
+
}
|
|
1392
|
+
async function getChangedSourceFiles(rootPath, ref = "HEAD") {
|
|
1393
|
+
const cwd = path2.resolve(rootPath);
|
|
1394
|
+
const unstaged = await runGit(cwd, ["diff", "--name-only", ref]);
|
|
1395
|
+
const staged = await runGit(cwd, ["diff", "--cached", "--name-only", ref]);
|
|
1396
|
+
const untracked = await runGit(cwd, [
|
|
1397
|
+
"ls-files",
|
|
1398
|
+
"--others",
|
|
1399
|
+
"--exclude-standard"
|
|
1400
|
+
]);
|
|
1401
|
+
return resolveAbsoluteFiles(
|
|
1402
|
+
[...unstaged, ...staged, ...untracked],
|
|
1403
|
+
cwd
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
async function findGitRoot(startPath) {
|
|
1407
|
+
try {
|
|
1408
|
+
const { stdout } = await execFileAsync(
|
|
1409
|
+
"git",
|
|
1410
|
+
["rev-parse", "--show-toplevel"],
|
|
1411
|
+
{ cwd: path2.resolve(startPath) }
|
|
1412
|
+
);
|
|
1413
|
+
return stdout.trim();
|
|
1414
|
+
} catch {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
async function isGitRepository(rootPath) {
|
|
1419
|
+
return await findGitRoot(rootPath) !== null;
|
|
1420
|
+
}
|
|
1421
|
+
async function getChangedFilesInScope(scopePath, ref = "HEAD") {
|
|
1422
|
+
const gitRoot = await findGitRoot(scopePath);
|
|
1423
|
+
if (!gitRoot) {
|
|
1424
|
+
throw new Error("Not a git repository. Remove --changed or run inside a git repo.");
|
|
1425
|
+
}
|
|
1426
|
+
const absoluteScope = path2.resolve(scopePath);
|
|
1427
|
+
const changed = await getChangedSourceFiles(gitRoot, ref);
|
|
1428
|
+
return changed.filter(
|
|
1429
|
+
(file) => file === absoluteScope || file.startsWith(`${absoluteScope}${path2.sep}`)
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1355
1433
|
// src/scanner/fileWalker.ts
|
|
1356
1434
|
import fg from "fast-glob";
|
|
1357
|
-
import
|
|
1358
|
-
var
|
|
1435
|
+
import path3 from "path";
|
|
1436
|
+
var SOURCE_EXTENSIONS2 = ["tsx", "jsx", "ts", "js"];
|
|
1359
1437
|
function toAbsolutePattern(basePath, pattern) {
|
|
1360
1438
|
const normalized = pattern.replace(/\\/g, "/");
|
|
1361
|
-
if (
|
|
1439
|
+
if (path3.isAbsolute(normalized)) {
|
|
1362
1440
|
return normalized;
|
|
1363
1441
|
}
|
|
1364
|
-
return
|
|
1442
|
+
return path3.join(basePath, normalized).replace(/\\/g, "/");
|
|
1365
1443
|
}
|
|
1366
1444
|
function buildIncludePatterns(basePath, include) {
|
|
1367
1445
|
if (include && include.length > 0) {
|
|
1368
1446
|
return include.map((pattern) => toAbsolutePattern(basePath, pattern));
|
|
1369
1447
|
}
|
|
1370
|
-
return
|
|
1371
|
-
(ext) =>
|
|
1448
|
+
return SOURCE_EXTENSIONS2.map(
|
|
1449
|
+
(ext) => path3.join(basePath, `**/*.${ext}`).replace(/\\/g, "/")
|
|
1372
1450
|
);
|
|
1373
1451
|
}
|
|
1374
1452
|
function buildIgnorePatterns(exclude) {
|
|
@@ -1382,7 +1460,7 @@ function buildIgnorePatterns(exclude) {
|
|
|
1382
1460
|
return [...IGNORE_PATTERNS, ...userExcludes];
|
|
1383
1461
|
}
|
|
1384
1462
|
async function walkSourceFiles(targetPath, options = {}) {
|
|
1385
|
-
const absolutePath =
|
|
1463
|
+
const absolutePath = path3.resolve(targetPath);
|
|
1386
1464
|
const patterns = buildIncludePatterns(absolutePath, options.include);
|
|
1387
1465
|
const ignore = buildIgnorePatterns(options.exclude);
|
|
1388
1466
|
const files = await fg(patterns, {
|
|
@@ -1397,7 +1475,7 @@ async function walkSourceFiles(targetPath, options = {}) {
|
|
|
1397
1475
|
|
|
1398
1476
|
// src/core/scanProject.ts
|
|
1399
1477
|
import fs3 from "fs/promises";
|
|
1400
|
-
import
|
|
1478
|
+
import path4 from "path";
|
|
1401
1479
|
async function pathExists2(targetPath) {
|
|
1402
1480
|
try {
|
|
1403
1481
|
await fs3.access(targetPath);
|
|
@@ -1407,18 +1485,23 @@ async function pathExists2(targetPath) {
|
|
|
1407
1485
|
}
|
|
1408
1486
|
}
|
|
1409
1487
|
async function scanProject(options) {
|
|
1410
|
-
const resolvedPath =
|
|
1488
|
+
const resolvedPath = path4.resolve(options.targetPath);
|
|
1411
1489
|
if (!await pathExists2(resolvedPath)) {
|
|
1412
1490
|
throw new Error(`Path does not exist: ${resolvedPath}`);
|
|
1413
1491
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1492
|
+
let files;
|
|
1493
|
+
if (options.changed !== void 0) {
|
|
1494
|
+
const ref = typeof options.changed === "string" ? options.changed : "HEAD";
|
|
1495
|
+
files = await getChangedFilesInScope(resolvedPath, ref);
|
|
1496
|
+
} else {
|
|
1497
|
+
files = await walkSourceFiles(resolvedPath, {
|
|
1498
|
+
include: options.include,
|
|
1499
|
+
exclude: options.exclude
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1418
1502
|
if (files.length === 0) {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
);
|
|
1503
|
+
const hint = options.changed !== void 0 ? "No changed source files found for the current git diff." : `No source files (.tsx, .jsx, .ts, .js) found in: ${resolvedPath}`;
|
|
1504
|
+
throw new Error(hint);
|
|
1422
1505
|
}
|
|
1423
1506
|
const occurrences = [];
|
|
1424
1507
|
const warnings = [];
|
|
@@ -1488,8 +1571,121 @@ async function scanProject(options) {
|
|
|
1488
1571
|
};
|
|
1489
1572
|
}
|
|
1490
1573
|
|
|
1491
|
-
// src/
|
|
1574
|
+
// src/commands/init.ts
|
|
1575
|
+
import fs4 from "fs/promises";
|
|
1576
|
+
import path5 from "path";
|
|
1492
1577
|
import chalk from "chalk";
|
|
1578
|
+
function suggestionToName(suggestion) {
|
|
1579
|
+
return suggestion.replace(/^\./, "");
|
|
1580
|
+
}
|
|
1581
|
+
function detectIncludePattern(targetPath) {
|
|
1582
|
+
const normalized = targetPath.replace(/\\/g, "/");
|
|
1583
|
+
if (normalized.endsWith("/src") || normalized === "src") {
|
|
1584
|
+
return ["src/**/*.tsx", "src/**/*.jsx"];
|
|
1585
|
+
}
|
|
1586
|
+
return ["**/*.tsx", "**/*.jsx"];
|
|
1587
|
+
}
|
|
1588
|
+
function buildConfigFromScan(scanResult, targetPath, options) {
|
|
1589
|
+
const extractable = scanResult.report.stats.topCombinations.filter(
|
|
1590
|
+
(combo) => combo.extractable
|
|
1591
|
+
);
|
|
1592
|
+
const names = {};
|
|
1593
|
+
for (const combo of extractable.slice(0, options.top ?? 10)) {
|
|
1594
|
+
const utilities = [...combo.classes].sort().join(" ");
|
|
1595
|
+
names[utilities] = suggestionToName(combo.suggestion);
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
include: detectIncludePattern(targetPath),
|
|
1599
|
+
exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
|
|
1600
|
+
names: Object.keys(names).length > 0 ? names : void 0,
|
|
1601
|
+
analyze: {
|
|
1602
|
+
minOccurrences: options.minOccurrences ?? 5,
|
|
1603
|
+
top: options.top ?? 10,
|
|
1604
|
+
dedupeSubsets: options.dedupeSubsets ?? true
|
|
1605
|
+
},
|
|
1606
|
+
generate: {
|
|
1607
|
+
minOccurrences: 3,
|
|
1608
|
+
prefix: options.prefix ?? "twu-",
|
|
1609
|
+
output: "src/styles/components.css",
|
|
1610
|
+
top: 20,
|
|
1611
|
+
extractableOnly: true
|
|
1612
|
+
},
|
|
1613
|
+
apply: {
|
|
1614
|
+
minOccurrences: 3,
|
|
1615
|
+
prefix: options.prefix ?? "twu-",
|
|
1616
|
+
output: "src/styles/components.css",
|
|
1617
|
+
prettier: true,
|
|
1618
|
+
extractableOnly: true
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
async function initCommand(targetPath, options = {}) {
|
|
1623
|
+
const resolvedPath = path5.resolve(targetPath);
|
|
1624
|
+
const outputPath = path5.resolve(
|
|
1625
|
+
options.output ?? path5.join(resolvedPath, "tailwind-unwind.config.json")
|
|
1626
|
+
);
|
|
1627
|
+
if (!options.force && await fileExists(outputPath)) {
|
|
1628
|
+
throw new Error(
|
|
1629
|
+
`Config already exists: ${outputPath}. Use --force to overwrite.`
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
const scanResult = await scanProject({
|
|
1633
|
+
targetPath: resolvedPath,
|
|
1634
|
+
minOccurrences: options.minOccurrences ?? 5,
|
|
1635
|
+
minSize: options.minSize,
|
|
1636
|
+
maxSize: options.maxSize,
|
|
1637
|
+
topLimit: options.top ?? 10,
|
|
1638
|
+
dedupeSubsets: options.dedupeSubsets ?? true,
|
|
1639
|
+
include: options.include,
|
|
1640
|
+
exclude: options.exclude,
|
|
1641
|
+
extractableMinOccurrences: 3
|
|
1642
|
+
});
|
|
1643
|
+
const config = buildConfigFromScan(scanResult, resolvedPath, options);
|
|
1644
|
+
const json = `${JSON.stringify(config, null, 2)}
|
|
1645
|
+
`;
|
|
1646
|
+
await fs4.mkdir(path5.dirname(outputPath), { recursive: true });
|
|
1647
|
+
await fs4.writeFile(outputPath, json, "utf-8");
|
|
1648
|
+
const extractableCount = scanResult.report.stats.topCombinations.filter(
|
|
1649
|
+
(combo) => combo.extractable
|
|
1650
|
+
).length;
|
|
1651
|
+
console.log("");
|
|
1652
|
+
console.log(chalk.bold.green("\u2705 Config created"));
|
|
1653
|
+
console.log(chalk.gray(" Output: ") + chalk.white(outputPath));
|
|
1654
|
+
console.log(
|
|
1655
|
+
chalk.gray(" Extractable patterns: ") + chalk.white(String(extractableCount))
|
|
1656
|
+
);
|
|
1657
|
+
console.log(
|
|
1658
|
+
chalk.gray(" Custom names: ") + chalk.white(String(Object.keys(config.names ?? {}).length))
|
|
1659
|
+
);
|
|
1660
|
+
console.log("");
|
|
1661
|
+
console.log(chalk.cyan("Next steps:"));
|
|
1662
|
+
console.log(
|
|
1663
|
+
chalk.white(
|
|
1664
|
+
` npx tailwind-unwind analyze ${targetPath} --config ${path5.basename(outputPath)}`
|
|
1665
|
+
)
|
|
1666
|
+
);
|
|
1667
|
+
console.log(
|
|
1668
|
+
chalk.white(
|
|
1669
|
+
` npx tailwind-unwind generate ${targetPath} --config ${path5.basename(outputPath)}`
|
|
1670
|
+
)
|
|
1671
|
+
);
|
|
1672
|
+
console.log("");
|
|
1673
|
+
return {
|
|
1674
|
+
configPath: outputPath,
|
|
1675
|
+
extractablePatterns: extractableCount
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
async function fileExists(targetPath) {
|
|
1679
|
+
try {
|
|
1680
|
+
await fs4.access(targetPath);
|
|
1681
|
+
return true;
|
|
1682
|
+
} catch {
|
|
1683
|
+
return false;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
// src/reporters/consoleReporter.ts
|
|
1688
|
+
import chalk2 from "chalk";
|
|
1493
1689
|
function formatNumber(value) {
|
|
1494
1690
|
return value.toLocaleString("en-US");
|
|
1495
1691
|
}
|
|
@@ -1505,55 +1701,55 @@ function printConsoleReport(report, options = {}) {
|
|
|
1505
1701
|
const { stats } = report;
|
|
1506
1702
|
const topLimit = options.topLimit ?? 10;
|
|
1507
1703
|
console.log("");
|
|
1508
|
-
console.log(
|
|
1509
|
-
console.log(
|
|
1510
|
-
console.log(`Files scanned: ${
|
|
1704
|
+
console.log(chalk2.bold.cyan("\u{1F4CA} Tailwind Analysis Report"));
|
|
1705
|
+
console.log(chalk2.cyan("\u2501".repeat(41)));
|
|
1706
|
+
console.log(`Files scanned: ${chalk2.white(formatNumber(stats.filesScanned))}`);
|
|
1511
1707
|
console.log(
|
|
1512
|
-
`Components with className: ${
|
|
1708
|
+
`Components with className: ${chalk2.white(formatNumber(stats.componentsWithClassName))}`
|
|
1513
1709
|
);
|
|
1514
1710
|
console.log(
|
|
1515
|
-
`Unique class combinations: ${
|
|
1711
|
+
`Unique class combinations: ${chalk2.white(formatNumber(stats.uniqueCombinations))}`
|
|
1516
1712
|
);
|
|
1517
1713
|
console.log("");
|
|
1518
1714
|
if (stats.topCombinations.length === 0) {
|
|
1519
1715
|
console.log(
|
|
1520
|
-
|
|
1716
|
+
chalk2.yellow(
|
|
1521
1717
|
"No frequent class combinations found matching the current filters."
|
|
1522
1718
|
)
|
|
1523
1719
|
);
|
|
1524
1720
|
} else {
|
|
1525
1721
|
console.log(
|
|
1526
|
-
|
|
1722
|
+
chalk2.bold.green(`\u{1F3C6} Top ${Math.min(topLimit, stats.topCombinations.length)} most frequent combinations:`)
|
|
1527
1723
|
);
|
|
1528
1724
|
console.log("");
|
|
1529
1725
|
stats.topCombinations.forEach((combo, index) => {
|
|
1530
1726
|
const displayClasses = normalizeClasses(combo.classes);
|
|
1531
1727
|
console.log(
|
|
1532
|
-
|
|
1728
|
+
chalk2.white(`${index + 1}. `) + chalk2.yellow(`"${displayClasses}"`)
|
|
1533
1729
|
);
|
|
1534
1730
|
console.log(
|
|
1535
|
-
|
|
1731
|
+
chalk2.gray(` Occurrences: `) + chalk2.white(String(combo.occurrences))
|
|
1536
1732
|
);
|
|
1537
1733
|
console.log(
|
|
1538
|
-
|
|
1734
|
+
chalk2.gray(` Suggestion: `) + chalk2.green(combo.suggestion)
|
|
1539
1735
|
);
|
|
1540
1736
|
if (combo.extractable) {
|
|
1541
1737
|
console.log(
|
|
1542
|
-
|
|
1738
|
+
chalk2.gray(` Extractable: `) + chalk2.green("yes \u2014 use generate/apply")
|
|
1543
1739
|
);
|
|
1544
1740
|
} else {
|
|
1545
1741
|
console.log(
|
|
1546
|
-
|
|
1742
|
+
chalk2.gray(` Extractable: `) + chalk2.yellow("subset only \u2014 analyze hint")
|
|
1547
1743
|
);
|
|
1548
1744
|
}
|
|
1549
1745
|
console.log(
|
|
1550
|
-
|
|
1746
|
+
chalk2.gray(` Found in: `) + chalk2.dim(formatLocations(combo.locations))
|
|
1551
1747
|
);
|
|
1552
1748
|
console.log("");
|
|
1553
1749
|
});
|
|
1554
1750
|
}
|
|
1555
1751
|
console.log(
|
|
1556
|
-
|
|
1752
|
+
chalk2.magenta(
|
|
1557
1753
|
`\u{1F4A1} Potential code reduction: ${stats.potentialReductionPercent}%`
|
|
1558
1754
|
)
|
|
1559
1755
|
);
|
|
@@ -1562,18 +1758,18 @@ function printConsoleReport(report, options = {}) {
|
|
|
1562
1758
|
).length;
|
|
1563
1759
|
if (extractableCount > 0) {
|
|
1564
1760
|
console.log(
|
|
1565
|
-
|
|
1761
|
+
chalk2.magenta(
|
|
1566
1762
|
`\u{1F4A1} ${extractableCount} pattern(s) ready for generate/apply`
|
|
1567
1763
|
)
|
|
1568
1764
|
);
|
|
1569
1765
|
}
|
|
1570
1766
|
console.log(
|
|
1571
|
-
|
|
1767
|
+
chalk2.magenta(
|
|
1572
1768
|
"\u{1F4A1} Generate CSS: npx tailwind-unwind generate <path> --output styles.css"
|
|
1573
1769
|
)
|
|
1574
1770
|
);
|
|
1575
1771
|
console.log(
|
|
1576
|
-
|
|
1772
|
+
chalk2.magenta(
|
|
1577
1773
|
"\u{1F4A1} Apply classes: npx tailwind-unwind apply <path> --output styles.css"
|
|
1578
1774
|
)
|
|
1579
1775
|
);
|
|
@@ -1586,7 +1782,7 @@ function printJsonReport(report) {
|
|
|
1586
1782
|
}
|
|
1587
1783
|
|
|
1588
1784
|
// src/commands/analyze.ts
|
|
1589
|
-
import
|
|
1785
|
+
import chalk3 from "chalk";
|
|
1590
1786
|
async function analyzeCommand(targetPath, options = {}) {
|
|
1591
1787
|
let scanResult;
|
|
1592
1788
|
try {
|
|
@@ -1599,16 +1795,17 @@ async function analyzeCommand(targetPath, options = {}) {
|
|
|
1599
1795
|
dedupeSubsets: options.dedupeSubsets,
|
|
1600
1796
|
include: options.include,
|
|
1601
1797
|
exclude: options.exclude,
|
|
1798
|
+
changed: options.changed,
|
|
1602
1799
|
extractableMinOccurrences: 3
|
|
1603
1800
|
});
|
|
1604
1801
|
} catch (error) {
|
|
1605
1802
|
const message = error instanceof Error ? error.message : String(error);
|
|
1606
|
-
console.error(
|
|
1803
|
+
console.error(chalk3.red(`Error: ${message}`));
|
|
1607
1804
|
process.exit(1);
|
|
1608
1805
|
}
|
|
1609
1806
|
if (options.format !== "json") {
|
|
1610
1807
|
for (const warning of scanResult.warnings) {
|
|
1611
|
-
console.warn(
|
|
1808
|
+
console.warn(chalk3.yellow(`\u26A0 ${warning}`));
|
|
1612
1809
|
}
|
|
1613
1810
|
}
|
|
1614
1811
|
const report = scanResult.report;
|
|
@@ -1620,9 +1817,36 @@ async function analyzeCommand(targetPath, options = {}) {
|
|
|
1620
1817
|
return report;
|
|
1621
1818
|
}
|
|
1622
1819
|
|
|
1820
|
+
// src/analyzer/savings.ts
|
|
1821
|
+
function calculateSavings(replacements) {
|
|
1822
|
+
if (replacements.length === 0) {
|
|
1823
|
+
return {
|
|
1824
|
+
replacementCount: 0,
|
|
1825
|
+
utilityTokensBefore: 0,
|
|
1826
|
+
utilityTokensAfter: 0,
|
|
1827
|
+
tokensSaved: 0,
|
|
1828
|
+
percentReduction: 0
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
let utilityTokensBefore = 0;
|
|
1832
|
+
for (const replacement of replacements) {
|
|
1833
|
+
utilityTokensBefore += replacement.from.split(/\s+/).filter(Boolean).length;
|
|
1834
|
+
}
|
|
1835
|
+
const utilityTokensAfter = replacements.length;
|
|
1836
|
+
const tokensSaved = Math.max(0, utilityTokensBefore - utilityTokensAfter);
|
|
1837
|
+
const percentReduction = utilityTokensBefore === 0 ? 0 : Math.round(tokensSaved / utilityTokensBefore * 100);
|
|
1838
|
+
return {
|
|
1839
|
+
replacementCount: replacements.length,
|
|
1840
|
+
utilityTokensBefore,
|
|
1841
|
+
utilityTokensAfter,
|
|
1842
|
+
tokensSaved,
|
|
1843
|
+
percentReduction
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1623
1847
|
// src/codemod/formatSource.ts
|
|
1624
1848
|
import { createRequire } from "module";
|
|
1625
|
-
import
|
|
1849
|
+
import path6 from "path";
|
|
1626
1850
|
var require2 = createRequire(import.meta.url);
|
|
1627
1851
|
async function loadPrettier() {
|
|
1628
1852
|
try {
|
|
@@ -1662,7 +1886,7 @@ async function formatModifiedFiles(files, sources, cwd = process.cwd()) {
|
|
|
1662
1886
|
continue;
|
|
1663
1887
|
}
|
|
1664
1888
|
const result = await formatSource(source, {
|
|
1665
|
-
filePath:
|
|
1889
|
+
filePath: path6.resolve(cwd, file),
|
|
1666
1890
|
cwd
|
|
1667
1891
|
});
|
|
1668
1892
|
if (result.formatted) {
|
|
@@ -1858,8 +2082,8 @@ function replaceClassNamesInSource(source, replacementMap, filePath) {
|
|
|
1858
2082
|
}
|
|
1859
2083
|
const variantRegistry = collectVariantRegistry(ast);
|
|
1860
2084
|
traverse3(ast, {
|
|
1861
|
-
JSXElement(
|
|
1862
|
-
const opening =
|
|
2085
|
+
JSXElement(path9) {
|
|
2086
|
+
const opening = path9.node.openingElement;
|
|
1863
2087
|
for (const attr of opening.attributes) {
|
|
1864
2088
|
if (attr.type !== "JSXAttribute" || !isClassAttribute(attr)) {
|
|
1865
2089
|
continue;
|
|
@@ -2042,7 +2266,7 @@ function buildComponentsFromCombinations(combinations, options) {
|
|
|
2042
2266
|
}
|
|
2043
2267
|
|
|
2044
2268
|
// src/core/loadAnalyzeReport.ts
|
|
2045
|
-
import
|
|
2269
|
+
import fs5 from "fs/promises";
|
|
2046
2270
|
function isAnalysisReport(value) {
|
|
2047
2271
|
if (typeof value !== "object" || value === null) {
|
|
2048
2272
|
return false;
|
|
@@ -2051,7 +2275,7 @@ function isAnalysisReport(value) {
|
|
|
2051
2275
|
return typeof report.targetPath === "string" && typeof report.stats === "object" && Array.isArray(report.stats.topCombinations);
|
|
2052
2276
|
}
|
|
2053
2277
|
async function loadExtractableCombinations(reportPath, options = {}) {
|
|
2054
|
-
const raw = await
|
|
2278
|
+
const raw = await fs5.readFile(reportPath, "utf-8");
|
|
2055
2279
|
const parsed = JSON.parse(raw);
|
|
2056
2280
|
if (!isAnalysisReport(parsed)) {
|
|
2057
2281
|
throw new Error(`Invalid analyze report: ${reportPath}`);
|
|
@@ -2079,9 +2303,9 @@ function printApplyJsonReport(report) {
|
|
|
2079
2303
|
}
|
|
2080
2304
|
|
|
2081
2305
|
// src/commands/apply.ts
|
|
2082
|
-
import
|
|
2083
|
-
import
|
|
2084
|
-
import
|
|
2306
|
+
import fs6 from "fs/promises";
|
|
2307
|
+
import path7 from "path";
|
|
2308
|
+
import chalk4 from "chalk";
|
|
2085
2309
|
async function applyCommand(targetPath, options) {
|
|
2086
2310
|
let scanResult;
|
|
2087
2311
|
try {
|
|
@@ -2089,16 +2313,17 @@ async function applyCommand(targetPath, options) {
|
|
|
2089
2313
|
targetPath,
|
|
2090
2314
|
include: options.include,
|
|
2091
2315
|
exclude: options.exclude,
|
|
2316
|
+
changed: options.changed,
|
|
2092
2317
|
extractableMinOccurrences: options.minOccurrences ?? 3
|
|
2093
2318
|
});
|
|
2094
2319
|
} catch (error) {
|
|
2095
2320
|
const message = error instanceof Error ? error.message : String(error);
|
|
2096
|
-
console.error(
|
|
2321
|
+
console.error(chalk4.red(`Error: ${message}`));
|
|
2097
2322
|
process.exit(1);
|
|
2098
2323
|
}
|
|
2099
2324
|
for (const warning of scanResult.warnings) {
|
|
2100
2325
|
if (options.format !== "json") {
|
|
2101
|
-
console.warn(
|
|
2326
|
+
console.warn(chalk4.yellow(`\u26A0 ${warning}`));
|
|
2102
2327
|
}
|
|
2103
2328
|
}
|
|
2104
2329
|
let components;
|
|
@@ -2145,18 +2370,18 @@ async function applyCommand(targetPath, options) {
|
|
|
2145
2370
|
}
|
|
2146
2371
|
} catch (error) {
|
|
2147
2372
|
const message = error instanceof Error ? error.message : String(error);
|
|
2148
|
-
console.error(
|
|
2373
|
+
console.error(chalk4.red(`Error: ${message}`));
|
|
2149
2374
|
process.exit(1);
|
|
2150
2375
|
}
|
|
2151
2376
|
if (components.length === 0) {
|
|
2152
2377
|
console.error(
|
|
2153
|
-
|
|
2378
|
+
chalk4.yellow(
|
|
2154
2379
|
"No repeated className sets found. Try lowering --min-occurrences."
|
|
2155
2380
|
)
|
|
2156
2381
|
);
|
|
2157
2382
|
process.exit(1);
|
|
2158
2383
|
}
|
|
2159
|
-
const outputPath =
|
|
2384
|
+
const outputPath = path7.resolve(options.output);
|
|
2160
2385
|
let filesModified = 0;
|
|
2161
2386
|
let replacementsTotal = 0;
|
|
2162
2387
|
const allReplacements = [];
|
|
@@ -2164,7 +2389,7 @@ async function applyCommand(targetPath, options) {
|
|
|
2164
2389
|
const modifiedSources = /* @__PURE__ */ new Map();
|
|
2165
2390
|
const modifiedFiles = [];
|
|
2166
2391
|
for (const file of scanResult.files) {
|
|
2167
|
-
const original = await
|
|
2392
|
+
const original = await fs6.readFile(file, "utf-8");
|
|
2168
2393
|
const result2 = replaceClassNamesInSource(
|
|
2169
2394
|
original,
|
|
2170
2395
|
replacementMap,
|
|
@@ -2192,12 +2417,13 @@ async function applyCommand(targetPath, options) {
|
|
|
2192
2417
|
for (const file of modifiedFiles) {
|
|
2193
2418
|
const source = modifiedSources.get(file);
|
|
2194
2419
|
if (source) {
|
|
2195
|
-
await
|
|
2420
|
+
await fs6.writeFile(file, source, "utf-8");
|
|
2196
2421
|
}
|
|
2197
2422
|
}
|
|
2198
|
-
await
|
|
2199
|
-
await
|
|
2423
|
+
await fs6.mkdir(path7.dirname(outputPath), { recursive: true });
|
|
2424
|
+
await fs6.writeFile(outputPath, css, "utf-8");
|
|
2200
2425
|
}
|
|
2426
|
+
const savings = calculateSavings(allReplacements);
|
|
2201
2427
|
const result = {
|
|
2202
2428
|
filesModified,
|
|
2203
2429
|
replacementsTotal,
|
|
@@ -2206,7 +2432,8 @@ async function applyCommand(targetPath, options) {
|
|
|
2206
2432
|
components,
|
|
2207
2433
|
replacements: allReplacements,
|
|
2208
2434
|
skipped: allSkipped,
|
|
2209
|
-
prettierFormatted
|
|
2435
|
+
prettierFormatted,
|
|
2436
|
+
savings
|
|
2210
2437
|
};
|
|
2211
2438
|
if (options.format === "json") {
|
|
2212
2439
|
printApplyJsonReport({
|
|
@@ -2218,58 +2445,75 @@ async function applyCommand(targetPath, options) {
|
|
|
2218
2445
|
componentsGenerated: components.length,
|
|
2219
2446
|
components,
|
|
2220
2447
|
replacements: allReplacements,
|
|
2221
|
-
skipped: allSkipped
|
|
2448
|
+
skipped: allSkipped,
|
|
2449
|
+
savings
|
|
2222
2450
|
});
|
|
2223
2451
|
return result;
|
|
2224
2452
|
}
|
|
2225
2453
|
console.log("");
|
|
2226
2454
|
if (options.dryRun) {
|
|
2227
|
-
console.log(
|
|
2455
|
+
console.log(chalk4.bold.yellow("\u{1F50D} Dry run \u2014 no files were modified"));
|
|
2228
2456
|
} else {
|
|
2229
|
-
console.log(
|
|
2457
|
+
console.log(chalk4.bold.green("\u2705 Classes applied successfully"));
|
|
2230
2458
|
}
|
|
2231
|
-
console.log(
|
|
2459
|
+
console.log(chalk4.gray(` CSS output: `) + chalk4.white(outputPath));
|
|
2232
2460
|
console.log(
|
|
2233
|
-
|
|
2461
|
+
chalk4.gray(` Component classes: `) + chalk4.white(String(components.length))
|
|
2234
2462
|
);
|
|
2235
2463
|
console.log(
|
|
2236
|
-
|
|
2464
|
+
chalk4.gray(` Files modified: `) + chalk4.white(String(filesModified))
|
|
2237
2465
|
);
|
|
2238
2466
|
console.log(
|
|
2239
|
-
|
|
2467
|
+
chalk4.gray(` Replacements: `) + chalk4.white(String(replacementsTotal))
|
|
2240
2468
|
);
|
|
2241
2469
|
if (prettierFormatted.length > 0) {
|
|
2242
2470
|
console.log(
|
|
2243
|
-
|
|
2471
|
+
chalk4.gray(` Prettier formatted: `) + chalk4.white(String(prettierFormatted.length))
|
|
2472
|
+
);
|
|
2473
|
+
}
|
|
2474
|
+
if (savings.replacementCount > 0) {
|
|
2475
|
+
console.log("");
|
|
2476
|
+
console.log(chalk4.bold("Savings:"));
|
|
2477
|
+
console.log(
|
|
2478
|
+
chalk4.gray(" Utility tokens before: ") + chalk4.white(String(savings.utilityTokensBefore))
|
|
2479
|
+
);
|
|
2480
|
+
console.log(
|
|
2481
|
+
chalk4.gray(" Utility tokens after: ") + chalk4.white(String(savings.utilityTokensAfter))
|
|
2482
|
+
);
|
|
2483
|
+
console.log(
|
|
2484
|
+
chalk4.gray(" Tokens saved: ") + chalk4.green(String(savings.tokensSaved))
|
|
2485
|
+
);
|
|
2486
|
+
console.log(
|
|
2487
|
+
chalk4.gray(" Reduction: ") + chalk4.green(`${savings.percentReduction}%`)
|
|
2244
2488
|
);
|
|
2245
2489
|
}
|
|
2246
2490
|
if (allReplacements.length > 0) {
|
|
2247
2491
|
console.log("");
|
|
2248
|
-
console.log(
|
|
2492
|
+
console.log(chalk4.bold("Replacements:"));
|
|
2249
2493
|
for (const item of allReplacements) {
|
|
2250
2494
|
const line = item.line ? `:${item.line}` : "";
|
|
2251
|
-
const partialTag = item.partial ?
|
|
2495
|
+
const partialTag = item.partial ? chalk4.dim(" (partial)") : "";
|
|
2252
2496
|
console.log(
|
|
2253
|
-
|
|
2497
|
+
chalk4.gray(` ${item.filePath}${line}`) + chalk4.white(` "${item.from}" `) + chalk4.cyan("\u2192") + chalk4.green(` "${item.to}"`) + partialTag
|
|
2254
2498
|
);
|
|
2255
2499
|
}
|
|
2256
2500
|
}
|
|
2257
2501
|
if (allSkipped.length > 0) {
|
|
2258
2502
|
console.log("");
|
|
2259
|
-
console.log(
|
|
2503
|
+
console.log(chalk4.bold.yellow(`Skipped (${allSkipped.length}):`));
|
|
2260
2504
|
for (const item of allSkipped) {
|
|
2261
2505
|
const line = item.line ? `:${item.line}` : "";
|
|
2262
2506
|
const classes = item.classes.join(" ");
|
|
2263
2507
|
console.log(
|
|
2264
|
-
|
|
2508
|
+
chalk4.gray(` ${item.filePath}${line}`) + chalk4.yellow(` [${item.reason}]`) + chalk4.dim(` "${classes}"`)
|
|
2265
2509
|
);
|
|
2266
2510
|
}
|
|
2267
2511
|
}
|
|
2268
2512
|
console.log("");
|
|
2269
2513
|
if (!options.dryRun) {
|
|
2270
2514
|
console.log(
|
|
2271
|
-
|
|
2272
|
-
`Import ${
|
|
2515
|
+
chalk4.cyan(
|
|
2516
|
+
`Import ${path7.basename(outputPath)} in your global CSS if you haven't already.`
|
|
2273
2517
|
)
|
|
2274
2518
|
);
|
|
2275
2519
|
console.log("");
|
|
@@ -2278,9 +2522,9 @@ async function applyCommand(targetPath, options) {
|
|
|
2278
2522
|
}
|
|
2279
2523
|
|
|
2280
2524
|
// src/commands/generate.ts
|
|
2281
|
-
import
|
|
2282
|
-
import
|
|
2283
|
-
import
|
|
2525
|
+
import fs7 from "fs/promises";
|
|
2526
|
+
import path8 from "path";
|
|
2527
|
+
import chalk5 from "chalk";
|
|
2284
2528
|
async function generateCommand(targetPath, options) {
|
|
2285
2529
|
let scanResult = null;
|
|
2286
2530
|
let components;
|
|
@@ -2302,6 +2546,7 @@ async function generateCommand(targetPath, options) {
|
|
|
2302
2546
|
targetPath,
|
|
2303
2547
|
include: options.include,
|
|
2304
2548
|
exclude: options.exclude,
|
|
2549
|
+
changed: options.changed,
|
|
2305
2550
|
extractableMinOccurrences: options.minOccurrences ?? 3
|
|
2306
2551
|
});
|
|
2307
2552
|
if (options.extractableOnly) {
|
|
@@ -2331,19 +2576,19 @@ async function generateCommand(targetPath, options) {
|
|
|
2331
2576
|
}
|
|
2332
2577
|
} catch (error) {
|
|
2333
2578
|
const message = error instanceof Error ? error.message : String(error);
|
|
2334
|
-
console.error(
|
|
2579
|
+
console.error(chalk5.red(`Error: ${message}`));
|
|
2335
2580
|
process.exit(1);
|
|
2336
2581
|
}
|
|
2337
2582
|
if (scanResult) {
|
|
2338
2583
|
for (const warning of scanResult.warnings) {
|
|
2339
2584
|
if (options.format !== "json") {
|
|
2340
|
-
console.warn(
|
|
2585
|
+
console.warn(chalk5.yellow(`\u26A0 ${warning}`));
|
|
2341
2586
|
}
|
|
2342
2587
|
}
|
|
2343
2588
|
}
|
|
2344
|
-
const outputPath =
|
|
2345
|
-
await
|
|
2346
|
-
await
|
|
2589
|
+
const outputPath = path8.resolve(options.output);
|
|
2590
|
+
await fs7.mkdir(path8.dirname(outputPath), { recursive: true });
|
|
2591
|
+
await fs7.writeFile(outputPath, css, "utf-8");
|
|
2347
2592
|
const result = {
|
|
2348
2593
|
outputPath,
|
|
2349
2594
|
componentsGenerated: components.length,
|
|
@@ -2361,28 +2606,28 @@ async function generateCommand(targetPath, options) {
|
|
|
2361
2606
|
return result;
|
|
2362
2607
|
}
|
|
2363
2608
|
console.log("");
|
|
2364
|
-
console.log(
|
|
2365
|
-
console.log(
|
|
2609
|
+
console.log(chalk5.bold.green("\u2705 CSS generated successfully"));
|
|
2610
|
+
console.log(chalk5.gray(` Output: `) + chalk5.white(outputPath));
|
|
2366
2611
|
console.log(
|
|
2367
|
-
|
|
2612
|
+
chalk5.gray(` Components: `) + chalk5.white(String(components.length))
|
|
2368
2613
|
);
|
|
2369
2614
|
if (components.length > 0) {
|
|
2370
2615
|
console.log("");
|
|
2371
|
-
console.log(
|
|
2616
|
+
console.log(chalk5.bold("Generated classes:"));
|
|
2372
2617
|
for (const component of components) {
|
|
2373
2618
|
console.log(
|
|
2374
|
-
|
|
2619
|
+
chalk5.green(` .${component.className}`) + chalk5.gray(` \u2014 ${component.occurrences} occurrences, `) + chalk5.dim(component.classes.join(" "))
|
|
2375
2620
|
);
|
|
2376
2621
|
}
|
|
2377
2622
|
console.log("");
|
|
2378
2623
|
console.log(
|
|
2379
|
-
|
|
2624
|
+
chalk5.cyan(
|
|
2380
2625
|
"Run apply to replace className strings: npx tailwind-unwind apply <path> --output styles.css"
|
|
2381
2626
|
)
|
|
2382
2627
|
);
|
|
2383
2628
|
} else {
|
|
2384
2629
|
console.log(
|
|
2385
|
-
|
|
2630
|
+
chalk5.yellow(
|
|
2386
2631
|
"\nNo repeated className sets matched the filters. Try lowering --min-occurrences."
|
|
2387
2632
|
)
|
|
2388
2633
|
);
|
|
@@ -2417,11 +2662,16 @@ export {
|
|
|
2417
2662
|
parseFile,
|
|
2418
2663
|
IGNORED_DIRECTORIES,
|
|
2419
2664
|
IGNORE_PATTERNS,
|
|
2665
|
+
getChangedSourceFiles,
|
|
2666
|
+
isGitRepository,
|
|
2667
|
+
getChangedFilesInScope,
|
|
2420
2668
|
walkSourceFiles,
|
|
2421
2669
|
scanProject,
|
|
2670
|
+
initCommand,
|
|
2422
2671
|
printConsoleReport,
|
|
2423
2672
|
printJsonReport,
|
|
2424
2673
|
analyzeCommand,
|
|
2674
|
+
calculateSavings,
|
|
2425
2675
|
formatSource,
|
|
2426
2676
|
formatModifiedFiles,
|
|
2427
2677
|
replaceClassNamesInSource,
|
|
@@ -2438,4 +2688,4 @@ export {
|
|
|
2438
2688
|
applyCommand,
|
|
2439
2689
|
generateCommand
|
|
2440
2690
|
};
|
|
2441
|
-
//# sourceMappingURL=chunk-
|
|
2691
|
+
//# sourceMappingURL=chunk-UXXIEFP4.js.map
|