onsystem 0.10.3 → 0.11.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/dist/cli.js +3 -3
- package/dist/{design-system-7wZoJsMm.js → design-system-Csqts7Eq.js} +75 -10
- package/dist/eslint-worker.d.ts +2 -0
- package/dist/eslint-worker.js +119 -30
- package/dist/eslint.js +54 -12
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -2
- package/dist/{stdio-B31hp2UZ.js → stdio-0TpyqZaD.js} +2 -2
- package/dist/{workspace-C5pMS6Xv.js → workspace-Dbxubo9n.js} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as DEFAULT_TOKENS, A as unrecordedRules, At as relativePath, B as statusKinds, C as countBaseline, D as pruneBaseline, Dt as isInside, E as onLines, Et as SCRIPT_FILE, It as unique, J as ConfigError, L as toDesignMd, M as writeBaseline, O as readBaseline, Ot as matchesGlob, Pt as plural, R as toDtcg, Rt as readProjectConfig, S as applyBaseline, St as isDesignSystemName, T as goneFiles, Y as DEFAULT_DOCS, _ as formatDiagnostics, _t as PACKAGE_FIELD, b as BASELINE_FILE, bt as detectProject, c as silentLogger, et as FALLBACK_TOKENS, g as escapeData, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, i as componentFiles, j as updateBaseline, jt as toPosix, k as rulesRun, l as stderrLogger, o as loadDesignSystem, ot as excludedBy, q as CONFIG_FILES, rt as RULE_IDS, s as tokenFiles, tt as LEGACY_CONFIG_FILES, ut as rulesFor, v as NAME, w as findMoves, x as LEGACY_BASELINE_FILE, y as VERSION, yt as declaredConfig } from "./design-system-
|
|
3
|
-
import { a as notReadNotices, i as missingDependencies, o as explainProject, s as librariesLeftOut, t as serveStdio } from "./stdio-
|
|
4
|
-
import { r as loadTarget } from "./workspace-
|
|
2
|
+
import { $ as DEFAULT_TOKENS, A as unrecordedRules, At as relativePath, B as statusKinds, C as countBaseline, D as pruneBaseline, Dt as isInside, E as onLines, Et as SCRIPT_FILE, It as unique, J as ConfigError, L as toDesignMd, M as writeBaseline, O as readBaseline, Ot as matchesGlob, Pt as plural, R as toDtcg, Rt as readProjectConfig, S as applyBaseline, St as isDesignSystemName, T as goneFiles, Y as DEFAULT_DOCS, _ as formatDiagnostics, _t as PACKAGE_FIELD, b as BASELINE_FILE, bt as detectProject, c as silentLogger, et as FALLBACK_TOKENS, g as escapeData, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, i as componentFiles, j as updateBaseline, jt as toPosix, k as rulesRun, l as stderrLogger, o as loadDesignSystem, ot as excludedBy, q as CONFIG_FILES, rt as RULE_IDS, s as tokenFiles, tt as LEGACY_CONFIG_FILES, ut as rulesFor, v as NAME, w as findMoves, x as LEGACY_BASELINE_FILE, y as VERSION, yt as declaredConfig } from "./design-system-Csqts7Eq.js";
|
|
3
|
+
import { a as notReadNotices, i as missingDependencies, o as explainProject, s as librariesLeftOut, t as serveStdio } from "./stdio-0TpyqZaD.js";
|
|
4
|
+
import { r as loadTarget } from "./workspace-Dbxubo9n.js";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { escapePath, glob } from "tinyglobby";
|
|
@@ -8146,21 +8146,29 @@ function toDesignMd(tokens, options) {
|
|
|
8146
8146
|
const list = tokens.filter((t) => t.category === section.category);
|
|
8147
8147
|
if (!list.length) continue;
|
|
8148
8148
|
if (section.key) {
|
|
8149
|
-
const values =
|
|
8149
|
+
const values = /* @__PURE__ */ new Map();
|
|
8150
|
+
let steps;
|
|
8150
8151
|
for (const token of list) {
|
|
8152
|
+
if (isSpacingUnit(token)) {
|
|
8153
|
+
steps = spacingSteps(token, lookup);
|
|
8154
|
+
continue;
|
|
8155
|
+
}
|
|
8151
8156
|
const key = keys.get(token);
|
|
8152
8157
|
if (key === void 0) continue;
|
|
8153
8158
|
const target = token.aliasOf ? byName.get(token.aliasOf) : void 0;
|
|
8154
|
-
const targetKey = target?.category === token.category && frontMatterValue(target, section.key, lookup) !== void 0 ? keys.get(target) : void 0;
|
|
8159
|
+
const targetKey = section.key !== "typography" && target?.category === token.category && !isSpacingUnit(target) && frontMatterValue(target, section.key, lookup) !== void 0 ? keys.get(target) : void 0;
|
|
8155
8160
|
const value = targetKey === void 0 ? frontMatterValue(token, section.key, lookup) : `{${section.key}.${targetKey}}`;
|
|
8156
8161
|
if (value === void 0) continue;
|
|
8157
|
-
if (key
|
|
8162
|
+
if (values.has(key)) {
|
|
8158
8163
|
warnings.push(`${token.name} is left out of the front matter: another token has its key`);
|
|
8159
8164
|
continue;
|
|
8160
8165
|
}
|
|
8161
|
-
values
|
|
8166
|
+
values.set(key, value);
|
|
8162
8167
|
}
|
|
8163
|
-
if (
|
|
8168
|
+
for (const [key, value] of steps ?? []) if (!values.has(key)) values.set(key, value);
|
|
8169
|
+
const step = (key) => Number.isFinite(Number(key)) ? Number(key) : Infinity;
|
|
8170
|
+
const ordered = steps ? new Map([...values].sort(([a], [b]) => step(a) - step(b) || 0)) : values;
|
|
8171
|
+
if (ordered.size) sections[section.key] = ordered;
|
|
8164
8172
|
}
|
|
8165
8173
|
body.push("", `## ${section.heading}`, "", section.intro, "", ...table(list, keys));
|
|
8166
8174
|
}
|
|
@@ -8180,6 +8188,59 @@ function designKeys(tokens) {
|
|
|
8180
8188
|
}
|
|
8181
8189
|
return out;
|
|
8182
8190
|
}
|
|
8191
|
+
/** Tailwind's spacing unit, `--spacing`, which `p-4` takes 4 times. */
|
|
8192
|
+
function isSpacingUnit(token) {
|
|
8193
|
+
return token.category === "spacing" && token.tailwind === "";
|
|
8194
|
+
}
|
|
8195
|
+
/** The steps of Tailwind's default spacing scale, `p-0.5` to `p-96`, in spacing units. */
|
|
8196
|
+
const SPACING_STEPS = [
|
|
8197
|
+
.5,
|
|
8198
|
+
1,
|
|
8199
|
+
1.5,
|
|
8200
|
+
2,
|
|
8201
|
+
2.5,
|
|
8202
|
+
3,
|
|
8203
|
+
3.5,
|
|
8204
|
+
4,
|
|
8205
|
+
5,
|
|
8206
|
+
6,
|
|
8207
|
+
7,
|
|
8208
|
+
8,
|
|
8209
|
+
9,
|
|
8210
|
+
10,
|
|
8211
|
+
11,
|
|
8212
|
+
12,
|
|
8213
|
+
14,
|
|
8214
|
+
16,
|
|
8215
|
+
20,
|
|
8216
|
+
24,
|
|
8217
|
+
28,
|
|
8218
|
+
32,
|
|
8219
|
+
36,
|
|
8220
|
+
40,
|
|
8221
|
+
44,
|
|
8222
|
+
48,
|
|
8223
|
+
52,
|
|
8224
|
+
56,
|
|
8225
|
+
60,
|
|
8226
|
+
64,
|
|
8227
|
+
72,
|
|
8228
|
+
80,
|
|
8229
|
+
96
|
|
8230
|
+
];
|
|
8231
|
+
/**
|
|
8232
|
+
* Tailwind's spacing unit as the steps it makes: `"4": 1rem` for `p-4`. A DESIGN.md has no
|
|
8233
|
+
* multiplier: a reader takes each spacing entry as a step, so the unit alone (`base: 0.25rem`)
|
|
8234
|
+
* would make 4px the only one.
|
|
8235
|
+
*/
|
|
8236
|
+
function spacingSteps(token, lookup) {
|
|
8237
|
+
const plain = PLAIN_LENGTH.exec(token.value.trim());
|
|
8238
|
+
const [amount, unit] = plain ? [Number(plain[1]), plain[2] ?? "px"] : [evaluateLength(token.value, lookup), "px"];
|
|
8239
|
+
const steps = /* @__PURE__ */ new Map();
|
|
8240
|
+
if (amount === void 0 || !Number.isFinite(amount) || amount <= 0) return steps;
|
|
8241
|
+
for (const n of SPACING_STEPS) steps.set(String(n), `${round(n * amount)}${unit}`);
|
|
8242
|
+
return steps;
|
|
8243
|
+
}
|
|
8183
8244
|
function frontMatterValue(token, key, lookup) {
|
|
8184
8245
|
const value = token.value.trim();
|
|
8185
8246
|
switch (key) {
|
|
@@ -8343,21 +8404,25 @@ function beforeV4(range) {
|
|
|
8343
8404
|
/**
|
|
8344
8405
|
* DTCG files and the CSS generated from them often describe the same tokens.
|
|
8345
8406
|
* Merge entries that share a custom property: keep the DTCG name and
|
|
8346
|
-
* description, and add the Tailwind mapping from CSS.
|
|
8407
|
+
* description, and add the Tailwind mapping from CSS. A token without one (a
|
|
8408
|
+
* `@theme inline` value, `rounded-sm`) merges with the token of its name and
|
|
8409
|
+
* Tailwind key, such as the one a DTCG file exported from the stylesheet has.
|
|
8347
8410
|
*/
|
|
8348
8411
|
function mergeTokens(tokens) {
|
|
8349
8412
|
const overridden = new Set(tokens.filter((t) => t.origin !== "tailwind-default").map((t) => themeKey(t)));
|
|
8350
|
-
const
|
|
8413
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
8351
8414
|
const out = [];
|
|
8352
8415
|
for (const token of tokens) {
|
|
8353
8416
|
if (token.origin === "tailwind-default" && overridden.has(themeKey(token))) continue;
|
|
8354
|
-
const
|
|
8417
|
+
const key = token.cssVar ?? (token.tailwind === void 0 ? void 0 : `${token.name}:${token.tailwind}`);
|
|
8418
|
+
const existing = key === void 0 ? void 0 : byKey.get(key);
|
|
8355
8419
|
if (!existing) {
|
|
8356
|
-
if (
|
|
8420
|
+
if (key !== void 0) byKey.set(key, token);
|
|
8357
8421
|
out.push(token);
|
|
8358
8422
|
continue;
|
|
8359
8423
|
}
|
|
8360
8424
|
if (existing.tailwind === void 0 && token.tailwind !== void 0) existing.tailwind = token.tailwind;
|
|
8425
|
+
if (existing.category === "other" && token.category !== "other") existing.category = token.category;
|
|
8361
8426
|
existing.usage = [.../* @__PURE__ */ new Set([...token.usage, ...existing.usage])];
|
|
8362
8427
|
existing.description ??= token.description;
|
|
8363
8428
|
if (token.modes) existing.modes = {
|
|
@@ -12243,7 +12308,7 @@ function isObject(value) {
|
|
|
12243
12308
|
// package.json
|
|
12244
12309
|
var name = "onsystem";
|
|
12245
12310
|
// src/version.ts
|
|
12246
|
-
const VERSION = "0.
|
|
12311
|
+
const VERSION = "0.11.0";
|
|
12247
12312
|
const NAME = name;
|
|
12248
12313
|
// src/lint/sarif.ts
|
|
12249
12314
|
const SCHEMA = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
|
package/dist/eslint-worker.d.ts
CHANGED
|
@@ -31,4 +31,6 @@ export interface Call {
|
|
|
31
31
|
signal: Int32Array;
|
|
32
32
|
port: MessagePort;
|
|
33
33
|
}
|
|
34
|
+
/** Resolves when the looks under way have ended. Nothing waits on them but tests. */
|
|
35
|
+
export declare function looking(): Promise<unknown>;
|
|
34
36
|
export declare function load(request: LoadRequest): Promise<LoadResponse>;
|
package/dist/eslint-worker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { At as relativePath, i as componentFiles, ot as excludedBy, r as buildModel } from "./design-system-
|
|
2
|
-
import { r as loadTarget } from "./workspace-
|
|
1
|
+
import { At as relativePath, a as fingerprint, i as componentFiles, ot as excludedBy, r as buildModel } from "./design-system-Csqts7Eq.js";
|
|
2
|
+
import { r as loadTarget } from "./workspace-Dbxubo9n.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import fs from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { parentPort } from "node:worker_threads";
|
|
@@ -8,13 +9,25 @@ import { parentPort } from "node:worker_threads";
|
|
|
8
9
|
* The ESLint plugin's loader, in a worker thread. ESLint rules run synchronously, and finding a
|
|
9
10
|
* project's design system does not (config files written in code, globs, the extraction
|
|
10
11
|
* cache), so the plugin asks this worker and waits for the answer (see `bridge.ts`).
|
|
12
|
+
*
|
|
13
|
+
* The worker keeps what it loaded and answers from it at once. Once that is two seconds old, the
|
|
14
|
+
* next lint starts a look at the project again (its config, then its design system's files) in
|
|
15
|
+
* the background, and the lints after the look get what it found. Waiting for the look instead
|
|
16
|
+
* held up every lint after a pause in an editor for as long as the look took, which grows with
|
|
17
|
+
* the design system.
|
|
11
18
|
*/
|
|
12
19
|
/** What the configs said (a design-system package's skipped, or its paths ignored), each said once. */
|
|
13
20
|
const warned = /* @__PURE__ */ new Set();
|
|
14
|
-
/** How long a
|
|
21
|
+
/** How long what a look found is used before a lint starts the next look. */
|
|
15
22
|
const FRESH_MS = 2e3;
|
|
23
|
+
/** By where ESLint runs and its settings. */
|
|
16
24
|
const targets = /* @__PURE__ */ new Map();
|
|
25
|
+
/**
|
|
26
|
+
* By target and project root: two targets can reach the same project with configs of their own
|
|
27
|
+
* (runs from two folders, two ESLint configs), and neither may be answered with the other's.
|
|
28
|
+
*/
|
|
17
29
|
const projects = /* @__PURE__ */ new Map();
|
|
30
|
+
const looks = /* @__PURE__ */ new Set();
|
|
18
31
|
function realPath(file) {
|
|
19
32
|
try {
|
|
20
33
|
return fs.realpathSync.native(file);
|
|
@@ -22,46 +35,122 @@ function realPath(file) {
|
|
|
22
35
|
return file;
|
|
23
36
|
}
|
|
24
37
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cwd,
|
|
28
|
-
root,
|
|
29
|
-
config
|
|
30
|
-
]);
|
|
31
|
-
const cached = targets.get(key);
|
|
32
|
-
if (cached && Date.now() - cached.at < FRESH_MS) return cached.target;
|
|
33
|
-
const target = loadTarget({
|
|
38
|
+
function loadTargetFor({ cwd, root, config }) {
|
|
39
|
+
return loadTarget({
|
|
34
40
|
cwd,
|
|
35
41
|
root: root === void 0 ? void 0 : path.resolve(cwd, root),
|
|
36
42
|
config: config === void 0 ? void 0 : path.resolve(cwd, config)
|
|
37
43
|
});
|
|
38
|
-
|
|
44
|
+
}
|
|
45
|
+
function keep(target) {
|
|
46
|
+
target.catch(() => void 0);
|
|
47
|
+
return {
|
|
39
48
|
at: Date.now(),
|
|
40
49
|
target
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** The config a file is checked with: its own project's, from a monorepo root. */
|
|
53
|
+
async function configFor(target, file) {
|
|
54
|
+
return target.workspace ? (await target.workspace.projectFor(file)).config : target.config;
|
|
55
|
+
}
|
|
56
|
+
function versionOf(key, config) {
|
|
57
|
+
return createHash("sha256").update(key).update(JSON.stringify(config)).digest("hex");
|
|
58
|
+
}
|
|
59
|
+
async function build(config) {
|
|
60
|
+
const { model, key = String(Date.now()), dependencies = [] } = await buildModel(config, { cache: true });
|
|
61
|
+
const own = new Set((await componentFiles(config)).map(realPath));
|
|
62
|
+
return {
|
|
63
|
+
at: Date.now(),
|
|
64
|
+
key,
|
|
65
|
+
dependencies,
|
|
66
|
+
version: versionOf(key, config),
|
|
67
|
+
config,
|
|
68
|
+
model,
|
|
69
|
+
own
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `project` for `config` as the files are now: the same model when nothing it is built from has
|
|
74
|
+
* changed (a config edit that only changes rules, or no change at all), else a new build.
|
|
75
|
+
*/
|
|
76
|
+
async function rebuild(project, config) {
|
|
77
|
+
if (await fingerprint(config, project.dependencies) !== project.key) return build(config);
|
|
78
|
+
const { key, dependencies, model, own } = project;
|
|
79
|
+
return {
|
|
80
|
+
at: Date.now(),
|
|
81
|
+
key,
|
|
82
|
+
dependencies,
|
|
83
|
+
version: versionOf(key, config),
|
|
84
|
+
config,
|
|
85
|
+
model,
|
|
86
|
+
own
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Runs a look in the background, held in `looks` until it ends. It starts on a later turn of the
|
|
91
|
+
* event loop, after the lint that started it has its answer: the start of a look (finding the
|
|
92
|
+
* project, reading its config, listing the design system's files) runs synchronously, and run at
|
|
93
|
+
* once it held up that answer for as long.
|
|
94
|
+
*/
|
|
95
|
+
function background(look) {
|
|
96
|
+
const running = new Promise((resolve) => setImmediate(resolve)).then(look).catch(() => void 0).finally(() => looks.delete(running));
|
|
97
|
+
looks.add(running);
|
|
98
|
+
return running;
|
|
99
|
+
}
|
|
100
|
+
function projectKey(key, config) {
|
|
101
|
+
return JSON.stringify([key, config.root]);
|
|
102
|
+
}
|
|
103
|
+
/** A build that fails drops the project, so that the next lint builds it again and says why. */
|
|
104
|
+
function lookAtProject(slot, project, config) {
|
|
105
|
+
project.looking = background(async () => {
|
|
106
|
+
try {
|
|
107
|
+
projects.set(slot, await rebuild(project, config));
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (projects.get(slot) === project) projects.delete(slot);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return project.looking;
|
|
114
|
+
}
|
|
115
|
+
function lookAtTarget(key, kept, request) {
|
|
116
|
+
kept.looking = background(async () => {
|
|
117
|
+
const loading = loadTargetFor(request);
|
|
118
|
+
await loading.catch(() => void 0);
|
|
119
|
+
targets.set(key, keep(loading));
|
|
120
|
+
const config = await configFor(await loading, request.file);
|
|
121
|
+
const slot = projectKey(key, config);
|
|
122
|
+
const project = projects.get(slot);
|
|
123
|
+
if (project && !project.looking) await lookAtProject(slot, project, config);
|
|
41
124
|
});
|
|
42
|
-
return
|
|
125
|
+
return kept.looking;
|
|
126
|
+
}
|
|
127
|
+
/** Resolves when the looks under way have ended. Nothing waits on them but tests. */
|
|
128
|
+
function looking() {
|
|
129
|
+
return Promise.all(looks);
|
|
43
130
|
}
|
|
44
131
|
async function load(request) {
|
|
45
|
-
const
|
|
46
|
-
|
|
132
|
+
const key = JSON.stringify([
|
|
133
|
+
request.cwd,
|
|
134
|
+
request.root,
|
|
135
|
+
request.config
|
|
136
|
+
]);
|
|
137
|
+
let kept = targets.get(key);
|
|
138
|
+
if (!kept) {
|
|
139
|
+
kept = keep(loadTargetFor(request));
|
|
140
|
+
targets.set(key, kept);
|
|
141
|
+
} else if (!kept.looking && Date.now() - kept.at >= FRESH_MS) lookAtTarget(key, kept, request);
|
|
142
|
+
const config = await configFor(await kept.target, request.file);
|
|
47
143
|
for (const text of config.warnings ?? []) {
|
|
48
144
|
if (warned.has(text)) continue;
|
|
49
145
|
warned.add(text);
|
|
50
146
|
process.stderr.write(`onsystem: ${text}\n`);
|
|
51
147
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
project
|
|
57
|
-
|
|
58
|
-
version: key ?? String(Date.now()),
|
|
59
|
-
config,
|
|
60
|
-
model,
|
|
61
|
-
own
|
|
62
|
-
};
|
|
63
|
-
projects.set(config.root, project);
|
|
64
|
-
}
|
|
148
|
+
const slot = projectKey(key, config);
|
|
149
|
+
let project = projects.get(slot);
|
|
150
|
+
if (!project) {
|
|
151
|
+
project = await build(config);
|
|
152
|
+
projects.set(slot, project);
|
|
153
|
+
} else if (!kept.looking && !project.looking && (project.config !== config || Date.now() - project.at >= FRESH_MS)) lookAtProject(slot, project, config);
|
|
65
154
|
const relative = relativePath(config.root, request.file);
|
|
66
155
|
const excluded = excludedBy(config, relative, { includeTests: true });
|
|
67
156
|
if (excluded) return { skip: `excluded by "${excluded}"` };
|
|
@@ -89,4 +178,4 @@ parentPort?.on("message", ({ request, signal, port }) => {
|
|
|
89
178
|
reply({ error: error.message });
|
|
90
179
|
});
|
|
91
180
|
});
|
|
92
|
-
export { load };
|
|
181
|
+
export { load, looking };
|
package/dist/eslint.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as relativePath, N as RULES, Q as DEFAULT_TEST_FOLDERS, X as DEFAULT_SEVERITY, Z as DEFAULT_TEST_EXCLUDE, t as DesignSystem, v as NAME, y as VERSION } from "./design-system-
|
|
1
|
+
import { At as relativePath, N as RULES, Q as DEFAULT_TEST_FOLDERS, X as DEFAULT_SEVERITY, Z as DEFAULT_TEST_EXCLUDE, m as checkSource, rt as RULE_IDS, t as DesignSystem, ut as rulesFor, v as NAME, y as VERSION } from "./design-system-Csqts7Eq.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { MessageChannel, Worker, receiveMessageOnPort } from "node:worker_threads";
|
|
4
4
|
// src/eslint/bridge.ts
|
|
@@ -51,7 +51,8 @@ function loadSync(request) {
|
|
|
51
51
|
* working directory), with the project's config: its `rules` and `overrides` turn rules off
|
|
52
52
|
* per file, `// onsystem-disable-*` comments suppress findings, and the files `check .` leaves
|
|
53
53
|
* out (the config's `exclude`, the design system's own components) are not checked. ESLint's
|
|
54
|
-
* config sets each rule's severity.
|
|
54
|
+
* config sets each rule's severity, and can set a rule's options (`allow`) over the project's.
|
|
55
|
+
* `onsystem/suppression` reports a misspelled rule in a suppression comment, as check does.
|
|
55
56
|
*/
|
|
56
57
|
const RULES_PAGE = "https://onsystem.vercel.app/rules";
|
|
57
58
|
/** Design systems by project root, at the version the loader last reported. */
|
|
@@ -101,7 +102,25 @@ function run(cwd, file, text, settings) {
|
|
|
101
102
|
if (result.skipped) return { skipped: true };
|
|
102
103
|
return {
|
|
103
104
|
result,
|
|
104
|
-
fromSystem: (specifier) => ds.lint.isDesignSystemImport(specifier, relative)
|
|
105
|
+
fromSystem: (specifier) => ds.lint.isDesignSystemImport(specifier, relative),
|
|
106
|
+
checkWith(id, options) {
|
|
107
|
+
const own = rulesFor(ds.config, relative)[id];
|
|
108
|
+
const off = {
|
|
109
|
+
severity: "off",
|
|
110
|
+
options: {}
|
|
111
|
+
};
|
|
112
|
+
const rules = Object.fromEntries(RULE_IDS.map((rule) => [rule, rule === id ? {
|
|
113
|
+
...own,
|
|
114
|
+
options: {
|
|
115
|
+
...own.options,
|
|
116
|
+
...options
|
|
117
|
+
}
|
|
118
|
+
} : off]));
|
|
119
|
+
return checkSource(text, ds.lint, {
|
|
120
|
+
filename: relative,
|
|
121
|
+
rules
|
|
122
|
+
});
|
|
123
|
+
}
|
|
105
124
|
};
|
|
106
125
|
}
|
|
107
126
|
/**
|
|
@@ -153,27 +172,45 @@ function typeOnly(def) {
|
|
|
153
172
|
const kind = (node) => node?.importKind;
|
|
154
173
|
return def.type === "ImportBinding" && (kind(def.node) === "type" || kind(def.parent) === "type");
|
|
155
174
|
}
|
|
175
|
+
/** A rule's options in ESLint's config, as in onsystem.config.json: `['error', { allow: ['#fff'] }]`. */
|
|
176
|
+
const OPTIONS_SCHEMA = [{
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: { allow: {
|
|
179
|
+
type: "array",
|
|
180
|
+
items: { type: "string" }
|
|
181
|
+
} },
|
|
182
|
+
additionalProperties: false
|
|
183
|
+
}];
|
|
184
|
+
/**
|
|
185
|
+
* Check's misspelled rule names in `// onsystem-disable-*` comments, which suppress nothing. A rule
|
|
186
|
+
* of its own, so that it is a warning, as check reports it, and can be turned off like the others.
|
|
187
|
+
*/
|
|
188
|
+
const SUPPRESSION = "suppression";
|
|
156
189
|
function createRule(id, description) {
|
|
190
|
+
const suppression = id === SUPPRESSION;
|
|
157
191
|
return {
|
|
158
192
|
meta: {
|
|
159
193
|
type: "problem",
|
|
160
194
|
docs: {
|
|
161
195
|
description,
|
|
162
|
-
url: `${RULES_PAGE}#${id}`
|
|
196
|
+
url: `${RULES_PAGE}#${suppression ? "suppressing-findings" : id}`
|
|
197
|
+
},
|
|
198
|
+
...suppression ? {} : {
|
|
199
|
+
fixable: "code",
|
|
200
|
+
hasSuggestions: true
|
|
163
201
|
},
|
|
164
|
-
|
|
165
|
-
hasSuggestions: true,
|
|
166
|
-
schema: []
|
|
202
|
+
schema: suppression ? [] : OPTIONS_SCHEMA
|
|
167
203
|
},
|
|
168
204
|
create(context) {
|
|
169
205
|
const file = path.resolve(context.cwd, context.filename);
|
|
170
206
|
const settings = context.settings.onsystem ?? {};
|
|
207
|
+
const options = context.options?.[0];
|
|
171
208
|
return { "Program:exit"(node) {
|
|
172
209
|
const pass = passFor(context, file, settings);
|
|
173
210
|
const { outcome } = pass;
|
|
174
211
|
if ("skipped" in outcome) return;
|
|
175
212
|
if ("error" in outcome) {
|
|
176
|
-
if (pass.reported) return;
|
|
213
|
+
if (suppression || pass.reported) return;
|
|
177
214
|
pass.reported = true;
|
|
178
215
|
context.report({
|
|
179
216
|
node,
|
|
@@ -181,7 +218,8 @@ function createRule(id, description) {
|
|
|
181
218
|
});
|
|
182
219
|
return;
|
|
183
220
|
}
|
|
184
|
-
|
|
221
|
+
const { diagnostics } = !suppression && options && Object.keys(options).length ? outcome.checkWith(id, options) : outcome.result;
|
|
222
|
+
for (const d of diagnostics) {
|
|
185
223
|
if (d.ruleId !== id) continue;
|
|
186
224
|
const edits = d.fix ?? [];
|
|
187
225
|
const fix = (fixer) => edits.map((e) => fixer.replaceTextRange(e.range, e.text));
|
|
@@ -209,6 +247,7 @@ function createRule(id, description) {
|
|
|
209
247
|
};
|
|
210
248
|
}
|
|
211
249
|
const rules = Object.fromEntries(RULES.map((rule) => [rule.id, createRule(rule.id, rule.description)]));
|
|
250
|
+
rules[SUPPRESSION] = createRule(SUPPRESSION, "Suppression comments must name rules that exist.");
|
|
212
251
|
const plugin = {
|
|
213
252
|
meta: {
|
|
214
253
|
name: NAME,
|
|
@@ -218,14 +257,17 @@ const plugin = {
|
|
|
218
257
|
configs: {}
|
|
219
258
|
};
|
|
220
259
|
/**
|
|
221
|
-
* Every rule at its default severity, for `.tsx` and `.jsx` files
|
|
222
|
-
* which `check` also leaves out unless asked (`--include-tests`).
|
|
260
|
+
* Every rule at its default severity, and `suppression` as a warning, for `.tsx` and `.jsx` files
|
|
261
|
+
* other than tests and stories, which `check` also leaves out unless asked (`--include-tests`).
|
|
223
262
|
*/
|
|
224
263
|
plugin.configs.recommended = {
|
|
225
264
|
name: "onsystem/recommended",
|
|
226
265
|
files: ["**/*.{tsx,jsx}"],
|
|
227
266
|
ignores: [DEFAULT_TEST_EXCLUDE, DEFAULT_TEST_FOLDERS],
|
|
228
267
|
plugins: { onsystem: plugin },
|
|
229
|
-
rules:
|
|
268
|
+
rules: {
|
|
269
|
+
...Object.fromEntries(RULES.map((rule) => [`onsystem/${rule.id}`, DEFAULT_SEVERITY[rule.id]])),
|
|
270
|
+
[`onsystem/${SUPPRESSION}`]: "warn"
|
|
271
|
+
}
|
|
230
272
|
};
|
|
231
273
|
export { plugin as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -157,7 +157,9 @@ export declare function loadTokens(root: string, sources: TokenSource[]): Promis
|
|
|
157
157
|
/**
|
|
158
158
|
* DTCG files and the CSS generated from them often describe the same tokens.
|
|
159
159
|
* Merge entries that share a custom property: keep the DTCG name and
|
|
160
|
-
* description, and add the Tailwind mapping from CSS.
|
|
160
|
+
* description, and add the Tailwind mapping from CSS. A token without one (a
|
|
161
|
+
* `@theme inline` value, `rounded-sm`) merges with the token of its name and
|
|
162
|
+
* Tailwind key, such as the one a DTCG file exported from the stylesheet has.
|
|
161
163
|
*/
|
|
162
164
|
export declare function mergeTokens(tokens: Token[]): Token[];
|
|
163
165
|
interface ColorCandidate {
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { F as loadTokens, G as parseDoc, H as parseDesignMd, I as mergeTokens, K as extractComponents, L as toDesignMd, N as RULES, P as TokenIndex, R as toDtcg, U as parseCssTokens, V as parseDtcgTokens, W as attachDocs, _ as formatDiagnostics, a as fingerprint, at as defineConfig, ct as loadConfig, f as searchComponents, ft as findVariantDefinitions, it as configJsonSchema, m as checkSource, n as DesignSystemHost, o as loadDesignSystem, p as applyFixes, r as buildModel, rt as RULE_IDS, t as DesignSystem, y as VERSION } from "./design-system-
|
|
2
|
-
import { n as INSTRUCTIONS, r as createServer, t as serveStdio } from "./stdio-
|
|
1
|
+
import { F as loadTokens, G as parseDoc, H as parseDesignMd, I as mergeTokens, K as extractComponents, L as toDesignMd, N as RULES, P as TokenIndex, R as toDtcg, U as parseCssTokens, V as parseDtcgTokens, W as attachDocs, _ as formatDiagnostics, a as fingerprint, at as defineConfig, ct as loadConfig, f as searchComponents, ft as findVariantDefinitions, it as configJsonSchema, m as checkSource, n as DesignSystemHost, o as loadDesignSystem, p as applyFixes, r as buildModel, rt as RULE_IDS, t as DesignSystem, y as VERSION } from "./design-system-Csqts7Eq.js";
|
|
2
|
+
import { n as INSTRUCTIONS, r as createServer, t as serveStdio } from "./stdio-0TpyqZaD.js";
|
|
3
3
|
export { DesignSystem, DesignSystemHost, INSTRUCTIONS, RULES, RULE_IDS, TokenIndex, VERSION, applyFixes, attachDocs, buildModel, checkSource, configJsonSchema, createServer, defineConfig, extractComponents, findVariantDefinitions, fingerprint, formatDiagnostics, loadConfig, loadDesignSystem, loadTokens, mergeTokens, parseCssTokens, parseDesignMd, parseDoc, parseDtcgTokens, searchComponents, serveStdio, toDesignMd, toDtcg };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Et as SCRIPT_FILE, Ft as truncate, It as unique, J as ConfigError, Lt as defaultProjectConfig, Mt as closest, Nt as json, Pt as plural, X as DEFAULT_SEVERITY, bt as detectProject, ct as loadConfig, d as descriptionOf, dt as formatPx, f as searchComponents, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, kt as pathOf, mt as LIBRARY_IDS, n as DesignSystemHost, ot as excludedBy, pt as LIBRARIES, q as CONFIG_FILES, r as buildModel, rt as RULE_IDS, tt as LEGACY_CONFIG_FILES, u as buildSearchIndex, v as NAME, xt as findDesignSystemPackage, y as VERSION, z as TOKEN_CATEGORIES } from "./design-system-
|
|
2
|
-
import { i as projectDirOf, n as hasProjectMarker, r as loadTarget, t as findProjectRoot } from "./workspace-
|
|
1
|
+
import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Et as SCRIPT_FILE, Ft as truncate, It as unique, J as ConfigError, Lt as defaultProjectConfig, Mt as closest, Nt as json, Pt as plural, X as DEFAULT_SEVERITY, bt as detectProject, ct as loadConfig, d as descriptionOf, dt as formatPx, f as searchComponents, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, kt as pathOf, mt as LIBRARY_IDS, n as DesignSystemHost, ot as excludedBy, pt as LIBRARIES, q as CONFIG_FILES, r as buildModel, rt as RULE_IDS, tt as LEGACY_CONFIG_FILES, u as buildSearchIndex, v as NAME, xt as findDesignSystemPackage, y as VERSION, z as TOKEN_CATEGORIES } from "./design-system-Csqts7Eq.js";
|
|
2
|
+
import { i as projectDirOf, n as hasProjectMarker, r as loadTarget, t as findProjectRoot } from "./workspace-Dbxubo9n.js";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Tt as workspacePatterns, ct as loadConfig, i as componentFiles, lt as loadConfigWith, nt as PROJECT_MARKERS, st as hasOwnConfig, vt as PackageFinder, wt as workspacePackageDirs, xt as findDesignSystemPackage } from "./design-system-
|
|
1
|
+
import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Tt as workspacePatterns, ct as loadConfig, i as componentFiles, lt as loadConfigWith, nt as PROJECT_MARKERS, st as hasOwnConfig, vt as PackageFinder, wt as workspacePackageDirs, xt as findDesignSystemPackage } from "./design-system-Csqts7Eq.js";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onsystem",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"mcpName": "io.github.dgesteves/onsystem",
|
|
5
5
|
"description": "Keeps coding agents on your design system: catches palette colors, native elements and APIs your system lacks, at every edit and pull request, with the fix. Set it up once in your design-system package; every app in your monorepo gets it.",
|
|
6
6
|
"keywords": [
|