design-constraint-validator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +659 -0
- package/adapters/README.md +46 -0
- package/adapters/css.d.ts +44 -0
- package/adapters/css.d.ts.map +1 -0
- package/adapters/css.js +97 -0
- package/adapters/css.ts +116 -0
- package/adapters/js.d.ts +3 -0
- package/adapters/js.d.ts.map +1 -0
- package/adapters/js.js +15 -0
- package/adapters/js.ts +14 -0
- package/adapters/json.d.ts +18 -0
- package/adapters/json.d.ts.map +1 -0
- package/adapters/json.js +35 -0
- package/adapters/json.ts +45 -0
- package/cli/build-css.d.ts +2 -0
- package/cli/build-css.d.ts.map +1 -0
- package/cli/build-css.js +23 -0
- package/cli/build-css.ts +32 -0
- package/cli/commands/build.d.ts +5 -0
- package/cli/commands/build.d.ts.map +1 -0
- package/cli/commands/build.js +89 -0
- package/cli/commands/build.ts +65 -0
- package/cli/commands/graph.d.ts +3 -0
- package/cli/commands/graph.d.ts.map +1 -0
- package/cli/commands/graph.js +219 -0
- package/cli/commands/graph.ts +137 -0
- package/cli/commands/index.d.ts +8 -0
- package/cli/commands/index.d.ts.map +1 -0
- package/cli/commands/index.js +7 -0
- package/cli/commands/index.ts +7 -0
- package/cli/commands/patch-apply.d.ts +3 -0
- package/cli/commands/patch-apply.d.ts.map +1 -0
- package/cli/commands/patch-apply.js +75 -0
- package/cli/commands/patch-apply.ts +80 -0
- package/cli/commands/patch.d.ts +3 -0
- package/cli/commands/patch.d.ts.map +1 -0
- package/cli/commands/patch.js +21 -0
- package/cli/commands/patch.ts +22 -0
- package/cli/commands/set.d.ts +3 -0
- package/cli/commands/set.d.ts.map +1 -0
- package/cli/commands/set.js +286 -0
- package/cli/commands/set.ts +225 -0
- package/cli/commands/utils.d.ts +4 -0
- package/cli/commands/utils.d.ts.map +1 -0
- package/cli/commands/utils.js +51 -0
- package/cli/commands/utils.ts +50 -0
- package/cli/commands/validate.d.ts +3 -0
- package/cli/commands/validate.d.ts.map +1 -0
- package/cli/commands/validate.js +131 -0
- package/cli/commands/validate.ts +115 -0
- package/cli/commands/why.d.ts +3 -0
- package/cli/commands/why.d.ts.map +1 -0
- package/cli/commands/why.js +64 -0
- package/cli/commands/why.ts +46 -0
- package/cli/config-schema.d.ts +238 -0
- package/cli/config-schema.d.ts.map +1 -0
- package/cli/config-schema.js +21 -0
- package/cli/config-schema.ts +27 -0
- package/cli/config.d.ts +4 -0
- package/cli/config.d.ts.map +1 -0
- package/cli/config.js +37 -0
- package/cli/config.ts +35 -0
- package/cli/dcv.d.ts +3 -0
- package/cli/dcv.d.ts.map +1 -0
- package/cli/dcv.js +86 -0
- package/cli/dcv.ts +107 -0
- package/cli/engine-helpers.d.ts +8 -0
- package/cli/engine-helpers.d.ts.map +1 -0
- package/cli/engine-helpers.js +70 -0
- package/cli/engine-helpers.ts +61 -0
- package/cli/graph-poset.d.ts +9 -0
- package/cli/graph-poset.d.ts.map +1 -0
- package/cli/graph-poset.js +58 -0
- package/cli/graph-poset.ts +74 -0
- package/cli/index.d.ts +3 -0
- package/cli/index.d.ts.map +1 -0
- package/cli/index.js +2 -0
- package/cli/index.ts +2 -0
- package/cli/result.d.ts +17 -0
- package/cli/result.d.ts.map +1 -0
- package/cli/result.js +29 -0
- package/cli/result.ts +27 -0
- package/cli/run.d.ts +3 -0
- package/cli/run.d.ts.map +1 -0
- package/cli/run.js +47 -0
- package/cli/run.ts +54 -0
- package/cli/smoke-test.d.ts +2 -0
- package/cli/smoke-test.d.ts.map +1 -0
- package/cli/smoke-test.js +33 -0
- package/cli/smoke-test.ts +40 -0
- package/cli/types.d.ts +86 -0
- package/cli/types.d.ts.map +1 -0
- package/cli/types.js +1 -0
- package/cli/types.ts +78 -0
- package/core/breakpoints.d.ts +12 -0
- package/core/breakpoints.d.ts.map +1 -0
- package/core/breakpoints.js +48 -0
- package/core/breakpoints.ts +50 -0
- package/core/cli-format.d.ts +8 -0
- package/core/cli-format.d.ts.map +1 -0
- package/core/cli-format.js +29 -0
- package/core/cli-format.ts +31 -0
- package/core/color.d.ts +14 -0
- package/core/color.d.ts.map +1 -0
- package/core/color.js +136 -0
- package/core/color.ts +148 -0
- package/core/constraints/cross-axis.d.ts +33 -0
- package/core/constraints/cross-axis.d.ts.map +1 -0
- package/core/constraints/cross-axis.js +93 -0
- package/core/constraints/cross-axis.ts +114 -0
- package/core/constraints/monotonic-lightness.d.ts +5 -0
- package/core/constraints/monotonic-lightness.d.ts.map +1 -0
- package/core/constraints/monotonic-lightness.js +37 -0
- package/core/constraints/monotonic-lightness.ts +38 -0
- package/core/constraints/monotonic.d.ts +7 -0
- package/core/constraints/monotonic.d.ts.map +1 -0
- package/core/constraints/monotonic.js +65 -0
- package/core/constraints/monotonic.ts +74 -0
- package/core/constraints/threshold.d.ts +10 -0
- package/core/constraints/threshold.d.ts.map +1 -0
- package/core/constraints/threshold.js +36 -0
- package/core/constraints/threshold.ts +43 -0
- package/core/constraints/wcag.d.ts +11 -0
- package/core/constraints/wcag.d.ts.map +1 -0
- package/core/constraints/wcag.js +53 -0
- package/core/constraints/wcag.ts +70 -0
- package/core/cross-axis-config.d.ts +5 -0
- package/core/cross-axis-config.d.ts.map +1 -0
- package/core/cross-axis-config.js +144 -0
- package/core/cross-axis-config.ts +152 -0
- package/core/engine.d.ts +32 -0
- package/core/engine.d.ts.map +1 -0
- package/core/engine.js +46 -0
- package/core/engine.ts +65 -0
- package/core/flatten.d.ts +20 -0
- package/core/flatten.d.ts.map +1 -0
- package/core/flatten.js +80 -0
- package/core/flatten.ts +116 -0
- package/core/image-export.d.ts +10 -0
- package/core/image-export.d.ts.map +1 -0
- package/core/image-export.js +43 -0
- package/core/image-export.ts +48 -0
- package/core/index.d.ts +31 -0
- package/core/index.d.ts.map +1 -0
- package/core/index.js +54 -0
- package/core/index.ts +72 -0
- package/core/patch.d.ts +28 -0
- package/core/patch.d.ts.map +1 -0
- package/core/patch.js +110 -0
- package/core/patch.ts +134 -0
- package/core/poset.d.ts +41 -0
- package/core/poset.d.ts.map +1 -0
- package/core/poset.js +275 -0
- package/core/poset.ts +311 -0
- package/core/why.d.ts +17 -0
- package/core/why.d.ts.map +1 -0
- package/core/why.js +45 -0
- package/core/why.ts +63 -0
- package/dist/test-overrides-removal.json +4 -0
- package/dist/tmp.patch.json +35 -0
- package/package.json +90 -0
- package/themes/color.lg.order.json +15 -0
- package/themes/color.md.order.json +15 -0
- package/themes/color.order.json +15 -0
- package/themes/color.sm.order.json +15 -0
- package/themes/cross-axis.rules.json +36 -0
- package/themes/cross-axis.sm.rules.json +12 -0
- package/themes/layout.lg.order.json +18 -0
- package/themes/layout.md.order.json +18 -0
- package/themes/layout.order.json +18 -0
- package/themes/layout.sm.order.json +18 -0
- package/themes/spacing.order.json +14 -0
- package/themes/typography.lg.order.json +15 -0
- package/themes/typography.md.order.json +15 -0
- package/themes/typography.order.json +15 -0
- package/themes/typography.sm.order.json +15 -0
- package/tokens/overrides/base.json +22 -0
- package/tokens/overrides/lg.json +20 -0
- package/tokens/overrides/md.json +16 -0
- package/tokens/overrides/sm.json +16 -0
- package/tokens/overrides/viol.color.json +6 -0
- package/tokens/overrides/viol.typography.json +6 -0
- package/tokens/tokens.demo-violations.json +116 -0
- package/tokens/tokens.example.json +128 -0
- package/tokens/tokens.json +67 -0
- package/tokens/tokens.multi-violations.json +21 -0
- package/tokens/tokens.schema.d.ts +2298 -0
- package/tokens/tokens.schema.d.ts.map +1 -0
- package/tokens/tokens.schema.js +148 -0
- package/tokens/tokens.schema.ts +196 -0
- package/tokens/tokens.test.json +38 -0
- package/tokens/tokens.touch-violation.json +8 -0
- package/tokens/typography.classes.css +11 -0
- package/tokens/typography.css +20 -0
package/cli/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CA6BzE"}
|
package/cli/config.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { validateConfig } from './config-schema.js';
|
|
3
|
+
import { ok, err } from './result.js';
|
|
4
|
+
export function loadConfig(configPath) {
|
|
5
|
+
const candidates = configPath ? [configPath] : [
|
|
6
|
+
'dcv.config.json',
|
|
7
|
+
'dcv.config.js',
|
|
8
|
+
'.dcvrc.json',
|
|
9
|
+
'package.json'
|
|
10
|
+
];
|
|
11
|
+
for (const p of candidates) {
|
|
12
|
+
if (!existsSync(p))
|
|
13
|
+
continue;
|
|
14
|
+
try {
|
|
15
|
+
const rawTxt = readFileSync(p, 'utf8');
|
|
16
|
+
let raw = JSON.parse(rawTxt);
|
|
17
|
+
if (p === 'package.json' && raw && typeof raw === 'object') {
|
|
18
|
+
const pkg = raw;
|
|
19
|
+
if ('dcv' in pkg) {
|
|
20
|
+
raw = pkg.dcv;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
continue; // No dcv config in package.json
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const { value, errors } = validateConfig(raw);
|
|
27
|
+
if (errors)
|
|
28
|
+
return err(`Config validation failed in ${p}:\n - ${errors.join('\n - ')}`);
|
|
29
|
+
return ok(value);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
33
|
+
return err(`Failed reading config ${p}: ${msg}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return ok({});
|
|
37
|
+
}
|
package/cli/config.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { validateConfig } from './config-schema.js';
|
|
3
|
+
import { ok, err, type Result } from './result.js';
|
|
4
|
+
import type { DcvConfig } from './types.js';
|
|
5
|
+
|
|
6
|
+
export function loadConfig(configPath?: string): Result<DcvConfig, string> {
|
|
7
|
+
const candidates = configPath ? [configPath] : [
|
|
8
|
+
'dcv.config.json',
|
|
9
|
+
'dcv.config.js',
|
|
10
|
+
'.dcvrc.json',
|
|
11
|
+
'package.json'
|
|
12
|
+
];
|
|
13
|
+
for (const p of candidates) {
|
|
14
|
+
if (!existsSync(p)) continue;
|
|
15
|
+
try {
|
|
16
|
+
const rawTxt = readFileSync(p, 'utf8');
|
|
17
|
+
let raw: unknown = JSON.parse(rawTxt);
|
|
18
|
+
if (p === 'package.json' && raw && typeof raw === 'object') {
|
|
19
|
+
const pkg = raw as Record<string, unknown>;
|
|
20
|
+
if ('dcv' in pkg) {
|
|
21
|
+
raw = pkg.dcv;
|
|
22
|
+
} else {
|
|
23
|
+
continue; // No dcv config in package.json
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const { value, errors } = validateConfig(raw);
|
|
27
|
+
if (errors) return err(`Config validation failed in ${p}:\n - ${errors.join('\n - ')}`);
|
|
28
|
+
return ok(value!);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
31
|
+
return err(`Failed reading config ${p}: ${msg}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return ok({});
|
|
35
|
+
}
|
package/cli/dcv.d.ts
ADDED
package/cli/dcv.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dcv.d.ts","sourceRoot":"","sources":["dcv.ts"],"names":[],"mappings":""}
|
package/cli/dcv.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Clean minimal DCV CLI entrypoint
|
|
3
|
+
import yargs from 'yargs/yargs';
|
|
4
|
+
import { hideBin } from 'yargs/helpers';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { setCommand, buildCommand, validateCommand, graphCommand, whyCommand, patchCommand, patchApplyCommand } from './commands/index.js';
|
|
9
|
+
// Early intercept: experimental graph diff helper
|
|
10
|
+
(() => {
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
if (args[0] === 'graph' && args[1] === 'diff') {
|
|
13
|
+
const pass = args.slice(2);
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
const tsx = (() => { try {
|
|
16
|
+
return require.resolve('tsx/dist/cli.mjs');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return path.resolve('node_modules/tsx/dist/cli.mjs');
|
|
20
|
+
} })();
|
|
21
|
+
const r = spawnSync(process.execPath, [tsx, path.resolve('scripts/graph-diff.ts'), ...pass], { stdio: 'inherit', env: process.env });
|
|
22
|
+
process.exit(r.status ?? 0);
|
|
23
|
+
}
|
|
24
|
+
})();
|
|
25
|
+
const cli = yargs(hideBin(process.argv))
|
|
26
|
+
.scriptName('dcv')
|
|
27
|
+
.parserConfiguration({ 'camel-case-expansion': false })
|
|
28
|
+
.option('quiet', { type: 'boolean' });
|
|
29
|
+
cli.command('set <expressions..>', 'Set token values', y => y
|
|
30
|
+
.positional('expressions', { type: 'string', array: true })
|
|
31
|
+
.option('dry-run', { type: 'boolean', default: false })
|
|
32
|
+
.option('write', { type: 'boolean' })
|
|
33
|
+
.option('json', { type: 'string' })
|
|
34
|
+
.option('unset', { type: 'array', string: true })
|
|
35
|
+
.option('format', { type: 'string', choices: ['json', 'css', 'js'], default: 'json' })
|
|
36
|
+
.option('output', { type: 'string' })
|
|
37
|
+
.option('theme', { type: 'string' })
|
|
38
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }), a => setCommand(a));
|
|
39
|
+
cli.command('build', 'Build token outputs', y => y
|
|
40
|
+
.option('format', { type: 'string', choices: ['css', 'json', 'js'], default: 'css' })
|
|
41
|
+
.option('all-formats', { type: 'boolean', default: false })
|
|
42
|
+
.option('output', { type: 'string' })
|
|
43
|
+
.option('mapper', { type: 'string' })
|
|
44
|
+
.option('theme', { type: 'string' })
|
|
45
|
+
.option('dry-run', { type: 'boolean', default: false })
|
|
46
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }), a => buildCommand(a));
|
|
47
|
+
cli.command('validate', 'Validate constraints', y => y
|
|
48
|
+
.option('fail-on', { type: 'string', choices: ['off', 'warn', 'error'], default: 'error' })
|
|
49
|
+
.option('summary', { type: 'string', choices: ['none', 'table', 'json'], default: 'none' })
|
|
50
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
|
|
51
|
+
.option('breakpoint', { type: 'string' })
|
|
52
|
+
.option('all-breakpoints', { type: 'boolean' })
|
|
53
|
+
.option('perf', { type: 'boolean', describe: 'Print timing info' })
|
|
54
|
+
.option('budget-total-ms', { type: 'number', describe: 'Fail if total validation exceeds this (ms)' })
|
|
55
|
+
.option('budget-per-bp-ms', { type: 'number', describe: 'Fail if any single breakpoint exceeds this (ms)' }), a => validateCommand(a));
|
|
56
|
+
cli.command('graph', 'Generate dependency / constraint graph', y => y
|
|
57
|
+
.option('format', { type: 'string', choices: ['json', 'mermaid', 'dot', 'svg', 'png'], default: 'json' })
|
|
58
|
+
.option('bundle', { type: 'boolean', describe: 'When used with --hasse export mermaid+dot (+image if svg/png requested)' })
|
|
59
|
+
.option('hasse', { type: 'string' })
|
|
60
|
+
.option('filter-prefix', { type: 'string' })
|
|
61
|
+
.option('exclude-prefix', { type: 'string' })
|
|
62
|
+
.option('only-violations', { type: 'boolean' })
|
|
63
|
+
.option('highlight-violations', { type: 'boolean' })
|
|
64
|
+
.option('label-violations', { type: 'boolean' })
|
|
65
|
+
.option('label-truncate', { type: 'number', default: 0 })
|
|
66
|
+
.option('min-severity', { type: 'string', choices: ['warn', 'error'], default: 'warn' })
|
|
67
|
+
.option('focus', { type: 'string' })
|
|
68
|
+
.option('radius', { type: 'number', default: 1 })
|
|
69
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }), a => graphCommand(a));
|
|
70
|
+
cli.command('why <tokenId>', 'Explain token provenance', y => y
|
|
71
|
+
.positional('tokenId', { type: 'string', demandOption: true })
|
|
72
|
+
.option('format', { type: 'string', choices: ['json', 'table'], default: 'json' })
|
|
73
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }), a => whyCommand(a));
|
|
74
|
+
cli.command('patch', 'Export patch (diff) from overrides', y => y
|
|
75
|
+
.option('overrides', { type: 'string', describe: 'Path or inline JSON of flat overrides' })
|
|
76
|
+
.option('format', { type: 'string', choices: ['json', 'css', 'js'], default: 'json' })
|
|
77
|
+
.option('output', { type: 'string' })
|
|
78
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }), a => patchCommand(a));
|
|
79
|
+
cli.command('patch:apply <patch>', 'Apply patch document to tokens', y => y
|
|
80
|
+
.positional('patch', { type: 'string', demandOption: true })
|
|
81
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
|
|
82
|
+
.option('output', { type: 'string', describe: 'Write updated tokens to this file' })
|
|
83
|
+
.option('dry-run', { type: 'boolean', default: false }), a => patchApplyCommand(a));
|
|
84
|
+
cli.help().alias('h', 'help').strict().wrap(cli.terminalWidth());
|
|
85
|
+
cli.parse();
|
|
86
|
+
// EOF
|
package/cli/dcv.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Clean minimal DCV CLI entrypoint
|
|
3
|
+
import yargs from 'yargs/yargs';
|
|
4
|
+
import { hideBin } from 'yargs/helpers';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { type SetOptions, type BuildOptions, type ValidateOptions, type GraphOptions, type WhyOptions, type PatchOptions, type PatchApplyOptions } from './types.js';
|
|
9
|
+
import { setCommand, buildCommand, validateCommand, graphCommand, whyCommand, patchCommand, patchApplyCommand } from './commands/index.js';
|
|
10
|
+
|
|
11
|
+
// Early intercept: experimental graph diff helper
|
|
12
|
+
(() => {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
if (args[0] === 'graph' && args[1] === 'diff') {
|
|
15
|
+
const pass = args.slice(2);
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
const tsx = (() => { try { return require.resolve('tsx/dist/cli.mjs'); } catch { return path.resolve('node_modules/tsx/dist/cli.mjs'); }})();
|
|
18
|
+
const r = spawnSync(process.execPath, [tsx, path.resolve('scripts/graph-diff.ts'), ...pass], { stdio: 'inherit', env: process.env });
|
|
19
|
+
process.exit(r.status ?? 0);
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
22
|
+
|
|
23
|
+
const cli = yargs(hideBin(process.argv))
|
|
24
|
+
.scriptName('dcv')
|
|
25
|
+
.parserConfiguration({ 'camel-case-expansion': false })
|
|
26
|
+
.option('quiet', { type: 'boolean' });
|
|
27
|
+
|
|
28
|
+
cli.command<SetOptions>('set <expressions..>', 'Set token values', y => y
|
|
29
|
+
.positional('expressions', { type: 'string', array: true })
|
|
30
|
+
.option('dry-run', { type: 'boolean', default: false })
|
|
31
|
+
.option('write', { type: 'boolean' })
|
|
32
|
+
.option('json', { type: 'string' })
|
|
33
|
+
.option('unset', { type: 'array', string: true })
|
|
34
|
+
.option('format', { type: 'string', choices: ['json','css','js'], default: 'json' })
|
|
35
|
+
.option('output', { type: 'string' })
|
|
36
|
+
.option('theme', { type: 'string' })
|
|
37
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
|
|
38
|
+
a => setCommand(a)
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
cli.command<BuildOptions>('build', 'Build token outputs', y => y
|
|
42
|
+
.option('format', { type: 'string', choices: ['css','json','js'], default: 'css' })
|
|
43
|
+
.option('all-formats', { type: 'boolean', default: false })
|
|
44
|
+
.option('output', { type: 'string' })
|
|
45
|
+
.option('mapper', { type: 'string' })
|
|
46
|
+
.option('theme', { type: 'string' })
|
|
47
|
+
.option('dry-run', { type: 'boolean', default: false })
|
|
48
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
|
|
49
|
+
a => buildCommand(a)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
cli.command<ValidateOptions>('validate', 'Validate constraints', y => y
|
|
53
|
+
.option('fail-on', { type: 'string', choices: ['off','warn','error'], default: 'error' })
|
|
54
|
+
.option('summary', { type: 'string', choices: ['none','table','json'], default: 'none' })
|
|
55
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
|
|
56
|
+
.option('breakpoint', { type: 'string' })
|
|
57
|
+
.option('all-breakpoints', { type: 'boolean' })
|
|
58
|
+
.option('perf', { type: 'boolean', describe: 'Print timing info' })
|
|
59
|
+
.option('budget-total-ms', { type: 'number', describe: 'Fail if total validation exceeds this (ms)' })
|
|
60
|
+
.option('budget-per-bp-ms', { type: 'number', describe: 'Fail if any single breakpoint exceeds this (ms)' }),
|
|
61
|
+
a => validateCommand(a)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
cli.command<GraphOptions>('graph', 'Generate dependency / constraint graph', y => y
|
|
65
|
+
.option('format', { type: 'string', choices: ['json','mermaid','dot','svg','png'], default: 'json' })
|
|
66
|
+
.option('bundle', { type: 'boolean', describe: 'When used with --hasse export mermaid+dot (+image if svg/png requested)' })
|
|
67
|
+
.option('hasse', { type: 'string' })
|
|
68
|
+
.option('filter-prefix', { type: 'string' })
|
|
69
|
+
.option('exclude-prefix', { type: 'string' })
|
|
70
|
+
.option('only-violations', { type: 'boolean' })
|
|
71
|
+
.option('highlight-violations', { type: 'boolean' })
|
|
72
|
+
.option('label-violations', { type: 'boolean' })
|
|
73
|
+
.option('label-truncate', { type: 'number', default: 0 })
|
|
74
|
+
.option('min-severity', { type: 'string', choices: ['warn','error'], default: 'warn' })
|
|
75
|
+
.option('focus', { type: 'string' })
|
|
76
|
+
.option('radius', { type: 'number', default: 1 })
|
|
77
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
|
|
78
|
+
a => graphCommand(a)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
cli.command<WhyOptions>('why <tokenId>', 'Explain token provenance', y => y
|
|
82
|
+
.positional('tokenId', { type: 'string', demandOption: true })
|
|
83
|
+
.option('format', { type: 'string', choices: ['json','table'], default: 'json' })
|
|
84
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
|
|
85
|
+
a => whyCommand(a)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
cli.command<PatchOptions>('patch', 'Export patch (diff) from overrides', y => y
|
|
89
|
+
.option('overrides', { type: 'string', describe: 'Path or inline JSON of flat overrides' })
|
|
90
|
+
.option('format', { type: 'string', choices: ['json','css','js'], default: 'json' })
|
|
91
|
+
.option('output', { type: 'string' })
|
|
92
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
|
|
93
|
+
a => patchCommand(a)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
cli.command<PatchApplyOptions>('patch:apply <patch>', 'Apply patch document to tokens', y => y
|
|
97
|
+
.positional('patch', { type: 'string', demandOption: true })
|
|
98
|
+
.option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
|
|
99
|
+
.option('output', { type: 'string', describe: 'Write updated tokens to this file' })
|
|
100
|
+
.option('dry-run', { type: 'boolean', default: false }),
|
|
101
|
+
a => patchApplyCommand(a)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
cli.help().alias('h','help').strict().wrap(cli.terminalWidth());
|
|
105
|
+
cli.parse();
|
|
106
|
+
|
|
107
|
+
// EOF
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-helpers.d.ts","sourceRoot":"","sources":["engine-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAkB,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAI3C,OAAO,EAA8B,wBAAwB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC/G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,YAAY,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,GAAE,SAAc,GAAG,MAAM,CA8BlF;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,CAkBnH;AAED,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
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 };
|
|
@@ -0,0 +1,61 @@
|
|
|
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 };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Order } from "../core/poset.js";
|
|
2
|
+
export interface OrderFile {
|
|
3
|
+
$description?: string;
|
|
4
|
+
order: Order[];
|
|
5
|
+
}
|
|
6
|
+
export declare function loadOrderFile(path: string): OrderFile;
|
|
7
|
+
export declare function generateHasseDiagram(orderPath: string, outputPath: string, format?: 'mermaid' | 'dot'): void;
|
|
8
|
+
export declare function generateAllHasseDiagrams(themesDir?: string, outputDir?: string): void;
|
|
9
|
+
//# sourceMappingURL=graph-poset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-poset.d.ts","sourceRoot":"","sources":["graph-poset.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8E,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE1H,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,KAAK,EAAE,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAKrD;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,GAAG,KAAiB,GAAG,IAAI,CAgCvH;AAED,wBAAgB,wBAAwB,CAAC,SAAS,GAAE,MAAiB,EAAE,SAAS,GAAE,MAAsB,GAAG,IAAI,CAuB9G"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// cli/graph-poset.ts
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
+
import { buildPoset, transitiveReduction, toMermaidHasse, toDotHasse, validatePoset } from "../core/poset.js";
|
|
4
|
+
export function loadOrderFile(path) {
|
|
5
|
+
if (!existsSync(path)) {
|
|
6
|
+
throw new Error(`Order file not found: ${path}`);
|
|
7
|
+
}
|
|
8
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
9
|
+
}
|
|
10
|
+
export function generateHasseDiagram(orderPath, outputPath, format = 'mermaid') {
|
|
11
|
+
const orderData = loadOrderFile(orderPath);
|
|
12
|
+
const { order, $description } = orderData;
|
|
13
|
+
// Build and validate poset
|
|
14
|
+
const poset = buildPoset(order);
|
|
15
|
+
const validation = validatePoset(poset);
|
|
16
|
+
if (!validation.valid) {
|
|
17
|
+
console.error("⚠️ Warning: Poset contains cycles:");
|
|
18
|
+
for (const cycle of validation.cycles || []) {
|
|
19
|
+
console.error(` ${cycle.join(" → ")}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Generate Hasse diagram (transitive reduction)
|
|
23
|
+
const hasse = transitiveReduction(poset);
|
|
24
|
+
const title = $description || "Poset Hierarchy";
|
|
25
|
+
const diagram = format === 'mermaid'
|
|
26
|
+
? toMermaidHasse(hasse, { title })
|
|
27
|
+
: toDotHasse(hasse, { title });
|
|
28
|
+
// Ensure output directory exists
|
|
29
|
+
mkdirSync(outputPath.split('/').slice(0, -1).join('/'), { recursive: true });
|
|
30
|
+
writeFileSync(outputPath, diagram);
|
|
31
|
+
console.log(`✅ Generated ${format.toUpperCase()} Hasse diagram: ${outputPath}`);
|
|
32
|
+
if (!validation.valid) {
|
|
33
|
+
console.log(`⚠️ Note: Diagram shows cycles that should be resolved`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function generateAllHasseDiagrams(themesDir = "themes", outputDir = "dist/graphs") {
|
|
37
|
+
const orderFiles = [
|
|
38
|
+
{ file: "typography.order.json", name: "typography" },
|
|
39
|
+
{ file: "spacing.order.json", name: "spacing" }
|
|
40
|
+
];
|
|
41
|
+
mkdirSync(outputDir, { recursive: true });
|
|
42
|
+
for (const { file, name } of orderFiles) {
|
|
43
|
+
const orderPath = `${themesDir}/${file}`;
|
|
44
|
+
if (existsSync(orderPath)) {
|
|
45
|
+
try {
|
|
46
|
+
// Generate both Mermaid and DOT formats
|
|
47
|
+
generateHasseDiagram(orderPath, `${outputDir}/${name}-hasse.mmd`, 'mermaid');
|
|
48
|
+
generateHasseDiagram(orderPath, `${outputDir}/${name}-hasse.dot`, 'dot');
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(`❌ Failed to generate diagram for ${name}:`, error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log(`⏭️ Skipping ${name} (no order file found)`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// cli/graph-poset.ts
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
+
import { buildPoset, transitiveReduction, toMermaidHasse, toDotHasse, validatePoset, type Order } from "../core/poset.js";
|
|
4
|
+
|
|
5
|
+
export interface OrderFile {
|
|
6
|
+
$description?: string;
|
|
7
|
+
order: Order[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function loadOrderFile(path: string): OrderFile {
|
|
11
|
+
if (!existsSync(path)) {
|
|
12
|
+
throw new Error(`Order file not found: ${path}`);
|
|
13
|
+
}
|
|
14
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function generateHasseDiagram(orderPath: string, outputPath: string, format: 'mermaid' | 'dot' = 'mermaid'): void {
|
|
18
|
+
const orderData = loadOrderFile(orderPath);
|
|
19
|
+
const { order, $description } = orderData;
|
|
20
|
+
|
|
21
|
+
// Build and validate poset
|
|
22
|
+
const poset = buildPoset(order);
|
|
23
|
+
const validation = validatePoset(poset);
|
|
24
|
+
|
|
25
|
+
if (!validation.valid) {
|
|
26
|
+
console.error("⚠️ Warning: Poset contains cycles:");
|
|
27
|
+
for (const cycle of validation.cycles || []) {
|
|
28
|
+
console.error(` ${cycle.join(" → ")}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Generate Hasse diagram (transitive reduction)
|
|
33
|
+
const hasse = transitiveReduction(poset);
|
|
34
|
+
|
|
35
|
+
const title = $description || "Poset Hierarchy";
|
|
36
|
+
const diagram = format === 'mermaid'
|
|
37
|
+
? toMermaidHasse(hasse, { title })
|
|
38
|
+
: toDotHasse(hasse, { title });
|
|
39
|
+
|
|
40
|
+
// Ensure output directory exists
|
|
41
|
+
mkdirSync(outputPath.split('/').slice(0, -1).join('/'), { recursive: true });
|
|
42
|
+
|
|
43
|
+
writeFileSync(outputPath, diagram);
|
|
44
|
+
console.log(`✅ Generated ${format.toUpperCase()} Hasse diagram: ${outputPath}`);
|
|
45
|
+
|
|
46
|
+
if (!validation.valid) {
|
|
47
|
+
console.log(`⚠️ Note: Diagram shows cycles that should be resolved`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function generateAllHasseDiagrams(themesDir: string = "themes", outputDir: string = "dist/graphs"): void {
|
|
52
|
+
const orderFiles = [
|
|
53
|
+
{ file: "typography.order.json", name: "typography" },
|
|
54
|
+
{ file: "spacing.order.json", name: "spacing" }
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
mkdirSync(outputDir, { recursive: true });
|
|
58
|
+
|
|
59
|
+
for (const { file, name } of orderFiles) {
|
|
60
|
+
const orderPath = `${themesDir}/${file}`;
|
|
61
|
+
|
|
62
|
+
if (existsSync(orderPath)) {
|
|
63
|
+
try {
|
|
64
|
+
// Generate both Mermaid and DOT formats
|
|
65
|
+
generateHasseDiagram(orderPath, `${outputDir}/${name}-hasse.mmd`, 'mermaid');
|
|
66
|
+
generateHasseDiagram(orderPath, `${outputDir}/${name}-hasse.dot`, 'dot');
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(`❌ Failed to generate diagram for ${name}:`, error);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
console.log(`⏭️ Skipping ${name} (no order file found)`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
package/cli/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AACA,OAAO,UAAU,CAAC"}
|
package/cli/index.js
ADDED
package/cli/index.ts
ADDED
package/cli/result.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type Ok<T> = {
|
|
2
|
+
ok: true;
|
|
3
|
+
value: T;
|
|
4
|
+
};
|
|
5
|
+
export type Err<E> = {
|
|
6
|
+
ok: false;
|
|
7
|
+
error: E;
|
|
8
|
+
};
|
|
9
|
+
export type Result<T, E = Error> = Ok<T> | Err<E>;
|
|
10
|
+
export declare function ok<T>(value: T): Ok<T>;
|
|
11
|
+
export declare function err<E>(error: E): Err<E>;
|
|
12
|
+
export declare function wrap<T>(fn: () => T): Result<T, unknown>;
|
|
13
|
+
export declare function wrapAsync<T>(fn: () => Promise<T>): Promise<Result<T, unknown>>;
|
|
14
|
+
export declare function map<T, U, E>(r: Result<T, E>, f: (v: T) => U): Result<U, E>;
|
|
15
|
+
export declare function chain<T, U, E>(r: Result<T, E>, f: (v: T) => Result<U, E>): Result<U, E>;
|
|
16
|
+
export declare function getOrThrow<T, E>(r: Result<T, E>): T;
|
|
17
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["result.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,EAAE,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAC7C,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;AAElD,wBAAgB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAgC;AACtE,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAiC;AAEzE,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAEvD;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAEpF;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAE1E;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAEvF;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAGnD"}
|