@cogitato/kit-cli 0.0.0-stage → 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.
Files changed (76) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +34 -2
  3. package/bin/kit.js +4 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +245 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/doctor.d.ts +23 -0
  8. package/dist/doctor.js +86 -0
  9. package/dist/doctor.js.map +1 -0
  10. package/dist/errors.d.ts +8 -0
  11. package/dist/errors.js +9 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/export-flows.d.ts +8 -0
  14. package/dist/export-flows.js +15 -0
  15. package/dist/export-flows.js.map +1 -0
  16. package/dist/flows.d.ts +38 -0
  17. package/dist/flows.js +141 -0
  18. package/dist/flows.js.map +1 -0
  19. package/dist/format.d.ts +3 -0
  20. package/dist/format.js +27 -0
  21. package/dist/format.js.map +1 -0
  22. package/dist/gaps.d.ts +21 -0
  23. package/dist/gaps.js +52 -0
  24. package/dist/gaps.js.map +1 -0
  25. package/dist/hook.d.ts +33 -0
  26. package/dist/hook.js +114 -0
  27. package/dist/hook.js.map +1 -0
  28. package/dist/hosting.d.ts +18 -0
  29. package/dist/hosting.js +51 -0
  30. package/dist/hosting.js.map +1 -0
  31. package/dist/index.d.ts +19 -0
  32. package/dist/index.js +21 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/issue.d.ts +17 -0
  35. package/dist/issue.js +65 -0
  36. package/dist/issue.js.map +1 -0
  37. package/dist/links.d.ts +15 -0
  38. package/dist/links.js +60 -0
  39. package/dist/links.js.map +1 -0
  40. package/dist/preview.d.ts +16 -0
  41. package/dist/preview.js +38 -0
  42. package/dist/preview.js.map +1 -0
  43. package/dist/project.d.ts +55 -0
  44. package/dist/project.js +145 -0
  45. package/dist/project.js.map +1 -0
  46. package/dist/registry.d.ts +67 -0
  47. package/dist/registry.js +227 -0
  48. package/dist/registry.js.map +1 -0
  49. package/dist/rules/css-values.d.ts +31 -0
  50. package/dist/rules/css-values.js +185 -0
  51. package/dist/rules/css-values.js.map +1 -0
  52. package/dist/rules/css.d.ts +4 -0
  53. package/dist/rules/css.js +39 -0
  54. package/dist/rules/css.js.map +1 -0
  55. package/dist/rules/tsx.d.ts +26 -0
  56. package/dist/rules/tsx.js +191 -0
  57. package/dist/rules/tsx.js.map +1 -0
  58. package/dist/save.d.ts +30 -0
  59. package/dist/save.js +164 -0
  60. package/dist/save.js.map +1 -0
  61. package/dist/scaffold.d.ts +42 -0
  62. package/dist/scaffold.js +232 -0
  63. package/dist/scaffold.js.map +1 -0
  64. package/dist/snapshots.d.ts +43 -0
  65. package/dist/snapshots.js +140 -0
  66. package/dist/snapshots.js.map +1 -0
  67. package/dist/status.d.ts +14 -0
  68. package/dist/status.js +38 -0
  69. package/dist/status.js.map +1 -0
  70. package/dist/validate.d.ts +18 -0
  71. package/dist/validate.js +72 -0
  72. package/dist/validate.js.map +1 -0
  73. package/dist/workspace.d.ts +18 -0
  74. package/dist/workspace.js +119 -0
  75. package/dist/workspace.js.map +1 -0
  76. package/package.json +52 -3
