design-constraint-validator 1.0.0 → 2.0.1
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/LICENSE +21 -21
- package/README.md +229 -659
- package/adapters/README.md +46 -46
- package/adapters/css.ts +116 -116
- package/adapters/decisionthemes.d.ts +44 -0
- package/adapters/decisionthemes.d.ts.map +1 -0
- package/adapters/decisionthemes.js +35 -0
- package/adapters/decisionthemes.ts +59 -0
- package/adapters/js.ts +14 -14
- package/adapters/json.ts +45 -45
- package/cli/build-css.ts +32 -32
- package/cli/commands/build.ts +65 -65
- package/cli/commands/graph.d.ts.map +1 -1
- package/cli/commands/graph.js +26 -10
- package/cli/commands/graph.ts +180 -137
- package/cli/commands/index.ts +7 -7
- package/cli/commands/patch-apply.ts +80 -80
- package/cli/commands/patch.ts +22 -22
- package/cli/commands/set.d.ts.map +1 -1
- package/cli/commands/set.js +12 -4
- package/cli/commands/set.ts +239 -225
- package/cli/commands/utils.ts +50 -50
- package/cli/commands/validate.d.ts.map +1 -1
- package/cli/commands/validate.js +89 -33
- package/cli/commands/validate.ts +180 -115
- package/cli/commands/why.d.ts.map +1 -1
- package/cli/commands/why.js +86 -20
- package/cli/commands/why.ts +158 -46
- package/cli/config-schema.ts +27 -27
- package/cli/config.ts +35 -35
- package/cli/constraint-registry.d.ts +101 -0
- package/cli/constraint-registry.d.ts.map +1 -0
- package/cli/constraint-registry.js +225 -0
- package/cli/constraint-registry.ts +304 -0
- package/cli/constraints-loader.d.ts.map +1 -0
- package/cli/cross-axis-loader.d.ts +91 -0
- package/cli/cross-axis-loader.d.ts.map +1 -0
- package/cli/cross-axis-loader.js +222 -0
- package/cli/cross-axis-loader.ts +289 -0
- package/cli/dcv.js +4 -0
- package/cli/dcv.ts +111 -107
- package/cli/engine-helpers.d.ts.map +1 -1
- package/cli/graph-poset.ts +74 -74
- package/cli/json-output.d.ts +69 -0
- package/cli/json-output.d.ts.map +1 -0
- package/cli/json-output.js +109 -0
- package/cli/json-output.ts +184 -0
- package/cli/result.ts +27 -27
- package/cli/run.ts +54 -54
- package/cli/smoke-test.ts +40 -40
- package/cli/types.d.ts +6 -0
- package/cli/types.d.ts.map +1 -1
- package/cli/types.ts +84 -78
- package/cli/version-banner.d.ts +20 -0
- package/cli/version-banner.d.ts.map +1 -0
- package/cli/version-banner.js +49 -0
- package/cli/version-banner.ts +61 -0
- package/core/breakpoints.ts +50 -50
- package/core/cli-format.ts +31 -31
- package/core/color.ts +148 -148
- package/core/constraints/cross-axis.ts +114 -114
- package/core/constraints/monotonic-lightness.ts +38 -38
- package/core/constraints/monotonic.ts +74 -74
- package/core/constraints/threshold.ts +43 -43
- package/core/constraints/wcag.ts +70 -70
- package/core/cross-axis-config.d.ts.map +1 -1
- package/core/engine.d.ts +95 -0
- package/core/engine.d.ts.map +1 -1
- package/core/engine.js +22 -0
- package/core/engine.ts +167 -65
- package/core/flatten.ts +116 -116
- package/core/image-export.ts +48 -48
- package/core/index.d.ts +9 -30
- package/core/index.d.ts.map +1 -1
- package/core/index.js +7 -54
- package/core/index.ts +10 -72
- package/core/patch.ts +134 -134
- package/core/poset.ts +311 -311
- package/core/why.ts +63 -63
- package/package.json +96 -90
- package/themes/color.lg.order.json +15 -15
- package/themes/color.md.order.json +15 -15
- package/themes/color.order.json +15 -15
- package/themes/color.sm.order.json +15 -15
- package/themes/cross-axis.rules.json +35 -35
- package/themes/cross-axis.sm.rules.json +12 -12
- package/themes/layout.lg.order.json +18 -18
- package/themes/layout.md.order.json +18 -18
- package/themes/layout.order.json +18 -18
- package/themes/layout.sm.order.json +18 -18
- package/themes/spacing.order.json +14 -14
- package/themes/typography.lg.order.json +15 -15
- package/themes/typography.md.order.json +15 -15
- package/themes/typography.order.json +15 -15
- package/themes/typography.sm.order.json +15 -15
- package/cli/engine-helpers.d.ts +0 -8
- package/cli/engine-helpers.js +0 -70
- package/cli/engine-helpers.ts +0 -61
- package/core/cross-axis-config.d.ts +0 -5
- package/core/cross-axis-config.js +0 -144
- package/core/cross-axis-config.ts +0 -152
- package/dist/test-overrides-removal.json +0 -4
- package/dist/tmp.patch.json +0 -35
- package/tokens/overrides/base.json +0 -22
- package/tokens/overrides/lg.json +0 -20
- package/tokens/overrides/md.json +0 -16
- package/tokens/overrides/sm.json +0 -16
- package/tokens/overrides/viol.color.json +0 -6
- package/tokens/overrides/viol.typography.json +0 -6
- package/tokens/tokens.demo-violations.json +0 -116
- package/tokens/tokens.example.json +0 -128
- package/tokens/tokens.json +0 -67
- package/tokens/tokens.multi-violations.json +0 -21
- package/tokens/tokens.schema.d.ts +0 -2298
- package/tokens/tokens.schema.d.ts.map +0 -1
- package/tokens/tokens.schema.js +0 -148
- package/tokens/tokens.schema.ts +0 -196
- package/tokens/tokens.test.json +0 -38
- package/tokens/tokens.touch-violation.json +0 -8
- package/tokens/typography.classes.css +0 -11
- package/tokens/typography.css +0 -20
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import { loadTokens, outputResult } from './utils.js';
|
|
2
|
-
import type { PatchApplyOptions } from '../types.js';
|
|
3
|
-
import type { TokenNode } from '../../core/flatten.js';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import { flattenTokens } from '../../core/flatten.js';
|
|
6
|
-
import { createHash } from 'node:crypto';
|
|
7
|
-
|
|
8
|
-
interface PatchDocumentV1 {
|
|
9
|
-
version: 1;
|
|
10
|
-
changes: Array<{ id: string; from: any; to: any; type: 'modify'|'add'|'remove' }>;
|
|
11
|
-
patch: Record<string, any>;
|
|
12
|
-
baseTokensHash?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function applyChange(root: any, id: string, to: any, type: 'modify'|'add'|'remove') {
|
|
16
|
-
const parts = id.split('.');
|
|
17
|
-
let cur: any = root;
|
|
18
|
-
for (let i = 0; i < parts.length; i++) {
|
|
19
|
-
const p = parts[i];
|
|
20
|
-
if (i === parts.length - 1) {
|
|
21
|
-
if (type === 'remove') {
|
|
22
|
-
if (cur[p] && typeof cur[p] === 'object') {
|
|
23
|
-
delete cur[p].$value; // delete leaf value
|
|
24
|
-
}
|
|
25
|
-
} else {
|
|
26
|
-
if (!cur[p] || typeof cur[p] !== 'object') cur[p] = {};
|
|
27
|
-
cur[p].$value = to;
|
|
28
|
-
}
|
|
29
|
-
} else {
|
|
30
|
-
if (!cur[p] || typeof cur[p] !== 'object') cur[p] = {};
|
|
31
|
-
cur = cur[p];
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function patchApplyCommand(opts: PatchApplyOptions): Promise<void> {
|
|
37
|
-
const tokens: TokenNode = loadTokens(opts.tokens || 'tokens/tokens.example.json');
|
|
38
|
-
// Compute current base tokens hash for drift detection (same logic as buildPatch)
|
|
39
|
-
function computeBaseHash(toks: TokenNode): string {
|
|
40
|
-
const flat = flattenTokens(JSON.parse(JSON.stringify(toks))).flat as Record<string, any>;
|
|
41
|
-
const values: Record<string, any> = {};
|
|
42
|
-
Object.keys(flat).sort().forEach(id => { values[id] = flat[id]?.value; });
|
|
43
|
-
// Keep deterministic ordering
|
|
44
|
-
const ordered = Object.keys(values).sort().reduce((acc, k) => { acc[k] = values[k]; return acc; }, {} as Record<string, any>);
|
|
45
|
-
return createHash('sha256').update(JSON.stringify(ordered)).digest('hex');
|
|
46
|
-
}
|
|
47
|
-
// Parse patch
|
|
48
|
-
let patchDoc: PatchDocumentV1;
|
|
49
|
-
if (fs.existsSync(opts.patch)) {
|
|
50
|
-
patchDoc = JSON.parse(fs.readFileSync(opts.patch, 'utf8'));
|
|
51
|
-
} else if (opts.patch.trim().startsWith('{')) {
|
|
52
|
-
patchDoc = JSON.parse(opts.patch);
|
|
53
|
-
} else {
|
|
54
|
-
throw new Error(`Patch not found: ${opts.patch}`);
|
|
55
|
-
}
|
|
56
|
-
if (patchDoc.version !== 1) throw new Error('Unsupported patch version');
|
|
57
|
-
if (patchDoc.baseTokensHash) {
|
|
58
|
-
const currentHash = computeBaseHash(tokens);
|
|
59
|
-
if (currentHash !== patchDoc.baseTokensHash) {
|
|
60
|
-
console.warn(`⚠ Base tokens hash mismatch. Patch built against ${patchDoc.baseTokensHash} but current base is ${currentHash}. Proceeding (use --dry-run to inspect first).`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Apply changes
|
|
65
|
-
for (const c of patchDoc.changes) {
|
|
66
|
-
applyChange(tokens, c.id, c.to, c.type);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (opts.dryRun) {
|
|
70
|
-
outputResult(tokens, 'json');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (opts.output) {
|
|
75
|
-
fs.writeFileSync(opts.output, JSON.stringify(tokens, null, 2));
|
|
76
|
-
if (!opts.quiet) console.log(`✔ Patch applied to ${opts.output}`);
|
|
77
|
-
} else {
|
|
78
|
-
outputResult(tokens, 'json');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
1
|
+
import { loadTokens, outputResult } from './utils.js';
|
|
2
|
+
import type { PatchApplyOptions } from '../types.js';
|
|
3
|
+
import type { TokenNode } from '../../core/flatten.js';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { flattenTokens } from '../../core/flatten.js';
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
|
|
8
|
+
interface PatchDocumentV1 {
|
|
9
|
+
version: 1;
|
|
10
|
+
changes: Array<{ id: string; from: any; to: any; type: 'modify'|'add'|'remove' }>;
|
|
11
|
+
patch: Record<string, any>;
|
|
12
|
+
baseTokensHash?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function applyChange(root: any, id: string, to: any, type: 'modify'|'add'|'remove') {
|
|
16
|
+
const parts = id.split('.');
|
|
17
|
+
let cur: any = root;
|
|
18
|
+
for (let i = 0; i < parts.length; i++) {
|
|
19
|
+
const p = parts[i];
|
|
20
|
+
if (i === parts.length - 1) {
|
|
21
|
+
if (type === 'remove') {
|
|
22
|
+
if (cur[p] && typeof cur[p] === 'object') {
|
|
23
|
+
delete cur[p].$value; // delete leaf value
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
if (!cur[p] || typeof cur[p] !== 'object') cur[p] = {};
|
|
27
|
+
cur[p].$value = to;
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
if (!cur[p] || typeof cur[p] !== 'object') cur[p] = {};
|
|
31
|
+
cur = cur[p];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function patchApplyCommand(opts: PatchApplyOptions): Promise<void> {
|
|
37
|
+
const tokens: TokenNode = loadTokens(opts.tokens || 'tokens/tokens.example.json');
|
|
38
|
+
// Compute current base tokens hash for drift detection (same logic as buildPatch)
|
|
39
|
+
function computeBaseHash(toks: TokenNode): string {
|
|
40
|
+
const flat = flattenTokens(JSON.parse(JSON.stringify(toks))).flat as Record<string, any>;
|
|
41
|
+
const values: Record<string, any> = {};
|
|
42
|
+
Object.keys(flat).sort().forEach(id => { values[id] = flat[id]?.value; });
|
|
43
|
+
// Keep deterministic ordering
|
|
44
|
+
const ordered = Object.keys(values).sort().reduce((acc, k) => { acc[k] = values[k]; return acc; }, {} as Record<string, any>);
|
|
45
|
+
return createHash('sha256').update(JSON.stringify(ordered)).digest('hex');
|
|
46
|
+
}
|
|
47
|
+
// Parse patch
|
|
48
|
+
let patchDoc: PatchDocumentV1;
|
|
49
|
+
if (fs.existsSync(opts.patch)) {
|
|
50
|
+
patchDoc = JSON.parse(fs.readFileSync(opts.patch, 'utf8'));
|
|
51
|
+
} else if (opts.patch.trim().startsWith('{')) {
|
|
52
|
+
patchDoc = JSON.parse(opts.patch);
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error(`Patch not found: ${opts.patch}`);
|
|
55
|
+
}
|
|
56
|
+
if (patchDoc.version !== 1) throw new Error('Unsupported patch version');
|
|
57
|
+
if (patchDoc.baseTokensHash) {
|
|
58
|
+
const currentHash = computeBaseHash(tokens);
|
|
59
|
+
if (currentHash !== patchDoc.baseTokensHash) {
|
|
60
|
+
console.warn(`⚠ Base tokens hash mismatch. Patch built against ${patchDoc.baseTokensHash} but current base is ${currentHash}. Proceeding (use --dry-run to inspect first).`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Apply changes
|
|
65
|
+
for (const c of patchDoc.changes) {
|
|
66
|
+
applyChange(tokens, c.id, c.to, c.type);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (opts.dryRun) {
|
|
70
|
+
outputResult(tokens, 'json');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (opts.output) {
|
|
75
|
+
fs.writeFileSync(opts.output, JSON.stringify(tokens, null, 2));
|
|
76
|
+
if (!opts.quiet) console.log(`✔ Patch applied to ${opts.output}`);
|
|
77
|
+
} else {
|
|
78
|
+
outputResult(tokens, 'json');
|
|
79
|
+
}
|
|
80
|
+
}
|
package/cli/commands/patch.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import type { TokenNode } from '../../core/flatten.js';
|
|
2
|
-
import { loadTokens, outputResult } from './utils.js';
|
|
3
|
-
import { buildPatch } from '../../core/patch.js';
|
|
4
|
-
import type { PatchOptions } from '../types.js';
|
|
5
|
-
|
|
6
|
-
export async function patchCommand(opts: PatchOptions): Promise<void> {
|
|
7
|
-
const tokens: TokenNode = loadTokens(opts.tokens || 'tokens/tokens.example.json');
|
|
8
|
-
// For now just accept a flat overrides JSON file if provided
|
|
9
|
-
let overrides: Record<string, any> | undefined;
|
|
10
|
-
if (opts.overrides) {
|
|
11
|
-
const fs = await import('node:fs');
|
|
12
|
-
if (fs.existsSync(opts.overrides)) {
|
|
13
|
-
overrides = JSON.parse(fs.readFileSync(opts.overrides, 'utf8'));
|
|
14
|
-
} else if (opts.overrides.startsWith('{')) {
|
|
15
|
-
overrides = JSON.parse(opts.overrides);
|
|
16
|
-
} else {
|
|
17
|
-
throw new Error(`Overrides not found: ${opts.overrides}`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const patchDoc = buildPatch({ tokens, overrides, baseFile: opts.tokens });
|
|
21
|
-
outputResult(patchDoc, opts.format || 'json', opts.output);
|
|
22
|
-
}
|
|
1
|
+
import type { TokenNode } from '../../core/flatten.js';
|
|
2
|
+
import { loadTokens, outputResult } from './utils.js';
|
|
3
|
+
import { buildPatch } from '../../core/patch.js';
|
|
4
|
+
import type { PatchOptions } from '../types.js';
|
|
5
|
+
|
|
6
|
+
export async function patchCommand(opts: PatchOptions): Promise<void> {
|
|
7
|
+
const tokens: TokenNode = loadTokens(opts.tokens || 'tokens/tokens.example.json');
|
|
8
|
+
// For now just accept a flat overrides JSON file if provided
|
|
9
|
+
let overrides: Record<string, any> | undefined;
|
|
10
|
+
if (opts.overrides) {
|
|
11
|
+
const fs = await import('node:fs');
|
|
12
|
+
if (fs.existsSync(opts.overrides)) {
|
|
13
|
+
overrides = JSON.parse(fs.readFileSync(opts.overrides, 'utf8'));
|
|
14
|
+
} else if (opts.overrides.startsWith('{')) {
|
|
15
|
+
overrides = JSON.parse(opts.overrides);
|
|
16
|
+
} else {
|
|
17
|
+
throw new Error(`Overrides not found: ${opts.overrides}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const patchDoc = buildPatch({ tokens, overrides, baseFile: opts.tokens });
|
|
21
|
+
outputResult(patchDoc, opts.format || 'json', opts.output);
|
|
22
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["set.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiB,UAAU,EAAe,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["set.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiB,UAAU,EAAe,MAAM,aAAa,CAAC;AAmE1E,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsKnE"}
|
package/cli/commands/set.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { readFileSync, existsSync } from 'node:fs';
|
|
3
3
|
import { loadConfig } from '../config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { Engine } from '../../core/engine.js';
|
|
5
5
|
import { flattenTokens } from '../../core/flatten.js';
|
|
6
6
|
import { loadTokens, outputResult } from './utils.js';
|
|
7
|
+
import { setupConstraints } from '../constraint-registry.js';
|
|
7
8
|
// Lightweight suggestion helpers (kept local – why command uses core formatter instead)
|
|
8
9
|
function levenshtein(a, b) {
|
|
9
10
|
const al = a.length, bl = b.length;
|
|
@@ -81,9 +82,16 @@ export async function setCommand(options) {
|
|
|
81
82
|
}
|
|
82
83
|
const config = cfgRes.value;
|
|
83
84
|
const tokens = loadTokens(tokensPath);
|
|
84
|
-
|
|
85
|
-
const { flat
|
|
86
|
-
const
|
|
85
|
+
// Create engine with flattened tokens
|
|
86
|
+
const { flat, edges } = flattenTokens(tokens);
|
|
87
|
+
const init = {};
|
|
88
|
+
for (const t of Object.values(flat)) {
|
|
89
|
+
init[t.id] = t.value;
|
|
90
|
+
}
|
|
91
|
+
const engine = new Engine(init, edges);
|
|
92
|
+
const knownIds = new Set(Object.keys(init));
|
|
93
|
+
// Discover and attach all constraints via centralized registry
|
|
94
|
+
setupConstraints(engine, { config, constraintsDir: 'themes' }, { knownIds });
|
|
87
95
|
function ensureKnownOrSuggest(id) {
|
|
88
96
|
if (!knownIds.has(id)) {
|
|
89
97
|
const suggestions = suggestIds(id, Array.from(knownIds));
|