onsystem 0.6.0 → 0.7.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/README.md +28 -21
- package/dist/cli.js +238 -157
- package/dist/{config-lbfW91qB.d.ts → config-1XSTYI44.d.ts} +8 -0
- package/dist/{design-system-A3pRX3Ka.js → design-system-C4bhqLLw.js} +1582 -282
- package/dist/eslint-worker.d.ts +1 -1
- package/dist/eslint-worker.js +2 -2
- package/dist/eslint.js +66 -5
- package/dist/index.d.ts +20 -1
- package/dist/index.js +2 -2
- package/dist/{stdio-CEChNFLR.js → stdio-BojAQ6JP.js} +214 -5
- package/dist/{workspace-CHvUwgz9.js → workspace-Z0tZf_Dv.js} +1 -1
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { t as serveStdio } from "./stdio-
|
|
4
|
-
import { r as loadTarget } from "./workspace-
|
|
2
|
+
import { C as pruneBaseline, Ct as relativePath, D as updateBaseline, E as unrecordedRules, H as CONFIG_FILES, J as LEGACY_CONFIG_FILES, N as toDesignMd, O as writeBaseline, P as toDtcg, S as countBaseline, St as matchesGlob, T as rulesRun, U as ConfigError, W as DEFAULT_DOCS, Y as RULE_IDS, _ as NAME, b as LEGACY_BASELINE_FILE, bt as unique, c as silentLogger, ct as PACKAGE_FIELD, dt as detectProject, g as formatDiagnostics, h as escapeData, i as componentFiles, l as stderrLogger, m as uncheckedNotice, o as loadDesignSystem, ot as libraryTitle, pt as isDesignSystemName, s as tokenFiles, st as notReadYet, tt as rulesFor, ut as declaredConfig, v as VERSION, vt as plural, w as readBaseline, wt as toPosix, x as applyBaseline, xt as isInside, y as BASELINE_FILE } from "./design-system-C4bhqLLw.js";
|
|
3
|
+
import { a as notReadNotices, i as missingDependencies, o as explainProject, s as librariesLeftOut, t as serveStdio } from "./stdio-BojAQ6JP.js";
|
|
4
|
+
import { r as loadTarget } from "./workspace-Z0tZf_Dv.js";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { escapePath, glob } from "tinyglobby";
|
|
@@ -9,132 +9,6 @@ import fsp from "node:fs/promises";
|
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
10
|
import readline from "node:readline/promises";
|
|
11
11
|
import { parseArgs } from "node:util";
|
|
12
|
-
//#region src/explain.ts
|
|
13
|
-
/**
|
|
14
|
-
* What `inspect --explain` says about one project: every candidate zero config looked
|
|
15
|
-
* at and why each was taken or not, the config it resolved to, tokens whose value did
|
|
16
|
-
* not resolve, and components without props, with the likely reason.
|
|
17
|
-
*/
|
|
18
|
-
function explainProject(ds, cwd, indent = "") {
|
|
19
|
-
const { config, model } = ds;
|
|
20
|
-
const lines = [];
|
|
21
|
-
const out = (line = "") => lines.push(line ? `${indent}${line}` : "");
|
|
22
|
-
out("How the design system was found");
|
|
23
|
-
for (const step of packageSteps(config, cwd)) out(` ${(step.accepted ? "found" : "no").padEnd(6)} ${step.candidate}: ${step.reason}`);
|
|
24
|
-
if (config.configured) out(` components come from ${config.configFile ? display(cwd, config.configFile) : "--components"}, so zero config did not look`);
|
|
25
|
-
else {
|
|
26
|
-
const steps = [];
|
|
27
|
-
detectProject(config.root, config.tsconfig, steps, config.designSystemPackage);
|
|
28
|
-
for (const step of steps) out(` ${(step.accepted ? "found" : "no").padEnd(6)} ${step.candidate}: ${step.reason}`);
|
|
29
|
-
if (!config.detected) out(` So the defaults apply: ${config.components.join(", ")}${model.stats.files.components ? "" : ", which match nothing"}.`);
|
|
30
|
-
}
|
|
31
|
-
const named = (library) => `${library.package}${library.version ? ` ${library.version}` : ""}`;
|
|
32
|
-
for (const library of config.libraries ?? []) {
|
|
33
|
-
const count = model.components.filter((c) => c.library === library.package).length;
|
|
34
|
-
out(library.dir ? ` ${"found".padEnd(6)} ${named(library)}: a dependency, ${count} components from its type declarations in ${display(cwd, library.dir)}` : ` ${"no".padEnd(6)} ${named(library)}: a dependency, but not installed`);
|
|
35
|
-
}
|
|
36
|
-
for (const library of config.librariesOff ?? []) out(` ${"no".padEnd(6)} ${named(library)}: a dependency, ${librariesLeftOut(config, cwd)}`);
|
|
37
|
-
out();
|
|
38
|
-
out("Resolved config");
|
|
39
|
-
const field = (name, value) => out(` ${name.padEnd(12)} ${value}`);
|
|
40
|
-
field("root", config.root);
|
|
41
|
-
field("config", config.configFile ? `${display(cwd, config.configFile)}${config.designSystemPackage ? ` (declared by ${config.designSystemPackage.name})` : ""}` : "(none)");
|
|
42
|
-
field("components", listOf$1(config.components) || "(none)");
|
|
43
|
-
field("exclude", listOf$1(config.exclude));
|
|
44
|
-
field("tokens", listOf$1(config.tokens.map((t) => `${t.path}${t.prefix ? ` (prefix ${t.prefix})` : ""}${t.fallback ? " (when nothing else is found)" : ""}`)));
|
|
45
|
-
field("docs", listOf$1(config.docs));
|
|
46
|
-
field("tsconfig", config.tsconfig ?? (fs.existsSync(path.join(config.root, "tsconfig.json")) ? "tsconfig.json" : "(none)"));
|
|
47
|
-
field("importPath", importPaths(ds));
|
|
48
|
-
field("libraries", config.libraryIds === void 0 ? `every library onsystem reads (${LIBRARY_IDS.join(", ")})` : config.libraryIds !== false && config.libraryIds.length ? config.libraryIds.join(", ") : `none ("libraries": ${JSON.stringify(config.libraryIds)})`);
|
|
49
|
-
if (config.tailwindConfig) field("tailwind", display(config.root, config.tailwindConfig));
|
|
50
|
-
if (config.designSystems) field("primary", config.designSystems.map((d, i) => `${i ? "then " : ""}${d.source} (${d.imports} imports)`).join("; "));
|
|
51
|
-
const changed = RULE_IDS.filter((id) => config.rules[id].severity !== DEFAULT_SEVERITY[id]);
|
|
52
|
-
field("rules", `${RULE_IDS.map((id) => `${id} ${config.rules[id].severity}`).join(", ")}${changed.length ? ` (changed: ${changed.join(", ")})` : ""}`);
|
|
53
|
-
if (config.overrides?.length) for (const override of config.overrides) field("override", `${override.files.join(", ")}: ${Object.entries(override.rules).map(([id, rule]) => `${id} ${rule.severity}`).join(", ")}`);
|
|
54
|
-
if (config.base) field("relative to", `${config.base} (a config at the workspace root)`);
|
|
55
|
-
out();
|
|
56
|
-
out("Tokens");
|
|
57
|
-
const found = ds.tokens;
|
|
58
|
-
const files = model.stats.files.tokens;
|
|
59
|
-
const themed = found.filter((t) => t.origin === "library");
|
|
60
|
-
out(` ${found.length - themed.length} tokens from ${files} ${files === 1 ? "file" : "files"}${files ? "" : `: no file matches ${config.tokens.map((t) => t.path).join(", ")}`}`);
|
|
61
|
-
if (themed.length) {
|
|
62
|
-
const sources = [...new Set(themed.map((t) => t.source.file))];
|
|
63
|
-
out(` ${themed.length} tokens from a library's theme in ${sources.join(", ")}`);
|
|
64
|
-
}
|
|
65
|
-
const defined = new Set(found.map((t) => t.cssVar).filter((v) => Boolean(v)));
|
|
66
|
-
const missing = (value) => [...value.matchAll(/var\(\s*(--[\w-]+)/g)].map((m) => m[1] ?? "").filter((v) => !defined.has(v));
|
|
67
|
-
const unresolved = found.filter((t) => missing(t.value).length);
|
|
68
|
-
if (unresolved.length) out(` ${unresolved.length} unresolved, reading a custom property no token file defines: ${unresolved.slice(0, 8).map((t) => `${t.cssVar ?? t.name}: ${t.value}`).join(", ")}${unresolved.length > 8 ? `, and ${unresolved.length - 8} more` : ""}`);
|
|
69
|
-
else if (found.length) out(" every value resolves");
|
|
70
|
-
for (const warning of model.warnings.filter((w) => /alias|token/i.test(w))) out(` ${warning}`);
|
|
71
|
-
out();
|
|
72
|
-
out("Props");
|
|
73
|
-
const empty = ds.roots().filter((c) => c.openProps && !c.props.length && !c.inherits.length);
|
|
74
|
-
const open = ds.components.filter((c) => c.openProps);
|
|
75
|
-
if (!open.length) out(` every component's props resolved (${ds.components.length} components)`);
|
|
76
|
-
else {
|
|
77
|
-
const installed = nodeModulesFor(config.root);
|
|
78
|
-
const names = (list) => `${list.slice(0, 6).map((c) => c.name).join(", ")}${list.length > 6 ? ` and ${list.length - 6} more` : ""}`;
|
|
79
|
-
if (empty.length) out(` ${empty.length} ${empty.length === 1 ? "component has" : "components have"} 0 props: ${names(empty)}.`);
|
|
80
|
-
out(` ${open.length} ${open.length === 1 ? "component has" : "components have"} props that did not fully resolve${empty.length === open.length ? "" : `: ${names(open)}`}, so no-unknown-prop skips ${open.length === 1 ? "it" : "them"}.`);
|
|
81
|
-
out(installed ? ` Dependencies are installed (${display(cwd, installed)}): a package without types, a type error, or a props type that depends on a type argument leaves them open.` : ` The dependencies are not installed: no node_modules in ${config.root} or a folder above it. Their props come from the packages they wrap (Radix, React Aria), so install them (npm install, pnpm install) and run again.`);
|
|
82
|
-
}
|
|
83
|
-
return lines;
|
|
84
|
-
}
|
|
85
|
-
/** Why a library the project depends on is not read: `left out by "libraries" in onsystem.config.json`. */
|
|
86
|
-
function librariesLeftOut(config, cwd) {
|
|
87
|
-
return `left out by "libraries" in ${config.configFile ? display(cwd, config.configFile) : "the config"}`;
|
|
88
|
-
}
|
|
89
|
-
/** The first few, then how many more: a package's exports can name hundreds of files. */
|
|
90
|
-
function listOf$1(items, max = 8) {
|
|
91
|
-
const shown = items.slice(0, max).join(", ");
|
|
92
|
-
return items.length > max ? `${shown}, and ${items.length - max} more` : shown;
|
|
93
|
-
}
|
|
94
|
-
/** How components are suggested to be imported. */
|
|
95
|
-
function importPaths(ds) {
|
|
96
|
-
if (ds.config.importPath) return `${ds.config.importPath} (from the config)`;
|
|
97
|
-
const counts = /* @__PURE__ */ new Map();
|
|
98
|
-
for (const c of ds.roots()) counts.set(c.importPath, (counts.get(c.importPath) ?? 0) + 1);
|
|
99
|
-
const top = [...counts].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
100
|
-
const from = ds.config.imports?.length ? "package exports" : "tsconfig paths or relative paths";
|
|
101
|
-
return top.length ? `inferred from ${from}: ${top.map(([p, n]) => `${p} (${n})`).join(", ")}${counts.size > 3 ? `, and ${counts.size - 3} more` : ""}` : `inferred from ${from}`;
|
|
102
|
-
}
|
|
103
|
-
/** The nearest node_modules at or above `dir`, if any. */
|
|
104
|
-
function nodeModulesFor(dir) {
|
|
105
|
-
for (let current = path.resolve(dir);; current = path.dirname(current)) {
|
|
106
|
-
const candidate = path.join(current, "node_modules");
|
|
107
|
-
if (fs.existsSync(candidate)) return candidate;
|
|
108
|
-
if (current === path.dirname(current)) return void 0;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function display(cwd, file) {
|
|
112
|
-
const rel = relativePath(cwd, file);
|
|
113
|
-
return rel && !rel.startsWith("..") ? rel : file;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Whether a dependency's declared config was taken on, for a project without a config of its
|
|
117
|
-
* own: the package it came from and the others that declare one, or that none does.
|
|
118
|
-
*/
|
|
119
|
-
function packageSteps(config, cwd) {
|
|
120
|
-
const declared = config.designSystemPackage;
|
|
121
|
-
if (config.configFile && !declared) return [];
|
|
122
|
-
const lookup = findDesignSystemPackage(config.root);
|
|
123
|
-
if (!lookup) return [];
|
|
124
|
-
const candidate = "a design-system package's config";
|
|
125
|
-
const skipped = lookup.skipped.length ? `; ${lookup.skipped.map((p) => p.name).join(", ")} ${lookup.skipped.length === 1 ? "declares one" : "declare one"} that cannot be read, skipped since installed` : "";
|
|
126
|
-
if (!declared) return [{
|
|
127
|
-
candidate,
|
|
128
|
-
accepted: false,
|
|
129
|
-
reason: `no dependency declares one ("onsystem" in its package.json) among ${lookup.dependencies} ${lookup.dependencies === 1 ? "dependency" : "dependencies"}${skipped}`
|
|
130
|
-
}];
|
|
131
|
-
return [{
|
|
132
|
-
candidate,
|
|
133
|
-
accepted: true,
|
|
134
|
-
reason: `${declared.name} (${declared.installed ? "installed" : "a workspace package"}) declares ${display(cwd, declared.config)}, which this project takes on, with its paths from ${display(cwd, declared.dir)}${lookup.others.length ? `; ${lookup.others.join(", ")} also declare one, not read: one config is taken on, from the package the code imports most` : ""}${skipped}`
|
|
135
|
-
}];
|
|
136
|
-
}
|
|
137
|
-
//#endregion
|
|
138
12
|
//#region src/init.ts
|
|
139
13
|
const SCHEMA = "https://unpkg.com/onsystem/schema.json";
|
|
140
14
|
/** More detected globs than this are files, not folders: zero config keeps them. */
|
|
@@ -174,6 +48,7 @@ async function init(configOptions, options, io, runCheck) {
|
|
|
174
48
|
const notes = [];
|
|
175
49
|
const relative = (abs) => relativePath(root, abs);
|
|
176
50
|
const inherited = options.force ? void 0 : config.designSystemPackage;
|
|
51
|
+
const replaced = options.force ? config.designSystemPackage : void 0;
|
|
177
52
|
if (inherited) notes.push(`This project takes on the config ${inherited.name} declares in its package.json (${relativePath(io.cwd, inherited.config) || inherited.config}), so it needs none of its own; nothing was written. Pass --force to write one, which is read instead.`);
|
|
178
53
|
else if (configOptions.components?.length) {
|
|
179
54
|
file.components = configOptions.components;
|
|
@@ -188,19 +63,22 @@ async function init(configOptions, options, io, runCheck) {
|
|
|
188
63
|
else if (config.components.length > MAX_GLOBS) notes.push(`"components" is left to zero config: it found ${config.components.length} files through the package's exports, which it keeps following as the package changes.`);
|
|
189
64
|
else {
|
|
190
65
|
file.components = config.components;
|
|
191
|
-
|
|
66
|
+
const [specifier] = specifiers;
|
|
67
|
+
if (specifier && !specifier.includes("*")) file.importPath = specifier;
|
|
192
68
|
const docs = config.docs.filter((d) => !DEFAULT_DOCS.includes(d));
|
|
193
69
|
if (docs.length) file.docs = [...DEFAULT_DOCS, ...docs];
|
|
194
70
|
}
|
|
195
71
|
if (tokens.length) file.tokens = tokens;
|
|
196
72
|
} else if ((await componentFiles(config)).length) {
|
|
73
|
+
const declared = replaced && config.configured;
|
|
197
74
|
const matching = [];
|
|
198
75
|
for (const glob of config.components) if ((await componentFiles({
|
|
199
76
|
...config,
|
|
200
77
|
components: [glob]
|
|
201
78
|
})).length) matching.push(glob);
|
|
202
|
-
notes.push(`Found the design system in the default folders: ${matching.join(", ")}.`);
|
|
79
|
+
notes.push(declared ? `Found the design system through the config ${replaced.name} declares: ${matching.join(", ")}.` : `Found the design system in the default folders: ${matching.join(", ")}.`);
|
|
203
80
|
file.components = matching;
|
|
81
|
+
if (declared && config.importPath) file.importPath = config.importPath;
|
|
204
82
|
const tokens = (await tokenFiles(config)).map(relative);
|
|
205
83
|
if (tokens.length) file.tokens = tokens;
|
|
206
84
|
} else {
|
|
@@ -238,10 +116,18 @@ async function init(configOptions, options, io, runCheck) {
|
|
|
238
116
|
fs.writeFileSync(out, `${JSON.stringify(file, null, 2)}\n`);
|
|
239
117
|
written.push(CONFIG_FILE);
|
|
240
118
|
}
|
|
119
|
+
if (replaced) {
|
|
120
|
+
const own = (await loadTarget({
|
|
121
|
+
...configOptions,
|
|
122
|
+
config: void 0
|
|
123
|
+
})).config;
|
|
124
|
+
notes.push(await replacedNote(replaced, config, own, io.cwd));
|
|
125
|
+
}
|
|
126
|
+
let registry;
|
|
241
127
|
if (designSystem && manifest && name) {
|
|
242
128
|
written.push(...declarePackage(root, manifest, name, options.force, notes));
|
|
243
|
-
|
|
244
|
-
if (
|
|
129
|
+
registry = await unshipped(config, manifest.json);
|
|
130
|
+
if (registry) notes.push(...registry.notes);
|
|
245
131
|
}
|
|
246
132
|
for (const note of notes) io.stdout(note);
|
|
247
133
|
if (written.length) io.stdout(`Wrote ${written.map((f) => relativePath(io.cwd, path.join(root, f)) || f).join(", ")}.`);
|
|
@@ -293,7 +179,7 @@ async function init(configOptions, options, io, runCheck) {
|
|
|
293
179
|
}
|
|
294
180
|
const commit = [...written, ...baselineWritten ? [BASELINE_FILE] : []];
|
|
295
181
|
const files = commit.length > 1 ? `${commit.slice(0, -1).join(", ")} and ${commit.at(-1) ?? ""}` : commit[0];
|
|
296
|
-
io.stdout(designSystem && name ? `Next: commit ${files}. An app that depends on ${name} now finds it with no config of its own: run \`npx ${NAME} inspect\` in one to see it, and give its team ${GUIDE_FILE}.` : `Next: ${files ? `commit ${files}, and ` : ""}run \`npx ${NAME} check . --format github --require-design-system\` in CI.`);
|
|
182
|
+
io.stdout(designSystem && name ? registry ? `Next: commit ${files}. A workspace app that depends on ${name} now finds it with no config of its own: run \`npx ${NAME} inspect\` in one to see it, and give its team ${GUIDE_FILE}. An app that installs ${name} from a registry ${registry.next}.` : `Next: commit ${files}. An app that depends on ${name} now finds it with no config of its own: run \`npx ${NAME} inspect\` in one to see it, and give its team ${GUIDE_FILE}.` : `Next: ${files ? `commit ${files}, and ` : ""}run \`npx ${NAME} check . --format github --require-design-system\` in CI.`);
|
|
297
183
|
return 0;
|
|
298
184
|
}
|
|
299
185
|
const CONFIG_FILE = "onsystem.config.json";
|
|
@@ -346,16 +232,82 @@ function declarePackage(root, manifest, name, force, notes) {
|
|
|
346
232
|
}
|
|
347
233
|
/**
|
|
348
234
|
* What apps that install the package from a registry will not find: the component and token
|
|
349
|
-
* files its `files` list leaves out of the published package
|
|
350
|
-
*
|
|
235
|
+
* files its `files` list leaves out of the published package, and those outside the package
|
|
236
|
+
* (`../tailwind-config/theme.css`), which an installed package's config cannot reach. Undefined
|
|
237
|
+
* for a private package, or when they find every file. Without a `files` list, npm publishes
|
|
238
|
+
* everything in the package.
|
|
351
239
|
*/
|
|
352
240
|
async function unshipped(config, json) {
|
|
353
|
-
if (json.private === true
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
241
|
+
if (json.private === true) return void 0;
|
|
242
|
+
const name = typeof json.name === "string" ? json.name : "the package";
|
|
243
|
+
const files = Array.isArray(json.files) ? json.files : void 0;
|
|
244
|
+
const relative = (abs) => abs.map((f) => relativePath(config.root, f));
|
|
245
|
+
const components = relative(await componentFiles(config));
|
|
246
|
+
const tokens = relative(await tokenFiles(config));
|
|
247
|
+
const outside = (f) => f.startsWith("../");
|
|
248
|
+
const left = (list) => files ? list.filter((f) => !outside(f) && !ships(files, f)) : [];
|
|
249
|
+
const missing = {
|
|
250
|
+
components: left(components),
|
|
251
|
+
tokens: left(tokens)
|
|
252
|
+
};
|
|
253
|
+
const away = {
|
|
254
|
+
components: components.filter(outside),
|
|
255
|
+
tokens: tokens.filter(outside)
|
|
256
|
+
};
|
|
257
|
+
const noComponents = components.length > 0 && missing.components.length + away.components.length === components.length;
|
|
258
|
+
const lost = (lists, one) => {
|
|
259
|
+
const c = lists.components.length;
|
|
260
|
+
const t = lists.tokens.length;
|
|
261
|
+
const kinds = andList([...c ? [count$1(c, "component file")] : [], ...t ? [count$1(t, "token file")] : []]);
|
|
262
|
+
const what = andList([...c ? ["components"] : [], ...t ? ["tokens"] : []], "or");
|
|
263
|
+
const checks = c && noComponents ? `: they get no component, prop, variant or native-element checks, and \`${NAME} check --require-design-system\` fails in them` : "";
|
|
264
|
+
return `(${kinds}). Those apps do not know the ${what} in ${one ? "it" : "them"}${checks}.`;
|
|
265
|
+
};
|
|
266
|
+
const notes = [];
|
|
267
|
+
const top = (f) => f.includes("/") ? `${f.slice(0, f.indexOf("/"))}/` : f;
|
|
268
|
+
const folders = [...new Set([...missing.components, ...missing.tokens].map(top))];
|
|
269
|
+
if (folders.length) {
|
|
270
|
+
const one = folders.length === 1;
|
|
271
|
+
notes.push(`Apps that install ${name} from a registry get only what "files" in package.json publishes, which leaves out ${andList(folders)} ${lost(missing, one)} Add ${one ? "it" : "them"} to "files" for them; workspace apps read the sources either way.`);
|
|
272
|
+
}
|
|
273
|
+
const beyond = [...away.components, ...away.tokens];
|
|
274
|
+
if (beyond.length) {
|
|
275
|
+
const one = beyond.length === 1;
|
|
276
|
+
notes.push(`Apps that install ${name} from a registry read only the files in it, so they skip ${andList(beyond)}, outside the package ${lost(away, one)} Workspace apps read ${one ? "it" : "them"}. To give installed apps what ${one ? "it holds" : "they hold"}, keep a copy in the package, publish it, and name the copy in the config instead.`);
|
|
277
|
+
}
|
|
278
|
+
if (!notes.length) return void 0;
|
|
279
|
+
return {
|
|
280
|
+
notes,
|
|
281
|
+
next: noComponents && missing.components.length ? `gets no component checks until "files" publishes ${andList([...new Set(missing.components.map(top))])}` : `does not read ${andList([...folders, ...beyond])}`
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Said when --force writes a config in an app that took on a design-system package's: the new
|
|
286
|
+
* one is read instead, and what it does not take from the package. `taken` is the config the
|
|
287
|
+
* app took on, `own` the one it reads now.
|
|
288
|
+
*/
|
|
289
|
+
async function replacedNote(pkg, taken, own, cwd) {
|
|
290
|
+
const lacks = async (list) => {
|
|
291
|
+
const kept = new Set((await list(own)).map((f) => path.resolve(f)));
|
|
292
|
+
return (await list(taken)).some((f) => !kept.has(path.resolve(f)));
|
|
293
|
+
};
|
|
294
|
+
const components = await lacks(componentFiles);
|
|
295
|
+
const importPath = taken.importPath !== void 0 && own.importPath === void 0;
|
|
296
|
+
const tokens = await lacks(tokenFiles);
|
|
297
|
+
const declared = relativePath(cwd, pkg.config) || pkg.config;
|
|
298
|
+
const replaced = `This project took on the config ${pkg.name} declares (${declared}); with --force it has one of its own, which is read instead, so ${pkg.name}'s rules and later changes to its config no longer reach it.`;
|
|
299
|
+
if (!components && !importPath && !tokens) return replaced;
|
|
300
|
+
const effects = andList([
|
|
301
|
+
...components ? [`does not know ${pkg.name}'s components`] : [],
|
|
302
|
+
...importPath && !components ? [`does not match imports from ${pkg.name} to its components`] : [],
|
|
303
|
+
...tokens ? [`does not read ${pkg.name}'s tokens`] : []
|
|
304
|
+
]);
|
|
305
|
+
const fields = [
|
|
306
|
+
...components ? ["\"components\""] : [],
|
|
307
|
+
...importPath || components && taken.importPath !== void 0 ? ["\"importPath\""] : [],
|
|
308
|
+
...tokens ? ["\"tokens\""] : []
|
|
309
|
+
];
|
|
310
|
+
return `${replaced} It ${effects}: ${(!components || taken.configured === true) && (!tokens || taken.tokensDetected !== true) ? `copy ${andList(fields)} from ${declared} into ${CONFIG_FILE}, or ` : ""}delete ${CONFIG_FILE} to take on ${pkg.name}'s config again.`;
|
|
359
311
|
}
|
|
360
312
|
/** Whether npm's `files` list publishes `file`, a path relative to the package. */
|
|
361
313
|
function ships(files, file) {
|
|
@@ -413,6 +365,10 @@ function consumerGuide(name) {
|
|
|
413
365
|
function count$1(n, word) {
|
|
414
366
|
return `${n.toLocaleString("en-US")} ${n === 1 ? word : `${word}s`}`;
|
|
415
367
|
}
|
|
368
|
+
/** `a`, `a and b`, `a, b and c` (or `or`). */
|
|
369
|
+
function andList(items, conjunction = "and") {
|
|
370
|
+
return items.length > 1 ? `${items.slice(0, -1).join(", ")} ${conjunction} ${items.at(-1) ?? ""}` : items.join("");
|
|
371
|
+
}
|
|
416
372
|
//#endregion
|
|
417
373
|
//#region src/cli.ts
|
|
418
374
|
const DOCS = "https://onsystem.vercel.app/docs";
|
|
@@ -453,6 +409,8 @@ Options
|
|
|
453
409
|
--out <file> export: write to this file instead of stdout
|
|
454
410
|
--max-warnings <n> check: exit 1 when there are more than n warnings
|
|
455
411
|
--update-baseline check: record the current findings as accepted, and exit 0
|
|
412
|
+
--prune-baseline check: drop baseline entries that no longer occur, and
|
|
413
|
+
accept nothing new
|
|
456
414
|
--baseline <file> check: baseline file (default: ${BASELINE_FILE} in the root,
|
|
457
415
|
used whenever it exists)
|
|
458
416
|
--ignore-baseline check: report every finding, baseline or not
|
|
@@ -544,6 +502,10 @@ async function main(argv, io = defaultIo) {
|
|
|
544
502
|
type: "boolean",
|
|
545
503
|
default: false
|
|
546
504
|
},
|
|
505
|
+
"prune-baseline": {
|
|
506
|
+
type: "boolean",
|
|
507
|
+
default: false
|
|
508
|
+
},
|
|
547
509
|
"ignore-baseline": {
|
|
548
510
|
type: "boolean",
|
|
549
511
|
default: false
|
|
@@ -666,6 +628,10 @@ async function check(patterns, configOptions, values, io) {
|
|
|
666
628
|
io.stderr("check: pass --quiet-without-design-system or --require-design-system, not both");
|
|
667
629
|
return 2;
|
|
668
630
|
}
|
|
631
|
+
for (const other of ["update-baseline", "ignore-baseline"]) if (values["prune-baseline"] && values[other]) {
|
|
632
|
+
io.stderr(`check: pass --prune-baseline or --${other}, not both`);
|
|
633
|
+
return 2;
|
|
634
|
+
}
|
|
669
635
|
if (!patterns.length) {
|
|
670
636
|
io.stderr(`check: pass files, folders or globs, e.g. ${NAME} check .`);
|
|
671
637
|
return 2;
|
|
@@ -699,6 +665,7 @@ async function check(patterns, configOptions, values, io) {
|
|
|
699
665
|
return 0;
|
|
700
666
|
}
|
|
701
667
|
const update = values["update-baseline"];
|
|
668
|
+
const prune = values["prune-baseline"];
|
|
702
669
|
const baselineFile = values.baseline ? path.resolve(io.cwd, values.baseline) : defaultBaseline(selection.root, values["ignore-baseline"] ? void 0 : (text) => {
|
|
703
670
|
warnDeprecated([text], format, io);
|
|
704
671
|
});
|
|
@@ -707,6 +674,10 @@ async function check(patterns, configOptions, values, io) {
|
|
|
707
674
|
io.stderr(`check: baseline not found: ${displayPath(io.cwd, baselineFile)}`);
|
|
708
675
|
return 2;
|
|
709
676
|
}
|
|
677
|
+
if (prune && !baseline) {
|
|
678
|
+
io.stderr(`check --prune-baseline: no baseline to prune at ${displayPath(io.cwd, baselineFile)}. Pass --baseline <file> to prune another one, or record the current findings with \`${checkCommand(patterns, configOptions, values, "--update-baseline")}\`.`);
|
|
679
|
+
return 2;
|
|
680
|
+
}
|
|
710
681
|
const disabledFor = (file) => {
|
|
711
682
|
const ds = selection.designSystemOf(file);
|
|
712
683
|
return new Set(Object.entries(rulesFor(ds.config, relativePath(ds.root, file))).filter(([, rule]) => rule.severity === "off").map(([id]) => id));
|
|
@@ -716,6 +687,7 @@ async function check(patterns, configOptions, values, io) {
|
|
|
716
687
|
const baselineKey = (file) => relativePath(realRoot, fs.realpathSync.native(file)).normalize("NFC");
|
|
717
688
|
const results = [];
|
|
718
689
|
const checked = /* @__PURE__ */ new Map();
|
|
690
|
+
const ran = /* @__PURE__ */ new Set();
|
|
719
691
|
let baselined = 0;
|
|
720
692
|
let fixed = 0;
|
|
721
693
|
for (const file of files) {
|
|
@@ -725,9 +697,12 @@ async function check(patterns, configOptions, values, io) {
|
|
|
725
697
|
const key = baselineKey(file);
|
|
726
698
|
const disabled = disabledFor(file);
|
|
727
699
|
disabledByKey.set(key, disabled);
|
|
728
|
-
if (update) checked.set(key, result.diagnostics);
|
|
700
|
+
if (update || prune) checked.set(key, result.diagnostics);
|
|
701
|
+
if (update) {
|
|
702
|
+
for (const rule of RULE_IDS) if (!disabled.has(rule) && canRun(ds, rule)) ran.add(rule);
|
|
703
|
+
}
|
|
729
704
|
if (baseline && !update) {
|
|
730
|
-
const match = applyBaseline(baseline, key, result, disabled);
|
|
705
|
+
const match = applyBaseline(baseline.entries, key, result, disabled);
|
|
731
706
|
result = {
|
|
732
707
|
...match.result,
|
|
733
708
|
baselined: match.baselined
|
|
@@ -744,22 +719,51 @@ async function check(patterns, configOptions, values, io) {
|
|
|
744
719
|
if (elsewhere) notes.push(`${plural(elsewhere, "image or email file")} not checked`);
|
|
745
720
|
const unchecked = uncheckedNotice(results);
|
|
746
721
|
if (update) {
|
|
747
|
-
const next = updateBaseline(baseline, checked, realRoot, (key) => disabledByKey.get(key) ?? /* @__PURE__ */ new Set());
|
|
748
|
-
|
|
722
|
+
const next = updateBaseline(baseline?.entries, checked, realRoot, (key) => disabledByKey.get(key) ?? /* @__PURE__ */ new Set());
|
|
723
|
+
const wholeRoot = patterns.some((pattern) => {
|
|
724
|
+
const dir = path.resolve(io.cwd, pattern);
|
|
725
|
+
return Boolean(fs.statSync(dir, { throwIfNoEntry: false })?.isDirectory() && isInside(realPath(dir), realRoot));
|
|
726
|
+
});
|
|
727
|
+
writeBaseline(baselineFile, next, {
|
|
728
|
+
onsystem: VERSION,
|
|
729
|
+
rules: rulesRun(baseline, next, checked, ran, wholeRoot)
|
|
730
|
+
});
|
|
749
731
|
const counts = countBaseline(next);
|
|
750
|
-
|
|
732
|
+
const summary = `Baseline: ${count(counts.findings, "finding")} in ${count(counts.files, "file")} → ${displayPath(io.cwd, baselineFile)}`;
|
|
733
|
+
if (format === "pretty") io.stdout(summary);
|
|
734
|
+
else io.stderr(summary);
|
|
751
735
|
for (const text of selection.notices) io.stderr(text);
|
|
752
736
|
if (unchecked) io.stderr(unchecked);
|
|
753
737
|
return 0;
|
|
754
738
|
}
|
|
739
|
+
const command = (flag) => checkCommand(patterns, configOptions, values, flag);
|
|
740
|
+
const baselineNotes = [];
|
|
741
|
+
if (baseline) {
|
|
742
|
+
const fresh = results.flatMap((r) => r.diagnostics);
|
|
743
|
+
const unrecorded = unrecordedRules(baseline, fresh);
|
|
744
|
+
if (unrecorded.size) baselineNotes.push(unrecordedNote(unrecorded, fresh.length, baseline.onsystem, command("--update-baseline")));
|
|
745
|
+
}
|
|
746
|
+
if (prune && baseline) {
|
|
747
|
+
const next = pruneBaseline(baseline.entries, checked, realRoot, (key) => disabledByKey.get(key) ?? /* @__PURE__ */ new Set());
|
|
748
|
+
const left = countBaseline(next);
|
|
749
|
+
const dropped = countBaseline(baseline.entries).findings - left.findings;
|
|
750
|
+
if (dropped) writeBaseline(baselineFile, next, {
|
|
751
|
+
onsystem: baseline.onsystem,
|
|
752
|
+
rules: baseline.rules
|
|
753
|
+
});
|
|
754
|
+
baselineNotes.push(`Baseline: ${dropped ? `dropped ${count(dropped, "finding")} that no longer ${dropped === 1 ? "occurs" : "occur"}` : "nothing to drop"}; ${count(left.findings, "finding")} in ${count(left.files, "file")} → ${displayPath(io.cwd, baselineFile)}`);
|
|
755
|
+
}
|
|
755
756
|
const output = formatDiagnostics(format === "json" ? results : results.filter((r) => !r.skipped), format, {
|
|
756
757
|
color: io.color,
|
|
757
758
|
uri: format === "sarif" || format === "github" ? repositoryPath(io.cwd, io.env ?? process.env) : void 0,
|
|
758
759
|
baselined: baseline ? baselined : void 0,
|
|
759
760
|
notes,
|
|
760
|
-
fixedHint: fixed ? `${count(fixed, "baseline finding")} no longer ${fixed === 1 ? "occurs" : "occur"}: run
|
|
761
|
+
fixedHint: fixed && !prune ? `${count(fixed, "baseline finding")} no longer ${fixed === 1 ? "occurs" : "occur"}: run \`${command("--prune-baseline")}\` to drop ${fixed === 1 ? "it" : "them"}.` : void 0
|
|
761
762
|
});
|
|
762
763
|
if (output) io.stdout(output);
|
|
764
|
+
for (const text of baselineNotes) if (format === "pretty") io.stdout(text);
|
|
765
|
+
else if (format === "github") io.stdout(`::notice title=${NAME} baseline::${escapeData(text)}`);
|
|
766
|
+
else io.stderr(text);
|
|
763
767
|
for (const text of [...selection.notices, unchecked]) {
|
|
764
768
|
if (!text) continue;
|
|
765
769
|
if (format === "pretty") io.stdout(text);
|
|
@@ -851,7 +855,7 @@ async function selectInProject(config, patterns, values, format, io) {
|
|
|
851
855
|
excluded: leftOut.filter((file) => !TEST_FILE.test(file)).length,
|
|
852
856
|
gitignored: ignored.size
|
|
853
857
|
};
|
|
854
|
-
let files =
|
|
858
|
+
let files = oncePerFile([...named, ...kept.filter((file) => !ignored.has(file))]).sort();
|
|
855
859
|
if (!files.length && !all.length) {
|
|
856
860
|
io.stderr(`check: no files match ${patterns.join(" ")}`);
|
|
857
861
|
return 2;
|
|
@@ -863,6 +867,7 @@ async function selectInProject(config, patterns, values, format, io) {
|
|
|
863
867
|
const notice = ds.notice();
|
|
864
868
|
if (notice && values["require-design-system"] && !ds.hasDesignSystem()) {
|
|
865
869
|
io.stderr(`check --require-design-system: ${notice}`);
|
|
870
|
+
for (const text of notReadNotices([{ ds }])) io.stderr(text);
|
|
866
871
|
return 2;
|
|
867
872
|
}
|
|
868
873
|
let skipped = 0;
|
|
@@ -877,7 +882,7 @@ async function selectInProject(config, patterns, values, format, io) {
|
|
|
877
882
|
designSystemOf: () => ds,
|
|
878
883
|
root: ds.root,
|
|
879
884
|
notes: scopeNotes(skipped, left),
|
|
880
|
-
notices: notice ? [notice] : [],
|
|
885
|
+
notices: [...notice ? [notice] : [], ...notReadNotices([{ ds }])],
|
|
881
886
|
skipped,
|
|
882
887
|
testsLeftOut: left.tests
|
|
883
888
|
};
|
|
@@ -927,7 +932,7 @@ async function selectInWorkspace(workspace, patterns, values, format, io) {
|
|
|
927
932
|
excluded: leftOut.filter((file) => !TEST_FILE.test(file)).length,
|
|
928
933
|
gitignored: ignored.size
|
|
929
934
|
};
|
|
930
|
-
let files =
|
|
935
|
+
let files = oncePerFile([...named, ...kept.filter((file) => !ignored.has(file))]).sort();
|
|
931
936
|
if (!files.length && !all.length) {
|
|
932
937
|
io.stderr(`check: no files match ${patterns.join(" ")}`);
|
|
933
938
|
return 2;
|
|
@@ -979,6 +984,14 @@ async function selectInWorkspace(workspace, patterns, values, format, io) {
|
|
|
979
984
|
gaps.set(notice, gap);
|
|
980
985
|
}
|
|
981
986
|
const notices = [...gaps.values()].map(({ ds, dirs }) => ds.notice(listOf(dirs)) ?? "");
|
|
987
|
+
const checkedProjects = unique(files.map(project)).flatMap((p) => {
|
|
988
|
+
const ds = loaded.get(p.root);
|
|
989
|
+
return ds ? [{
|
|
990
|
+
ds,
|
|
991
|
+
dir: p.dir
|
|
992
|
+
}] : [];
|
|
993
|
+
});
|
|
994
|
+
notices.push(...notReadNotices(checkedProjects));
|
|
982
995
|
const used = unique(files.map((file) => project(file).root)).length;
|
|
983
996
|
return {
|
|
984
997
|
files,
|
|
@@ -1041,6 +1054,8 @@ async function inspect(configOptions, values, io) {
|
|
|
1041
1054
|
}
|
|
1042
1055
|
if (values.explain) lines.push("", ...explainProject(ds, io.cwd));
|
|
1043
1056
|
io.stdout(lines.join("\n"));
|
|
1057
|
+
const missing = values.explain ? void 0 : missingDependencies([{ ds }]);
|
|
1058
|
+
if (missing) io.stderr(missing);
|
|
1044
1059
|
return 0;
|
|
1045
1060
|
}
|
|
1046
1061
|
/**
|
|
@@ -1048,12 +1063,16 @@ async function inspect(configOptions, values, io) {
|
|
|
1048
1063
|
* project depends on, including those the config's `libraries` leaves out.
|
|
1049
1064
|
*/
|
|
1050
1065
|
function describeLibraries(config, model, cwd) {
|
|
1051
|
-
const name = (library) => `${
|
|
1052
|
-
return [
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1066
|
+
const name = (library) => `${libraryTitle(library.package)} (${library.package}${library.version ? ` ${library.version}` : ""})`;
|
|
1067
|
+
return [
|
|
1068
|
+
...(config.libraries ?? []).map((library) => {
|
|
1069
|
+
if (!library.dir) return `${name(library)}: a dependency, but not installed`;
|
|
1070
|
+
const count = model.components.filter((c) => c.library === library.package).length;
|
|
1071
|
+
return `${name(library)}: ${plural(count, "component")} from its type declarations`;
|
|
1072
|
+
}),
|
|
1073
|
+
...(config.librariesOff ?? []).map((library) => `${name(library)}: ${librariesLeftOut(config, cwd)}`),
|
|
1074
|
+
...(config.librariesUnread ?? []).map((library) => `${name(library)}: ${notReadYet()}`)
|
|
1075
|
+
];
|
|
1057
1076
|
}
|
|
1058
1077
|
/**
|
|
1059
1078
|
* `export --format dtcg|design-md`: the project's tokens as a DTCG 2025.10 file or a
|
|
@@ -1149,6 +1168,11 @@ async function inspectWorkspace(workspace, values, io) {
|
|
|
1149
1168
|
lines.push("", `Each file is checked by its project's design system. For one project's components, tokens and warnings: ${NAME} inspect --root <folder>`);
|
|
1150
1169
|
if (values.explain) lines.push("", ...explainWorkspace(workspace, loaded, io.cwd));
|
|
1151
1170
|
io.stdout(lines.join("\n"));
|
|
1171
|
+
const missing = values.explain ? void 0 : missingDependencies(loaded.map(({ project, ds }) => ({
|
|
1172
|
+
ds,
|
|
1173
|
+
dir: project.dir
|
|
1174
|
+
})));
|
|
1175
|
+
if (missing) io.stderr(missing);
|
|
1152
1176
|
return 0;
|
|
1153
1177
|
}
|
|
1154
1178
|
/** Why the root is read as a monorepo, then each project's explanation. */
|
|
@@ -1259,6 +1283,50 @@ function gitIgnored(cwd, files) {
|
|
|
1259
1283
|
function count(n, word) {
|
|
1260
1284
|
return `${n.toLocaleString("en-US")} ${n === 1 ? word : `${word}s`}`;
|
|
1261
1285
|
}
|
|
1286
|
+
/**
|
|
1287
|
+
* `check` as this run was given, with `flag`: the patterns and the options that decide what it
|
|
1288
|
+
* finds and which baseline it reads (output options left out).
|
|
1289
|
+
*/
|
|
1290
|
+
function checkCommand(patterns, configOptions, values, flag) {
|
|
1291
|
+
const args = [...patterns];
|
|
1292
|
+
const option = (name, value) => {
|
|
1293
|
+
if (value !== void 0) args.push(`--${name}`, value);
|
|
1294
|
+
};
|
|
1295
|
+
option("root", configOptions.root);
|
|
1296
|
+
option("config", configOptions.config);
|
|
1297
|
+
for (const name of [
|
|
1298
|
+
"components",
|
|
1299
|
+
"tokens",
|
|
1300
|
+
"docs"
|
|
1301
|
+
]) for (const value of configOptions[name] ?? []) option(name, value);
|
|
1302
|
+
option("baseline", values.baseline);
|
|
1303
|
+
if (configOptions.includeDesignSystem !== void 0) args.push(`--${configOptions.includeDesignSystem ? "" : "no-"}include-design-system`);
|
|
1304
|
+
if (values["include-tests"]) args.push("--include-tests");
|
|
1305
|
+
return `check ${args.map(shellQuote).join(" ")} ${flag}`;
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Whether a rule has something to compare against in a design system, as `notice()` says when
|
|
1309
|
+
* it has not: no-hardcoded-color needs color tokens, the spacing and radius rules tokens of
|
|
1310
|
+
* their kind, and the component rules components. Keep it in step with `DesignSystem.notice()`
|
|
1311
|
+
* in src/design-system.ts, or the baseline records a rule as run when it was skipped, or the
|
|
1312
|
+
* other way round.
|
|
1313
|
+
*/
|
|
1314
|
+
function canRun(ds, rule) {
|
|
1315
|
+
switch (rule) {
|
|
1316
|
+
case "no-hardcoded-color": return ds.tokenIndex.has("color");
|
|
1317
|
+
case "no-hardcoded-spacing": return ds.tokenIndex.has("spacing");
|
|
1318
|
+
case "no-hardcoded-radius": return ds.tokenIndex.has("radius");
|
|
1319
|
+
case "icon-button-accessible-name": return true;
|
|
1320
|
+
default: return ds.model.components.length > 0;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
/** Why findings of rules that did not run when the baseline was recorded are new to it. */
|
|
1324
|
+
function unrecordedNote(byRule, total, version, update) {
|
|
1325
|
+
const rules = [...byRule.keys()].sort();
|
|
1326
|
+
const n = [...byRule.values()].reduce((sum, k) => sum + k, 0);
|
|
1327
|
+
const others = total - n;
|
|
1328
|
+
return `${listOf(rules, rules.length)} did not run when the baseline was written${version ? ` (onsystem ${version})` : ""}, so ${rules.length === 1 ? "its" : "their"} ${count(n, "finding")} ${n === 1 ? "is" : "are"} new to the baseline, not necessarily to the code. To accept ${n === 1 ? "it" : "them"}, run \`${update}\`${others ? `, which also accepts the ${count(others, "other new finding")}` : ""}.`;
|
|
1329
|
+
}
|
|
1262
1330
|
/** Quotes a pattern for the hint when the shell would expand or split it. */
|
|
1263
1331
|
function shellQuote(pattern) {
|
|
1264
1332
|
return /^[\w./@-]+$/.test(pattern) ? pattern : `"${pattern.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
@@ -1271,6 +1339,19 @@ function realPath(file) {
|
|
|
1271
1339
|
return file;
|
|
1272
1340
|
}
|
|
1273
1341
|
}
|
|
1342
|
+
/**
|
|
1343
|
+
* One path per file, links resolved: a folder that links back to its parent reaches the same
|
|
1344
|
+
* files again. The shortest path stays.
|
|
1345
|
+
*/
|
|
1346
|
+
function oncePerFile(files) {
|
|
1347
|
+
const kept = /* @__PURE__ */ new Map();
|
|
1348
|
+
for (const file of files) {
|
|
1349
|
+
const real = realPath(file);
|
|
1350
|
+
const previous = kept.get(real);
|
|
1351
|
+
if (previous === void 0 || file.length < previous.length) kept.set(real, file);
|
|
1352
|
+
}
|
|
1353
|
+
return [...kept.values()];
|
|
1354
|
+
}
|
|
1274
1355
|
/** Relative to cwd when the file is below it, absolute otherwise. */
|
|
1275
1356
|
function displayPath(cwd, file) {
|
|
1276
1357
|
const rel = path.relative(cwd, file);
|
|
@@ -391,6 +391,12 @@ interface ResolvedConfig {
|
|
|
391
391
|
*/
|
|
392
392
|
designSystemExclude?: string[] | undefined;
|
|
393
393
|
tokens: TokenSourceConfig[];
|
|
394
|
+
/**
|
|
395
|
+
* `tokens` comes from zero config (detection or the default paths), not the config or
|
|
396
|
+
* `--tokens`: the stylesheets those files import are read too, and so are the ones in a
|
|
397
|
+
* shared Tailwind preset's workspace package that set the colors the config maps.
|
|
398
|
+
*/
|
|
399
|
+
tokensDetected?: boolean | undefined;
|
|
394
400
|
docs: string[];
|
|
395
401
|
tsconfig?: string | undefined;
|
|
396
402
|
importPath?: string | undefined;
|
|
@@ -418,6 +424,8 @@ interface ResolvedConfig {
|
|
|
418
424
|
libraryIds?: false | LibraryId[] | undefined;
|
|
419
425
|
/** Libraries the project depends on that `libraries` leaves out, so they are not read. */
|
|
420
426
|
librariesOff?: LibrarySource[] | undefined;
|
|
427
|
+
/** Component libraries the project depends on that onsystem does not read yet (Chakra UI). */
|
|
428
|
+
librariesUnread?: LibrarySource[] | undefined;
|
|
421
429
|
elements: Record<string, string>;
|
|
422
430
|
/** `check` lints the component files themselves, which it skips by default. */
|
|
423
431
|
includeDesignSystem: boolean;
|