sovr-patch 0.2.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.
Potentially problematic release.
This version of sovr-patch might be problematic. Click here for more details.
- package/LICENSE +66 -0
- package/README.md +112 -0
- package/dist/audit/engine.js +140 -0
- package/dist/audit/engine.js.map +1 -0
- package/dist/audit/packs.js +21 -0
- package/dist/audit/packs.js.map +1 -0
- package/dist/audit/report.js +79 -0
- package/dist/audit/report.js.map +1 -0
- package/dist/audit/types.js +5 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/cli/help.js +108 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/parse-argv.js +73 -0
- package/dist/cli/parse-argv.js.map +1 -0
- package/dist/commands/activate.js +179 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/audit.js +89 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/ci-gate.js +239 -0
- package/dist/commands/ci-gate.js.map +1 -0
- package/dist/commands/list-rules.js +11 -0
- package/dist/commands/list-rules.js.map +1 -0
- package/dist/commands/run.js +107 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/status.js +174 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/core/engine.js +68 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/license.js +243 -0
- package/dist/core/license.js.map +1 -0
- package/dist/core/load-project.js +41 -0
- package/dist/core/load-project.js.map +1 -0
- package/dist/core/report.js +68 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/rollback.js +160 -0
- package/dist/core/rollback.js.map +1 -0
- package/dist/core/safe-apply.js +75 -0
- package/dist/core/safe-apply.js.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/update-check.js +162 -0
- package/dist/core/update-check.js.map +1 -0
- package/dist/core/verify.js +32 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/index.js +124 -0
- package/dist/index.js.map +1 -0
- package/dist/patch/apply.js +14 -0
- package/dist/patch/apply.js.map +1 -0
- package/dist/patch/overlap.js +12 -0
- package/dist/patch/overlap.js.map +1 -0
- package/dist/patch/preview.js +48 -0
- package/dist/patch/preview.js.map +1 -0
- package/dist/rules/design-token/classify.js +16 -0
- package/dist/rules/design-token/classify.js.map +1 -0
- package/dist/rules/design-token/locate.js +113 -0
- package/dist/rules/design-token/locate.js.map +1 -0
- package/dist/rules/design-token/presets.js +53 -0
- package/dist/rules/design-token/presets.js.map +1 -0
- package/dist/rules/design-token/rule.js +13 -0
- package/dist/rules/design-token/rule.js.map +1 -0
- package/dist/rules/design-token/schema.js +84 -0
- package/dist/rules/design-token/schema.js.map +1 -0
- package/dist/rules/design-token/transform.js +27 -0
- package/dist/rules/design-token/transform.js.map +1 -0
- package/dist/rules/field-rename/classify.js +77 -0
- package/dist/rules/field-rename/classify.js.map +1 -0
- package/dist/rules/field-rename/locate.js +294 -0
- package/dist/rules/field-rename/locate.js.map +1 -0
- package/dist/rules/field-rename/rule.js +13 -0
- package/dist/rules/field-rename/rule.js.map +1 -0
- package/dist/rules/field-rename/schema.js +35 -0
- package/dist/rules/field-rename/schema.js.map +1 -0
- package/dist/rules/field-rename/transform.js +160 -0
- package/dist/rules/field-rename/transform.js.map +1 -0
- package/dist/rules/inline-style/classify.js +17 -0
- package/dist/rules/inline-style/classify.js.map +1 -0
- package/dist/rules/inline-style/locate.js +163 -0
- package/dist/rules/inline-style/locate.js.map +1 -0
- package/dist/rules/inline-style/presets.js +71 -0
- package/dist/rules/inline-style/presets.js.map +1 -0
- package/dist/rules/inline-style/rule.js +13 -0
- package/dist/rules/inline-style/rule.js.map +1 -0
- package/dist/rules/inline-style/schema.js +97 -0
- package/dist/rules/inline-style/schema.js.map +1 -0
- package/dist/rules/inline-style/transform.js +52 -0
- package/dist/rules/inline-style/transform.js.map +1 -0
- package/dist/rules/nullish-fallback/classify.js +22 -0
- package/dist/rules/nullish-fallback/classify.js.map +1 -0
- package/dist/rules/nullish-fallback/locate.js +175 -0
- package/dist/rules/nullish-fallback/locate.js.map +1 -0
- package/dist/rules/nullish-fallback/rule.js +13 -0
- package/dist/rules/nullish-fallback/rule.js.map +1 -0
- package/dist/rules/nullish-fallback/schema.js +10 -0
- package/dist/rules/nullish-fallback/schema.js.map +1 -0
- package/dist/rules/nullish-fallback/transform.js +49 -0
- package/dist/rules/nullish-fallback/transform.js.map +1 -0
- package/dist/rules/registry.js +21 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/license.json +13 -0
- package/package.json +64 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { createTwoFilesPatch } from "diff";
|
|
3
|
+
import { detectOverlaps } from "./overlap.js";
|
|
4
|
+
function groupByFile(patches) {
|
|
5
|
+
const map = new Map();
|
|
6
|
+
for (const patch of patches) {
|
|
7
|
+
const list = map.get(patch.filePath) ?? [];
|
|
8
|
+
list.push(patch);
|
|
9
|
+
map.set(patch.filePath, list);
|
|
10
|
+
}
|
|
11
|
+
return map;
|
|
12
|
+
}
|
|
13
|
+
function applyPatchesToText(original, patches) {
|
|
14
|
+
const sorted = [...patches].sort((a, b) => b.start - a.start);
|
|
15
|
+
let next = original;
|
|
16
|
+
for (const patch of sorted) {
|
|
17
|
+
next = next.slice(0, patch.start) + patch.after + next.slice(patch.end);
|
|
18
|
+
}
|
|
19
|
+
return next;
|
|
20
|
+
}
|
|
21
|
+
export function previewPatchedFiles(loaded, patches) {
|
|
22
|
+
const previews = new Map();
|
|
23
|
+
const grouped = groupByFile(patches);
|
|
24
|
+
for (const [filePath, filePatches] of grouped.entries()) {
|
|
25
|
+
detectOverlaps(filePatches);
|
|
26
|
+
const absolute = path.resolve(loaded.repoPath, filePath);
|
|
27
|
+
const sourceFile = loaded.project.getSourceFile(absolute);
|
|
28
|
+
if (!sourceFile)
|
|
29
|
+
throw new Error(`source file not found: ${filePath}`);
|
|
30
|
+
const before = sourceFile.getFullText();
|
|
31
|
+
const after = applyPatchesToText(before, filePatches);
|
|
32
|
+
previews.set(filePath, { before, after });
|
|
33
|
+
}
|
|
34
|
+
return previews;
|
|
35
|
+
}
|
|
36
|
+
export function buildDiffFromPreview(previewMap) {
|
|
37
|
+
const changedFiles = [];
|
|
38
|
+
let diffText = "";
|
|
39
|
+
for (const [filePath, preview] of previewMap.entries()) {
|
|
40
|
+
if (preview.before === preview.after)
|
|
41
|
+
continue;
|
|
42
|
+
changedFiles.push(filePath);
|
|
43
|
+
diffText += createTwoFilesPatch(filePath, filePath, preview.before, preview.after, "before", "after");
|
|
44
|
+
diffText += "\n";
|
|
45
|
+
}
|
|
46
|
+
return { changedFiles, diffText };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/patch/preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design token classification.
|
|
3
|
+
*
|
|
4
|
+
* In run mode (with map): all hits are "safe" — we have a known token mapping.
|
|
5
|
+
* In audit mode (matchAllHex): mapped hits are "safe", unmapped are "ambiguous".
|
|
6
|
+
* The severity is already set by locate based on whether a token mapping exists.
|
|
7
|
+
*/
|
|
8
|
+
export function classifyDesignToken(located, _context) {
|
|
9
|
+
// Severity is pre-set by locate:
|
|
10
|
+
// - "safe" if tokenName exists (hex-color-mapped)
|
|
11
|
+
// - "ambiguous" if no token mapping (hex-color-unmapped, audit discovery)
|
|
12
|
+
return {
|
|
13
|
+
...located.hit,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../../src/rules/design-token/classify.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAyB,EACzB,QAAwC;IAExC,iCAAiC;IACjC,kDAAkD;IAClD,0EAA0E;IAC1E,OAAO;QACL,GAAG,OAAO,CAAC,GAAG;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SyntaxKind } from "ts-morph";
|
|
3
|
+
function getColumnFromNode(node) {
|
|
4
|
+
const sourceFile = node.getSourceFile();
|
|
5
|
+
const lineStarts = sourceFile.compilerNode.getLineStarts();
|
|
6
|
+
const lineIndex = node.getStartLineNumber() - 1;
|
|
7
|
+
const lineStart = lineStarts[lineIndex] ?? 0;
|
|
8
|
+
return node.getStart() - lineStart + 1;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Normalize a hex color to lowercase 6-digit format.
|
|
12
|
+
* #FFF → #ffffff
|
|
13
|
+
* #ff0000 → #ff0000
|
|
14
|
+
* #FF0000 → #ff0000
|
|
15
|
+
*/
|
|
16
|
+
function normalizeHex(hex) {
|
|
17
|
+
const clean = hex.toLowerCase().trim();
|
|
18
|
+
if (/^#[0-9a-f]{3}$/.test(clean)) {
|
|
19
|
+
// Expand shorthand: #abc → #aabbcc
|
|
20
|
+
return `#${clean[1]}${clean[1]}${clean[2]}${clean[2]}${clean[3]}${clean[3]}`;
|
|
21
|
+
}
|
|
22
|
+
return clean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Scan all string literals in source files for hex color values
|
|
26
|
+
* that match entries in the design token map.
|
|
27
|
+
*
|
|
28
|
+
* Matches patterns like:
|
|
29
|
+
* "#ff0000"
|
|
30
|
+
* '#FF0000'
|
|
31
|
+
* `color: #ff0000` (template literal parts)
|
|
32
|
+
* "background-color: #ff0000; color: #00ff00" (multiple in one string)
|
|
33
|
+
*/
|
|
34
|
+
export function locateDesignToken(context) {
|
|
35
|
+
const results = [];
|
|
36
|
+
// Build normalized lookup map (empty in all-hex audit mode)
|
|
37
|
+
const normalizedMap = new Map();
|
|
38
|
+
for (const [hex, token] of Object.entries(context.config.map)) {
|
|
39
|
+
normalizedMap.set(normalizeHex(hex), token);
|
|
40
|
+
}
|
|
41
|
+
const matchAllHex = context.config.matchAllHex;
|
|
42
|
+
for (const sourceFile of context.loaded.sourceFiles) {
|
|
43
|
+
const filePath = path.relative(context.loaded.repoPath, sourceFile.getFilePath());
|
|
44
|
+
// Scan StringLiteral nodes
|
|
45
|
+
const stringNodes = sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral);
|
|
46
|
+
for (const node of stringNodes) {
|
|
47
|
+
const text = node.getLiteralText();
|
|
48
|
+
scanTextForHexColors(node, text, filePath, normalizedMap, results, context, matchAllHex);
|
|
49
|
+
}
|
|
50
|
+
// Scan NoSubstitutionTemplateLiteral nodes
|
|
51
|
+
const templateNodes = sourceFile.getDescendantsOfKind(SyntaxKind.NoSubstitutionTemplateLiteral);
|
|
52
|
+
for (const node of templateNodes) {
|
|
53
|
+
const text = node.getLiteralText();
|
|
54
|
+
scanTextForHexColors(node, text, filePath, normalizedMap, results, context, matchAllHex);
|
|
55
|
+
}
|
|
56
|
+
// Scan TemplateHead, TemplateMiddle, TemplateTail for template literals with expressions
|
|
57
|
+
for (const kind of [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail]) {
|
|
58
|
+
const nodes = sourceFile.getDescendantsOfKind(kind);
|
|
59
|
+
for (const node of nodes) {
|
|
60
|
+
// Template parts have getLiteralText via their specific types
|
|
61
|
+
const fullText = node.getText();
|
|
62
|
+
// Strip template delimiters: `...${ or }...${ or }...`
|
|
63
|
+
const text = fullText.replace(/^[`}]/, "").replace(/[$`]$/, "").replace(/\$\{$/, "");
|
|
64
|
+
scanTextForHexColors(node, text, filePath, normalizedMap, results, context, matchAllHex);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
function scanTextForHexColors(node, text, filePath, normalizedMap, results, context, matchAllHex) {
|
|
71
|
+
const hexPattern = /#[0-9a-fA-F]{3,8}\b/g;
|
|
72
|
+
let match;
|
|
73
|
+
while ((match = hexPattern.exec(text)) !== null) {
|
|
74
|
+
const rawHex = match[0];
|
|
75
|
+
const normalized = normalizeHex(rawHex);
|
|
76
|
+
// In matchAllHex mode (audit discovery), match everything
|
|
77
|
+
// In map mode, only match hex values present in the map
|
|
78
|
+
const token = normalizedMap.get(normalized);
|
|
79
|
+
if (!matchAllHex && !token)
|
|
80
|
+
continue;
|
|
81
|
+
// Determine severity based on mode
|
|
82
|
+
// - If we have a token mapping → safe (can auto-fix)
|
|
83
|
+
// - If matchAllHex with no mapping → "unmapped" (discovery hit, no auto-fix)
|
|
84
|
+
const hasMappedToken = !!token;
|
|
85
|
+
const severity = hasMappedToken ? "safe" : "ambiguous";
|
|
86
|
+
const reason = hasMappedToken ? "hex-color-mapped" : "hex-color-unmapped";
|
|
87
|
+
const nodeText = node.getText();
|
|
88
|
+
const nodeStart = node.getStart();
|
|
89
|
+
const quoteOffset = nodeText.indexOf(text);
|
|
90
|
+
const hexOffsetInNode = quoteOffset + match.index;
|
|
91
|
+
results.push({
|
|
92
|
+
node,
|
|
93
|
+
hit: {
|
|
94
|
+
filePath,
|
|
95
|
+
line: node.getStartLineNumber(),
|
|
96
|
+
column: getColumnFromNode(node),
|
|
97
|
+
sourceText: rawHex,
|
|
98
|
+
severity,
|
|
99
|
+
reason,
|
|
100
|
+
start: nodeStart + hexOffsetInNode,
|
|
101
|
+
end: nodeStart + hexOffsetInNode + rawHex.length,
|
|
102
|
+
metadata: {
|
|
103
|
+
kind: "hex-to-token",
|
|
104
|
+
rawHex,
|
|
105
|
+
normalizedHex: normalized,
|
|
106
|
+
tokenName: token ?? null,
|
|
107
|
+
fullNodeText: nodeText,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=locate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locate.js","sourceRoot":"","sources":["../../../src/rules/design-token/locate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design Token Presets
|
|
3
|
+
*
|
|
4
|
+
* Presets provide predefined hex → token mappings for common design systems.
|
|
5
|
+
* Used in audit mode to give meaningful results without user-provided maps.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* sovr-core preset — common UI colors used in the SOVR design system
|
|
9
|
+
*/
|
|
10
|
+
const SOVR_CORE = {
|
|
11
|
+
name: "sovr-core",
|
|
12
|
+
description: "SOVR design system core color tokens",
|
|
13
|
+
map: {
|
|
14
|
+
"#00ff94": "--sovr-primary",
|
|
15
|
+
"#ff0000": "--sovr-danger",
|
|
16
|
+
"#ff4444": "--sovr-danger-light",
|
|
17
|
+
"#00ff00": "--sovr-success",
|
|
18
|
+
"#0066cc": "--sovr-info",
|
|
19
|
+
"#ffaa00": "--sovr-warning",
|
|
20
|
+
"#ffffff": "--sovr-white",
|
|
21
|
+
"#000000": "--sovr-black",
|
|
22
|
+
"#333333": "--sovr-text",
|
|
23
|
+
"#666666": "--sovr-text-muted",
|
|
24
|
+
"#999999": "--sovr-text-subtle",
|
|
25
|
+
"#f5f5f5": "--sovr-bg-light",
|
|
26
|
+
"#e0e0e0": "--sovr-border",
|
|
27
|
+
"#1a1a1a": "--sovr-bg-dark",
|
|
28
|
+
"#2d2d2d": "--sovr-surface",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* all-hex preset — matches ANY hex color found in the codebase.
|
|
33
|
+
* Used in audit mode to discover the full scope of hardcoded colors.
|
|
34
|
+
* Does not provide token names (uses placeholder).
|
|
35
|
+
*/
|
|
36
|
+
const ALL_HEX = {
|
|
37
|
+
name: "all-hex",
|
|
38
|
+
description: "Match all hex colors (audit discovery mode)",
|
|
39
|
+
map: {}, // Empty map signals "match everything" in audit mode
|
|
40
|
+
};
|
|
41
|
+
export const PRESETS = {
|
|
42
|
+
"sovr-core": SOVR_CORE,
|
|
43
|
+
"all-hex": ALL_HEX,
|
|
44
|
+
};
|
|
45
|
+
export function getPresetOrThrow(name) {
|
|
46
|
+
const preset = PRESETS[name];
|
|
47
|
+
if (!preset) {
|
|
48
|
+
const available = Object.keys(PRESETS).join(", ");
|
|
49
|
+
throw new Error(`design-token: unknown preset "${name}". Available: ${available}`);
|
|
50
|
+
}
|
|
51
|
+
return preset;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.js","sourceRoot":"","sources":["../../../src/rules/design-token/presets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;GAEG;AACH,MAAM,SAAS,GAAsB;IACnC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,sCAAsC;IACnD,GAAG,EAAE;QACH,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,eAAe;QAC1B,SAAS,EAAE,qBAAqB;QAChC,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,aAAa;QACxB,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,cAAc;QACzB,SAAS,EAAE,cAAc;QACzB,SAAS,EAAE,aAAa;QACxB,SAAS,EAAE,mBAAmB;QAC9B,SAAS,EAAE,oBAAoB;QAC/B,SAAS,EAAE,iBAAiB;QAC5B,SAAS,EAAE,eAAe;QAC1B,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,gBAAgB;KAC5B;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,GAAsB;IACjC,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,6CAA6C;IAC1D,GAAG,EAAE,EAAE,EAAE,qDAAqD;CAC/D,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAsC;IACxD,WAAW,EAAE,SAAS;IACtB,SAAS,EAAE,OAAO;CACnB,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,iCAAiC,IAAI,iBAAiB,SAAS,EAAE,CAClE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { parseDesignTokenConfig } from "./schema.js";
|
|
2
|
+
import { locateDesignToken } from "./locate.js";
|
|
3
|
+
import { classifyDesignToken } from "./classify.js";
|
|
4
|
+
import { transformDesignToken } from "./transform.js";
|
|
5
|
+
export const designTokenRule = {
|
|
6
|
+
name: "design-token",
|
|
7
|
+
description: "Replace hardcoded hex color values with CSS custom property references (design tokens).",
|
|
8
|
+
parseConfig: parseDesignTokenConfig,
|
|
9
|
+
locate: locateDesignToken,
|
|
10
|
+
classify: classifyDesignToken,
|
|
11
|
+
transform: transformDesignToken,
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/rules/design-token/rule.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,CAAC,MAAM,eAAe,GAAsC;IAChE,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,yFAAyF;IAC3F,WAAW,EAAE,sBAAsB;IACnC,MAAM,EAAE,iBAAiB;IACzB,QAAQ,EAAE,mBAAmB;IAC7B,SAAS,EAAE,oBAAoB;CAChC,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design Token Migration Rule — Dual Mode Schema
|
|
3
|
+
*
|
|
4
|
+
* RUN mode: Requires --map (strict, produces patches)
|
|
5
|
+
* AUDIT mode: Zero-config (scans all hex colors), optional --preset for known tokens
|
|
6
|
+
*
|
|
7
|
+
* Example (run):
|
|
8
|
+
* sovr-patch run --rule design-token --map "#ff0000:--danger,#00ff00:--success" --repo ./web
|
|
9
|
+
*
|
|
10
|
+
* Example (audit):
|
|
11
|
+
* sovr-patch audit --rule design-token --repo ./web
|
|
12
|
+
* sovr-patch audit --rule design-token --preset sovr-core --repo ./web
|
|
13
|
+
*/
|
|
14
|
+
import { getPresetOrThrow } from "./presets.js";
|
|
15
|
+
/**
|
|
16
|
+
* Parse CLI map string format: "#ff0000:--danger,#00ff00:--success"
|
|
17
|
+
*/
|
|
18
|
+
function parseMapString(mapStr) {
|
|
19
|
+
const map = {};
|
|
20
|
+
for (const pair of mapStr.split(",")) {
|
|
21
|
+
const [hex, token] = pair.split(":").map((s) => s.trim());
|
|
22
|
+
if (!hex || !token) {
|
|
23
|
+
throw new Error(`design-token: invalid map entry "${pair}". Expected "hex:token".`);
|
|
24
|
+
}
|
|
25
|
+
map[hex.toLowerCase()] = token;
|
|
26
|
+
}
|
|
27
|
+
return map;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse config input into DesignTokenConfig.
|
|
31
|
+
*
|
|
32
|
+
* Behavior depends on mode:
|
|
33
|
+
* - mode="run" (default for run command): map is REQUIRED
|
|
34
|
+
* - mode="audit" (default for audit command): map is optional, defaults to all-hex scan
|
|
35
|
+
*/
|
|
36
|
+
export function parseDesignTokenConfig(input) {
|
|
37
|
+
const mode = input.mode === "audit" ? "audit" : "run";
|
|
38
|
+
// Handle preset
|
|
39
|
+
const presetName = input.preset;
|
|
40
|
+
let presetMap = {};
|
|
41
|
+
if (presetName) {
|
|
42
|
+
const preset = getPresetOrThrow(presetName);
|
|
43
|
+
presetMap = { ...preset.map };
|
|
44
|
+
}
|
|
45
|
+
// Handle explicit map
|
|
46
|
+
let explicitMap = {};
|
|
47
|
+
const mapRaw = input.map;
|
|
48
|
+
if (mapRaw && typeof mapRaw === "object") {
|
|
49
|
+
for (const [key, value] of Object.entries(mapRaw)) {
|
|
50
|
+
explicitMap[key.toLowerCase()] = String(value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (mapRaw) {
|
|
54
|
+
const mapStr = String(mapRaw).trim();
|
|
55
|
+
if (mapStr) {
|
|
56
|
+
explicitMap = parseMapString(mapStr);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Merge preset + explicit map (explicit wins)
|
|
60
|
+
const mergedMap = { ...presetMap, ...explicitMap };
|
|
61
|
+
if (mode === "run") {
|
|
62
|
+
// Run mode: map is required
|
|
63
|
+
if (Object.keys(mergedMap).length === 0) {
|
|
64
|
+
throw new Error(`design-token: "map" is required in run mode. Provide as --map "#ff0000:--danger,#00ff00:--success" or use --preset sovr-core`);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
mode: "run",
|
|
68
|
+
map: mergedMap,
|
|
69
|
+
matchAllHex: false,
|
|
70
|
+
preset: presetName,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Audit mode: zero-config is OK
|
|
74
|
+
// If no map and no preset, or preset is "all-hex", scan everything
|
|
75
|
+
const isAllHex = presetName === "all-hex" ||
|
|
76
|
+
(Object.keys(mergedMap).length === 0 && !presetName);
|
|
77
|
+
return {
|
|
78
|
+
mode: "audit",
|
|
79
|
+
map: mergedMap,
|
|
80
|
+
matchAllHex: isAllHex,
|
|
81
|
+
preset: presetName ?? (isAllHex ? "all-hex" : undefined),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/rules/design-token/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform a hex color hit into a design token reference.
|
|
3
|
+
*
|
|
4
|
+
* The hit's start/end point directly to the hex value within the string literal,
|
|
5
|
+
* so we just replace the hex with var(--token-name).
|
|
6
|
+
*
|
|
7
|
+
* Examples:
|
|
8
|
+
* "#ff0000" → "var(--sovr-danger)"
|
|
9
|
+
* "color: #ff0000" → "color: var(--sovr-danger)"
|
|
10
|
+
*/
|
|
11
|
+
export function transformDesignToken(hit, context) {
|
|
12
|
+
const tokenName = String(hit.metadata?.tokenName ?? "");
|
|
13
|
+
if (!tokenName) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const replacement = `var(${tokenName})`;
|
|
17
|
+
return {
|
|
18
|
+
filePath: hit.filePath,
|
|
19
|
+
start: hit.start,
|
|
20
|
+
end: hit.end,
|
|
21
|
+
before: hit.sourceText,
|
|
22
|
+
after: replacement,
|
|
23
|
+
ruleName: "design-token",
|
|
24
|
+
reason: "hex-to-token",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/rules/design-token/transform.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAgB,EAChB,OAAuC;IAEvC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;IAExD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,SAAS,GAAG,CAAC;IAExC,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,MAAM,EAAE,GAAG,CAAC,UAAU;QACtB,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,cAAc;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Node } from "ts-morph";
|
|
2
|
+
// ─── Context detectors ──────────────────────────────────────────
|
|
3
|
+
function isDeleteContext(node) {
|
|
4
|
+
const parent = node.getParent();
|
|
5
|
+
return parent != null && Node.isDeleteExpression(parent);
|
|
6
|
+
}
|
|
7
|
+
function isAssignmentLhs(node) {
|
|
8
|
+
const parent = node.getParent();
|
|
9
|
+
if (!parent || !Node.isBinaryExpression(parent))
|
|
10
|
+
return false;
|
|
11
|
+
const operator = parent.getOperatorToken().getText();
|
|
12
|
+
if (operator !== "=")
|
|
13
|
+
return false;
|
|
14
|
+
return parent.getLeft() === node;
|
|
15
|
+
}
|
|
16
|
+
function isComputedLikeContext(node) {
|
|
17
|
+
const parent = node.getParent();
|
|
18
|
+
if (!parent)
|
|
19
|
+
return false;
|
|
20
|
+
if (Node.isElementAccessExpression(parent))
|
|
21
|
+
return true;
|
|
22
|
+
if (Node.isComputedPropertyName(parent))
|
|
23
|
+
return true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
function isDestructureContext(node) {
|
|
27
|
+
let current = node;
|
|
28
|
+
while (current) {
|
|
29
|
+
if (Node.isBindingElement(current))
|
|
30
|
+
return true;
|
|
31
|
+
if (Node.isObjectBindingPattern(current))
|
|
32
|
+
return true;
|
|
33
|
+
current = current.getParent();
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
function nodeHasOptionalChain(node) {
|
|
38
|
+
let current = node;
|
|
39
|
+
while (Node.isPropertyAccessExpression(current)) {
|
|
40
|
+
if (current.hasQuestionDotToken()) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
current = current.getExpression();
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
// ─── Classify ───────────────────────────────────────────────────
|
|
48
|
+
export function classifyFieldRename(located, _context) {
|
|
49
|
+
const node = located.node;
|
|
50
|
+
const hit = { ...located.hit };
|
|
51
|
+
const kind = String(hit.metadata?.kind ?? "");
|
|
52
|
+
// Property keys, type fields, and shorthand are already classified in locate
|
|
53
|
+
if (kind === "property-key" || kind === "type-field" || kind === "shorthand") {
|
|
54
|
+
return hit;
|
|
55
|
+
}
|
|
56
|
+
// Value references need additional classification
|
|
57
|
+
if (kind === "value-ref") {
|
|
58
|
+
if (isDeleteContext(node)) {
|
|
59
|
+
return { ...hit, severity: "unsafe", reason: "delete-expression" };
|
|
60
|
+
}
|
|
61
|
+
if (isAssignmentLhs(node)) {
|
|
62
|
+
return { ...hit, severity: "unsafe", reason: "assignment-left-hand-side" };
|
|
63
|
+
}
|
|
64
|
+
if (isComputedLikeContext(node)) {
|
|
65
|
+
return { ...hit, severity: "ambiguous", reason: "computed-or-element-access-context" };
|
|
66
|
+
}
|
|
67
|
+
if (isDestructureContext(node)) {
|
|
68
|
+
return { ...hit, severity: "ambiguous", reason: "destructuring-context" };
|
|
69
|
+
}
|
|
70
|
+
if (Node.isPropertyAccessExpression(node) && nodeHasOptionalChain(node)) {
|
|
71
|
+
return { ...hit, severity: "safe", reason: "optional-chain-access" };
|
|
72
|
+
}
|
|
73
|
+
return { ...hit, severity: "safe", reason: "direct-property-access" };
|
|
74
|
+
}
|
|
75
|
+
return hit;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../../src/rules/field-rename/classify.ts"],"names":[],"mappings":"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"}
|