@@ -0,0 +1,227 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { readSchema } from "@cogitato/contract";
3
+ import ts from "typescript";
4
+ import { findCalls, getProperty, issueAt, literalText, parseSource, propertyName } from "./issue.js";
5
+ import { LAYOUT, isScreenFile } from "./project.js";
6
+ // Id formats come from the shared schema so the reader and the validators can't disagree.
7
+ const common = readSchema("common.schema.json");
8
+ const pattern = (name) => new RegExp(common.$defs[name].pattern);
9
+ export const ID_PATTERNS = {
10
+ screenId: pattern("screenId"),
11
+ slug: pattern("slug"),
12
+ kitId: pattern("kitId"),
13
+ nodeRef: pattern("nodeRef"),
14
+ };
15
+ /** "card-declined" -> "Card declined". Same rule as the runtime's fixture titles. */
16
+ export function humanize(key) {
17
+ const words = key.replace(/[-_.]+/g, " ").trim();
18
+ return words.charAt(0).toUpperCase() + words.slice(1);
19
+ }
20
+ function locate(sf, node) {
21
+ const { line, character } = sf.getLineAndCharacterOfPosition(node.getStart(sf));
22
+ return { file: sf.fileName, line: line + 1, column: character + 1 };
23
+ }
24
+ const notStatic = (sf, node, what, example) => issueAt("code-screen-not-static", sf, node, `Write ${what} as plain text, like ${example}, so Cogitato can find it without running the code.`);
25
+ /** Reads the defineScreen({...}) registration in one screen.tsx. */
26
+ export function readScreen(sf) {
27
+ const issues = [];
28
+ const calls = findCalls(sf, "defineScreen");
29
+ if (calls.length === 0) {
30
+ const first = sf.statements[0] ?? sf;
31
+ issues.push(issueAt("code-screen-missing", sf, first, `${sf.fileName} doesn't register a screen. Export one with \`export default defineScreen({ ... })\`.`));
32
+ return { issues };
33
+ }
34
+ const call = calls[0];
35
+ const arg = call.arguments[0];
36
+ if (!arg || !ts.isObjectLiteralExpression(arg)) {
37
+ issues.push(notStatic(sf, arg ?? call, "the screen's settings", "`defineScreen({ id: \"cart\", ... })`"));
38
+ return { issues };
39
+ }
40
+ const text = (key, example) => {
41
+ const prop = getProperty(arg, key);
42
+ const shorthand = arg.properties.find((p) => ts.isShorthandPropertyAssignment(p) && p.name.text === key);
43
+ if (!prop && shorthand) {
44
+ issues.push(notStatic(sf, shorthand, `the screen's ${key}`, `\`${key}: ${example}\``));
45
+ return undefined;
46
+ }
47
+ if (!prop) {
48
+ issues.push(issueAt("code-screen-incomplete", sf, arg, `This screen is missing its \`${key}\`. Add it, like \`${key}: ${example}\`.`));
49
+ return undefined;
50
+ }
51
+ const value = literalText(prop.initializer);
52
+ if (value === undefined)
53
+ issues.push(notStatic(sf, prop.initializer, `the screen's ${key}`, `\`${example}\``));
54
+ return value;
55
+ };
56
+ const id = text("id", '"checkout.payment"');
57
+ const route = text("route", '"/checkout/payment"');
58
+ const title = text("title", '"Payment"');
59
+ if (id !== undefined && !ID_PATTERNS.screenId.test(id)) {
60
+ issues.push(issueAt("code-invalid-screen-id", sf, getProperty(arg, "id").initializer, `"${id}" isn't a valid screen id. Screen ids are lowercase words separated by dots, like "checkout.payment".`));
61
+ }
62
+ if (route === "/") {
63
+ issues.push(issueAt("code-invalid-route", sf, getProperty(arg, "route").initializer, `The address "/" is the prototype's list of screens, so a screen can't use it. Pick another one, like "/home".`));
64
+ }
65
+ if (route !== undefined && !route.startsWith("/")) {
66
+ issues.push(issueAt("code-invalid-route", sf, getProperty(arg, "route").initializer, `The address "${route}" needs to start with "/", like "/checkout/payment".`));
67
+ }
68
+ const fixtures = [];
69
+ const fixturesProp = getProperty(arg, "fixtures");
70
+ if (!fixturesProp) {
71
+ issues.push(issueAt("code-missing-default-fixture", sf, arg, `This screen has no states. Add \`fixtures: { default: { data: {} } }\`; every screen needs a "default" state.`));
72
+ }
73
+ else if (!ts.isObjectLiteralExpression(fixturesProp.initializer)) {
74
+ issues.push(notStatic(sf, fixturesProp.initializer, "the screen's states (fixtures)", "`fixtures: { default: { data: {...} } }`"));
75
+ }
76
+ else {
77
+ for (const p of fixturesProp.initializer.properties) {
78
+ const key = ts.isPropertyAssignment(p) ? propertyName(p.name) : undefined;
79
+ if (!ts.isPropertyAssignment(p) || key === undefined) {
80
+ issues.push(notStatic(sf, p, "each state's name", '`"card-declined": { data: {...} }`'));
81
+ continue;
82
+ }
83
+ if (!ID_PATTERNS.slug.test(key)) {
84
+ issues.push(issueAt("code-invalid-fixture-name", sf, p.name, `The state name "${key}" isn't valid. Use lowercase words joined by dashes, like "card-declined".`));
85
+ }
86
+ let fixtureTitle = humanize(key);
87
+ if (ts.isObjectLiteralExpression(p.initializer)) {
88
+ const titleProp = getProperty(p.initializer, "title");
89
+ if (titleProp) {
90
+ const t = literalText(titleProp.initializer);
91
+ if (t === undefined)
92
+ issues.push(notStatic(sf, titleProp.initializer, "the state's title", '`title: "Card declined"`'));
93
+ else
94
+ fixtureTitle = t;
95
+ }
96
+ }
97
+ else {
98
+ issues.push(notStatic(sf, p.initializer, `the "${key}" state`, '`{ title: "Card declined", data: {...} }`'));
99
+ }
100
+ fixtures.push({ key, title: fixtureTitle, ...locate(sf, p.name) });
101
+ }
102
+ if (!fixtures.some((f) => f.key === "default")) {
103
+ issues.push(issueAt("code-missing-default-fixture", sf, fixturesProp.name, `${title ?? "This screen"} has no "default" state. Every screen needs one; it's what the screen shows when it first opens.`));
104
+ }
105
+ }
106
+ if (id === undefined || route === undefined || title === undefined)
107
+ return { issues };
108
+ const dir = sf.fileName.slice(0, sf.fileName.lastIndexOf("/"));
109
+ return { screen: { id, route, title, fixtures, dir, ...locate(sf, call) }, issues };
110
+ }
111
+ /** Reads defineFlows([...]) from src/flows.ts. */
112
+ export function readFlows(sf) {
113
+ const issues = [];
114
+ const flows = [];
115
+ const flowsNotStatic = (node, what, example) => issueAt("code-flows-not-static", sf, node, `Write ${what} as plain text, like ${example}, so Cogitato can read it without running the code.`);
116
+ const call = findCalls(sf, "defineFlows")[0];
117
+ if (!call)
118
+ return { flows, issues };
119
+ const list = call.arguments[0];
120
+ if (!list || !ts.isArrayLiteralExpression(list)) {
121
+ issues.push(flowsNotStatic(list ?? call, "the list of flows", "`defineFlows([{ id: \"checkout\", ... }])`"));
122
+ return { flows, issues };
123
+ }
124
+ for (const el of list.elements) {
125
+ if (!ts.isObjectLiteralExpression(el)) {
126
+ issues.push(flowsNotStatic(el, "each flow", '`{ id: "checkout", title: "Checkout", edges: [...] }`'));
127
+ continue;
128
+ }
129
+ const idProp = getProperty(el, "id");
130
+ const titleProp = getProperty(el, "title");
131
+ const id = literalText(idProp?.initializer);
132
+ const title = literalText(titleProp?.initializer);
133
+ if (id === undefined)
134
+ issues.push(flowsNotStatic(idProp?.initializer ?? el, "the flow's id", '`id: "checkout"`'));
135
+ if (title === undefined)
136
+ issues.push(flowsNotStatic(titleProp?.initializer ?? el, "the flow's title", '`title: "Checkout"`'));
137
+ const edges = [];
138
+ const edgesProp = getProperty(el, "edges");
139
+ if (!edgesProp || !ts.isArrayLiteralExpression(edgesProp.initializer)) {
140
+ issues.push(flowsNotStatic(edgesProp?.initializer ?? el, "the flow's arrows (edges)", '`edges: [{ from: "cart.default", to: "checkout.shipping.default" }]`'));
141
+ }
142
+ else {
143
+ for (const e of edgesProp.initializer.elements) {
144
+ const from = ts.isObjectLiteralExpression(e) ? literalText(getProperty(e, "from")?.initializer) : undefined;
145
+ const to = ts.isObjectLiteralExpression(e) ? literalText(getProperty(e, "to")?.initializer) : undefined;
146
+ if (!ts.isObjectLiteralExpression(e) || from === undefined || to === undefined) {
147
+ issues.push(flowsNotStatic(e, "each arrow", '`{ from: "cart.default", to: "checkout.shipping.default" }`'));
148
+ continue;
149
+ }
150
+ const labelProp = getProperty(e, "label");
151
+ const label = literalText(labelProp?.initializer);
152
+ if (labelProp && label === undefined)
153
+ issues.push(flowsNotStatic(labelProp.initializer, "the arrow's label", '`label: "Pay now"`'));
154
+ edges.push({ from, to, ...(label !== undefined ? { label } : {}), ...locate(sf, e) });
155
+ }
156
+ }
157
+ let nodes;
158
+ const nodesProp = getProperty(el, "nodes");
159
+ if (nodesProp) {
160
+ if (!ts.isArrayLiteralExpression(nodesProp.initializer)) {
161
+ issues.push(flowsNotStatic(nodesProp.initializer, "the flow's steps (nodes)", '`nodes: ["cart.default"]`'));
162
+ }
163
+ else {
164
+ nodes = [];
165
+ for (const n of nodesProp.initializer.elements) {
166
+ const ref = literalText(n);
167
+ if (ref === undefined)
168
+ issues.push(flowsNotStatic(n, "each step", '`"cart.default"`'));
169
+ else
170
+ nodes.push({ ref, at: locate(sf, n) });
171
+ }
172
+ }
173
+ }
174
+ if (id !== undefined && title !== undefined)
175
+ flows.push({ id, title, edges, nodes, ...locate(sf, el) });
176
+ }
177
+ return { flows, issues };
178
+ }
179
+ const cache = new Map();
180
+ /** Parses a project file, reusing the parse while its text is unchanged (keeps repeat runs fast). */
181
+ export function parseProjectFile(project, rel) {
182
+ const text = readFileSync(project.abs(rel), "utf8");
183
+ const hit = cache.get(project.abs(rel));
184
+ if (hit && hit.text === text)
185
+ return hit.sf;
186
+ const sf = parseSource(rel, text);
187
+ cache.set(project.abs(rel), { text, sf });
188
+ return sf;
189
+ }
190
+ /** Reads every screen registration and src/flows.ts. Never runs prototype code. */
191
+ export function readRegistry(project, files) {
192
+ const issues = [];
193
+ const screens = [];
194
+ const seen = new Map();
195
+ let complete = true;
196
+ for (const rel of files.filter(isScreenFile)) {
197
+ const r = readScreen(parseProjectFile(project, rel));
198
+ issues.push(...r.issues);
199
+ if (!r.screen) {
200
+ complete = false;
201
+ continue;
202
+ }
203
+ const other = seen.get(r.screen.id);
204
+ if (other) {
205
+ issues.push({
206
+ code: "code-duplicate-screen",
207
+ severity: "error",
208
+ file: r.screen.file,
209
+ path: "",
210
+ line: r.screen.line,
211
+ column: r.screen.column,
212
+ message: `The screen id "${r.screen.id}" is already used by ${other.file}. Each screen needs its own id.`,
213
+ });
214
+ continue;
215
+ }
216
+ seen.set(r.screen.id, r.screen);
217
+ screens.push(r.screen);
218
+ }
219
+ let flows = [];
220
+ if (existsSync(project.abs(LAYOUT.flowsTs))) {
221
+ const r = readFlows(parseProjectFile(project, LAYOUT.flowsTs));
222
+ issues.push(...r.issues);
223
+ flows = r.flows;
224
+ }
225
+ return { screens, flows, issues, complete };
226
+ }
227
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ContractView } from "../project.js";
2
+ /** Stands in for a `${...}` substitution when a template literal's text is checked. */
3
+ export declare const SUBSTITUTION = "\u0001";
4
+ export type Finding = {
5
+ kind: "color";
6
+ literal: string;
7
+ message: string;
8
+ suggestion?: string;
9
+ } | {
10
+ kind: "size";
11
+ literal: string;
12
+ message: string;
13
+ suggestion?: string;
14
+ } | {
15
+ kind: "unknown-token";
16
+ literal: string;
17
+ message: string;
18
+ suggestion?: string;
19
+ };
20
+ /**
21
+ * Checks one CSS declaration (or inline style property). `prop` may be camelCase.
22
+ * Token variables, 0, auto, inherit, transparent, currentColor and percentages are fine.
23
+ */
24
+ export declare function checkCssValue(contract: ContractView, rawProp: string, value: string): Finding[];
25
+ /** A bare number in a React inline style means pixels. */
26
+ export declare function checkCssNumber(contract: ContractView, rawProp: string, n: number): Finding[];
27
+ export declare const FINDING_CODES: {
28
+ readonly color: "code-hardcoded-color";
29
+ readonly size: "code-hardcoded-size";
30
+ readonly "unknown-token": "code-unknown-token";
31
+ };
@@ -0,0 +1,185 @@
1
+ import { tokenCssVar } from "@cogitato/contract";
2
+ // Which CSS properties must use tokens, and which token group sizes come from.
3
+ const COLOR_PROP = /^(color|background(-color|-image)?|border(-(top|right|bottom|left|block|inline)(-(start|end))?)?(-color)?|outline(-color)?|fill|stroke|box-shadow|text-shadow|text-decoration(-color)?|caret-color|accent-color|column-rule(-color)?)$/;
4
+ const SIZE_GROUPS = [
5
+ { prop: /^(padding|margin)(-(top|right|bottom|left|block|inline)(-(start|end))?)?$|^(row-|column-)?gap$/, group: "space", scale: "spacing scale" },
6
+ { prop: /^border(-(top|bottom)-(left|right)|-(start|end)-(start|end))?-radius$/, group: "radius", scale: "corner radius scale" },
7
+ { prop: /^font-size$/, group: "font.size", scale: "font size scale" },
8
+ ];
9
+ // Named colors designers are likely to type, with values so the nearest token can be suggested.
10
+ const NAMED_COLORS = {
11
+ black: "#000000", white: "#ffffff", red: "#ff0000", green: "#008000", blue: "#0000ff",
12
+ yellow: "#ffff00", orange: "#ffa500", purple: "#800080", pink: "#ffc0cb", gray: "#808080",
13
+ grey: "#808080", silver: "#c0c0c0", navy: "#000080", teal: "#008080", maroon: "#800000",
14
+ olive: "#808000", lime: "#00ff00", aqua: "#00ffff", cyan: "#00ffff", magenta: "#ff00ff",
15
+ fuchsia: "#ff00ff", brown: "#a52a2a", gold: "#ffd700", indigo: "#4b0082", violet: "#ee82ee",
16
+ coral: "#ff7f50", salmon: "#fa8072", tomato: "#ff6347", crimson: "#dc143c", beige: "#f5f5dc",
17
+ ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", tan: "#d2b48c", turquoise: "#40e0d0",
18
+ darkgray: "#a9a9a9", darkgrey: "#a9a9a9", lightgray: "#d3d3d3", lightgrey: "#d3d3d3",
19
+ whitesmoke: "#f5f5f5", gainsboro: "#dcdcdc", slategray: "#708090", dimgray: "#696969",
20
+ };
21
+ const HEX = /#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4})\b/g;
22
+ const COLOR_FN = /\b(rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\([^()]*\)/gi;
23
+ const NAMED = new RegExp(`(?<![\\w-])(${Object.keys(NAMED_COLORS).join("|")})(?![\\w-])`, "gi");
24
+ const LENGTH = /(?<![\w.-])(-?\d*\.?\d+)(px|rem|em)\b/g;
25
+ const VAR = /var\(\s*(--[A-Za-z0-9_-]+)/g;
26
+ /** Stands in for a `${...}` substitution when a template literal's text is checked. */
27
+ export const SUBSTITUTION = "\u0001";
28
+ const toKebab = (prop) => prop.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
29
+ function parseColor(literal) {
30
+ const named = NAMED_COLORS[literal.toLowerCase()];
31
+ const value = named ?? literal;
32
+ const hex = /^#([0-9a-f]{3,8})$/i.exec(value)?.[1];
33
+ if (hex) {
34
+ const full = hex.length <= 4 ? [...hex.slice(0, 3)].map((c) => c + c).join("") : hex.slice(0, 6);
35
+ return [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16));
36
+ }
37
+ const rgb = /^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(value);
38
+ if (rgb)
39
+ return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
40
+ return undefined;
41
+ }
42
+ const distance = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
43
+ // Semantic token group that fits a property best, used to break ties between equal colors.
44
+ function colorContext(prop) {
45
+ if (prop === "color" || prop.startsWith("text-") || prop === "caret-color")
46
+ return "color.text.";
47
+ if (prop.startsWith("background"))
48
+ return "color.surface.";
49
+ if (prop.startsWith("border") || prop.startsWith("outline") || prop.startsWith("column-rule"))
50
+ return "color.border.";
51
+ return "";
52
+ }
53
+ function nearestColor(contract, rgb, prop) {
54
+ const context = colorContext(prop);
55
+ const scored = contract.tokens
56
+ .filter((t) => t.type === "color" && typeof t.resolved === "string")
57
+ .map((t) => ({ t, rgb: parseColor(t.resolved) }))
58
+ .filter((x) => x.rgb !== undefined)
59
+ .map(({ t, rgb: c }) => ({
60
+ t,
61
+ key: [distance(rgb, c), context && t.path.startsWith(context) ? 0 : 1, t.aliasOf ? 1 : 0],
62
+ }))
63
+ .sort((a, b) => a.key[0] - b.key[0] || a.key[1] - b.key[1] || a.key[2] - b.key[2] || a.t.path.localeCompare(b.t.path));
64
+ const best = scored[0];
65
+ return best ? { token: best.t, exact: best.key[0] === 0 } : undefined;
66
+ }
67
+ function toPx(value) {
68
+ if (typeof value === "string") {
69
+ const m = /^(-?\d*\.?\d+)(px|rem)$/.exec(value);
70
+ return m ? Number(m[1]) * (m[2] === "rem" ? 16 : 1) : undefined;
71
+ }
72
+ if (value && typeof value === "object" && "value" in value && "unit" in value) {
73
+ const v = value;
74
+ return v.unit === "rem" ? v.value * 16 : v.unit === "px" ? v.value : undefined;
75
+ }
76
+ return undefined;
77
+ }
78
+ function nearestSize(contract, px, group) {
79
+ const best = contract.tokens
80
+ .filter((t) => t.type === "dimension" && t.path.startsWith(`${group}.`))
81
+ .map((t) => ({ t, px: toPx(t.resolved) }))
82
+ .filter((x) => x.px !== undefined)
83
+ .sort((a, b) => Math.abs(a.px - px) - Math.abs(b.px - px) || (a.t.aliasOf ? 1 : 0) - (b.t.aliasOf ? 1 : 0))[0];
84
+ return best ? { token: best.t, px: best.px, exact: best.px === px } : undefined;
85
+ }
86
+ function levenshtein(a, b) {
87
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
88
+ for (let i = 1; i <= a.length; i++) {
89
+ let prev = row[0];
90
+ row[0] = i;
91
+ for (let j = 1; j <= b.length; j++) {
92
+ const tmp = row[j];
93
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
94
+ prev = tmp;
95
+ }
96
+ }
97
+ return row[b.length];
98
+ }
99
+ const label = (t) => (t.description ? `${t.path} (${t.description})` : t.path);
100
+ /**
101
+ * Checks one CSS declaration (or inline style property). `prop` may be camelCase.
102
+ * Token variables, 0, auto, inherit, transparent, currentColor and percentages are fine.
103
+ */
104
+ export function checkCssValue(contract, rawProp, value) {
105
+ const prop = toKebab(rawProp.trim()).toLowerCase();
106
+ if (prop.startsWith("--"))
107
+ return [];
108
+ const findings = [];
109
+ for (const m of value.matchAll(VAR)) {
110
+ const name = m[1];
111
+ // A name finished by a substitution (`var(--kit-space-${gap})`) is only known at runtime.
112
+ if (value[m.index + m[0].length] === SUBSTITUTION)
113
+ continue;
114
+ if (!name.startsWith("--kit-") || contract.cssVars.has(name))
115
+ continue;
116
+ // Ties go to names of the same length: --kit-color-brand-650 is nearer 600 than 50.
117
+ const closest = [...contract.cssVars].sort((a, b) => levenshtein(name, a) - levenshtein(name, b) ||
118
+ Math.abs(a.length - name.length) - Math.abs(b.length - name.length))[0];
119
+ findings.push({
120
+ kind: "unknown-token",
121
+ literal: name,
122
+ message: `var(${name}) isn't a token in the design system. Check the spelling${closest ? `; did you mean var(${closest})?` : ", or pick an existing token."}`,
123
+ ...(closest ? { suggestion: `var(${closest})` } : {}),
124
+ });
125
+ }
126
+ // Token references are fine; check only what's left once they're removed.
127
+ let rest = value.replaceAll(SUBSTITUTION, " ");
128
+ while (/var\([^()]*\)/.test(rest))
129
+ rest = rest.replace(/var\([^()]*\)/g, " ");
130
+ if (COLOR_PROP.test(prop)) {
131
+ const literals = [...rest.matchAll(HEX), ...rest.matchAll(COLOR_FN), ...rest.matchAll(NAMED)].map((m) => m[0]);
132
+ for (const literal of literals) {
133
+ const rgb = parseColor(literal);
134
+ const hit = rgb && nearestColor(contract, rgb, prop);
135
+ const suggestion = hit ? `var(${tokenCssVar(hit.token.path)})` : undefined;
136
+ findings.push({
137
+ kind: "color",
138
+ literal,
139
+ message: !hit
140
+ ? `This color (${literal}) isn't from the palette. Use a color token instead.`
141
+ : hit.exact
142
+ ? `${literal} is ${label(hit.token)} in the palette. Use the token instead of typing the color, so it stays in sync with the design system.`
143
+ : `This color (${literal}) isn't in the palette. Use ${label(hit.token)} instead; it's the closest match.`,
144
+ ...(suggestion ? { suggestion } : {}),
145
+ });
146
+ }
147
+ }
148
+ const size = SIZE_GROUPS.find((g) => g.prop.test(prop));
149
+ if (size) {
150
+ for (const m of rest.matchAll(LENGTH)) {
151
+ const amount = Number(m[1]);
152
+ if (amount === 0)
153
+ continue;
154
+ const px = amount * (m[2] === "px" ? 1 : 16);
155
+ const hit = nearestSize(contract, px, size.group);
156
+ const suggestion = hit ? `var(${tokenCssVar(hit.token.path)})` : undefined;
157
+ findings.push({
158
+ kind: "size",
159
+ literal: m[0],
160
+ message: !hit
161
+ ? `${m[0]} isn't on the ${size.scale}. Use a ${size.group} token instead.`
162
+ : hit.exact
163
+ ? `${m[0]} is ${hit.token.path} on the ${size.scale}. Use the token instead of typing the size.`
164
+ : `${m[0]} isn't on the ${size.scale}. Use ${hit.token.path} (${hit.px}px) instead; it's the closest size.`,
165
+ ...(suggestion ? { suggestion } : {}),
166
+ });
167
+ }
168
+ }
169
+ return findings;
170
+ }
171
+ /** A bare number in a React inline style means pixels. */
172
+ export function checkCssNumber(contract, rawProp, n) {
173
+ if (n === 0)
174
+ return [];
175
+ const prop = toKebab(rawProp).toLowerCase();
176
+ if (!SIZE_GROUPS.some((g) => g.prop.test(prop)))
177
+ return [];
178
+ return checkCssValue(contract, prop, `${n}px`);
179
+ }
180
+ export const FINDING_CODES = {
181
+ color: "code-hardcoded-color",
182
+ size: "code-hardcoded-size",
183
+ "unknown-token": "code-unknown-token",
184
+ };
185
+ //# sourceMappingURL=css-values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-values.js","sourceRoot":"","sources":["../../src/rules/css-values.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Issue } from "../issue.js";
2
+ import type { ContractView } from "../project.js";
3
+ /** Token rule for a .css / .module.css file. */
4
+ export declare function checkCssFile(file: string, text: string, contract: ContractView): Issue[];
@@ -0,0 +1,39 @@
1
+ import postcss from "postcss";
2
+ import { FINDING_CODES, checkCssValue } from "./css-values.js";
3
+ /** Token rule for a .css / .module.css file. */
4
+ export function checkCssFile(file, text, contract) {
5
+ let root;
6
+ try {
7
+ root = postcss.parse(text, { from: file });
8
+ }
9
+ catch (e) {
10
+ const err = e;
11
+ return [
12
+ {
13
+ code: "code-css-unreadable",
14
+ severity: "error",
15
+ file,
16
+ path: "",
17
+ ...(err.line ? { line: err.line } : {}),
18
+ ...(err.column ? { column: err.column } : {}),
19
+ message: `This stylesheet has a typo Cogitato can't read past (${err.reason ?? "invalid CSS"}).`,
20
+ },
21
+ ];
22
+ }
23
+ const issues = [];
24
+ root.walkDecls((decl) => {
25
+ for (const f of checkCssValue(contract, decl.prop, decl.value)) {
26
+ issues.push({
27
+ code: FINDING_CODES[f.kind],
28
+ severity: "error",
29
+ file,
30
+ path: "",
31
+ ...(decl.source?.start ? { line: decl.source.start.line, column: decl.source.start.column } : {}),
32
+ message: f.message,
33
+ ...(f.suggestion ? { suggestion: f.suggestion } : {}),
34
+ });
35
+ }
36
+ });
37
+ return issues;
38
+ }
39
+ //# sourceMappingURL=css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/rules/css.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE/D,gDAAgD;AAChD,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAY,EAAE,QAAsB;IAC7E,IAAI,IAAkB,CAAC;IACvB,IAAI,CAAC;QACH,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAwD,CAAC;QACrE,OAAO;YACL;gBACE,IAAI,EAAE,qBAAqB;gBAC3B,QAAQ,EAAE,OAAO;gBACjB,IAAI;gBACJ,IAAI,EAAE,EAAE;gBACR,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,EAAE,wDAAwD,GAAG,CAAC,MAAM,IAAI,aAAa,IAAI;aACjG;SACF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;QACtB,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/D,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC3B,QAAQ,EAAE,OAAO;gBACjB,IAAI;gBACJ,IAAI,EAAE,EAAE;gBACR,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjG,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtD,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import ts from "typescript";
2
+ import { type Issue } from "../issue.js";
3
+ import { type ContractView } from "../project.js";
4
+ import { type ScreenEntry } from "../registry.js";
5
+ /** A literal data-kit-id found in code, for duplicate detection across a screen's files. */
6
+ export interface KitIdUse {
7
+ id: string;
8
+ owner?: ScreenEntry;
9
+ file: string;
10
+ line: number;
11
+ column: number;
12
+ }
13
+ export interface TsxContext {
14
+ contract: ContractView;
15
+ /** The screen whose folder contains this file, if any. */
16
+ owner?: ScreenEntry;
17
+ /** True for a file in the shared kit: screen-only rules (kit imports, data-kit-id on elements) don't apply. */
18
+ inKit?: boolean;
19
+ }
20
+ /** Token, component and data-kit-id rules for one .ts/.tsx file. */
21
+ export declare function checkTsxFile(sf: ts.SourceFile, ctx: TsxContext): {
22
+ issues: Issue[];
23
+ kitIds: KitIdUse[];
24
+ };
25
+ /** Duplicate data-kit-ids within one screen. Reported where the later use is. */
26
+ export declare function duplicateKitIds(uses: KitIdUse[]): Issue[];