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
package/themes/layout.order.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Layout hierarchy order constraints - container widths grow, padding hierarchy, gutters ≤ paddings, control touch targets",
|
|
3
|
-
"order": [
|
|
4
|
-
["layout.container.xs", "<=", "layout.container.sm"],
|
|
5
|
-
["layout.container.sm", "<=", "layout.container.md"],
|
|
6
|
-
["layout.container.md", "<=", "layout.container.lg"],
|
|
7
|
-
["layout.container.lg", "<=", "layout.container.xl"],
|
|
8
|
-
|
|
9
|
-
["layout.padding.page", ">=", "layout.padding.section"],
|
|
10
|
-
["layout.padding.section", ">=", "layout.padding.card"],
|
|
11
|
-
["layout.padding.card", ">=", "layout.padding.chip"],
|
|
12
|
-
|
|
13
|
-
["layout.grid.gutter", "<=", "layout.padding.section"],
|
|
14
|
-
["layout.grid.gutter", "<=", "layout.padding.card"],
|
|
15
|
-
|
|
16
|
-
["control.size.min", ">=", "layout.padding.card"]
|
|
17
|
-
]
|
|
18
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Layout hierarchy order constraints - container widths grow, padding hierarchy, gutters ≤ paddings, control touch targets",
|
|
3
|
+
"order": [
|
|
4
|
+
["layout.container.xs", "<=", "layout.container.sm"],
|
|
5
|
+
["layout.container.sm", "<=", "layout.container.md"],
|
|
6
|
+
["layout.container.md", "<=", "layout.container.lg"],
|
|
7
|
+
["layout.container.lg", "<=", "layout.container.xl"],
|
|
8
|
+
|
|
9
|
+
["layout.padding.page", ">=", "layout.padding.section"],
|
|
10
|
+
["layout.padding.section", ">=", "layout.padding.card"],
|
|
11
|
+
["layout.padding.card", ">=", "layout.padding.chip"],
|
|
12
|
+
|
|
13
|
+
["layout.grid.gutter", "<=", "layout.padding.section"],
|
|
14
|
+
["layout.grid.gutter", "<=", "layout.padding.card"],
|
|
15
|
+
|
|
16
|
+
["control.size.min", ">=", "layout.padding.card"]
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Layout hierarchy order constraints - SM breakpoint specific",
|
|
3
|
-
"order": [
|
|
4
|
-
["layout.container.xs", "<=", "layout.container.sm"],
|
|
5
|
-
["layout.container.sm", "<=", "layout.container.md"],
|
|
6
|
-
["layout.container.md", "<=", "layout.container.lg"],
|
|
7
|
-
["layout.container.lg", "<=", "layout.container.xl"],
|
|
8
|
-
|
|
9
|
-
["layout.padding.page", ">=", "layout.padding.section"],
|
|
10
|
-
["layout.padding.section", ">=", "layout.padding.card"],
|
|
11
|
-
["layout.padding.card", ">=", "layout.padding.chip"],
|
|
12
|
-
|
|
13
|
-
["layout.grid.gutter", "<=", "layout.padding.section"],
|
|
14
|
-
["layout.grid.gutter", "<=", "layout.padding.card"],
|
|
15
|
-
|
|
16
|
-
["control.size.min", ">=", "layout.padding.card"]
|
|
17
|
-
]
|
|
18
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Layout hierarchy order constraints - SM breakpoint specific",
|
|
3
|
+
"order": [
|
|
4
|
+
["layout.container.xs", "<=", "layout.container.sm"],
|
|
5
|
+
["layout.container.sm", "<=", "layout.container.md"],
|
|
6
|
+
["layout.container.md", "<=", "layout.container.lg"],
|
|
7
|
+
["layout.container.lg", "<=", "layout.container.xl"],
|
|
8
|
+
|
|
9
|
+
["layout.padding.page", ">=", "layout.padding.section"],
|
|
10
|
+
["layout.padding.section", ">=", "layout.padding.card"],
|
|
11
|
+
["layout.padding.card", ">=", "layout.padding.chip"],
|
|
12
|
+
|
|
13
|
+
["layout.grid.gutter", "<=", "layout.padding.section"],
|
|
14
|
+
["layout.grid.gutter", "<=", "layout.padding.card"],
|
|
15
|
+
|
|
16
|
+
["control.size.min", ">=", "layout.padding.card"]
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Spacing scale order constraints - defines monotonic spacing hierarchy",
|
|
3
|
-
"order": [
|
|
4
|
-
["size.spacing.12", ">=", "size.spacing.10"],
|
|
5
|
-
["size.spacing.10", ">=", "size.spacing.8"],
|
|
6
|
-
["size.spacing.8", ">=", "size.spacing.6"],
|
|
7
|
-
["size.spacing.6", ">=", "size.spacing.4"],
|
|
8
|
-
["size.spacing.4", ">=", "size.spacing.3"],
|
|
9
|
-
["size.spacing.3", ">=", "size.spacing.2-5"],
|
|
10
|
-
["size.spacing.2-5", ">=", "size.spacing.2"],
|
|
11
|
-
["size.spacing.2", ">=", "size.spacing.1"],
|
|
12
|
-
["size.spacing.1", ">=", "size.spacing.0-5"]
|
|
13
|
-
]
|
|
14
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Spacing scale order constraints - defines monotonic spacing hierarchy",
|
|
3
|
+
"order": [
|
|
4
|
+
["size.spacing.12", ">=", "size.spacing.10"],
|
|
5
|
+
["size.spacing.10", ">=", "size.spacing.8"],
|
|
6
|
+
["size.spacing.8", ">=", "size.spacing.6"],
|
|
7
|
+
["size.spacing.6", ">=", "size.spacing.4"],
|
|
8
|
+
["size.spacing.4", ">=", "size.spacing.3"],
|
|
9
|
+
["size.spacing.3", ">=", "size.spacing.2-5"],
|
|
10
|
+
["size.spacing.2-5", ">=", "size.spacing.2"],
|
|
11
|
+
["size.spacing.2", ">=", "size.spacing.1"],
|
|
12
|
+
["size.spacing.1", ">=", "size.spacing.0-5"]
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Typography hierarchy order constraints - LG breakpoint specific",
|
|
3
|
-
"order": [
|
|
4
|
-
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
-
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
-
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
-
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
-
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
-
|
|
10
|
-
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
-
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
-
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
-
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
-
]
|
|
15
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Typography hierarchy order constraints - LG breakpoint specific",
|
|
3
|
+
"order": [
|
|
4
|
+
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
+
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
+
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
+
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
+
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
+
|
|
10
|
+
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
+
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
+
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
+
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Typography hierarchy order constraints - MD breakpoint specific",
|
|
3
|
-
"order": [
|
|
4
|
-
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
-
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
-
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
-
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
-
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
-
|
|
10
|
-
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
-
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
-
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
-
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
-
]
|
|
15
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Typography hierarchy order constraints - MD breakpoint specific",
|
|
3
|
+
"order": [
|
|
4
|
+
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
+
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
+
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
+
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
+
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
+
|
|
10
|
+
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
+
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
+
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
+
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Typography hierarchy order constraints - defines that h1 ≥ h2 ≥ ... ≥ h6 by font size",
|
|
3
|
-
"order": [
|
|
4
|
-
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
-
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
-
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
-
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
-
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
-
|
|
10
|
-
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
-
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
-
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
-
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
-
]
|
|
15
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Typography hierarchy order constraints - defines that h1 ≥ h2 ≥ ... ≥ h6 by font size",
|
|
3
|
+
"order": [
|
|
4
|
+
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
+
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
+
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
+
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
+
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
+
|
|
10
|
+
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
+
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
+
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
+
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$description": "Typography hierarchy order constraints - SM breakpoint specific",
|
|
3
|
-
"order": [
|
|
4
|
-
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
-
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
-
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
-
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
-
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
-
|
|
10
|
-
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
-
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
-
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
-
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
-
]
|
|
15
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$description": "Typography hierarchy order constraints - SM breakpoint specific",
|
|
3
|
+
"order": [
|
|
4
|
+
["typography.size.h1", ">=", "typography.size.h2"],
|
|
5
|
+
["typography.size.h2", ">=", "typography.size.h3"],
|
|
6
|
+
["typography.size.h3", ">=", "typography.size.h4"],
|
|
7
|
+
["typography.size.h4", ">=", "typography.size.h5"],
|
|
8
|
+
["typography.size.h5", ">=", "typography.size.h6"],
|
|
9
|
+
|
|
10
|
+
["typography.size.xl", ">=", "typography.size.lg"],
|
|
11
|
+
["typography.size.lg", ">=", "typography.size.base"],
|
|
12
|
+
["typography.size.base", ">=", "typography.size.sm"],
|
|
13
|
+
["typography.size.sm", ">=", "typography.size.xs"]
|
|
14
|
+
]
|
|
15
|
+
}
|
package/cli/engine-helpers.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type TokenNode } from '../core/flatten.js';
|
|
2
|
-
import { Engine } from '../core/engine.js';
|
|
3
|
-
import { loadTokensWithBreakpoint, type Breakpoint } from '../core/breakpoints.js';
|
|
4
|
-
import type { DcvConfig } from './types.js';
|
|
5
|
-
export declare function createEngine(tokensRoot: TokenNode, config?: DcvConfig): Engine;
|
|
6
|
-
export declare function createValidationEngine(tokensRoot: TokenNode, bp: Breakpoint | undefined, config: DcvConfig): Engine;
|
|
7
|
-
export { loadTokensWithBreakpoint };
|
|
8
|
-
//# sourceMappingURL=engine-helpers.d.ts.map
|
package/cli/engine-helpers.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { flattenTokens } from '../core/flatten.js';
|
|
2
|
-
import { Engine } from '../core/engine.js';
|
|
3
|
-
import { MonotonicPlugin, parseSize as parseSizePx } from '../core/constraints/monotonic.js';
|
|
4
|
-
import { MonotonicLightness } from '../core/constraints/monotonic-lightness.js';
|
|
5
|
-
import { WcagContrastPlugin } from '../core/constraints/wcag.js';
|
|
6
|
-
import { loadOrders as loadOrdersBP, loadTokensWithBreakpoint } from '../core/breakpoints.js';
|
|
7
|
-
export function createEngine(tokensRoot, config = {}) {
|
|
8
|
-
const { flat, edges } = flattenTokens(tokensRoot);
|
|
9
|
-
const init = {};
|
|
10
|
-
for (const [id, token] of Object.entries(flat))
|
|
11
|
-
init[id] = token.value;
|
|
12
|
-
const engine = new Engine(init, edges);
|
|
13
|
-
function loadOrders(path) {
|
|
14
|
-
try {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
-
return JSON.parse(require('node:fs').readFileSync(path, 'utf8')).order;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
const typOrders = loadOrders('themes/typography.order.json');
|
|
23
|
-
const spacingOrders = loadOrders('themes/spacing.order.json');
|
|
24
|
-
const layoutOrders = loadOrders('themes/layout.order.json');
|
|
25
|
-
const colorOrders = loadOrders('themes/color.order.json');
|
|
26
|
-
if (typOrders.length)
|
|
27
|
-
engine.use(MonotonicPlugin(typOrders, parseSizePx, 'monotonic-typography'));
|
|
28
|
-
if (spacingOrders.length)
|
|
29
|
-
engine.use(MonotonicPlugin(spacingOrders, parseSizePx, 'monotonic-spacing'));
|
|
30
|
-
if (layoutOrders.length)
|
|
31
|
-
engine.use(MonotonicPlugin(layoutOrders, parseSizePx, 'monotonic-layout'));
|
|
32
|
-
if (colorOrders.length)
|
|
33
|
-
engine.use(MonotonicLightness(colorOrders));
|
|
34
|
-
if (config.constraints?.wcag) {
|
|
35
|
-
const wcagRules = config.constraints.wcag.map((r) => ({ fg: r.foreground, bg: r.background, min: r.ratio || 4.5, where: r.description || 'Unknown' }));
|
|
36
|
-
engine.use(WcagContrastPlugin(wcagRules));
|
|
37
|
-
}
|
|
38
|
-
const defaultWcagPairs = [
|
|
39
|
-
{ fg: 'color.role.text.default', bg: 'color.role.bg.surface', min: 4.5, where: 'Body text on surface' },
|
|
40
|
-
{ fg: 'color.role.accent.default', bg: 'color.role.bg.surface', min: 3.0, where: 'Accent on surface' },
|
|
41
|
-
{ fg: 'color.role.focus.ring', bg: 'color.role.bg.surface', min: 3.0, where: 'Focus ring on surface', backdrop: '#ffffff' }
|
|
42
|
-
];
|
|
43
|
-
engine.use(WcagContrastPlugin(defaultWcagPairs));
|
|
44
|
-
return engine;
|
|
45
|
-
}
|
|
46
|
-
export function createValidationEngine(tokensRoot, bp, config) {
|
|
47
|
-
const { flat, edges } = flattenTokens(tokensRoot);
|
|
48
|
-
const init = {};
|
|
49
|
-
for (const t of Object.values(flat))
|
|
50
|
-
init[t.id] = t.value;
|
|
51
|
-
const engine = new Engine(init, edges);
|
|
52
|
-
const typ = loadOrdersBP('typography', bp);
|
|
53
|
-
const spc = loadOrdersBP('spacing', bp);
|
|
54
|
-
const lay = loadOrdersBP('layout', bp);
|
|
55
|
-
const col = loadOrdersBP('color', bp);
|
|
56
|
-
if (typ.length)
|
|
57
|
-
engine.use(MonotonicPlugin(typ, parseSizePx, 'monotonic-typography'));
|
|
58
|
-
if (spc.length)
|
|
59
|
-
engine.use(MonotonicPlugin(spc, parseSizePx, 'monotonic-spacing'));
|
|
60
|
-
if (lay.length)
|
|
61
|
-
engine.use(MonotonicPlugin(lay, parseSizePx, 'monotonic-layout'));
|
|
62
|
-
if (col.length)
|
|
63
|
-
engine.use(MonotonicLightness(col));
|
|
64
|
-
if (config.constraints?.wcag) {
|
|
65
|
-
const wcagRules = config.constraints.wcag.map((rule) => ({ fg: rule.foreground, bg: rule.background, min: rule.ratio || 4.5, where: rule.description || 'Unknown' }));
|
|
66
|
-
engine.use(WcagContrastPlugin(wcagRules));
|
|
67
|
-
}
|
|
68
|
-
return engine;
|
|
69
|
-
}
|
|
70
|
-
export { loadTokensWithBreakpoint };
|
package/cli/engine-helpers.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { flattenTokens, type TokenNode, type FlatToken } from '../core/flatten.js';
|
|
2
|
-
import { Engine } from '../core/engine.js';
|
|
3
|
-
import { MonotonicPlugin, parseSize as parseSizePx } from '../core/constraints/monotonic.js';
|
|
4
|
-
import { MonotonicLightness } from '../core/constraints/monotonic-lightness.js';
|
|
5
|
-
import { WcagContrastPlugin } from '../core/constraints/wcag.js';
|
|
6
|
-
import { loadOrders as loadOrdersBP, loadTokensWithBreakpoint, type Breakpoint } from '../core/breakpoints.js';
|
|
7
|
-
import type { DcvConfig } from './types.js';
|
|
8
|
-
|
|
9
|
-
export function createEngine(tokensRoot: TokenNode, config: DcvConfig = {}): Engine {
|
|
10
|
-
const { flat, edges } = flattenTokens(tokensRoot);
|
|
11
|
-
const init: Record<string, string | number> = {};
|
|
12
|
-
for (const [id, token] of Object.entries(flat)) init[id] = (token as FlatToken).value;
|
|
13
|
-
const engine = new Engine(init, edges);
|
|
14
|
-
function loadOrders(path: string) {
|
|
15
|
-
try {
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
|
-
return JSON.parse(require('node:fs').readFileSync(path, 'utf8')).order as [string, '<='|'>=', string][];
|
|
18
|
-
} catch { return []; }
|
|
19
|
-
}
|
|
20
|
-
const typOrders = loadOrders('themes/typography.order.json');
|
|
21
|
-
const spacingOrders = loadOrders('themes/spacing.order.json');
|
|
22
|
-
const layoutOrders = loadOrders('themes/layout.order.json');
|
|
23
|
-
const colorOrders = loadOrders('themes/color.order.json');
|
|
24
|
-
if (typOrders.length) engine.use(MonotonicPlugin(typOrders, parseSizePx, 'monotonic-typography'));
|
|
25
|
-
if (spacingOrders.length) engine.use(MonotonicPlugin(spacingOrders, parseSizePx, 'monotonic-spacing'));
|
|
26
|
-
if (layoutOrders.length) engine.use(MonotonicPlugin(layoutOrders, parseSizePx, 'monotonic-layout'));
|
|
27
|
-
if (colorOrders.length) engine.use(MonotonicLightness(colorOrders));
|
|
28
|
-
if (config.constraints?.wcag) {
|
|
29
|
-
const wcagRules = config.constraints.wcag.map((r: any) => ({ fg: r.foreground, bg: r.background, min: r.ratio || 4.5, where: r.description || 'Unknown' }));
|
|
30
|
-
engine.use(WcagContrastPlugin(wcagRules));
|
|
31
|
-
}
|
|
32
|
-
const defaultWcagPairs = [
|
|
33
|
-
{ fg: 'color.role.text.default', bg: 'color.role.bg.surface', min: 4.5, where: 'Body text on surface' },
|
|
34
|
-
{ fg: 'color.role.accent.default', bg: 'color.role.bg.surface', min: 3.0, where: 'Accent on surface' },
|
|
35
|
-
{ fg: 'color.role.focus.ring', bg: 'color.role.bg.surface', min: 3.0, where: 'Focus ring on surface', backdrop: '#ffffff' }
|
|
36
|
-
];
|
|
37
|
-
engine.use(WcagContrastPlugin(defaultWcagPairs));
|
|
38
|
-
return engine;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function createValidationEngine(tokensRoot: TokenNode, bp: Breakpoint | undefined, config: DcvConfig): Engine {
|
|
42
|
-
const { flat, edges } = flattenTokens(tokensRoot);
|
|
43
|
-
const init: Record<string, string | number> = {};
|
|
44
|
-
for (const t of Object.values(flat)) init[(t as FlatToken).id] = (t as FlatToken).value;
|
|
45
|
-
const engine = new Engine(init, edges);
|
|
46
|
-
const typ = loadOrdersBP('typography', bp);
|
|
47
|
-
const spc = loadOrdersBP('spacing', bp);
|
|
48
|
-
const lay = loadOrdersBP('layout', bp);
|
|
49
|
-
const col = loadOrdersBP('color', bp);
|
|
50
|
-
if (typ.length) engine.use(MonotonicPlugin(typ, parseSizePx, 'monotonic-typography'));
|
|
51
|
-
if (spc.length) engine.use(MonotonicPlugin(spc, parseSizePx, 'monotonic-spacing'));
|
|
52
|
-
if (lay.length) engine.use(MonotonicPlugin(lay, parseSizePx, 'monotonic-layout'));
|
|
53
|
-
if (col.length) engine.use(MonotonicLightness(col));
|
|
54
|
-
if (config.constraints?.wcag) {
|
|
55
|
-
const wcagRules = config.constraints.wcag.map((rule: any) => ({ fg: rule.foreground, bg: rule.background, min: rule.ratio || 4.5, where: rule.description || 'Unknown' }));
|
|
56
|
-
engine.use(WcagContrastPlugin(wcagRules));
|
|
57
|
-
}
|
|
58
|
-
return engine;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export { loadTokensWithBreakpoint };
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { CrossAxisPlugin } from "./constraints/cross-axis.js";
|
|
3
|
-
export function loadCrossAxisPlugin(path, bp, opts) {
|
|
4
|
-
const debug = !!opts?.debug;
|
|
5
|
-
const known = opts?.knownIds ?? new Set();
|
|
6
|
-
const log = (...args) => { if (debug)
|
|
7
|
-
console.log("[cross-axis]", ...args); };
|
|
8
|
-
if (!fs.existsSync(path)) {
|
|
9
|
-
log(`no rules file at ${path} (bp=${bp ?? "global"})`);
|
|
10
|
-
return CrossAxisPlugin([], bp);
|
|
11
|
-
}
|
|
12
|
-
const raw = JSON.parse(fs.readFileSync(path, "utf8"));
|
|
13
|
-
const rules = [];
|
|
14
|
-
const unknownIds = new Set();
|
|
15
|
-
const skipped = [];
|
|
16
|
-
// Fuzzy suggestion helpers (lightweight Levenshtein)
|
|
17
|
-
function levenshtein(a, b) {
|
|
18
|
-
const dp = Array(b.length + 1).fill(0).map((_, j) => j);
|
|
19
|
-
for (let i = 1; i <= a.length; i++) {
|
|
20
|
-
let prev = i - 1, cur = i;
|
|
21
|
-
for (let j = 1; j <= b.length; j++) {
|
|
22
|
-
const tmp = cur;
|
|
23
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
24
|
-
cur = Math.min(dp[j] + 1, cur + 1, prev + cost);
|
|
25
|
-
dp[j] = tmp;
|
|
26
|
-
prev = tmp;
|
|
27
|
-
}
|
|
28
|
-
dp[b.length] = cur;
|
|
29
|
-
}
|
|
30
|
-
return dp[b.length];
|
|
31
|
-
}
|
|
32
|
-
function suggest(id, k = 3) {
|
|
33
|
-
return [...known].map(c => ({ id: c, d: levenshtein(id, c) }))
|
|
34
|
-
.sort((a, b) => a.d - b.d)
|
|
35
|
-
.slice(0, k);
|
|
36
|
-
}
|
|
37
|
-
const needId = (id) => {
|
|
38
|
-
if (!id)
|
|
39
|
-
return false;
|
|
40
|
-
if (!known.has(id)) {
|
|
41
|
-
unknownIds.add(id);
|
|
42
|
-
}
|
|
43
|
-
return true;
|
|
44
|
-
};
|
|
45
|
-
for (const r of raw.rules || []) {
|
|
46
|
-
if (r.bp && bp && r.bp !== bp) {
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (r.bp && !bp) { // rule targets specific breakpoint; skip in global run
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
if (r.when && r.require) {
|
|
54
|
-
// Validate IDs
|
|
55
|
-
needId(r.when.id);
|
|
56
|
-
needId(r.require.id);
|
|
57
|
-
if (r.require.ref)
|
|
58
|
-
needId(r.require.ref);
|
|
59
|
-
rules.push({
|
|
60
|
-
id: r.id, level: r.level, where: r.where,
|
|
61
|
-
when: { id: r.when.id, test: makeOp(r.when.op, r.when.value) },
|
|
62
|
-
require: {
|
|
63
|
-
id: r.require.id,
|
|
64
|
-
test: (v, ctx) => {
|
|
65
|
-
const rhs = valueOrRef(ctx, r.require.ref, r.require.fallback);
|
|
66
|
-
return cmp(v, rhs, r.require.op);
|
|
67
|
-
},
|
|
68
|
-
msg: (v, ctx) => {
|
|
69
|
-
const rhs = valueOrRef(ctx, r.require.ref, r.require.fallback);
|
|
70
|
-
return `${r.require.id} ${prettyFail(r.require.op)} ${fmt(rhs)} (was ${fmt(v)})`;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
else if (r.compare) {
|
|
76
|
-
needId(r.compare.a);
|
|
77
|
-
needId(r.compare.b);
|
|
78
|
-
rules.push({
|
|
79
|
-
id: r.id, level: r.level, where: r.where,
|
|
80
|
-
when: { id: r.compare.a, test: () => true },
|
|
81
|
-
require: {
|
|
82
|
-
id: r.compare.a,
|
|
83
|
-
test: (_, ctx) => {
|
|
84
|
-
const a = ctx.getPx(r.compare.a) ?? NaN;
|
|
85
|
-
const b = ctx.getPx(r.compare.b) ?? NaN;
|
|
86
|
-
const delta = px(r.compare.delta ?? 0);
|
|
87
|
-
if (Number.isNaN(a) || Number.isNaN(b))
|
|
88
|
-
return true; // skip check if missing
|
|
89
|
-
return cmp(a, b + delta, r.compare.op);
|
|
90
|
-
},
|
|
91
|
-
msg: (_, ctx) => {
|
|
92
|
-
const a = ctx.getPx(r.compare.a);
|
|
93
|
-
const b = ctx.getPx(r.compare.b);
|
|
94
|
-
const delta = px(r.compare.delta ?? 0);
|
|
95
|
-
return `${r.compare.a} ${prettyFail(r.compare.op)} ${fmt((b ?? 0) + delta)} (was ${fmt(a ?? NaN)})`;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
skipped.push({ id: r.id, reason: "neither when+require nor compare present" });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
skipped.push({ id: r.id, reason: `exception: ${e?.message ?? e}` });
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
log(`loaded ${rules.length} rule(s) from ${path}${bp ? ` [bp=${bp}]` : ""}`);
|
|
109
|
-
if (unknownIds.size) {
|
|
110
|
-
log(`unknown ids referenced:`, [...unknownIds].join(", "));
|
|
111
|
-
for (const u of unknownIds) {
|
|
112
|
-
const s = suggest(u, 3);
|
|
113
|
-
if (s.length)
|
|
114
|
-
log(` did you mean: ${s.map(x => `${x.id} (d=${x.d})`).join(', ')}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (skipped.length) {
|
|
118
|
-
for (const s of skipped)
|
|
119
|
-
log(`skipped rule ${s.id ?? "(no id)"} — ${s.reason}`);
|
|
120
|
-
}
|
|
121
|
-
// Extra hint for common anchor pitfall
|
|
122
|
-
for (const r of raw.rules || []) {
|
|
123
|
-
if (r.require?.ref && !known.has(r.require.ref)) {
|
|
124
|
-
log(`anchor missing: ${r.require.ref} → will use fallback=${JSON.stringify(r.require.fallback)} when evaluating`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return CrossAxisPlugin(rules, bp);
|
|
128
|
-
}
|
|
129
|
-
// helpers
|
|
130
|
-
const px = (v) => typeof v === "number" ? v : parseFloat(String(v)) * (String(v).trim().endsWith("rem") ? 16 : 1);
|
|
131
|
-
const cmp = (a, b, op) => op === ">=" ? a >= b : op === ">" ? a > b : op === "<=" ? a <= b : op === "<" ? a < b : op === "==" ? a === b : a !== b;
|
|
132
|
-
const prettyFail = (op) => ({ ">=": "<", ">": "≤", "<=": ">", "<": "≥", "==": "≠", "!=": "=" }[op] || "≠");
|
|
133
|
-
const fmt = (v) => Number.isFinite(Number(v)) ? `${Number(v)}px` : String(v);
|
|
134
|
-
function valueOrRef(ctx, ref, fallback) {
|
|
135
|
-
if (ref) {
|
|
136
|
-
const v = ctx.getPx(ref);
|
|
137
|
-
if (v != null)
|
|
138
|
-
return v;
|
|
139
|
-
}
|
|
140
|
-
return typeof fallback === "number" ? fallback : px(fallback ?? 0);
|
|
141
|
-
}
|
|
142
|
-
function makeOp(op, rhs) {
|
|
143
|
-
return (v) => cmp(v, rhs, op);
|
|
144
|
-
}
|