@codefast/cli 0.3.16-canary.2 → 0.4.0-canary.4
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 +254 -86
- package/dist/arrange/analyze.mjs +2 -2
- package/dist/arrange/command.mjs +51 -27
- package/dist/arrange/domain/analyze-service.mjs +2 -2
- package/dist/arrange/domain/ast/collectors-cn.mjs +1 -1
- package/dist/arrange/domain/ast/collectors-tv.mjs +2 -2
- package/dist/arrange/domain/ast/helpers.mjs +2 -2
- package/dist/arrange/domain/ast/simplify-targets.mjs +109 -0
- package/dist/arrange/domain/ast/targets.mjs +25 -10
- package/dist/arrange/domain/grouping-service.mjs +1 -1
- package/dist/arrange/domain/grouping.mjs +5 -5
- package/dist/arrange/domain/imports.mjs +50 -2
- package/dist/arrange/domain/token-classifier.mjs +12 -12
- package/dist/arrange/output.mjs +11 -3
- package/dist/arrange/process-file.mjs +1 -1
- package/dist/arrange/scan-target.mjs +10 -1
- package/dist/arrange/simplify-process-file.mjs +35 -0
- package/dist/arrange/simplify-sync.mjs +32 -0
- package/dist/arrange/suggest.mjs +1 -1
- package/dist/arrange/sync.mjs +1 -1
- package/dist/arrange/workspace.mjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/core/cli/result-handle.mjs +1 -1
- package/dist/core/config/schema.mjs +31 -10
- package/dist/core/source-text-edit.mjs +1 -1
- package/dist/core/workspace/resolver.mjs +3 -3
- package/dist/mirror/cli-result.mjs +2 -1
- package/dist/mirror/cli-schema.mjs +2 -1
- package/dist/mirror/command.mjs +12 -11
- package/dist/mirror/domain/exports.mjs +16 -19
- package/dist/mirror/output.mjs +3 -0
- package/dist/mirror/supplement-exports.mjs +114 -0
- package/dist/mirror/sync-reporter.mjs +5 -2
- package/dist/mirror/sync-workspace-package.mjs +35 -29
- package/dist/mirror/sync.mjs +2 -1
- package/dist/mirror/write-exports.mjs +3 -2
- package/dist/tag/command.mjs +6 -6
- package/dist/tag/output.mjs +1 -1
- package/dist/tag/sync.mjs +1 -1
- package/dist/tag/target-runner.mjs +1 -1
- package/package.json +10 -10
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "../constants.mjs";
|
|
2
|
-
import { tokenizeClassString } from "../tailwind-token.mjs";
|
|
3
1
|
import { forEachDomainChild, isDomainArrayLiteralExpression, isDomainCallExpression, isDomainObjectLiteralExpression, isDomainPropertyAssignment, isDomainSpreadElement, isDomainTailwindClassLiteral } from "./ast-node.mjs";
|
|
2
|
+
import "../constants.mjs";
|
|
4
3
|
import { CN_APPLY_LITERAL_WALK_OPTS, collectUnconditionalTailwindLiteralsFromCnArguments, forEachStringLiteralInClassExpression, isUnsafeLiteralForCnStyleApplySplit } from "./collectors-cn.mjs";
|
|
5
4
|
import { buildKnownCnTvBindings, isCnOrTvIdentifier, propertyAssignmentNameText } from "./helpers.mjs";
|
|
5
|
+
import { tokenizeClassString } from "../tailwind-token.mjs";
|
|
6
6
|
//#region src/arrange/domain/ast/collectors-tv.ts
|
|
7
7
|
/**
|
|
8
8
|
* @since 0.3.16-canary.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EMPTY_CN_TV_BINDINGS } from "../constants.mjs";
|
|
2
1
|
import { isDomainIdentifier, isDomainImportDeclaration, isDomainNamedImports, isDomainNamespaceImport, isDomainPropertyAccessExpression, isDomainStringLiteral, lineOfSourcePosition } from "./ast-node.mjs";
|
|
2
|
+
import { EMPTY_CN_TV_BINDINGS } from "../constants.mjs";
|
|
3
3
|
import { applyEditsDescending, indentOfLineContaining } from "../../../core/source-text-edit.mjs";
|
|
4
4
|
//#region src/arrange/domain/ast/helpers.ts
|
|
5
5
|
/**
|
|
@@ -21,7 +21,7 @@ const KNOWN_CN_TV_MODULES = new Set([
|
|
|
21
21
|
function moduleLooksLikeCnTvReexport(moduleSpecifier) {
|
|
22
22
|
const norm = moduleSpecifier.replace(/\\/g, "/");
|
|
23
23
|
if (/(?:^|[./])utils(?:\/|$)/.test(norm)) return true;
|
|
24
|
-
if (/\/utils\//.test(norm) ||
|
|
24
|
+
if (/\/utils\//.test(norm) || norm.endsWith("/utils")) return true;
|
|
25
25
|
if (/(?:^|\/)cn\.tsx?$/.test(norm)) return true;
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { forEachDomainChild, isDomainArrayLiteralExpression, isDomainCallExpression, isDomainJsxAttribute, isDomainJsxExpression, isDomainObjectLiteralExpression, isDomainPropertyAssignment, isDomainSpreadElement, isDomainTailwindClassLiteral } from "./ast-node.mjs";
|
|
2
|
+
import "../constants.mjs";
|
|
3
|
+
import { indentOfLineContaining } from "../../../core/source-text-edit.mjs";
|
|
4
|
+
import { buildKnownCnTvBindings, isCnOrTvIdentifier } from "./helpers.mjs";
|
|
5
|
+
import { escapeTsStringLiteralContent } from "../source-text-formatters.mjs";
|
|
6
|
+
//#region src/arrange/domain/ast/simplify-targets.ts
|
|
7
|
+
function toFlatString(text) {
|
|
8
|
+
return `"${escapeTsStringLiteralContent(text.trim())}"`;
|
|
9
|
+
}
|
|
10
|
+
function joinLiterals(args) {
|
|
11
|
+
return args.filter(isDomainTailwindClassLiteral).map((lit) => lit.text).join(" ");
|
|
12
|
+
}
|
|
13
|
+
function isAllStaticLiterals(args) {
|
|
14
|
+
return args.length > 0 && args.every(isDomainTailwindClassLiteral);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* For a cn() call that has both static and dynamic args:
|
|
18
|
+
* - Merge ALL static string literals (regardless of position) into one string.
|
|
19
|
+
* - Place the merged string first.
|
|
20
|
+
* - Append all dynamic args in their original relative order.
|
|
21
|
+
*
|
|
22
|
+
* Returns `null` when nothing changes (0 statics, or already 1 static at arg[0]).
|
|
23
|
+
*/
|
|
24
|
+
function buildMixedCnReplacement(call, sourceText) {
|
|
25
|
+
const args = [...call.arguments];
|
|
26
|
+
if (args.length === 0) return null;
|
|
27
|
+
const staticTexts = [];
|
|
28
|
+
const dynamicSrcs = [];
|
|
29
|
+
for (const arg of args) if (isDomainTailwindClassLiteral(arg)) staticTexts.push(arg.text);
|
|
30
|
+
else dynamicSrcs.push(sourceText.slice(arg.pos, arg.end));
|
|
31
|
+
if (staticTexts.length === 0) return null;
|
|
32
|
+
const firstArg = args[0];
|
|
33
|
+
if (staticTexts.length === 1 && firstArg !== void 0 && isDomainTailwindClassLiteral(firstArg)) return null;
|
|
34
|
+
const flatStatic = staticTexts.join(" ").trim();
|
|
35
|
+
const baseIndent = indentOfLineContaining(sourceText, call.pos);
|
|
36
|
+
const argIndent = `${baseIndent} `;
|
|
37
|
+
return `cn(\n${[`${argIndent}"${escapeTsStringLiteralContent(flatStatic)}",`, ...dynamicSrcs.map((src) => `${argIndent}${src},`)].join("\n")}\n${baseIndent})`;
|
|
38
|
+
}
|
|
39
|
+
function collectTvArrayEdits(obj, results, depth) {
|
|
40
|
+
if (depth > 12) return;
|
|
41
|
+
for (const prop of obj.properties) {
|
|
42
|
+
if (!isDomainPropertyAssignment(prop)) continue;
|
|
43
|
+
const init = prop.initializer;
|
|
44
|
+
if (isDomainArrayLiteralExpression(init)) {
|
|
45
|
+
const elements = [...init.elements];
|
|
46
|
+
if (!elements.some(isDomainSpreadElement) && elements.length > 0 && isAllStaticLiterals(elements)) results.push({
|
|
47
|
+
start: init.pos,
|
|
48
|
+
end: init.end,
|
|
49
|
+
replacement: toFlatString(joinLiterals(elements)),
|
|
50
|
+
label: "tv-array"
|
|
51
|
+
});
|
|
52
|
+
} else if (isDomainObjectLiteralExpression(init)) collectTvArrayEdits(init, results, depth + 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Collect all simplify edits for a source file:
|
|
57
|
+
* - Arrays of pure string literals inside tv() slots → flat string
|
|
58
|
+
* - cn() calls whose every argument is a static string literal → flat string (unwrap cn)
|
|
59
|
+
* - cn() calls with mixed static + dynamic args → merge adjacent statics into one string
|
|
60
|
+
* - JSX className={cn(...all-static...)} → className="flat string"
|
|
61
|
+
*
|
|
62
|
+
* @since 0.3.16-canary.0
|
|
63
|
+
*/
|
|
64
|
+
function collectSimplifyTargets(sourceFile) {
|
|
65
|
+
const sourceText = sourceFile.text;
|
|
66
|
+
const results = [];
|
|
67
|
+
const knownBindings = buildKnownCnTvBindings(sourceFile);
|
|
68
|
+
const seenCnPos = /* @__PURE__ */ new Set();
|
|
69
|
+
const visitNode = (node) => {
|
|
70
|
+
if (isDomainCallExpression(node)) {
|
|
71
|
+
if (isCnOrTvIdentifier(node.expression, "tv", knownBindings)) {
|
|
72
|
+
const arg0 = node.arguments[0];
|
|
73
|
+
if (arg0 && isDomainObjectLiteralExpression(arg0)) collectTvArrayEdits(arg0, results, 0);
|
|
74
|
+
} else if (isCnOrTvIdentifier(node.expression, "cn", knownBindings) && !seenCnPos.has(node.pos)) {
|
|
75
|
+
seenCnPos.add(node.pos);
|
|
76
|
+
const args = [...node.arguments];
|
|
77
|
+
if (isAllStaticLiterals(args)) {
|
|
78
|
+
const flat = joinLiterals(args);
|
|
79
|
+
const parent = node.parent;
|
|
80
|
+
if (parent && isDomainJsxExpression(parent) && parent.parent && isDomainJsxAttribute(parent.parent)) results.push({
|
|
81
|
+
start: parent.pos,
|
|
82
|
+
end: parent.end,
|
|
83
|
+
replacement: toFlatString(flat),
|
|
84
|
+
label: "jsx-cn"
|
|
85
|
+
});
|
|
86
|
+
else results.push({
|
|
87
|
+
start: node.pos,
|
|
88
|
+
end: node.end,
|
|
89
|
+
replacement: toFlatString(flat),
|
|
90
|
+
label: "cn-static"
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
const replacement = buildMixedCnReplacement(node, sourceText);
|
|
94
|
+
if (replacement !== null) results.push({
|
|
95
|
+
start: node.pos,
|
|
96
|
+
end: node.end,
|
|
97
|
+
replacement,
|
|
98
|
+
label: "cn-merge"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
forEachDomainChild(node, visitNode);
|
|
104
|
+
};
|
|
105
|
+
for (const stmt of sourceFile.statements) visitNode(stmt);
|
|
106
|
+
return results;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { collectSimplifyTargets };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { forEachDomainChild, isDomainArrayLiteralExpression, isDomainJsxAttribute, isDomainPropertyAssignment, isDomainTailwindClassLiteral } from "./ast-node.mjs";
|
|
1
2
|
import "../constants.mjs";
|
|
2
|
-
import { tokenizeClassString } from "../tailwind-token.mjs";
|
|
3
|
-
import { forEachDomainChild, isDomainArrayLiteralExpression, isDomainJsxAttribute, isDomainTailwindClassLiteral } from "./ast-node.mjs";
|
|
4
3
|
import { isUnsafeLiteralForCnStyleApplySplit } from "./collectors-cn.mjs";
|
|
5
4
|
import { jsxClassNameStaticLiteral } from "./collectors-jsx.mjs";
|
|
6
5
|
import { endAfterOptionalCommaFollowingInSource, indentOfLineContaining, textPrefixFromLineStartToPosition } from "../../../core/source-text-edit.mjs";
|
|
6
|
+
import { tokenizeClassString } from "../tailwind-token.mjs";
|
|
7
7
|
import { collectGroupableStringNodes, slotClassString } from "./collectors-tv.mjs";
|
|
8
|
-
import { areCnTailwindPartitionsEquivalent, suggestCnGroups, summarizeGroupBucketLabels } from "../grouping.mjs";
|
|
9
8
|
import { escapeTsStringLiteralContent, formatArray, formatArrayElementsAsSiblingLines, formatJsxCnAttributeValue } from "../source-text-formatters.mjs";
|
|
9
|
+
import { areCnTailwindPartitionsEquivalent, suggestCnGroups, summarizeGroupBucketLabels } from "../grouping.mjs";
|
|
10
10
|
//#region src/arrange/domain/ast/targets.ts
|
|
11
11
|
/**
|
|
12
12
|
* @since 0.3.16-canary.0
|
|
@@ -62,7 +62,7 @@ function formatCnCallReplacement(stringNode, sourceText, withClassName) {
|
|
|
62
62
|
for (const dynamicArgumentSource of dynamicArgTexts) allArgs.push(`${argIndent}${dynamicArgumentSource}`);
|
|
63
63
|
if (withClassName) allArgs.push(`${argIndent}className`);
|
|
64
64
|
const commaAfterLastArg = allArgs.length > 1;
|
|
65
|
-
return `cn(\n${allArgs.map((argLine, lineIndex) => lineIndex < allArgs.length - 1 || commaAfterLastArg ? `${argLine},` :
|
|
65
|
+
return `cn(\n${allArgs.map((argLine, lineIndex) => lineIndex < allArgs.length - 1 || commaAfterLastArg ? `${argLine},` : argLine).join("\n")}\n${baseIndent})`;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* @since 0.3.16-canary.0
|
|
@@ -88,14 +88,29 @@ function planGroupEditForTarget(target, textAfterUnwrap, withClassName) {
|
|
|
88
88
|
if (areCnTailwindPartitionsEquivalent(target.item.nodes.map((classLiteral) => classLiteral.text), groups)) return;
|
|
89
89
|
if (!target.item.cnCall) {
|
|
90
90
|
const anchorClassLiteral = target.item.primaryClassLiteral;
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
91
|
+
const parentNode = anchorClassLiteral.parent;
|
|
92
|
+
const parentArray = target.item.nodes.length > 1 && parentNode !== null && isDomainArrayLiteralExpression(parentNode) ? parentNode : null;
|
|
93
|
+
const bareStringProperty = !parentArray && parentNode !== null && isDomainPropertyAssignment(parentNode);
|
|
94
|
+
if (parentArray || bareStringProperty) {
|
|
95
|
+
const start = parentArray ? parentArray.pos : anchorClassLiteral.pos;
|
|
96
|
+
const end = parentArray ? parentArray.end : anchorClassLiteral.end;
|
|
97
|
+
const baseIndent = indentOfLineContaining(textAfterUnwrap, start);
|
|
98
|
+
return {
|
|
99
|
+
start,
|
|
100
|
+
end,
|
|
101
|
+
replacement: formatArray(groups).split("\n").map((line, lineIndex) => lineIndex === 0 ? line : `${baseIndent}${line}`).join("\n"),
|
|
102
|
+
bucketSummary: summarizeGroupBucketLabels(groups),
|
|
103
|
+
jsxCn: false,
|
|
104
|
+
lineSf: target.item.sf,
|
|
105
|
+
reportNode: anchorClassLiteral,
|
|
106
|
+
label: target.item.isTvContext ? "tv" : "cn"
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const start = anchorClassLiteral.pos;
|
|
95
110
|
return {
|
|
96
111
|
start,
|
|
97
|
-
end,
|
|
98
|
-
replacement:
|
|
112
|
+
end: endAfterOptionalCommaFollowingInSource(textAfterUnwrap, anchorClassLiteral.end),
|
|
113
|
+
replacement: formatArrayElementsAsSiblingLines(groups, textPrefixFromLineStartToPosition(textAfterUnwrap, start)),
|
|
99
114
|
bucketSummary: summarizeGroupBucketLabels(groups),
|
|
100
115
|
jsxCn: false,
|
|
101
116
|
lineSf: target.item.sf,
|
|
@@ -42,7 +42,7 @@ function tryBuildGroupFileWorkPlan(input) {
|
|
|
42
42
|
if (domainSfGrouped.text !== unwrap.textAfterUnwrap) throw new Error("Domain invariant: domainSfGrouped.text must match unwrap phase textAfterUnwrap");
|
|
43
43
|
const groupTargets = collectGroupTargets(domainSfGrouped, filePath);
|
|
44
44
|
if (unwrap.cnInTvCalls.length === 0 && groupTargets.length === 0) return null;
|
|
45
|
-
const sortedTargets = [...groupTargets].
|
|
45
|
+
const sortedTargets = [...groupTargets].toSorted((leftTarget, rightTarget) => targetReplaceStart(rightTarget) - targetReplaceStart(leftTarget));
|
|
46
46
|
const plannedGroupEdits = [];
|
|
47
47
|
for (const groupTarget of sortedTargets) {
|
|
48
48
|
const plan = planGroupEditForTarget(groupTarget, unwrap.textAfterUnwrap, withClassName);
|
|
@@ -72,7 +72,7 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
|
|
|
72
72
|
function areCnTailwindPartitionsEquivalent(staticLiteralTexts, suggestedGroups) {
|
|
73
73
|
const partitionSignatures = (chunks) => chunks.map((chunk) => {
|
|
74
74
|
const classTokens = tokenizeClassString(chunk);
|
|
75
|
-
return classTokens.length === 0 ? "" : [...classTokens].
|
|
75
|
+
return classTokens.length === 0 ? "" : [...classTokens].toSorted(compareClassTokensCanonically).join(" ");
|
|
76
76
|
}).filter((s) => s.length > 0).sort(compareClassTokensCanonically);
|
|
77
77
|
const staticPartitionSigs = partitionSignatures(staticLiteralTexts);
|
|
78
78
|
const suggestedPartitionSigs = partitionSignatures(suggestedGroups);
|
|
@@ -230,7 +230,7 @@ function mergeEaseTimingIntoFollowingAnimatedState(groups) {
|
|
|
230
230
|
const chunkJ = result[j];
|
|
231
231
|
if (chunkJ === void 0) throw new Error("invariant: mergeEase following chunk missing");
|
|
232
232
|
if (dominantBucketOfGroup(chunkJ) !== "state") continue;
|
|
233
|
-
if (!tokenizeClassString(chunkJ).some((classToken) =>
|
|
233
|
+
if (!tokenizeClassString(chunkJ).some((classToken) => stripVariants(classToken).startsWith("animate"))) continue;
|
|
234
234
|
target = j;
|
|
235
235
|
break;
|
|
236
236
|
}
|
|
@@ -248,7 +248,7 @@ function mergeEaseTimingIntoFollowingAnimatedState(groups) {
|
|
|
248
248
|
function chunkIsOnlyEaseTimingMotion(groupStr) {
|
|
249
249
|
const classTokens = tokenizeClassString(groupStr);
|
|
250
250
|
if (classTokens.length === 0) return false;
|
|
251
|
-
return classTokens.every((classToken) => classifyToken(classToken) === "motion" &&
|
|
251
|
+
return classTokens.every((classToken) => classifyToken(classToken) === "motion" && stripVariants(classToken).startsWith("ease-"));
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
254
|
* @since 0.3.16-canary.0
|
|
@@ -328,9 +328,9 @@ function suggestCnGroups(classString) {
|
|
|
328
328
|
function summarizeGroupBucketLabels(groups) {
|
|
329
329
|
return groups.map((g) => {
|
|
330
330
|
const uniq = new Set(tokenizeClassString(g).map(classifyToken));
|
|
331
|
-
if (uniq.size !== 1) return `mixed:${[...uniq].
|
|
331
|
+
if (uniq.size !== 1) return `mixed:${[...uniq].toSorted(compareClassTokensCanonically).join("+")}`;
|
|
332
332
|
const onlyBucket = [...uniq][0];
|
|
333
|
-
if (onlyBucket === void 0) return `mixed:${[...uniq].
|
|
333
|
+
if (onlyBucket === void 0) return `mixed:${[...uniq].toSorted(compareClassTokensCanonically).join("+")}`;
|
|
334
334
|
return onlyBucket;
|
|
335
335
|
});
|
|
336
336
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isDomainImportDeclaration, isDomainNamedImports, isDomainStringLiteral } from "./ast/ast-node.mjs";
|
|
1
|
+
import { forEachDomainChild, isDomainIdentifier, isDomainImportDeclaration, isDomainNamedImports, isDomainStringLiteral } from "./ast/ast-node.mjs";
|
|
2
|
+
import { endAfterOptionalCommaFollowingInSource } from "../../core/source-text-edit.mjs";
|
|
2
3
|
//#region src/arrange/domain/imports.ts
|
|
3
4
|
function sourceFileImportsCn(sourceFile) {
|
|
4
5
|
for (const statement of sourceFile.statements) {
|
|
@@ -25,6 +26,53 @@ function findImportDeclarationFromModule(sourceFile, moduleSpecifier) {
|
|
|
25
26
|
if (isDomainStringLiteral(spec) && spec.text === moduleSpecifier) return statement;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
function cnIsUsedInBody(node) {
|
|
30
|
+
if (isDomainIdentifier(node) && node.text === "cn") return true;
|
|
31
|
+
let found = false;
|
|
32
|
+
forEachDomainChild(node, (child) => {
|
|
33
|
+
if (!found) found = cnIsUsedInBody(child);
|
|
34
|
+
});
|
|
35
|
+
return found;
|
|
36
|
+
}
|
|
37
|
+
function removeCnSpecifier(sourceText, specifier, allSpecifiers) {
|
|
38
|
+
const idx = allSpecifiers.indexOf(specifier);
|
|
39
|
+
if (idx === 0) {
|
|
40
|
+
const next = allSpecifiers[1];
|
|
41
|
+
if (!next) return sourceText;
|
|
42
|
+
return sourceText.slice(0, specifier.pos) + sourceText.slice(next.pos);
|
|
43
|
+
}
|
|
44
|
+
const prev = allSpecifiers[idx - 1];
|
|
45
|
+
if (!prev) return sourceText;
|
|
46
|
+
const endWithTrailingComma = endAfterOptionalCommaFollowingInSource(sourceText, specifier.end);
|
|
47
|
+
return sourceText.slice(0, prev.end) + sourceText.slice(endWithTrailingComma);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* After simplify rewrites, drop the `cn` import specifier when `cn` is no
|
|
51
|
+
* longer referenced anywhere outside the import declarations.
|
|
52
|
+
*
|
|
53
|
+
* @since 0.3.16-canary.0
|
|
54
|
+
*/
|
|
55
|
+
function dropCnImportIfUnused(sourceFile) {
|
|
56
|
+
const sourceText = sourceFile.text;
|
|
57
|
+
for (const stmt of sourceFile.statements) {
|
|
58
|
+
if (isDomainImportDeclaration(stmt)) continue;
|
|
59
|
+
if (cnIsUsedInBody(stmt)) return sourceText;
|
|
60
|
+
}
|
|
61
|
+
for (const stmt of sourceFile.statements) {
|
|
62
|
+
if (!isDomainImportDeclaration(stmt) || !stmt.importClause) continue;
|
|
63
|
+
const clause = stmt.importClause;
|
|
64
|
+
if (!clause.namedBindings || !isDomainNamedImports(clause.namedBindings)) continue;
|
|
65
|
+
const elements = clause.namedBindings.elements;
|
|
66
|
+
const cnSpecifier = elements.find((el) => el.name.text === "cn");
|
|
67
|
+
if (!cnSpecifier) continue;
|
|
68
|
+
if (elements.length === 1) {
|
|
69
|
+
const lineEnd = sourceText.indexOf("\n", stmt.end);
|
|
70
|
+
return sourceText.slice(0, stmt.pos) + (lineEnd !== -1 ? sourceText.slice(lineEnd + 1) : sourceText.slice(stmt.end));
|
|
71
|
+
}
|
|
72
|
+
return removeCnSpecifier(sourceText, cnSpecifier, elements);
|
|
73
|
+
}
|
|
74
|
+
return sourceText;
|
|
75
|
+
}
|
|
28
76
|
/**
|
|
29
77
|
* @since 0.3.16-canary.0
|
|
30
78
|
*/
|
|
@@ -56,4 +104,4 @@ function ensureCnImport(sourceFile, cnImportOverride) {
|
|
|
56
104
|
return `${sourceText.slice(0, insertAt)}${importLine}\n${sourceText.slice(insertAt)}`;
|
|
57
105
|
}
|
|
58
106
|
//#endregion
|
|
59
|
-
export { ensureCnImport };
|
|
107
|
+
export { dropCnImportIfUnused, ensureCnImport };
|
|
@@ -50,9 +50,9 @@ function isStateToken(token) {
|
|
|
50
50
|
if (prefix.startsWith("supports-[")) return true;
|
|
51
51
|
if (prefix.startsWith("in-data-")) return true;
|
|
52
52
|
if (/^data-(?!\[)/.test(prefix)) return true;
|
|
53
|
-
if (
|
|
53
|
+
if (prefix.startsWith("aria-")) return true;
|
|
54
54
|
if (/^(group|peer)-/.test(prefix)) return true;
|
|
55
|
-
if (
|
|
55
|
+
if (prefix.startsWith("not-")) return true;
|
|
56
56
|
if (isCompoundOrMediaVariantPrefix(prefix)) return true;
|
|
57
57
|
if (prefix.startsWith("[") && prefix.endsWith("]")) return true;
|
|
58
58
|
return false;
|
|
@@ -65,13 +65,13 @@ function isStateToken(token) {
|
|
|
65
65
|
*/
|
|
66
66
|
function compositeSecondaryOrder(bareUtility) {
|
|
67
67
|
const b = bareUtility;
|
|
68
|
-
if (/^opacity(?:-|$)/.test(b) ||
|
|
68
|
+
if (/^opacity(?:-|$)/.test(b) || b.startsWith("mix-blend-") || /^isolation(?:-|$)/.test(b) || b === "isolate") return 0;
|
|
69
69
|
if (b === "transform-3d" || /^perspective(?:-|$)/.test(b)) return 10;
|
|
70
70
|
if (/^rotate-[xyz](?:-|$)/.test(b) || /^translate-z(?:-|$)/.test(b) || /^scale-z(?:-|$)/.test(b)) return 20;
|
|
71
71
|
if (/^translate(?:-|$)/.test(b) || /^scale(?:-|$)/.test(b) || /^rotate(?:-|$)/.test(b) || /^skew(?:-|$)/.test(b)) return 30;
|
|
72
72
|
if (/^transform(?:-(?:gpu|cpu|none))?$/.test(b)) return 35;
|
|
73
|
-
if (/^blur(?:-|$)/.test(b) ||
|
|
74
|
-
if (
|
|
73
|
+
if (/^blur(?:-|$)/.test(b) || b.startsWith("backdrop-") || /^filter(?:-|$)/.test(b) || b.startsWith("brightness-") || b.startsWith("contrast-") || /^grayscale(?:-|$)/.test(b) || b.startsWith("hue-rotate-") || /^invert(?:-|$)/.test(b) || b.startsWith("saturate-") || /^sepia(?:-|$)/.test(b)) return 40;
|
|
74
|
+
if (b.startsWith("will-change")) return 50;
|
|
75
75
|
return 99;
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
@@ -82,7 +82,7 @@ function classifyBareUtility(bareUtility) {
|
|
|
82
82
|
if (/^@container(?:\/[a-z][a-z0-9-]*)?$/i.test(b)) return "existence";
|
|
83
83
|
if (/^(?:hidden|contents|sr-only|not-sr-only|list-item|flow-root)$/.test(b) || /^(?:block|inline-block|inline)$/.test(b)) return "existence";
|
|
84
84
|
if (/^table(?:$|-)/.test(b)) return "existence";
|
|
85
|
-
if (
|
|
85
|
+
if (b.startsWith("contain-")) return "existence";
|
|
86
86
|
if (/^(?:group|peer)(?:\/[a-z][a-z0-9-]*)?$/.test(b)) return "existence";
|
|
87
87
|
if (/^(?:static|relative|absolute|fixed|sticky)$/.test(b)) return "position";
|
|
88
88
|
if (/^-?(?:inset|top|right|bottom|left|start|end)(?:-|\/|$)/.test(b) || /^-?(?:inset-x|inset-y|inset-s|inset-e)-/.test(b)) return "position";
|
|
@@ -90,13 +90,13 @@ function classifyBareUtility(bareUtility) {
|
|
|
90
90
|
if (/^(?:flex|inline-flex|grid|inline-grid|subgrid|masonry)(?:$|-)/.test(b) || /^(?:items|justify|justify-items|justify-self|content|self|place|place-content|place-items|place-self)-/.test(b) || /^-?(?:gap|space-[xy]|col-|row-|grid-|auto-cols|auto-rows|order-|order$)/.test(b) || /^-?(?:auto-flow|grid-flow)-/.test(b) || /^(?:columns-|break-after|break-before|break-inside)-/.test(b) || /^(?:float|clear)-/.test(b) || /^(?:wrap-|flex-wrap|flex-nowrap|flex-row|flex-col|flex-auto|flex-initial|flex-none|flex-1|flex-\[)/.test(b) || /^container$/.test(b)) return "layout";
|
|
91
91
|
if (/^-?(?:w|h|min-w|max-w|min-h|max-h|size|aspect|shrink|grow|basis)-/.test(b) || /^(?:shrink|grow)(?:$|-)/.test(b) || /^-?overflow-/.test(b) || /^-?object-/.test(b)) return "sizing";
|
|
92
92
|
if (/^-?(?:p|px|py|pt|pb|pl|pr|ps|pe|m|mx|my|mt|mb|ml|mr|ms|me)-/.test(b)) return "spacing";
|
|
93
|
-
if (/^-?(?:rounded|border|ring|divide|inset-ring)(?:-|\/|$)/.test(b) || /^(?:rounded|border|ring|divide|inset-ring)$/.test(b) ||
|
|
93
|
+
if (/^-?(?:rounded|border|ring|divide|inset-ring)(?:-|\/|$)/.test(b) || /^(?:rounded|border|ring|divide|inset-ring)$/.test(b) || b.startsWith("ring-offset-")) return "shape";
|
|
94
94
|
if (/^-?bg-/.test(b) || /^(?:from|via|to)(?:-|\/|$)/.test(b) || /^bg-(?:linear|radial|conic)-/.test(b) || /^-?mask-/.test(b) || /^-?(?:fill|stroke)-/.test(b)) return "background";
|
|
95
95
|
if (/^-?(?:shadow|inset-shadow|drop-shadow|text-shadow)(?:-|\/|$)/.test(b) || /^(?:shadow|inset-shadow)$/.test(b) || /^-?outline(?:-|\/|$)/.test(b) || /^outline$/.test(b)) return "shadow";
|
|
96
|
-
if (/^-?(?:font|leading|tracking|list|indent|align|decoration|underline-offset|text-wrap|text-balance|text-pretty)-/.test(b) || /^-?(?:text-(?!shadow))/.test(b) || /^-?(?:whitespace|break|line-clamp|hyphens)-/.test(b) || /^(?:antialiased|subpixel-antialiased|italic|not-italic|overline|line-through|underline|no-underline|uppercase|lowercase|capitalize|normal-case|truncate|text-wrap|text-balance|text-pretty)$/.test(b) ||
|
|
97
|
-
if (/^opacity(?:-|$)/.test(b) ||
|
|
98
|
-
if (/^(?:transition|duration|ease|delay|animate)(?:-|$)/.test(b) ||
|
|
99
|
-
if (/^(?:cursor|pointer-events|select|touch|scroll|scroll-m|scroll-p|overscroll|snap|resize|field-sizing|appearance|scheme|color-scheme|accent|caret)(?:-|$)/.test(b) || /^(?:cursor|select|resize)$/.test(b) ||
|
|
96
|
+
if (/^-?(?:font|leading|tracking|list|indent|align|decoration|underline-offset|text-wrap|text-balance|text-pretty)-/.test(b) || /^-?(?:text-(?!shadow))/.test(b) || /^-?(?:whitespace|break|line-clamp|hyphens)-/.test(b) || /^(?:antialiased|subpixel-antialiased|italic|not-italic|overline|line-through|underline|no-underline|uppercase|lowercase|capitalize|normal-case|truncate|text-wrap|text-balance|text-pretty)$/.test(b) || b.startsWith("text-wrap-") || /^tabular-nums$|^slashed-zero$|^lining-nums$|^oldstyle-nums$|^proportional-nums$/.test(b)) return "typography";
|
|
97
|
+
if (/^opacity(?:-|$)/.test(b) || b.startsWith("mix-blend-") || /^isolation(?:-|$)/.test(b) || b === "isolate" || /^transform(?:-(?:gpu|cpu|none))?$/.test(b) || /^-?(?:translate|scale|rotate|skew)-/.test(b) || /^perspective(?:-|$)/.test(b) || b === "transform-3d" || /^blur(?:-|$)/.test(b) || b.startsWith("backdrop-") || /^filter(?:-|$)/.test(b) || b.startsWith("brightness-") || b.startsWith("contrast-") || /^grayscale(?:-|$)/.test(b) || b.startsWith("hue-rotate-") || /^invert(?:-|$)/.test(b) || b.startsWith("saturate-") || /^sepia(?:-|$)/.test(b) || b.startsWith("will-change")) return "composite";
|
|
98
|
+
if (/^(?:transition|duration|ease|delay|animate)(?:-|$)/.test(b) || b.startsWith("ease-")) return "motion";
|
|
99
|
+
if (/^(?:cursor|pointer-events|select|touch|scroll|scroll-m|scroll-p|overscroll|snap|resize|field-sizing|appearance|scheme|color-scheme|accent|caret)(?:-|$)/.test(b) || /^(?:cursor|select|resize)$/.test(b) || b.startsWith("scroll-behavior") || b.startsWith("scroll-snap") || b.startsWith("touch-action") || b === "inert") return "behavior";
|
|
100
100
|
return "other";
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
@@ -106,7 +106,7 @@ function classifyBareUtility(bareUtility) {
|
|
|
106
106
|
* Pure arbitrary **properties** (`[--x]:`, `[color:red]`) have no `&` in the leading `[…]` segment.
|
|
107
107
|
*/
|
|
108
108
|
function isArbitraryParentSelectorStateToken(token) {
|
|
109
|
-
if (
|
|
109
|
+
if (token.startsWith("[&")) return true;
|
|
110
110
|
if (!token.startsWith("[")) return false;
|
|
111
111
|
const colonIdx = indexOfFirstVariantColon(token);
|
|
112
112
|
if (colonIdx === -1 || colonIdx === 0 || token[colonIdx - 1] !== "]") return false;
|
package/dist/arrange/output.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { logger } from "../core/logger.mjs";
|
|
2
1
|
import "./domain/constants.mjs";
|
|
3
2
|
import { lineOf } from "./domain/ast/helpers.mjs";
|
|
3
|
+
import { logger } from "../core/logger.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
//#region src/arrange/output.ts
|
|
6
6
|
/**
|
|
@@ -30,13 +30,21 @@ function printAnalyzeReport(resolvedTargetPath, report) {
|
|
|
30
30
|
function printSyncResult(result, write) {
|
|
31
31
|
logger.out(`\nTotal: ${result.filePaths.length} file(s), ${result.totalFound} site(s) (cn/tv/JSX className) to review.`);
|
|
32
32
|
if (write) logger.out(`Applied: ${result.totalChanged} site(s) updated.`);
|
|
33
|
-
else logger.out(`(
|
|
33
|
+
else logger.out(`(Re-run without --dry-run to write changes, or "pnpm cli:arrange" / "pnpm exec codefast arrange")`);
|
|
34
34
|
if (write ? result.totalChanged > 0 : result.totalFound > 0) logger.out("Note: class order may change across concern groups — smoke-test the UI if you rely on cascade order.");
|
|
35
35
|
if (result.hookError !== null) logger.err(result.hookError);
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* @since 0.3.16-canary.0
|
|
39
39
|
*/
|
|
40
|
+
function printSimplifyResult(result, write) {
|
|
41
|
+
logger.out(`\nTotal: ${result.filePaths.length} file(s), ${result.totalFound} site(s) to simplify.`);
|
|
42
|
+
if (write) logger.out(`Applied: ${result.totalChanged} site(s) updated.`);
|
|
43
|
+
else logger.out(`(Run "simplify" to write changes, or "pnpm cli:arrange-simplify" / "pnpm exec codefast arrange simplify")`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @since 0.3.16-canary.0
|
|
47
|
+
*/
|
|
40
48
|
function printGroupFilePreviewFromWork(work) {
|
|
41
49
|
printGroupFilePreviewBody({
|
|
42
50
|
filePath: work.filePath,
|
|
@@ -76,4 +84,4 @@ function printGroupFilePreviewBody(args) {
|
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
86
|
//#endregion
|
|
79
|
-
export { printAnalyzeReport, printGroupFilePreviewFromWork, printSyncResult };
|
|
87
|
+
export { printAnalyzeReport, printGroupFilePreviewFromWork, printSimplifyResult, printSyncResult };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseDomainSourceFile } from "./source-parse.mjs";
|
|
2
|
-
import { buildGroupFileUnwrapState, countPersistedGroupFileEdits, groupFileDryRunNoEdits, groupFileEditsTouchJsxCn, groupFilePreviewTotals, groupFileWorkHasNothingToReport, mergeGroupFileBodyText, tryBuildGroupFileWorkPlan } from "./domain/grouping-service.mjs";
|
|
3
2
|
import { ensureCnImport } from "./domain/imports.mjs";
|
|
3
|
+
import { buildGroupFileUnwrapState, countPersistedGroupFileEdits, groupFileDryRunNoEdits, groupFileEditsTouchJsxCn, groupFilePreviewTotals, groupFileWorkHasNothingToReport, mergeGroupFileBodyText, tryBuildGroupFileWorkPlan } from "./domain/grouping-service.mjs";
|
|
4
4
|
//#region src/arrange/process-file.ts
|
|
5
5
|
/**
|
|
6
6
|
* @since 0.3.16-canary.0
|
|
@@ -3,13 +3,22 @@ import { logger } from "../core/logger.mjs";
|
|
|
3
3
|
import { walkTsxFiles } from "../core/workspace/typescript-walk.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
//#region src/arrange/scan-target.ts
|
|
6
|
+
const TEST_FILE_PATTERN = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
7
|
+
/**
|
|
8
|
+
* Test/spec files are excluded from directory scans: arrange rewrites class strings,
|
|
9
|
+
* and a `cn(...)` inside an assertion (e.g. `expect(cn("a", "b"))`) is intentional test
|
|
10
|
+
* data, not styling to reformat. An explicit single-file target is still honored.
|
|
11
|
+
*/
|
|
12
|
+
function isTestFile(filePath) {
|
|
13
|
+
return TEST_FILE_PATTERN.test(path.basename(filePath));
|
|
14
|
+
}
|
|
6
15
|
/**
|
|
7
16
|
* @since 0.3.16-canary.0
|
|
8
17
|
*/
|
|
9
18
|
function scanArrangeTargets(fs, targetPath) {
|
|
10
19
|
const resolvedTargetPath = path.resolve(targetPath);
|
|
11
20
|
if (fs.statSync(resolvedTargetPath).isDirectory()) try {
|
|
12
|
-
return walkTsxFiles(resolvedTargetPath, fs);
|
|
21
|
+
return walkTsxFiles(resolvedTargetPath, fs).filter((filePath) => !isTestFile(filePath));
|
|
13
22
|
} catch (caughtError) {
|
|
14
23
|
logger.err(`[file-walker] ${messageFrom(caughtError)}`);
|
|
15
24
|
throw caughtError;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { applyEditsDescending } from "../core/source-text-edit.mjs";
|
|
2
|
+
import { parseDomainSourceFile } from "./source-parse.mjs";
|
|
3
|
+
import { collectSimplifyTargets } from "./domain/ast/simplify-targets.mjs";
|
|
4
|
+
import { dropCnImportIfUnused } from "./domain/imports.mjs";
|
|
5
|
+
//#region src/arrange/simplify-process-file.ts
|
|
6
|
+
/**
|
|
7
|
+
* @since 0.3.16-canary.0
|
|
8
|
+
*/
|
|
9
|
+
function processArrangeSimplifyFile(fs, args) {
|
|
10
|
+
const { filePath, write } = args;
|
|
11
|
+
const sourceText = fs.readFileSync(filePath, "utf8");
|
|
12
|
+
const meaningful = collectSimplifyTargets(parseDomainSourceFile(filePath, sourceText)).filter((edit) => sourceText.slice(edit.start, edit.end) !== edit.replacement);
|
|
13
|
+
const textAfterEdits = meaningful.length > 0 ? applyEditsDescending(sourceText, meaningful) : sourceText;
|
|
14
|
+
const textAfterImportDrop = dropCnImportIfUnused(parseDomainSourceFile(filePath, textAfterEdits));
|
|
15
|
+
const importDropped = textAfterImportDrop !== textAfterEdits;
|
|
16
|
+
const totalFound = meaningful.length + (importDropped ? 1 : 0);
|
|
17
|
+
if (totalFound === 0) return {
|
|
18
|
+
filePath,
|
|
19
|
+
totalFound: 0,
|
|
20
|
+
changed: 0
|
|
21
|
+
};
|
|
22
|
+
if (!write) return {
|
|
23
|
+
filePath,
|
|
24
|
+
totalFound,
|
|
25
|
+
changed: 0
|
|
26
|
+
};
|
|
27
|
+
fs.writeFileSync(filePath, textAfterImportDrop, "utf8");
|
|
28
|
+
return {
|
|
29
|
+
filePath,
|
|
30
|
+
totalFound,
|
|
31
|
+
changed: totalFound
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { processArrangeSimplifyFile };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { scanArrangeTargets } from "./scan-target.mjs";
|
|
2
|
+
import { ok } from "../core/result.mjs";
|
|
3
|
+
import { processArrangeSimplifyFile } from "./simplify-process-file.mjs";
|
|
4
|
+
//#region src/arrange/simplify-sync.ts
|
|
5
|
+
/**
|
|
6
|
+
* @since 0.3.16-canary.0
|
|
7
|
+
*/
|
|
8
|
+
async function runArrangeSimplify(fs, args) {
|
|
9
|
+
const filePaths = scanArrangeTargets(fs, args.targetPath);
|
|
10
|
+
const modifiedFiles = [];
|
|
11
|
+
let totalFound = 0;
|
|
12
|
+
let totalChanged = 0;
|
|
13
|
+
for (const filePath of filePaths) {
|
|
14
|
+
const result = processArrangeSimplifyFile(fs, {
|
|
15
|
+
filePath,
|
|
16
|
+
write: args.write
|
|
17
|
+
});
|
|
18
|
+
totalFound += result.totalFound;
|
|
19
|
+
totalChanged += result.changed;
|
|
20
|
+
if (result.changed > 0) modifiedFiles.push(result.filePath);
|
|
21
|
+
}
|
|
22
|
+
return ok({
|
|
23
|
+
filePaths,
|
|
24
|
+
modifiedFiles,
|
|
25
|
+
totalFound,
|
|
26
|
+
totalChanged,
|
|
27
|
+
hookError: null,
|
|
28
|
+
previewPlans: []
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { runArrangeSimplify };
|
package/dist/arrange/suggest.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { suggestCnGroups, summarizeGroupBucketLabels } from "./domain/grouping.mjs";
|
|
2
1
|
import { formatArray, formatCnCall } from "./domain/source-text-formatters.mjs";
|
|
2
|
+
import { suggestCnGroups, summarizeGroupBucketLabels } from "./domain/grouping.mjs";
|
|
3
3
|
//#region src/arrange/suggest.ts
|
|
4
4
|
/**
|
|
5
5
|
* @since 0.3.16-canary.0
|
package/dist/arrange/sync.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { messageFrom } from "../core/errors.mjs";
|
|
2
|
-
import { ok } from "../core/result.mjs";
|
|
3
2
|
import { scanArrangeTargets } from "./scan-target.mjs";
|
|
3
|
+
import { ok } from "../core/result.mjs";
|
|
4
4
|
import { processArrangeGroupFile } from "./process-file.mjs";
|
|
5
5
|
//#region src/arrange/sync.ts
|
|
6
6
|
async function runOnAfterWriteHook(hook, modifiedFiles) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AppError, messageFrom } from "../core/errors.mjs";
|
|
2
2
|
import { err, ok } from "../core/result.mjs";
|
|
3
|
+
import { resolveArrangeTargetPath } from "./resolve-target.mjs";
|
|
3
4
|
import { loadCodefastConfig } from "../core/config.mjs";
|
|
4
5
|
import { findRepoRoot } from "../core/workspace/resolver.mjs";
|
|
5
|
-
import { resolveArrangeTargetPath } from "./resolve-target.mjs";
|
|
6
6
|
//#region src/arrange/workspace.ts
|
|
7
7
|
/**
|
|
8
8
|
* @since 0.3.16-canary.0
|
package/dist/cli.mjs
CHANGED
|
@@ -22,7 +22,7 @@ function readVersion() {
|
|
|
22
22
|
*/
|
|
23
23
|
async function runCli(argv) {
|
|
24
24
|
const program = new Command();
|
|
25
|
-
program.name("codefast").description("Codefast monorepo developer CLI").version(readVersion()).option("--no-color", "Disable ANSI color output").configureHelp({ sortSubcommands: true }).showHelpAfterError("(use --help for usage)");
|
|
25
|
+
program.name("codefast").description("Codefast monorepo developer CLI").version(readVersion()).option("--no-color", "Disable ANSI color output").enablePositionalOptions().configureHelp({ sortSubcommands: true }).showHelpAfterError("(use --help for usage)");
|
|
26
26
|
program.addCommand(createArrangeCommand());
|
|
27
27
|
program.addCommand(createMirrorCommand());
|
|
28
28
|
program.addCommand(createTagCommand());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { logger } from "../logger.mjs";
|
|
1
2
|
import { formatAppError } from "./format-error.mjs";
|
|
2
3
|
import "../exit-codes.mjs";
|
|
3
|
-
import { logger } from "../logger.mjs";
|
|
4
4
|
import { isVerboseCliDiagnostics } from "../verbose-diagnostics.mjs";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
//#region src/core/cli/result-handle.ts
|
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
//#region src/core/config/schema.ts
|
|
3
3
|
const afterWriteHookSchema = z.custom((value) => typeof value === "function", { message: "Expected a function" });
|
|
4
|
+
const mirrorCssConfigSchema = z.union([z.boolean(), z.object({
|
|
5
|
+
enabled: z.boolean().optional(),
|
|
6
|
+
customExports: z.record(z.string(), z.string()).optional(),
|
|
7
|
+
forceExportFiles: z.boolean().optional()
|
|
8
|
+
}).strict()]);
|
|
4
9
|
/**
|
|
10
|
+
* Per-package mirror configuration. Setting a package to `false` skips it entirely.
|
|
11
|
+
*
|
|
12
|
+
* - `source` — include a `source` condition pointing to the original `.ts` file (default `true`).
|
|
13
|
+
* Pass a string to override the root-export source path explicitly.
|
|
14
|
+
* - `types` — include the `types` condition when `.d.ts` files exist (default `true`).
|
|
15
|
+
* - `import` — include the `import` condition (default `true`).
|
|
16
|
+
* - `css` — CSS export configuration (wildcard or per-file).
|
|
17
|
+
*
|
|
5
18
|
* @since 0.3.16-canary.0
|
|
6
19
|
*/
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
const mirrorPackageConfigSchema = z.object({
|
|
21
|
+
/** Preserve the existing `package.json#exports` map and only add missing conditions
|
|
22
|
+
* (`source`, `types`, `import`). No dist/ scan is performed. */
|
|
23
|
+
preserve: z.boolean().optional(),
|
|
24
|
+
strip: z.string().optional(),
|
|
25
|
+
exports: z.record(z.string(), z.string()).optional(),
|
|
26
|
+
source: z.union([z.boolean(), z.string()]).default(true),
|
|
27
|
+
types: z.boolean().default(true),
|
|
28
|
+
import: z.boolean().default(true),
|
|
29
|
+
css: mirrorCssConfigSchema.optional()
|
|
16
30
|
}).strict();
|
|
17
31
|
/**
|
|
32
|
+
* Mirror config: a record keyed by package name. Set a package to `false` to skip it;
|
|
33
|
+
* omit it entirely to process it with default settings.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.3.16-canary.0
|
|
36
|
+
*/
|
|
37
|
+
const mirrorConfigSchema = z.record(z.string(), z.union([z.literal(false), mirrorPackageConfigSchema]));
|
|
38
|
+
/**
|
|
18
39
|
* @since 0.3.16-canary.0
|
|
19
40
|
*/
|
|
20
41
|
const codefastTagConfigSchema = z.object({
|
|
@@ -36,4 +57,4 @@ const codefastConfigRootSchema = z.object({
|
|
|
36
57
|
arrange: codefastArrangeConfigSchema.optional()
|
|
37
58
|
}).strict();
|
|
38
59
|
//#endregion
|
|
39
|
-
export { codefastArrangeConfigSchema, codefastConfigRootSchema, codefastTagConfigSchema, mirrorConfigSchema };
|
|
60
|
+
export { codefastArrangeConfigSchema, codefastConfigRootSchema, codefastTagConfigSchema, mirrorConfigSchema, mirrorPackageConfigSchema };
|