onsystem 0.10.1 → 0.10.3
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 +23 -13
- package/dist/{config-BbTW1jKU.d.ts → config-JaNAiR9Z.d.ts} +6 -8
- package/dist/{design-system-CPH4C7-1.js → design-system-7wZoJsMm.js} +240 -121
- package/dist/eslint-worker.d.ts +2 -4
- package/dist/eslint-worker.js +3 -4
- package/dist/eslint.d.ts +0 -2
- package/dist/eslint.js +3 -5
- package/dist/index.d.ts +40 -55
- package/dist/index.js +2 -2
- package/dist/{stdio-BK2kMN1x.js → stdio-B31hp2UZ.js} +182 -57
- package/dist/{workspace-BINqxi7p.js → workspace-C5pMS6Xv.js} +10 -19
- package/package.json +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
1
3
|
import fs from "node:fs";
|
|
2
4
|
import path from "node:path";
|
|
3
5
|
import { pathToFileURL } from "node:url";
|
|
@@ -7,11 +9,10 @@ import ts from "typescript";
|
|
|
7
9
|
import os from "node:os";
|
|
8
10
|
import postcss from "postcss";
|
|
9
11
|
import { clampChroma, colorsNamed, converter, differenceEuclidean, formatHex, parse, wcagContrast } from "culori";
|
|
10
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
11
12
|
import fsp from "node:fs/promises";
|
|
12
13
|
import { LineCounter, isMap, isScalar, parseDocument, stringify } from "yaml";
|
|
13
14
|
import { stemmer } from "stemmer";
|
|
14
|
-
|
|
15
|
+
// src/extract/program.ts
|
|
15
16
|
const DEFAULT_OPTIONS = {
|
|
16
17
|
target: ts.ScriptTarget.ES2022,
|
|
17
18
|
module: ts.ModuleKind.ESNext,
|
|
@@ -121,8 +122,7 @@ function projectFiles(program) {
|
|
|
121
122
|
function isProjectFile(file) {
|
|
122
123
|
return !/[\\/]node_modules[\\/]/.test(file);
|
|
123
124
|
}
|
|
124
|
-
|
|
125
|
-
//#region src/util/strings.ts
|
|
125
|
+
// src/util/strings.ts
|
|
126
126
|
/** `alert-dialog` / `alert_dialog` / `alertDialog` → `AlertDialog`. */
|
|
127
127
|
function pascalCase(input) {
|
|
128
128
|
return input.replace(/\.[^.]+$/, "").split(/[^a-zA-Z0-9]+|(?<=[a-z0-9])(?=[A-Z])/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
|
|
@@ -189,8 +189,7 @@ function plural$1(count, word, pluralWord = `${word}s`) {
|
|
|
189
189
|
function unique(items) {
|
|
190
190
|
return [...new Set(items)];
|
|
191
191
|
}
|
|
192
|
-
|
|
193
|
-
//#region src/util/paths.ts
|
|
192
|
+
// src/util/paths.ts
|
|
194
193
|
function toPosix(p) {
|
|
195
194
|
return p.split(path.sep).join("/");
|
|
196
195
|
}
|
|
@@ -347,8 +346,7 @@ function escapeRegExp(text) {
|
|
|
347
346
|
function pathOf(file) {
|
|
348
347
|
return /^[\p{L}\p{N}_@$.+~/\\:()[\]{}#%,=!-]+$/u.test(file) ? file : json(file);
|
|
349
348
|
}
|
|
350
|
-
|
|
351
|
-
//#region src/package-source.ts
|
|
349
|
+
// src/package-source.ts
|
|
352
350
|
/**
|
|
353
351
|
* A package's sources behind its build output: `exports` that point at `dist/` mapped
|
|
354
352
|
* back to `src/`, and barrels (`index.ts` files that re-export) followed to the files
|
|
@@ -389,7 +387,7 @@ function moduleFile(base, extensions = MODULE_EXTENSIONS, within) {
|
|
|
389
387
|
base,
|
|
390
388
|
...extensions.map((ext) => `${base}${ext}`),
|
|
391
389
|
...extensions.map((ext) => path.join(base, `index${ext}`))
|
|
392
|
-
].find((file) => isFile$
|
|
390
|
+
].find((file) => isFile$2(file) && !/(?:^|[\\/])node_modules[\\/]/.test(within ? path.relative(within, file) : file));
|
|
393
391
|
}
|
|
394
392
|
const BUILD_CONFIGS = [
|
|
395
393
|
"tsup",
|
|
@@ -582,15 +580,14 @@ function reexports(source) {
|
|
|
582
580
|
barrel
|
|
583
581
|
};
|
|
584
582
|
}
|
|
585
|
-
function isFile$
|
|
583
|
+
function isFile$2(file) {
|
|
586
584
|
try {
|
|
587
585
|
return fs.statSync(file).isFile();
|
|
588
586
|
} catch {
|
|
589
587
|
return false;
|
|
590
588
|
}
|
|
591
589
|
}
|
|
592
|
-
|
|
593
|
-
//#region src/tokens/imports.ts
|
|
590
|
+
// src/tokens/imports.ts
|
|
594
591
|
/** Enough stylesheets for a theme split over files, without reading a whole design system. */
|
|
595
592
|
const MAX_IMPORTED = 50;
|
|
596
593
|
/**
|
|
@@ -642,8 +639,7 @@ function importedStylesheets(files, resolve) {
|
|
|
642
639
|
for (const file of files) visit(file);
|
|
643
640
|
return out;
|
|
644
641
|
}
|
|
645
|
-
|
|
646
|
-
//#region src/detect.ts
|
|
642
|
+
// src/detect.ts
|
|
647
643
|
const ignore = () => void 0;
|
|
648
644
|
const COMPONENT_FILE = /\.[jt]sx$/;
|
|
649
645
|
/** A module a barrel can lead to: TypeScript or JavaScript, not a declaration file. */
|
|
@@ -989,7 +985,7 @@ function fromComponentsJson(root, dir, json, project, packages, note = ignore) {
|
|
|
989
985
|
*/
|
|
990
986
|
function withAppStylesheets(root, named) {
|
|
991
987
|
if (named.every((file) => isInside(root, file))) return named;
|
|
992
|
-
const own = APP_STYLESHEETS.map((f) => path.join(root, f)).filter(isFile);
|
|
988
|
+
const own = APP_STYLESHEETS.map((f) => path.join(root, f)).filter(isFile$1);
|
|
993
989
|
if (!own.length) return named;
|
|
994
990
|
const imported = new Set(importedStylesheets(own, stylesheetResolver(root)));
|
|
995
991
|
return [...own, ...named.filter((file) => !imported.has(file))];
|
|
@@ -1149,7 +1145,7 @@ function fromPackage(root, dir, pkg, note = ignore, installed = false) {
|
|
|
1149
1145
|
for (const mapping of mappings) {
|
|
1150
1146
|
const file = path.resolve(root, mapping.target);
|
|
1151
1147
|
if (installed && isCompiledExport(dir, file)) continue;
|
|
1152
|
-
if (mapping.target.includes("*") || !SCRIPT_FILE$1.test(file) || !isFile(file)) {
|
|
1148
|
+
if (mapping.target.includes("*") || !SCRIPT_FILE$1.test(file) || !isFile$1(file)) {
|
|
1153
1149
|
add(mapping, COMPONENT_FILE.test(mapping.target));
|
|
1154
1150
|
continue;
|
|
1155
1151
|
}
|
|
@@ -1464,7 +1460,7 @@ function stylesheetResolver(root, { importPath } = {}) {
|
|
|
1464
1460
|
}
|
|
1465
1461
|
return designSystems.get(name);
|
|
1466
1462
|
};
|
|
1467
|
-
const stylesheet = (file, within) => [file, `${file}.css`].find((f) => f.endsWith(".css") && (within === void 0 || isInside(within, f) && !isBuildOutput(within, f)) && !/(?:^|[\\/])node_modules[\\/]/.test(within === void 0 ? f : path.relative(within, f)) && isFile(f));
|
|
1463
|
+
const stylesheet = (file, within) => [file, `${file}.css`].find((f) => f.endsWith(".css") && (within === void 0 || isInside(within, f) && !isBuildOutput(within, f)) && !/(?:^|[\\/])node_modules[\\/]/.test(within === void 0 ? f : path.relative(within, f)) && isFile$1(f));
|
|
1468
1464
|
return (specifier, fromFile) => {
|
|
1469
1465
|
if (/^(?:[a-z][\w+.-]*:|\/)/i.test(specifier)) return void 0;
|
|
1470
1466
|
const relative = stylesheet(path.resolve(path.dirname(fromFile), specifier), installedPackageOf(fromFile));
|
|
@@ -1495,7 +1491,7 @@ function installedStylesheet(dir, subpath) {
|
|
|
1495
1491
|
if (!match) return void 0;
|
|
1496
1492
|
const target = exportTargets(match.value, APP_CONDITIONS).map((t) => t.replaceAll("*", match.middle)).find((t) => t.endsWith(".css"));
|
|
1497
1493
|
const file = target === void 0 ? void 0 : path.resolve(dir, target);
|
|
1498
|
-
return file && isInside(dir, file) && !isBuildOutput(dir, file) && isFile(file) ? file : null;
|
|
1494
|
+
return file && isInside(dir, file) && !isBuildOutput(dir, file) && isFile$1(file) ? file : null;
|
|
1499
1495
|
}
|
|
1500
1496
|
/**
|
|
1501
1497
|
* The folder of `name` installed from a registry, when zero config reads it as a design system:
|
|
@@ -1525,12 +1521,15 @@ function installedPackageOf(file) {
|
|
|
1525
1521
|
function exportedFile(dir, subpath, accept) {
|
|
1526
1522
|
for (const { middle, value } of exportMatches(dir, subpath)) for (const target of exportTargets(value)) {
|
|
1527
1523
|
const file = path.resolve(dir, target.replaceAll("*", middle));
|
|
1528
|
-
if (isInside(dir, file) && accept(file) && isFile(file)) return file;
|
|
1524
|
+
if (isInside(dir, file) && accept(file) && isFile$1(file)) return file;
|
|
1529
1525
|
}
|
|
1530
1526
|
}
|
|
1531
1527
|
/**
|
|
1532
1528
|
* The entries of package `dir`'s `exports` that map `subpath`, with what their `*` stands for:
|
|
1533
|
-
* an exact subpath first, then the
|
|
1529
|
+
* an exact subpath first, then the patterns, as Node.js orders them (`PATTERN_KEY_COMPARE`): the
|
|
1530
|
+
* longest prefix before the `*` first, then the longest key, so `./components/*.js` comes before
|
|
1531
|
+
* `./components/*` for `./components/tip.js`. A pattern has one `*`, which stands for at least
|
|
1532
|
+
* one character.
|
|
1534
1533
|
*/
|
|
1535
1534
|
function exportMatches(dir, subpath) {
|
|
1536
1535
|
const exports = readJson$1(path.join(dir, "package.json"))?.exports;
|
|
@@ -1542,22 +1541,35 @@ function exportMatches(dir, subpath) {
|
|
|
1542
1541
|
if (key === subpath) matches.push({
|
|
1543
1542
|
middle: "",
|
|
1544
1543
|
value,
|
|
1545
|
-
rank: Infinity
|
|
1544
|
+
rank: Infinity,
|
|
1545
|
+
length: key.length
|
|
1546
1546
|
});
|
|
1547
1547
|
continue;
|
|
1548
1548
|
}
|
|
1549
|
+
if (key.lastIndexOf("*") !== star) continue;
|
|
1549
1550
|
const prefix = key.slice(0, star);
|
|
1550
1551
|
const suffix = key.slice(star + 1);
|
|
1551
|
-
if (subpath.startsWith(prefix) && subpath.endsWith(suffix) && subpath.length
|
|
1552
|
+
if (subpath.startsWith(prefix) && subpath.endsWith(suffix) && subpath.length > prefix.length + suffix.length) {
|
|
1552
1553
|
const middle = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
1553
1554
|
matches.push({
|
|
1554
1555
|
middle,
|
|
1555
1556
|
value,
|
|
1556
|
-
rank: prefix.length
|
|
1557
|
+
rank: prefix.length,
|
|
1558
|
+
length: key.length
|
|
1557
1559
|
});
|
|
1558
1560
|
}
|
|
1559
1561
|
}
|
|
1560
|
-
return matches.sort((a, b) => b.rank - a.rank);
|
|
1562
|
+
return matches.sort((a, b) => b.rank - a.rank || b.length - a.length);
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* The targets package `dir`'s `exports` map `subpath` (`./components/tooltip`) to, as written:
|
|
1566
|
+
* through the entry Node.js picks (its exact subpath, else the most specific pattern) and every
|
|
1567
|
+
* condition but `types`, with `*` filled in. Undefined when no entry maps the subpath; none when
|
|
1568
|
+
* the entry blocks it (`null`).
|
|
1569
|
+
*/
|
|
1570
|
+
function exportedTargets(dir, subpath) {
|
|
1571
|
+
const [match] = exportMatches(dir, subpath);
|
|
1572
|
+
return match && exportTargets(match.value).map((t) => t.replaceAll("*", match.middle));
|
|
1561
1573
|
}
|
|
1562
1574
|
/** Every target of an export condition map, in order, but its types, or those of `conditions` alone. */
|
|
1563
1575
|
function exportTargets(value, conditions) {
|
|
@@ -1607,7 +1619,7 @@ function declaredConfig(dir, pkg = readJson$1(path.join(dir, "package.json"))) {
|
|
|
1607
1619
|
if (typeof value !== "string" || !value.endsWith(".json")) return { problem: `${field} is ${JSON.stringify(value)}: it names a JSON config file in the package, such as "./onsystem.config.json".` };
|
|
1608
1620
|
const file = path.resolve(dir, value);
|
|
1609
1621
|
if (!isInside(dir, file) || file === path.resolve(dir)) return { problem: `${field} names ${JSON.stringify(value)}, which is outside the package.` };
|
|
1610
|
-
if (!isFile(file)) return { problem: `${field} names ${JSON.stringify(value)}, which does not exist.` };
|
|
1622
|
+
if (!isFile$1(file)) return { problem: `${field} names ${JSON.stringify(value)}, which does not exist.` };
|
|
1611
1623
|
return { file };
|
|
1612
1624
|
}
|
|
1613
1625
|
/**
|
|
@@ -1726,7 +1738,7 @@ function scopeOf$1(name) {
|
|
|
1726
1738
|
function declaresTypes(dir, pkg) {
|
|
1727
1739
|
if (typeof pkg.types === "string" || typeof pkg.typings === "string") return true;
|
|
1728
1740
|
const hasTypes = (value) => Array.isArray(value) ? value.some(hasTypes) : value !== null && typeof value === "object" && Object.entries(value).some(([key, nested]) => key === "types" || hasTypes(nested));
|
|
1729
|
-
return hasTypes(pkg.exports) || isFile(path.join(dir, "index.d.ts"));
|
|
1741
|
+
return hasTypes(pkg.exports) || isFile$1(path.join(dir, "index.d.ts"));
|
|
1730
1742
|
}
|
|
1731
1743
|
/** Files that hold no component to read: declarations, tests and stories, as `exclude` has them. */
|
|
1732
1744
|
const NOT_COMPONENT_SOURCES = [
|
|
@@ -2065,7 +2077,7 @@ function readJson$1(file) {
|
|
|
2065
2077
|
function asRecord(value) {
|
|
2066
2078
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2067
2079
|
}
|
|
2068
|
-
function isFile(file) {
|
|
2080
|
+
function isFile$1(file) {
|
|
2069
2081
|
try {
|
|
2070
2082
|
return fs.statSync(file).isFile();
|
|
2071
2083
|
} catch {
|
|
@@ -2079,8 +2091,7 @@ function isDirectory$1(dir) {
|
|
|
2079
2091
|
return false;
|
|
2080
2092
|
}
|
|
2081
2093
|
}
|
|
2082
|
-
|
|
2083
|
-
//#region src/tokens/color.ts
|
|
2094
|
+
// src/tokens/color.ts
|
|
2084
2095
|
const toOklch$1 = converter("oklch");
|
|
2085
2096
|
/**
|
|
2086
2097
|
* Euclidean distance in OKLCH with hue handled as an angle, which is ΔE in
|
|
@@ -2251,8 +2262,7 @@ function formatDistance(color, token) {
|
|
|
2251
2262
|
if (deltaE < 5e-4 && formatOpacityGap(color, token, "")) return "same color";
|
|
2252
2263
|
return formatDeltaE(deltaE);
|
|
2253
2264
|
}
|
|
2254
|
-
|
|
2255
|
-
//#region src/libraries/mui.ts
|
|
2265
|
+
// src/libraries/mui.ts
|
|
2256
2266
|
/** The palette groups MUI's components take as a `color` value (`<Button color="primary">`). */
|
|
2257
2267
|
const ROLES = /* @__PURE__ */ new Set([
|
|
2258
2268
|
"primary",
|
|
@@ -2588,8 +2598,7 @@ function themeTokens(options, source) {
|
|
|
2588
2598
|
for (const token of tokens) token.origin = "library";
|
|
2589
2599
|
return tokens;
|
|
2590
2600
|
}
|
|
2591
|
-
|
|
2592
|
-
//#region src/libraries/index.ts
|
|
2601
|
+
// src/libraries/index.ts
|
|
2593
2602
|
/** The ids of the libraries onsystem can read, for the config's `libraries` list. */
|
|
2594
2603
|
const LIBRARY_IDS = ["mui"];
|
|
2595
2604
|
/** The libraries onsystem can read, by package. */
|
|
@@ -2718,8 +2727,7 @@ function readJson(file) {
|
|
|
2718
2727
|
return;
|
|
2719
2728
|
}
|
|
2720
2729
|
}
|
|
2721
|
-
|
|
2722
|
-
//#region src/extract/cva.ts
|
|
2730
|
+
// src/extract/cva.ts
|
|
2723
2731
|
const FACTORIES = /* @__PURE__ */ new Set(["cva", "tv"]);
|
|
2724
2732
|
/** Finds every `const x = cva(base, { variants, defaultVariants })` in a file. */
|
|
2725
2733
|
function findVariantDefinitions(sourceFile) {
|
|
@@ -2961,8 +2969,7 @@ function evaluateCalc(expression) {
|
|
|
2961
2969
|
const result = sum();
|
|
2962
2970
|
return result && Number.isFinite(result.n) ? result.n : void 0;
|
|
2963
2971
|
}
|
|
2964
|
-
|
|
2965
|
-
//#region src/tokens/usage.ts
|
|
2972
|
+
// src/tokens/usage.ts
|
|
2966
2973
|
/**
|
|
2967
2974
|
* Ready-to-paste ways to use a token, most idiomatic first. With a Tailwind
|
|
2968
2975
|
* theme key that is the utility class; otherwise the custom property.
|
|
@@ -3020,8 +3027,7 @@ function utilities(namespace, key) {
|
|
|
3020
3027
|
default: return [];
|
|
3021
3028
|
}
|
|
3022
3029
|
}
|
|
3023
|
-
|
|
3024
|
-
//#region src/tokens/tailwind-config.ts
|
|
3030
|
+
// src/tokens/tailwind-config.ts
|
|
3025
3031
|
/** A preset chain is a few files deep; anything beyond is not a Tailwind config. */
|
|
3026
3032
|
const MAX_FILES = 12;
|
|
3027
3033
|
/** A config or preset module, which is read for its theme. */
|
|
@@ -3521,8 +3527,7 @@ function findTailwindConfig(root) {
|
|
|
3521
3527
|
"mts"
|
|
3522
3528
|
].map((ext) => path.join(root, `tailwind.config.${ext}`)).find((file) => fs.existsSync(file));
|
|
3523
3529
|
}
|
|
3524
|
-
|
|
3525
|
-
//#region src/config.ts
|
|
3530
|
+
// src/config.ts
|
|
3526
3531
|
const RULE_IDS = [
|
|
3527
3532
|
"no-hardcoded-color",
|
|
3528
3533
|
"no-hardcoded-spacing",
|
|
@@ -3614,6 +3619,19 @@ const CONFIG_FILES = [
|
|
|
3614
3619
|
* They are still read, when no `onsystem.config.*` exists, with a notice to rename the file.
|
|
3615
3620
|
*/
|
|
3616
3621
|
const LEGACY_CONFIG_FILES = CONFIG_FILES.map((name) => name.replace(/^onsystem\./, "design-system-mcp."));
|
|
3622
|
+
/**
|
|
3623
|
+
* What marks the folder a file belongs to: the nearest one at or above it with a config,
|
|
3624
|
+
* a components.json, a package.json or an Nx project.json. The Claude Code hook finds a
|
|
3625
|
+
* file's project the same way, so `check` from a workspace root and the hook agree on
|
|
3626
|
+
* every file.
|
|
3627
|
+
*/
|
|
3628
|
+
const PROJECT_MARKERS = [
|
|
3629
|
+
...CONFIG_FILES,
|
|
3630
|
+
...LEGACY_CONFIG_FILES,
|
|
3631
|
+
"components.json",
|
|
3632
|
+
"package.json",
|
|
3633
|
+
"project.json"
|
|
3634
|
+
];
|
|
3617
3635
|
const DEFAULT_COMPONENTS = ["components/ui/**/*.{tsx,jsx}", "src/components/ui/**/*.{tsx,jsx}"];
|
|
3618
3636
|
/** Tests and stories: neither component sources nor UI that `check` lints by default. */
|
|
3619
3637
|
const DEFAULT_TEST_EXCLUDE = "**/*.{test,spec,stories}.{ts,tsx,js,jsx}";
|
|
@@ -3906,11 +3924,17 @@ function nodeRunsTypeScript(version = process.versions.node) {
|
|
|
3906
3924
|
/** A config file, read and validated: a JSON one only with `jsonConfigOnly`. */
|
|
3907
3925
|
async function readConfig(file, options) {
|
|
3908
3926
|
if (!fs.existsSync(file)) throw new ConfigError(`Config file not found: ${file}`);
|
|
3909
|
-
if (options.jsonConfigOnly && !file.endsWith(".json"))
|
|
3927
|
+
if (options.jsonConfigOnly && !file.endsWith(".json")) {
|
|
3928
|
+
const within = options.jsonConfigWithin;
|
|
3929
|
+
const project = options.root ? path.resolve(options.cwd ?? process.cwd(), options.root) : path.dirname(file);
|
|
3930
|
+
throw new ConfigError(within ? `${pathOf(file)} was not run: the server was started for ${pathOf(within)} (with --root or the client's root), which holds no project of its own, only projects in its folders, and a project found in it from the path a tool call names reads only a JSON config. Start the server with --root ${pathOf(project)} to use this config, or use onsystem.config.json.` : `${pathOf(file)} was not run: the server runs a JavaScript or TypeScript config only in a project it was started in (its working directory, --root or the client's roots), not in one found from the path a tool call names. Start the server in the project, or use onsystem.config.json.`);
|
|
3931
|
+
}
|
|
3910
3932
|
const parsed = configSchema.safeParse(await readConfigFile(file));
|
|
3911
3933
|
if (!parsed.success) throw new ConfigError(`Invalid config in ${file}:\n${z.prettifyError(parsed.error)}`);
|
|
3912
3934
|
return parsed.data;
|
|
3913
3935
|
}
|
|
3936
|
+
/** Where require keeps the CommonJS modules it has loaded, by path. */
|
|
3937
|
+
const requireCache = createRequire(import.meta.url).cache;
|
|
3914
3938
|
async function readConfigFile(file) {
|
|
3915
3939
|
if (/\.[cm]?ts$/.test(file) && !nodeRunsTypeScript()) throw new ConfigError(`${file} is TypeScript, which Node.js runs from 22.18; this is ${process.version}. Use onsystem.config.json or .mjs, or Node.js 22.18 or later.`);
|
|
3916
3940
|
if (file.endsWith(".json")) try {
|
|
@@ -3919,7 +3943,9 @@ async function readConfigFile(file) {
|
|
|
3919
3943
|
throw new ConfigError(`Could not parse ${file}: ${error.message}`);
|
|
3920
3944
|
}
|
|
3921
3945
|
try {
|
|
3922
|
-
const
|
|
3946
|
+
const version = createHash("sha256").update(fs.readFileSync(file)).digest("hex").slice(0, 16);
|
|
3947
|
+
delete requireCache[fs.realpathSync(file)];
|
|
3948
|
+
const mod = await import(`${pathToFileURL(file).href}?v=${version}`);
|
|
3923
3949
|
return mod.default ?? mod;
|
|
3924
3950
|
} catch (error) {
|
|
3925
3951
|
const message = error.message;
|
|
@@ -3943,8 +3969,7 @@ function configJsonSchema() {
|
|
|
3943
3969
|
io: "input"
|
|
3944
3970
|
});
|
|
3945
3971
|
}
|
|
3946
|
-
|
|
3947
|
-
//#region src/extract/components.ts
|
|
3972
|
+
// src/extract/components.ts
|
|
3948
3973
|
/**
|
|
3949
3974
|
* Extracts React components from source with the TypeScript checker: props
|
|
3950
3975
|
* (types, required, defaults, JSDoc), `cva()`/`tv()` variants, the native
|
|
@@ -4790,8 +4815,7 @@ function valueExports(checker, sourceFile) {
|
|
|
4790
4815
|
return ((symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol).flags & ts.SymbolFlags.Value) !== 0;
|
|
4791
4816
|
}).map((symbol) => symbol.name);
|
|
4792
4817
|
}
|
|
4793
|
-
|
|
4794
|
-
//#region src/extract/docs.ts
|
|
4818
|
+
// src/extract/docs.ts
|
|
4795
4819
|
const EXAMPLE_LANGS = /* @__PURE__ */ new Set([
|
|
4796
4820
|
"tsx",
|
|
4797
4821
|
"jsx",
|
|
@@ -4959,8 +4983,7 @@ function dedent(code) {
|
|
|
4959
4983
|
const indent = Math.min(...lines.filter((l) => l.trim()).map((l) => /^\s*/.exec(l)?.[0].length ?? 0));
|
|
4960
4984
|
return lines.map((l) => l.slice(Number.isFinite(indent) ? indent : 0)).join("\n");
|
|
4961
4985
|
}
|
|
4962
|
-
|
|
4963
|
-
//#region src/libraries/declarations.ts
|
|
4986
|
+
// src/libraries/declarations.ts
|
|
4964
4987
|
/** A component's name: a capitalised identifier (`Button`, `ButtonBase`, `Unstable_Grid2`). */
|
|
4965
4988
|
const COMPONENT_NAME = /^[A-Z][\w$]*$/;
|
|
4966
4989
|
/** Packages contributing more props than this are summarised, as in source extraction. */
|
|
@@ -5498,8 +5521,7 @@ function linkSlots(components, icons) {
|
|
|
5498
5521
|
}
|
|
5499
5522
|
}
|
|
5500
5523
|
}
|
|
5501
|
-
|
|
5502
|
-
//#region src/libraries/load.ts
|
|
5524
|
+
// src/libraries/load.ts
|
|
5503
5525
|
/** App sources read for module augmentations and themes, at most. */
|
|
5504
5526
|
const MAX_SOURCES = 5e3;
|
|
5505
5527
|
const SOURCE_FILE = /\.(?:[cm]?[jt]sx?)$/;
|
|
@@ -5612,8 +5634,7 @@ async function appSources(root, project, patterns) {
|
|
|
5612
5634
|
}
|
|
5613
5635
|
return found;
|
|
5614
5636
|
}
|
|
5615
|
-
|
|
5616
|
-
//#region src/lint/analyze.ts
|
|
5637
|
+
// src/lint/analyze.ts
|
|
5617
5638
|
const CLASS_FUNCTIONS = /^(cn|clsx|cx|twMerge|twJoin|classNames|classnames|cva|tv)$/;
|
|
5618
5639
|
/** The node's children in source order. */
|
|
5619
5640
|
function childrenOf(node) {
|
|
@@ -5967,8 +5988,7 @@ function styleObjects(expr) {
|
|
|
5967
5988
|
const fn = asFunction(expr);
|
|
5968
5989
|
return (fn ? returnedExpressions(fn).flatMap(valueBranches) : valueBranches(expr)).filter(ts.isObjectLiteralExpression);
|
|
5969
5990
|
}
|
|
5970
|
-
|
|
5971
|
-
//#region src/lint/context.ts
|
|
5991
|
+
// src/lint/context.ts
|
|
5972
5992
|
/**
|
|
5973
5993
|
* Resolves a JSX tag to a design-system component, following imports and
|
|
5974
5994
|
* aliases. `file` is the checked file relative to the root, for relative imports.
|
|
@@ -6063,8 +6083,7 @@ function fromLibrary(target, library, name, source, member) {
|
|
|
6063
6083
|
source
|
|
6064
6084
|
};
|
|
6065
6085
|
}
|
|
6066
|
-
|
|
6067
|
-
//#region src/lint/tailwind-palette.ts
|
|
6086
|
+
// src/lint/tailwind-palette.ts
|
|
6068
6087
|
/** Tailwind's default color palette: name → shade → OKLCH value. */
|
|
6069
6088
|
const TAILWIND_PALETTE = {
|
|
6070
6089
|
red: {
|
|
@@ -6411,8 +6430,7 @@ const TAILWIND_KEYWORD_COLORS = {
|
|
|
6411
6430
|
black: "#000",
|
|
6412
6431
|
white: "#fff"
|
|
6413
6432
|
};
|
|
6414
|
-
|
|
6415
|
-
//#region src/tokens/css.ts
|
|
6433
|
+
// src/tokens/css.ts
|
|
6416
6434
|
/** Tailwind v4 theme namespaces → token category. Longest prefixes first. */
|
|
6417
6435
|
const NAMESPACES = [
|
|
6418
6436
|
["--inset-shadow-", "shadow"],
|
|
@@ -6793,8 +6811,7 @@ function trails(comment, before) {
|
|
|
6793
6811
|
const line = before?.source?.end?.line;
|
|
6794
6812
|
return line !== void 0 && comment.source?.start?.line === line;
|
|
6795
6813
|
}
|
|
6796
|
-
|
|
6797
|
-
//#region src/tokens/design-md.ts
|
|
6814
|
+
// src/tokens/design-md.ts
|
|
6798
6815
|
/**
|
|
6799
6816
|
* Reads the tokens in a DESIGN.md's YAML front matter (https://github.com/google-labs-code/design.md):
|
|
6800
6817
|
* `colors`, `typography`, `rounded` and `spacing`. Tokens are named by their path
|
|
@@ -6930,8 +6947,7 @@ function reference(value) {
|
|
|
6930
6947
|
function isReference(value) {
|
|
6931
6948
|
return reference(value) !== void 0;
|
|
6932
6949
|
}
|
|
6933
|
-
|
|
6934
|
-
//#region src/tokens/dtcg.ts
|
|
6950
|
+
// src/tokens/dtcg.ts
|
|
6935
6951
|
/**
|
|
6936
6952
|
* The `$extensions` key under which onsystem keeps what DTCG has no field for: the token's
|
|
6937
6953
|
* own name, the custom property code uses, the Tailwind theme variable and the values in
|
|
@@ -7293,8 +7309,7 @@ function lineIndex(text) {
|
|
|
7293
7309
|
return lineAt(offset);
|
|
7294
7310
|
};
|
|
7295
7311
|
}
|
|
7296
|
-
|
|
7297
|
-
//#region src/tokens/roles.ts
|
|
7312
|
+
// src/tokens/roles.ts
|
|
7298
7313
|
/** The role of a Tailwind color prefix (`bg`, `text`, `border-t`); undefined for shadows and the like. */
|
|
7299
7314
|
function colorRole(prefix) {
|
|
7300
7315
|
if (!prefix) return void 0;
|
|
@@ -7528,8 +7543,7 @@ function roleFit(token, role) {
|
|
|
7528
7543
|
};
|
|
7529
7544
|
}
|
|
7530
7545
|
}
|
|
7531
|
-
|
|
7532
|
-
//#region src/types.ts
|
|
7546
|
+
// src/types.ts
|
|
7533
7547
|
/**
|
|
7534
7548
|
* The design-system model the server exposes. Everything here is plain JSON so
|
|
7535
7549
|
* it can be cached on disk and returned as MCP structured content.
|
|
@@ -7542,8 +7556,7 @@ const TOKEN_CATEGORIES = [
|
|
|
7542
7556
|
"shadow",
|
|
7543
7557
|
"other"
|
|
7544
7558
|
];
|
|
7545
|
-
|
|
7546
|
-
//#region src/tokens/export.ts
|
|
7559
|
+
// src/tokens/export.ts
|
|
7547
7560
|
/** Group names a DTCG or DESIGN.md token path may already start with, per category. */
|
|
7548
7561
|
const GROUP_NAMES = {
|
|
7549
7562
|
color: [
|
|
@@ -8241,12 +8254,12 @@ function table(tokens, keys) {
|
|
|
8241
8254
|
function use(token) {
|
|
8242
8255
|
return [token.usage.find((u) => !u.includes("(")), cssReference(token)].filter((u) => Boolean(u)).map((u) => `\`${u}\``).join(", ");
|
|
8243
8256
|
}
|
|
8244
|
-
|
|
8245
|
-
//#region src/tokens/index.ts
|
|
8257
|
+
// src/tokens/index.ts
|
|
8246
8258
|
async function loadTokens(root, sources) {
|
|
8247
8259
|
const warnings = [];
|
|
8248
8260
|
const all = [];
|
|
8249
8261
|
const sheets = [];
|
|
8262
|
+
const sheetFiles = [];
|
|
8250
8263
|
let sheetsAt;
|
|
8251
8264
|
for (const source of sources) {
|
|
8252
8265
|
const rel = relativePath(root, source.file);
|
|
@@ -8270,6 +8283,7 @@ async function loadTokens(root, sources) {
|
|
|
8270
8283
|
warnings.push(...result.warnings);
|
|
8271
8284
|
} else {
|
|
8272
8285
|
sheets.push(readCssSheet(text, rel));
|
|
8286
|
+
sheetFiles.push(rel);
|
|
8273
8287
|
sheetsAt ??= all.length;
|
|
8274
8288
|
}
|
|
8275
8289
|
} catch (error) {
|
|
@@ -8277,6 +8291,9 @@ async function loadTokens(root, sources) {
|
|
|
8277
8291
|
}
|
|
8278
8292
|
}
|
|
8279
8293
|
if (sheets.length) {
|
|
8294
|
+
const partial = sheets.findIndex((s) => s.theme.size > 0 || s.resets.size > 0);
|
|
8295
|
+
const sheet = sheets[partial];
|
|
8296
|
+
if (sheet && !sheets.some((s) => s.tailwind) && await runsTailwindV4(root)) sheet.tailwind = sheetFiles[partial];
|
|
8280
8297
|
const result = cssSheetTokens(sheets);
|
|
8281
8298
|
all.splice(sheetsAt ?? all.length, 0, ...result.tokens);
|
|
8282
8299
|
warnings.push(...result.warnings);
|
|
@@ -8286,6 +8303,43 @@ async function loadTokens(root, sources) {
|
|
|
8286
8303
|
warnings
|
|
8287
8304
|
};
|
|
8288
8305
|
}
|
|
8306
|
+
/** The packages that run Tailwind CSS v4 in a build: PostCSS's, Vite's and its CLI. */
|
|
8307
|
+
const TAILWIND_V4_PACKAGES = [
|
|
8308
|
+
"@tailwindcss/postcss",
|
|
8309
|
+
"@tailwindcss/vite",
|
|
8310
|
+
"@tailwindcss/cli"
|
|
8311
|
+
];
|
|
8312
|
+
/**
|
|
8313
|
+
* Whether the project at `root` runs Tailwind CSS v4, whose default theme has a spacing unit and
|
|
8314
|
+
* the radius scale up to `rounded-4xl`: its package.json depends on a package that runs v4, or on
|
|
8315
|
+
* `tailwindcss` in a range that is not v3's (`^3.4.1`, `~3.4`, `3.x`), which has neither. Its
|
|
8316
|
+
* plugins (`@tailwindcss/typography`) say nothing of the version.
|
|
8317
|
+
*/
|
|
8318
|
+
async function runsTailwindV4(root) {
|
|
8319
|
+
let pkg;
|
|
8320
|
+
try {
|
|
8321
|
+
pkg = JSON.parse(await fsp.readFile(path.join(root, "package.json"), "utf8"));
|
|
8322
|
+
} catch {
|
|
8323
|
+
return false;
|
|
8324
|
+
}
|
|
8325
|
+
if (typeof pkg !== "object" || pkg === null) return false;
|
|
8326
|
+
const lists = [];
|
|
8327
|
+
for (const field of [
|
|
8328
|
+
"dependencies",
|
|
8329
|
+
"devDependencies",
|
|
8330
|
+
"peerDependencies"
|
|
8331
|
+
]) {
|
|
8332
|
+
const dependencies = pkg[field];
|
|
8333
|
+
if (typeof dependencies === "object" && dependencies !== null) lists.push(dependencies);
|
|
8334
|
+
}
|
|
8335
|
+
if (lists.some((list) => TAILWIND_V4_PACKAGES.some((name) => Object.hasOwn(list, name)))) return true;
|
|
8336
|
+
const declared = lists.find((list) => Object.hasOwn(list, "tailwindcss"));
|
|
8337
|
+
return declared !== void 0 && !beforeV4(declared.tailwindcss);
|
|
8338
|
+
}
|
|
8339
|
+
/** A range of Tailwind CSS v3 or older in package.json: `^3.4.1`, `~3`, `3.x`, `^2 || ^3`. */
|
|
8340
|
+
function beforeV4(range) {
|
|
8341
|
+
return typeof range === "string" && range.split("||").every((alternative) => /^(?:npm:tailwindcss@)?[\^~=v]*\s*[0-3](?:\.|$)/.test(alternative.trim()));
|
|
8342
|
+
}
|
|
8289
8343
|
/**
|
|
8290
8344
|
* DTCG files and the CSS generated from them often describe the same tokens.
|
|
8291
8345
|
* Merge entries that share a custom property: keep the DTCG name and
|
|
@@ -8565,8 +8619,7 @@ function preferTailwind(a, b) {
|
|
|
8565
8619
|
function byDistance(a, b) {
|
|
8566
8620
|
return Math.round(a.distance * 1e4) - Math.round(b.distance * 1e4) || Number(b.inScope) - Number(a.inScope) || a.fit.rank - b.fit.rank || preferTailwind(a.candidate.token, b.candidate.token) || a.index - b.index;
|
|
8567
8621
|
}
|
|
8568
|
-
|
|
8569
|
-
//#region src/lint/tailwind.ts
|
|
8622
|
+
// src/lint/tailwind.ts
|
|
8570
8623
|
function splitClasses(text, offset) {
|
|
8571
8624
|
const tokens = [];
|
|
8572
8625
|
for (const match of text.matchAll(/\S+/g)) tokens.push({
|
|
@@ -8844,8 +8897,7 @@ const STYLE_RADIUS_PROPERTIES = {
|
|
|
8844
8897
|
function isColorAttribute(name) {
|
|
8845
8898
|
return /^(color|fill|stroke|stopColor|stop-color|floodColor|flood-color|lightingColor|bgcolor)$|Color$/.test(name);
|
|
8846
8899
|
}
|
|
8847
|
-
|
|
8848
|
-
//#region src/lint/target.ts
|
|
8900
|
+
// src/lint/target.ts
|
|
8849
8901
|
/**
|
|
8850
8902
|
* Elements a design system typically replaces. Containers (`div`, `span`,
|
|
8851
8903
|
* `p`) are deliberately absent: `Card` renders a `div`, but not every `div`
|
|
@@ -8982,6 +9034,8 @@ var LintTarget = class {
|
|
|
8982
9034
|
sources;
|
|
8983
9035
|
/** Published libraries the model read from type declarations: `@mui/material`. */
|
|
8984
9036
|
libraries;
|
|
9037
|
+
/** The name in each package.json read, by its path: undefined where there is none. */
|
|
9038
|
+
manifests = /* @__PURE__ */ new Map();
|
|
8985
9039
|
/**
|
|
8986
9040
|
* `resolver` finds the module an import points at and what it exports
|
|
8987
9041
|
* (`@calcom/ui/components/icon` → `components/icon/index.ts`), so a name from
|
|
@@ -9058,14 +9112,49 @@ var LintTarget = class {
|
|
|
9058
9112
|
* model left out rather than an invented one: its module exists outside the
|
|
9059
9113
|
* folders extraction read and exports `name`, or re-exports from such a
|
|
9060
9114
|
* module (an excluded `icons/` folder, a barrel's `export * from "./icons"`).
|
|
9061
|
-
* A module declared in a package's `exports` but not on disk counts too
|
|
9062
|
-
* except the design system's own import path
|
|
9115
|
+
* A module declared in a package's `exports` but not on disk counts too (built to `dist/`,
|
|
9116
|
+
* not built yet), except the design system's own import path, and one whose export, as the
|
|
9117
|
+
* package.json writes it, leads to a source file only: `@acme/ui/components/tooltip` is
|
|
9118
|
+
* `src/components/tooltip.tsx` through `"./components/*": "./src/components/*.tsx"`, and with
|
|
9119
|
+
* no such file it does not exist.
|
|
9063
9120
|
*/
|
|
9064
9121
|
outsideModel(specifier, name, fromFile = "snippet.tsx") {
|
|
9065
9122
|
const location = this.resolver?.locate(specifier, fromFile);
|
|
9066
9123
|
if (!location) return false;
|
|
9067
|
-
if (
|
|
9068
|
-
|
|
9124
|
+
if (location.file) return this.mayExport(location.file, name, 0);
|
|
9125
|
+
if (this.importPaths.has(specifier) || specifier === this.config.importPath) return false;
|
|
9126
|
+
const source = this.exportedSource(specifier);
|
|
9127
|
+
return source === void 0 || this.mayExport(source, name, 0);
|
|
9128
|
+
}
|
|
9129
|
+
/**
|
|
9130
|
+
* The module a design-system package's `exports` lead `specifier` to, relative to the root, as
|
|
9131
|
+
* its package.json writes them: the first of the entry's targets that is on disk, else, when
|
|
9132
|
+
* each of them is a source file, the first (which does not exist). Undefined when a target
|
|
9133
|
+
* leads to a build folder (`"./*": "./dist/components/*.js"`, maybe not built yet, whose
|
|
9134
|
+
* sources may be anywhere), the entry blocks the subpath (`null`), or no entry maps it.
|
|
9135
|
+
*/
|
|
9136
|
+
exportedSource(specifier) {
|
|
9137
|
+
const name = packageOf(specifier);
|
|
9138
|
+
const led = this.config.imports?.find((m) => packageOf(m.specifier) === name);
|
|
9139
|
+
const dir = led && this.packageDir(path.resolve(this.model.root, led.target), name);
|
|
9140
|
+
const targets = dir ? exportedTargets(dir, `.${specifier.slice(name.length)}`) : void 0;
|
|
9141
|
+
if (!dir || !targets?.length) return void 0;
|
|
9142
|
+
const files = targets.map((target) => path.resolve(dir, target));
|
|
9143
|
+
const file = files.flatMap((f) => f.endsWith(".jsx") ? [f, `${f.slice(0, -4)}.tsx`] : [f]).find((f) => isInside(dir, f) && isFile(f)) ?? (targets.every(isSourceFile) ? files[0] : void 0);
|
|
9144
|
+
return file && relativePath(this.model.root, file);
|
|
9145
|
+
}
|
|
9146
|
+
/**
|
|
9147
|
+
* The folder of package `name` that `file` is in: the nearest package.json above it with a
|
|
9148
|
+
* name, when that name is `name`. One without a name (`{"type": "module"}` in a subfolder) is
|
|
9149
|
+
* no package's. Each package.json is read once.
|
|
9150
|
+
*/
|
|
9151
|
+
packageDir(file, name) {
|
|
9152
|
+
for (let dir = path.dirname(file); dir !== path.dirname(dir); dir = path.dirname(dir)) {
|
|
9153
|
+
const manifest = path.join(dir, "package.json");
|
|
9154
|
+
if (!this.manifests.has(manifest)) this.manifests.set(manifest, manifestName(manifest));
|
|
9155
|
+
const found = this.manifests.get(manifest);
|
|
9156
|
+
if (found !== void 0) return found === name ? dir : void 0;
|
|
9157
|
+
}
|
|
9069
9158
|
}
|
|
9070
9159
|
/**
|
|
9071
9160
|
* The component a JSX tag means when `name` is imported from `specifier`, among the
|
|
@@ -9242,6 +9331,30 @@ function fit(component, element) {
|
|
|
9242
9331
|
function isPackageName(specifier) {
|
|
9243
9332
|
return specifier.startsWith("@") ? /^@[^/]+\/[^/]+$/.test(specifier) : /^[a-z0-9][^/]*$/i.test(specifier);
|
|
9244
9333
|
}
|
|
9334
|
+
/** The name a package.json gives its package; undefined when it has none, or there is none. */
|
|
9335
|
+
function manifestName(manifest) {
|
|
9336
|
+
try {
|
|
9337
|
+
const { name } = JSON.parse(fs.readFileSync(manifest, "utf8")) ?? {};
|
|
9338
|
+
return typeof name === "string" && name ? name : void 0;
|
|
9339
|
+
} catch {
|
|
9340
|
+
return;
|
|
9341
|
+
}
|
|
9342
|
+
}
|
|
9343
|
+
function isFile(file) {
|
|
9344
|
+
return fs.statSync(file, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
9345
|
+
}
|
|
9346
|
+
/** The package an import specifier names: `@acme/ui` for `@acme/ui/components/button`. */
|
|
9347
|
+
function packageOf(specifier) {
|
|
9348
|
+
return specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
|
|
9349
|
+
}
|
|
9350
|
+
/**
|
|
9351
|
+
* An export target that is a TypeScript or JSX source file (`./src/components/tooltip.tsx`), not
|
|
9352
|
+
* a declaration file or a file in a build folder (`./dist/tooltip.tsx`, not built yet).
|
|
9353
|
+
*/
|
|
9354
|
+
function isSourceFile(target) {
|
|
9355
|
+
if (/\.d\.[cm]?ts$/.test(target) || /^(?:\.\/)?(?:dist|build|lib|out|esm|cjs)\//.test(target)) return false;
|
|
9356
|
+
return /\.(?:[cm]?tsx?|jsx)$/.test(target);
|
|
9357
|
+
}
|
|
9245
9358
|
function stripExtension(file) {
|
|
9246
9359
|
return file.replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
9247
9360
|
}
|
|
@@ -9250,8 +9363,7 @@ function modulePaths(file) {
|
|
|
9250
9363
|
const module = stripExtension(file);
|
|
9251
9364
|
return module.endsWith("/index") ? [module, module.slice(0, -6)] : [module];
|
|
9252
9365
|
}
|
|
9253
|
-
|
|
9254
|
-
//#region src/lint/rules/components.ts
|
|
9366
|
+
// src/lint/rules/components.ts
|
|
9255
9367
|
function renameTag(context, element, name) {
|
|
9256
9368
|
const edits = [{
|
|
9257
9369
|
range: [element.tagName.getStart(context.sourceFile), element.tagName.end],
|
|
@@ -10406,8 +10518,7 @@ const iconButtonAccessibleName = {
|
|
|
10406
10518
|
}
|
|
10407
10519
|
}
|
|
10408
10520
|
};
|
|
10409
|
-
|
|
10410
|
-
//#region src/lint/rules/hardcoded.ts
|
|
10521
|
+
// src/lint/rules/hardcoded.ts
|
|
10411
10522
|
/** `var(--color-green-50)`: a shade of Tailwind's palette through its CSS variable. */
|
|
10412
10523
|
const PALETTE_VARIABLE = /^var\(\s*--color-([a-z]+-(?:50|[1-9]00|950))\s*\)$/;
|
|
10413
10524
|
/** Luminance values, not design colors: `white`/`black` (and hex twins). */
|
|
@@ -11698,8 +11809,7 @@ function lengthReplacement(context, category, px, prefix) {
|
|
|
11698
11809
|
tailwind: false
|
|
11699
11810
|
};
|
|
11700
11811
|
}
|
|
11701
|
-
|
|
11702
|
-
//#region src/lint/rules/index.ts
|
|
11812
|
+
// src/lint/rules/index.ts
|
|
11703
11813
|
/** Every rule, in the order they run. */
|
|
11704
11814
|
const RULES = [
|
|
11705
11815
|
noHardcodedColor,
|
|
@@ -11719,8 +11829,7 @@ const RULES = [
|
|
|
11719
11829
|
noUnknownVariant,
|
|
11720
11830
|
iconButtonAccessibleName
|
|
11721
11831
|
];
|
|
11722
|
-
|
|
11723
|
-
//#region src/lint/suppress.ts
|
|
11832
|
+
// src/lint/suppress.ts
|
|
11724
11833
|
/**
|
|
11725
11834
|
* The prefix of suppression comments, the one place it is spelled:
|
|
11726
11835
|
* `// onsystem-disable-next-line <rule>[, <rule>]`, `// onsystem-disable-line
|
|
@@ -11795,8 +11904,7 @@ function insideLiteral(sourceFile, offset) {
|
|
|
11795
11904
|
}
|
|
11796
11905
|
return ts.isStringLiteralLike(node) || ts.isTemplateLiteralToken(node) || ts.isJsxText(node) || ts.isRegularExpressionLiteral(node);
|
|
11797
11906
|
}
|
|
11798
|
-
|
|
11799
|
-
//#region src/baseline.ts
|
|
11907
|
+
// src/baseline.ts
|
|
11800
11908
|
const BASELINE_FILE = "onsystem.baseline.json";
|
|
11801
11909
|
/** The baseline's name from before the rename to onsystem: still read when the new one is absent. */
|
|
11802
11910
|
const LEGACY_BASELINE_FILE = "design-system-mcp.baseline.json";
|
|
@@ -12132,15 +12240,12 @@ function writeBaseline(file, entries, about = {}) {
|
|
|
12132
12240
|
function isObject(value) {
|
|
12133
12241
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12134
12242
|
}
|
|
12135
|
-
|
|
12136
|
-
//#region package.json
|
|
12243
|
+
// package.json
|
|
12137
12244
|
var name = "onsystem";
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
const VERSION = "0.10.1";
|
|
12245
|
+
// src/version.ts
|
|
12246
|
+
const VERSION = "0.10.3";
|
|
12141
12247
|
const NAME = name;
|
|
12142
|
-
|
|
12143
|
-
//#region src/lint/sarif.ts
|
|
12248
|
+
// src/lint/sarif.ts
|
|
12144
12249
|
const SCHEMA = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
|
|
12145
12250
|
const REPO = "https://github.com/dgesteves/onsystem";
|
|
12146
12251
|
const RULES_PAGE = "https://onsystem.vercel.app/rules";
|
|
@@ -12248,8 +12353,7 @@ function sarifResult(d, uri, ruleIndex, fingerprint) {
|
|
|
12248
12353
|
function fingerprint$1(uri, identity, occurrence) {
|
|
12249
12354
|
return createHash("sha256").update(`${uri}\0${identity}\0${String(occurrence)}`).digest("hex");
|
|
12250
12355
|
}
|
|
12251
|
-
|
|
12252
|
-
//#region src/lint/format.ts
|
|
12356
|
+
// src/lint/format.ts
|
|
12253
12357
|
/** From this many findings, `pretty` ends with a summary: more than a screen to scroll back through. */
|
|
12254
12358
|
const SUMMARY_FROM = 50;
|
|
12255
12359
|
/** The palette classes the summary lists. */
|
|
@@ -12348,8 +12452,7 @@ function escapeData(value) {
|
|
|
12348
12452
|
function escapeProperty(value) {
|
|
12349
12453
|
return escapeData(value).replace(/:/g, "%3A").replace(/,/g, "%2C");
|
|
12350
12454
|
}
|
|
12351
|
-
|
|
12352
|
-
//#region src/lint/index.ts
|
|
12455
|
+
// src/lint/index.ts
|
|
12353
12456
|
/**
|
|
12354
12457
|
* Lints a TSX/JSX snippet or file against the design system. Purely
|
|
12355
12458
|
* syntactic: no type-checking of the snippet, so it works on fragments an
|
|
@@ -12563,8 +12666,7 @@ function applyFixes(code, diagnostics) {
|
|
|
12563
12666
|
}
|
|
12564
12667
|
return out;
|
|
12565
12668
|
}
|
|
12566
|
-
|
|
12567
|
-
//#region src/modules.ts
|
|
12669
|
+
// src/modules.ts
|
|
12568
12670
|
/**
|
|
12569
12671
|
* Finds the modules a checked file imports from the design system, and what
|
|
12570
12672
|
* they export, so the linter can tell a name the model left out (an excluded
|
|
@@ -12677,8 +12779,7 @@ function readExports(sourceFile, resolve) {
|
|
|
12677
12779
|
namespaces
|
|
12678
12780
|
};
|
|
12679
12781
|
}
|
|
12680
|
-
|
|
12681
|
-
//#region src/search/bm25.ts
|
|
12782
|
+
// src/search/bm25.ts
|
|
12682
12783
|
var Bm25Index = class {
|
|
12683
12784
|
docs = [];
|
|
12684
12785
|
df = /* @__PURE__ */ new Map();
|
|
@@ -12735,8 +12836,7 @@ var Bm25Index = class {
|
|
|
12735
12836
|
return hits.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
12736
12837
|
}
|
|
12737
12838
|
};
|
|
12738
|
-
|
|
12739
|
-
//#region src/search/tokenize.ts
|
|
12839
|
+
// src/search/tokenize.ts
|
|
12740
12840
|
const STOP_WORDS = new Set(`a an and are as at be but by can do does for from has have how i if in into is it its me my need
|
|
12741
12841
|
of on or our should so some that the their them then there these this to use used using want we
|
|
12742
12842
|
what when where which while who will with without would you your show make let lets get add want
|
|
@@ -12810,8 +12910,7 @@ function expandQuery(query) {
|
|
|
12810
12910
|
}
|
|
12811
12911
|
return weights;
|
|
12812
12912
|
}
|
|
12813
|
-
|
|
12814
|
-
//#region src/search/index.ts
|
|
12913
|
+
// src/search/index.ts
|
|
12815
12914
|
/** Field weights: names matter most, then descriptions and variant values. */
|
|
12816
12915
|
const WEIGHTS = {
|
|
12817
12916
|
name: 4,
|
|
@@ -12964,8 +13063,7 @@ function searchComponents(index, query, limit = 5) {
|
|
|
12964
13063
|
weak: true
|
|
12965
13064
|
}));
|
|
12966
13065
|
}
|
|
12967
|
-
|
|
12968
|
-
//#region src/util/log.ts
|
|
13066
|
+
// src/util/log.ts
|
|
12969
13067
|
const stderrLogger = {
|
|
12970
13068
|
info: (message) => process.stderr.write(`[onsystem] ${message}\n`),
|
|
12971
13069
|
warn: (message) => process.stderr.write(`[onsystem] warning: ${message}\n`),
|
|
@@ -12976,8 +13074,7 @@ const silentLogger = {
|
|
|
12976
13074
|
warn: () => void 0,
|
|
12977
13075
|
error: () => void 0
|
|
12978
13076
|
};
|
|
12979
|
-
|
|
12980
|
-
//#region src/design-system.ts
|
|
13077
|
+
// src/design-system.ts
|
|
12981
13078
|
/** A loaded design system: the model plus the indexes the tools query. */
|
|
12982
13079
|
var DesignSystem = class {
|
|
12983
13080
|
model;
|
|
@@ -13259,9 +13356,10 @@ function outsideWarning(root, files) {
|
|
|
13259
13356
|
/**
|
|
13260
13357
|
* Absolute paths matching `patterns` under the root. `node_modules` is skipped
|
|
13261
13358
|
* even when `exclude` replaces the defaults, except by patterns that name it
|
|
13262
|
-
* (tokens published as a package). Files that lead outside the project are left out.
|
|
13359
|
+
* (tokens published as a package). Files that lead outside the project are left out. Those that
|
|
13360
|
+
* `skip` accepts are left out before that check, so they are not named as leading outside.
|
|
13263
13361
|
*/
|
|
13264
|
-
async function find(config, patterns, contain = new Containment(config)) {
|
|
13362
|
+
async function find(config, patterns, contain = new Containment(config), skip = () => false) {
|
|
13265
13363
|
const options = {
|
|
13266
13364
|
cwd: config.root,
|
|
13267
13365
|
absolute: true,
|
|
@@ -13294,7 +13392,7 @@ async function find(config, patterns, contain = new Containment(config)) {
|
|
|
13294
13392
|
});
|
|
13295
13393
|
})
|
|
13296
13394
|
]);
|
|
13297
|
-
return (await contain.filter(unique(found.flat()))).sort();
|
|
13395
|
+
return (await contain.filter(unique(found.flat()).filter((file) => !skip(file)))).sort();
|
|
13298
13396
|
}
|
|
13299
13397
|
/** The design system's own component files, as absolute paths. */
|
|
13300
13398
|
function componentFiles(config) {
|
|
@@ -13316,8 +13414,9 @@ async function resolveFiles(config) {
|
|
|
13316
13414
|
seen.add(real);
|
|
13317
13415
|
return true;
|
|
13318
13416
|
};
|
|
13417
|
+
const others = config.tokensDetected && !config.configFile && !holdsProject(config.root) ? (file) => inProjectBelow(config.root, file) : () => false;
|
|
13319
13418
|
const add = async (sources) => {
|
|
13320
|
-
for (const source of sources) for (const file of await find(config, [source.path], contain)) if (await isNew(file)) tokens.push({
|
|
13419
|
+
for (const source of sources) for (const file of await find(config, [source.path], contain, others)) if (await isNew(file)) tokens.push({
|
|
13321
13420
|
file,
|
|
13322
13421
|
prefix: source.prefix
|
|
13323
13422
|
});
|
|
@@ -13335,6 +13434,17 @@ async function resolveFiles(config) {
|
|
|
13335
13434
|
outside: [...contain.outside]
|
|
13336
13435
|
};
|
|
13337
13436
|
}
|
|
13437
|
+
function holdsProject(dir) {
|
|
13438
|
+
return PROJECT_MARKERS.some((name) => fs.existsSync(path.join(dir, name)));
|
|
13439
|
+
}
|
|
13440
|
+
/** Whether a folder between `root` and `file` (in it) holds a project. */
|
|
13441
|
+
function inProjectBelow(root, file) {
|
|
13442
|
+
for (let dir = path.dirname(path.resolve(file)); isInside(root, dir); dir = path.dirname(dir)) {
|
|
13443
|
+
if (path.relative(root, dir) === "") return false;
|
|
13444
|
+
if (holdsProject(dir)) return true;
|
|
13445
|
+
}
|
|
13446
|
+
return false;
|
|
13447
|
+
}
|
|
13338
13448
|
/**
|
|
13339
13449
|
* The stylesheets zero config reads besides the ones it found: those they `@import` (by a
|
|
13340
13450
|
* path, or from a workspace package: `@import "@acme/theme"`), then those that set the colors
|
|
@@ -13586,19 +13696,29 @@ var DesignSystemHost = class {
|
|
|
13586
13696
|
this.options = options;
|
|
13587
13697
|
}
|
|
13588
13698
|
get() {
|
|
13589
|
-
this.current ??= this.build(false);
|
|
13699
|
+
this.current ??= this.kept(this.build(false));
|
|
13590
13700
|
return this.current;
|
|
13591
13701
|
}
|
|
13592
13702
|
reload() {
|
|
13593
13703
|
const next = this.build(true);
|
|
13594
13704
|
const previous = this.current;
|
|
13595
|
-
this.current = next.catch(async (error) => {
|
|
13705
|
+
this.current = this.kept(next.catch(async (error) => {
|
|
13596
13706
|
(this.options.logger ?? silentLogger).error(`reload failed: ${error.message}`);
|
|
13597
13707
|
if (previous) return previous;
|
|
13598
13708
|
throw error;
|
|
13599
|
-
});
|
|
13709
|
+
}));
|
|
13600
13710
|
return this.current;
|
|
13601
13711
|
}
|
|
13712
|
+
/**
|
|
13713
|
+
* `build`, dropped if it fails with no earlier model to serve instead (a first build that
|
|
13714
|
+
* fails), so the next `get()` builds again.
|
|
13715
|
+
*/
|
|
13716
|
+
kept(build) {
|
|
13717
|
+
build.catch(() => {
|
|
13718
|
+
if (this.current === build) this.current = void 0;
|
|
13719
|
+
});
|
|
13720
|
+
return build;
|
|
13721
|
+
}
|
|
13602
13722
|
onChange(listener) {
|
|
13603
13723
|
this.listeners.add(listener);
|
|
13604
13724
|
return () => this.listeners.delete(listener);
|
|
@@ -13729,5 +13849,4 @@ var DesignSystemHost = class {
|
|
|
13729
13849
|
return ds;
|
|
13730
13850
|
}
|
|
13731
13851
|
};
|
|
13732
|
-
|
|
13733
|
-
export { DEFAULT_TOKENS as $, unrecordedRules as A, toPosix as At, statusKinds as B, countBaseline as C, workspacePackageDirs as Ct, pruneBaseline as D, matchesGlob as Dt, onLines as E, isInside as Et, loadTokens as F, unique as Ft, parseDoc as G, parseDesignMd as H, mergeTokens as I, defaultProjectConfig as It, ConfigError as J, extractComponents as K, toDesignMd as L, readProjectConfig as Lt, writeBaseline as M, json as Mt, RULES as N, plural$1 as Nt, readBaseline as O, pathOf as Ot, TokenIndex as P, truncate as Pt, DEFAULT_TEST_FOLDERS as Q, toDtcg as R, applyBaseline as S, isWorkspaceRoot as St, goneFiles as T, SCRIPT_FILE$2 as Tt, parseCssTokens as U, parseDtcgTokens as V, attachDocs as W, DEFAULT_SEVERITY as X, DEFAULT_DOCS as Y, DEFAULT_TEST_EXCLUDE as Z, formatDiagnostics as _, PackageFinder as _t, fingerprint as a, excludedBy as at, BASELINE_FILE as b, findDesignSystemPackage as bt, silentLogger as c, loadConfigWith as ct, descriptionOf as d, findVariantDefinitions as dt, FALLBACK_TOKENS as et, searchComponents as f, LIBRARIES as ft, escapeData as g, PACKAGE_FIELD as gt, uncheckedNotice as h, notReadYet as ht, componentFiles as i, defineConfig as it, updateBaseline as j, closest as jt, rulesRun as k, relativePath as kt, stderrLogger as l, rulesFor as lt, checkSource as m, libraryTitle as mt, DesignSystemHost as n, RULE_IDS as nt, loadDesignSystem as o, hasOwnConfig as ot, applyFixes as p, LIBRARY_IDS as pt, CONFIG_FILES as q, buildModel as r, configJsonSchema as rt, tokenFiles as s, loadConfig as st, DesignSystem as t, LEGACY_CONFIG_FILES as tt, buildSearchIndex as u, formatPx as ut, NAME as v, declaredConfig as vt, findMoves as w, workspacePatterns as wt, LEGACY_BASELINE_FILE as x, isDesignSystemName as xt, VERSION as y, detectProject as yt, TOKEN_CATEGORIES as z };
|
|
13852
|
+
export { DEFAULT_TOKENS as $, unrecordedRules as A, relativePath as At, statusKinds as B, countBaseline as C, isWorkspaceRoot as Ct, pruneBaseline as D, isInside as Dt, onLines as E, SCRIPT_FILE$2 as Et, loadTokens as F, truncate as Ft, parseDoc as G, parseDesignMd as H, mergeTokens as I, unique as It, ConfigError as J, extractComponents as K, toDesignMd as L, defaultProjectConfig as Lt, writeBaseline as M, closest as Mt, RULES as N, json as Nt, readBaseline as O, matchesGlob as Ot, TokenIndex as P, plural$1 as Pt, DEFAULT_TEST_FOLDERS as Q, toDtcg as R, readProjectConfig as Rt, applyBaseline as S, isDesignSystemName as St, goneFiles as T, workspacePatterns as Tt, parseCssTokens as U, parseDtcgTokens as V, attachDocs as W, DEFAULT_SEVERITY as X, DEFAULT_DOCS as Y, DEFAULT_TEST_EXCLUDE as Z, formatDiagnostics as _, PACKAGE_FIELD as _t, fingerprint as a, defineConfig as at, BASELINE_FILE as b, detectProject as bt, silentLogger as c, loadConfig as ct, descriptionOf as d, formatPx as dt, FALLBACK_TOKENS as et, searchComponents as f, findVariantDefinitions as ft, escapeData as g, notReadYet as gt, uncheckedNotice as h, libraryTitle as ht, componentFiles as i, configJsonSchema as it, updateBaseline as j, toPosix as jt, rulesRun as k, pathOf as kt, stderrLogger as l, loadConfigWith as lt, checkSource as m, LIBRARY_IDS as mt, DesignSystemHost as n, PROJECT_MARKERS as nt, loadDesignSystem as o, excludedBy as ot, applyFixes as p, LIBRARIES as pt, CONFIG_FILES as q, buildModel as r, RULE_IDS as rt, tokenFiles as s, hasOwnConfig as st, DesignSystem as t, LEGACY_CONFIG_FILES as tt, buildSearchIndex as u, rulesFor as ut, NAME as v, PackageFinder as vt, findMoves as w, workspacePackageDirs as wt, LEGACY_BASELINE_FILE as x, findDesignSystemPackage as xt, VERSION as y, declaredConfig as yt, TOKEN_CATEGORIES as z };
|