onsystem 0.10.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as DEFAULT_TOKENS, A as unrecordedRules, At as relativePath, B as statusKinds, C as countBaseline, D as pruneBaseline, Dt as isInside, E as onLines, Et as SCRIPT_FILE, It as unique, J as ConfigError, L as toDesignMd, M as writeBaseline, O as readBaseline, Ot as matchesGlob, Pt as plural, R as toDtcg, Rt as readProjectConfig, S as applyBaseline, St as isDesignSystemName, T as goneFiles, Y as DEFAULT_DOCS, _ as formatDiagnostics, _t as PACKAGE_FIELD, b as BASELINE_FILE, bt as detectProject, c as silentLogger, et as FALLBACK_TOKENS, g as escapeData, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, i as componentFiles, j as updateBaseline, jt as toPosix, k as rulesRun, l as stderrLogger, o as loadDesignSystem, ot as excludedBy, q as CONFIG_FILES, rt as RULE_IDS, s as tokenFiles, tt as LEGACY_CONFIG_FILES, ut as rulesFor, v as NAME, w as findMoves, x as LEGACY_BASELINE_FILE, y as VERSION, yt as declaredConfig } from "./design-system-rl__N6tw.js";
3
- import { a as notReadNotices, i as missingDependencies, o as explainProject, s as librariesLeftOut, t as serveStdio } from "./stdio-CkczPt_1.js";
4
- import { r as loadTarget } from "./workspace-INef0Yqt.js";
2
+ import { $ as DEFAULT_TOKENS, A as unrecordedRules, At as relativePath, B as statusKinds, C as countBaseline, D as pruneBaseline, Dt as isInside, E as onLines, Et as SCRIPT_FILE, It as unique, J as ConfigError, L as toDesignMd, M as writeBaseline, O as readBaseline, Ot as matchesGlob, Pt as plural, R as toDtcg, Rt as readProjectConfig, S as applyBaseline, St as isDesignSystemName, T as goneFiles, Y as DEFAULT_DOCS, _ as formatDiagnostics, _t as PACKAGE_FIELD, b as BASELINE_FILE, bt as detectProject, c as silentLogger, et as FALLBACK_TOKENS, g as escapeData, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, i as componentFiles, j as updateBaseline, jt as toPosix, k as rulesRun, l as stderrLogger, o as loadDesignSystem, ot as excludedBy, q as CONFIG_FILES, rt as RULE_IDS, s as tokenFiles, tt as LEGACY_CONFIG_FILES, ut as rulesFor, v as NAME, w as findMoves, x as LEGACY_BASELINE_FILE, y as VERSION, yt as declaredConfig } from "./design-system-7wZoJsMm.js";
3
+ import { a as notReadNotices, i as missingDependencies, o as explainProject, s as librariesLeftOut, t as serveStdio } from "./stdio-B31hp2UZ.js";
4
+ import { r as loadTarget } from "./workspace-C5pMS6Xv.js";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
7
  import { escapePath, glob } from "tinyglobby";
@@ -324,7 +324,9 @@ function readManifest(root) {
324
324
  /**
325
325
  * Declares the config in the package's package.json, `"onsystem": "./onsystem.config.json"`,
326
326
  * and adds it to `files` when the package lists the files it publishes, so apps that install it
327
- * get it too. Writes ONSYSTEM.md for the apps' teams unless one exists. Returns what it wrote.
327
+ * get it too. Writes ONSYSTEM.md for the apps' teams unless one exists, and adds it to `files`
328
+ * as well, for the teams of apps that install the package, unless `files` leaves it out
329
+ * (`!ONSYSTEM.md`). Returns what it wrote.
328
330
  */
329
331
  function declarePackage(root, manifest, name, force, notes) {
330
332
  const written = [];
@@ -336,15 +338,18 @@ function declarePackage(root, manifest, name, force, notes) {
336
338
  changed = true;
337
339
  }
338
340
  const files = Array.isArray(json.files) ? json.files : void 0;
339
- if (files && !ships(files, CONFIG_FILE)) {
340
- files.push(CONFIG_FILE);
341
+ const added = [];
342
+ for (const file of [CONFIG_FILE, GUIDE_FILE]) {
343
+ if (!files || ships(files, file) || file === "ONSYSTEM.md" && excludes(files, file)) continue;
344
+ files.push(file);
345
+ added.push(file);
341
346
  changed = true;
342
347
  }
343
348
  if (changed) {
344
349
  fs.writeFileSync(manifest.file, `${JSON.stringify(json, null, manifest.indent)}${manifest.newline ? "\n" : ""}`);
345
350
  written.push("package.json");
346
351
  }
347
- notes.push(`This is a design-system package: package.json declares its config ("${PACKAGE_FIELD}": "${value}"${files ? ", also added to \"files\"" : ""}), which every app that depends on ${name} reads, under its own config when it has one.`);
352
+ notes.push(`This is a design-system package: package.json declares its config ("${PACKAGE_FIELD}": "${value}"${added.length ? `, with ${added.join(" and ")} added to "files"` : ""}), which every app that depends on ${name} reads, under its own config when it has one.`);
348
353
  const guide = path.join(root, GUIDE_FILE);
349
354
  if (force || !fs.existsSync(guide)) {
350
355
  fs.writeFileSync(guide, consumerGuide(name));
@@ -625,6 +630,14 @@ function aliasFolder(alias, project) {
625
630
  }
626
631
  }
627
632
  }
633
+ /** Whether npm's `files` list leaves `file` out with a negated entry: `!ONSYSTEM.md`, `!*.md`. */
634
+ function excludes(files, file) {
635
+ return files.some((entry) => {
636
+ if (typeof entry !== "string" || !entry.startsWith("!")) return false;
637
+ const pattern = entry.slice(1).replace(/^\.?\//, "");
638
+ return pattern === file || matchesGlob(file, pattern);
639
+ });
640
+ }
628
641
  /** Whether npm's `files` list publishes `file`, a path relative to the package. */
629
642
  function ships(files, file) {
630
643
  return files.some((entry) => {
@@ -387,7 +387,7 @@ function moduleFile(base, extensions = MODULE_EXTENSIONS, within) {
387
387
  base,
388
388
  ...extensions.map((ext) => `${base}${ext}`),
389
389
  ...extensions.map((ext) => path.join(base, `index${ext}`))
390
- ].find((file) => isFile$1(file) && !/(?:^|[\\/])node_modules[\\/]/.test(within ? path.relative(within, file) : file));
390
+ ].find((file) => isFile$2(file) && !/(?:^|[\\/])node_modules[\\/]/.test(within ? path.relative(within, file) : file));
391
391
  }
392
392
  const BUILD_CONFIGS = [
393
393
  "tsup",
@@ -580,7 +580,7 @@ function reexports(source) {
580
580
  barrel
581
581
  };
582
582
  }
583
- function isFile$1(file) {
583
+ function isFile$2(file) {
584
584
  try {
585
585
  return fs.statSync(file).isFile();
586
586
  } catch {
@@ -985,7 +985,7 @@ function fromComponentsJson(root, dir, json, project, packages, note = ignore) {
985
985
  */
986
986
  function withAppStylesheets(root, named) {
987
987
  if (named.every((file) => isInside(root, file))) return named;
988
- 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);
989
989
  if (!own.length) return named;
990
990
  const imported = new Set(importedStylesheets(own, stylesheetResolver(root)));
991
991
  return [...own, ...named.filter((file) => !imported.has(file))];
@@ -1145,7 +1145,7 @@ function fromPackage(root, dir, pkg, note = ignore, installed = false) {
1145
1145
  for (const mapping of mappings) {
1146
1146
  const file = path.resolve(root, mapping.target);
1147
1147
  if (installed && isCompiledExport(dir, file)) continue;
1148
- if (mapping.target.includes("*") || !SCRIPT_FILE$1.test(file) || !isFile(file)) {
1148
+ if (mapping.target.includes("*") || !SCRIPT_FILE$1.test(file) || !isFile$1(file)) {
1149
1149
  add(mapping, COMPONENT_FILE.test(mapping.target));
1150
1150
  continue;
1151
1151
  }
@@ -1460,7 +1460,7 @@ function stylesheetResolver(root, { importPath } = {}) {
1460
1460
  }
1461
1461
  return designSystems.get(name);
1462
1462
  };
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(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));
1464
1464
  return (specifier, fromFile) => {
1465
1465
  if (/^(?:[a-z][\w+.-]*:|\/)/i.test(specifier)) return void 0;
1466
1466
  const relative = stylesheet(path.resolve(path.dirname(fromFile), specifier), installedPackageOf(fromFile));
@@ -1491,7 +1491,7 @@ function installedStylesheet(dir, subpath) {
1491
1491
  if (!match) return void 0;
1492
1492
  const target = exportTargets(match.value, APP_CONDITIONS).map((t) => t.replaceAll("*", match.middle)).find((t) => t.endsWith(".css"));
1493
1493
  const file = target === void 0 ? void 0 : path.resolve(dir, target);
1494
- 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;
1495
1495
  }
1496
1496
  /**
1497
1497
  * The folder of `name` installed from a registry, when zero config reads it as a design system:
@@ -1521,12 +1521,15 @@ function installedPackageOf(file) {
1521
1521
  function exportedFile(dir, subpath, accept) {
1522
1522
  for (const { middle, value } of exportMatches(dir, subpath)) for (const target of exportTargets(value)) {
1523
1523
  const file = path.resolve(dir, target.replaceAll("*", middle));
1524
- if (isInside(dir, file) && accept(file) && isFile(file)) return file;
1524
+ if (isInside(dir, file) && accept(file) && isFile$1(file)) return file;
1525
1525
  }
1526
1526
  }
1527
1527
  /**
1528
1528
  * The entries of package `dir`'s `exports` that map `subpath`, with what their `*` stands for:
1529
- * an exact subpath first, then the most specific pattern, as Node.js picks them.
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.
1530
1533
  */
1531
1534
  function exportMatches(dir, subpath) {
1532
1535
  const exports = readJson$1(path.join(dir, "package.json"))?.exports;
@@ -1538,22 +1541,35 @@ function exportMatches(dir, subpath) {
1538
1541
  if (key === subpath) matches.push({
1539
1542
  middle: "",
1540
1543
  value,
1541
- rank: Infinity
1544
+ rank: Infinity,
1545
+ length: key.length
1542
1546
  });
1543
1547
  continue;
1544
1548
  }
1549
+ if (key.lastIndexOf("*") !== star) continue;
1545
1550
  const prefix = key.slice(0, star);
1546
1551
  const suffix = key.slice(star + 1);
1547
- if (subpath.startsWith(prefix) && subpath.endsWith(suffix) && subpath.length >= prefix.length + suffix.length) {
1552
+ if (subpath.startsWith(prefix) && subpath.endsWith(suffix) && subpath.length > prefix.length + suffix.length) {
1548
1553
  const middle = subpath.slice(prefix.length, subpath.length - suffix.length);
1549
1554
  matches.push({
1550
1555
  middle,
1551
1556
  value,
1552
- rank: prefix.length
1557
+ rank: prefix.length,
1558
+ length: key.length
1553
1559
  });
1554
1560
  }
1555
1561
  }
1556
- 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));
1557
1573
  }
1558
1574
  /** Every target of an export condition map, in order, but its types, or those of `conditions` alone. */
1559
1575
  function exportTargets(value, conditions) {
@@ -1603,7 +1619,7 @@ function declaredConfig(dir, pkg = readJson$1(path.join(dir, "package.json"))) {
1603
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".` };
1604
1620
  const file = path.resolve(dir, value);
1605
1621
  if (!isInside(dir, file) || file === path.resolve(dir)) return { problem: `${field} names ${JSON.stringify(value)}, which is outside the package.` };
1606
- 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.` };
1607
1623
  return { file };
1608
1624
  }
1609
1625
  /**
@@ -1722,7 +1738,7 @@ function scopeOf$1(name) {
1722
1738
  function declaresTypes(dir, pkg) {
1723
1739
  if (typeof pkg.types === "string" || typeof pkg.typings === "string") return true;
1724
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));
1725
- return hasTypes(pkg.exports) || isFile(path.join(dir, "index.d.ts"));
1741
+ return hasTypes(pkg.exports) || isFile$1(path.join(dir, "index.d.ts"));
1726
1742
  }
1727
1743
  /** Files that hold no component to read: declarations, tests and stories, as `exclude` has them. */
1728
1744
  const NOT_COMPONENT_SOURCES = [
@@ -2061,7 +2077,7 @@ function readJson$1(file) {
2061
2077
  function asRecord(value) {
2062
2078
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2063
2079
  }
2064
- function isFile(file) {
2080
+ function isFile$1(file) {
2065
2081
  try {
2066
2082
  return fs.statSync(file).isFile();
2067
2083
  } catch {
@@ -8243,6 +8259,7 @@ async function loadTokens(root, sources) {
8243
8259
  const warnings = [];
8244
8260
  const all = [];
8245
8261
  const sheets = [];
8262
+ const sheetFiles = [];
8246
8263
  let sheetsAt;
8247
8264
  for (const source of sources) {
8248
8265
  const rel = relativePath(root, source.file);
@@ -8266,6 +8283,7 @@ async function loadTokens(root, sources) {
8266
8283
  warnings.push(...result.warnings);
8267
8284
  } else {
8268
8285
  sheets.push(readCssSheet(text, rel));
8286
+ sheetFiles.push(rel);
8269
8287
  sheetsAt ??= all.length;
8270
8288
  }
8271
8289
  } catch (error) {
@@ -8273,6 +8291,9 @@ async function loadTokens(root, sources) {
8273
8291
  }
8274
8292
  }
8275
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];
8276
8297
  const result = cssSheetTokens(sheets);
8277
8298
  all.splice(sheetsAt ?? all.length, 0, ...result.tokens);
8278
8299
  warnings.push(...result.warnings);
@@ -8282,6 +8303,43 @@ async function loadTokens(root, sources) {
8282
8303
  warnings
8283
8304
  };
8284
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
+ }
8285
8343
  /**
8286
8344
  * DTCG files and the CSS generated from them often describe the same tokens.
8287
8345
  * Merge entries that share a custom property: keep the DTCG name and
@@ -8976,6 +9034,8 @@ var LintTarget = class {
8976
9034
  sources;
8977
9035
  /** Published libraries the model read from type declarations: `@mui/material`. */
8978
9036
  libraries;
9037
+ /** The name in each package.json read, by its path: undefined where there is none. */
9038
+ manifests = /* @__PURE__ */ new Map();
8979
9039
  /**
8980
9040
  * `resolver` finds the module an import points at and what it exports
8981
9041
  * (`@calcom/ui/components/icon` → `components/icon/index.ts`), so a name from
@@ -9052,14 +9112,49 @@ var LintTarget = class {
9052
9112
  * model left out rather than an invented one: its module exists outside the
9053
9113
  * folders extraction read and exports `name`, or re-exports from such a
9054
9114
  * module (an excluded `icons/` folder, a barrel's `export * from "./icons"`).
9055
- * A module declared in a package's `exports` but not on disk counts too,
9056
- * 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.
9057
9120
  */
9058
9121
  outsideModel(specifier, name, fromFile = "snippet.tsx") {
9059
9122
  const location = this.resolver?.locate(specifier, fromFile);
9060
9123
  if (!location) return false;
9061
- if (!location.file) return !this.importPaths.has(specifier) && specifier !== this.config.importPath;
9062
- return this.mayExport(location.file, name, 0);
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
+ }
9063
9158
  }
9064
9159
  /**
9065
9160
  * The component a JSX tag means when `name` is imported from `specifier`, among the
@@ -9236,6 +9331,30 @@ function fit(component, element) {
9236
9331
  function isPackageName(specifier) {
9237
9332
  return specifier.startsWith("@") ? /^@[^/]+\/[^/]+$/.test(specifier) : /^[a-z0-9][^/]*$/i.test(specifier);
9238
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
+ }
9239
9358
  function stripExtension(file) {
9240
9359
  return file.replace(/\.(?:[cm]?[jt]sx?)$/, "");
9241
9360
  }
@@ -12124,7 +12243,7 @@ function isObject(value) {
12124
12243
  // package.json
12125
12244
  var name = "onsystem";
12126
12245
  // src/version.ts
12127
- const VERSION = "0.10.2";
12246
+ const VERSION = "0.10.3";
12128
12247
  const NAME = name;
12129
12248
  // src/lint/sarif.ts
12130
12249
  const SCHEMA = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
@@ -1,5 +1,5 @@
1
- import { At as relativePath, i as componentFiles, ot as excludedBy, r as buildModel } from "./design-system-rl__N6tw.js";
2
- import { r as loadTarget } from "./workspace-INef0Yqt.js";
1
+ import { At as relativePath, i as componentFiles, ot as excludedBy, r as buildModel } from "./design-system-7wZoJsMm.js";
2
+ import { r as loadTarget } from "./workspace-C5pMS6Xv.js";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { parentPort } from "node:worker_threads";
package/dist/eslint.js CHANGED
@@ -1,4 +1,4 @@
1
- import { At as relativePath, N as RULES, Q as DEFAULT_TEST_FOLDERS, X as DEFAULT_SEVERITY, Z as DEFAULT_TEST_EXCLUDE, t as DesignSystem, v as NAME, y as VERSION } from "./design-system-rl__N6tw.js";
1
+ import { At as relativePath, N as RULES, Q as DEFAULT_TEST_FOLDERS, X as DEFAULT_SEVERITY, Z as DEFAULT_TEST_EXCLUDE, t as DesignSystem, v as NAME, y as VERSION } from "./design-system-7wZoJsMm.js";
2
2
  import path from "node:path";
3
3
  import { MessageChannel, Worker, receiveMessageOnPort } from "node:worker_threads";
4
4
  // src/eslint/bridge.ts
package/dist/index.d.ts CHANGED
@@ -269,7 +269,7 @@ export declare class TokenIndex {
269
269
  /** What the lint rules need to know about the design system. */
270
270
  declare class LintTarget {
271
271
  readonly model: DesignSystemModel;
272
- readonly config: Pick<ResolvedConfig, 'elements' | 'importPath'> & Partial<Pick<ResolvedConfig, 'designSystems'>>;
272
+ readonly config: Pick<ResolvedConfig, 'elements' | 'importPath'> & Partial<Pick<ResolvedConfig, 'designSystems' | 'imports'>>;
273
273
  private readonly resolver?;
274
274
  readonly components: Map<string, ComponentInfo>;
275
275
  /** Every component under each name and alias, when two design systems share one (`Button`). */
@@ -291,13 +291,15 @@ declare class LintTarget {
291
291
  private readonly sources?;
292
292
  /** Published libraries the model read from type declarations: `@mui/material`. */
293
293
  readonly libraries: Set<string>;
294
+ /** The name in each package.json read, by its path: undefined where there is none. */
295
+ private readonly manifests;
294
296
  /**
295
297
  * `resolver` finds the module an import points at and what it exports
296
298
  * (`@calcom/ui/components/icon` → `components/icon/index.ts`), so a name from
297
299
  * a module the model left out is not taken for an invented one. Without it,
298
300
  * every missing name is invented.
299
301
  */
300
- constructor(model: DesignSystemModel, config: Pick<ResolvedConfig, 'elements' | 'importPath'> & Partial<Pick<ResolvedConfig, 'designSystems'>>, resolver?: Pick<ModuleResolver, "locate" | "exportsOf"> | undefined);
302
+ constructor(model: DesignSystemModel, config: Pick<ResolvedConfig, 'elements' | 'importPath'> & Partial<Pick<ResolvedConfig, 'designSystems' | 'imports'>>, resolver?: Pick<ModuleResolver, "locate" | "exportsOf"> | undefined);
301
303
  /**
302
304
  * Whether an import specifier points at the design system. Relative
303
305
  * specifiers are resolved against `fromFile` (relative to the root) and
@@ -318,10 +320,27 @@ declare class LintTarget {
318
320
  * model left out rather than an invented one: its module exists outside the
319
321
  * folders extraction read and exports `name`, or re-exports from such a
320
322
  * module (an excluded `icons/` folder, a barrel's `export * from "./icons"`).
321
- * A module declared in a package's `exports` but not on disk counts too,
322
- * except the design system's own import path.
323
+ * A module declared in a package's `exports` but not on disk counts too (built to `dist/`,
324
+ * not built yet), except the design system's own import path, and one whose export, as the
325
+ * package.json writes it, leads to a source file only: `@acme/ui/components/tooltip` is
326
+ * `src/components/tooltip.tsx` through `"./components/*": "./src/components/*.tsx"`, and with
327
+ * no such file it does not exist.
323
328
  */
324
329
  outsideModel(specifier: string, name: string, fromFile?: string): boolean;
330
+ /**
331
+ * The module a design-system package's `exports` lead `specifier` to, relative to the root, as
332
+ * its package.json writes them: the first of the entry's targets that is on disk, else, when
333
+ * each of them is a source file, the first (which does not exist). Undefined when a target
334
+ * leads to a build folder (`"./*": "./dist/components/*.js"`, maybe not built yet, whose
335
+ * sources may be anywhere), the entry blocks the subpath (`null`), or no entry maps it.
336
+ */
337
+ private exportedSource;
338
+ /**
339
+ * The folder of package `name` that `file` is in: the nearest package.json above it with a
340
+ * name, when that name is `name`. One without a name (`{"type": "module"}` in a subfolder) is
341
+ * no package's. Each package.json is read once.
342
+ */
343
+ private packageDir;
325
344
  /**
326
345
  * The component a JSX tag means when `name` is imported from `specifier`, among the
327
346
  * model's components of that name: the one declared where the import leads, through
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { F as loadTokens, G as parseDoc, H as parseDesignMd, I as mergeTokens, K as extractComponents, L as toDesignMd, N as RULES, P as TokenIndex, R as toDtcg, U as parseCssTokens, V as parseDtcgTokens, W as attachDocs, _ as formatDiagnostics, a as fingerprint, at as defineConfig, ct as loadConfig, f as searchComponents, ft as findVariantDefinitions, it as configJsonSchema, m as checkSource, n as DesignSystemHost, o as loadDesignSystem, p as applyFixes, r as buildModel, rt as RULE_IDS, t as DesignSystem, y as VERSION } from "./design-system-rl__N6tw.js";
2
- import { n as INSTRUCTIONS, r as createServer, t as serveStdio } from "./stdio-CkczPt_1.js";
1
+ import { F as loadTokens, G as parseDoc, H as parseDesignMd, I as mergeTokens, K as extractComponents, L as toDesignMd, N as RULES, P as TokenIndex, R as toDtcg, U as parseCssTokens, V as parseDtcgTokens, W as attachDocs, _ as formatDiagnostics, a as fingerprint, at as defineConfig, ct as loadConfig, f as searchComponents, ft as findVariantDefinitions, it as configJsonSchema, m as checkSource, n as DesignSystemHost, o as loadDesignSystem, p as applyFixes, r as buildModel, rt as RULE_IDS, t as DesignSystem, y as VERSION } from "./design-system-7wZoJsMm.js";
2
+ import { n as INSTRUCTIONS, r as createServer, t as serveStdio } from "./stdio-B31hp2UZ.js";
3
3
  export { DesignSystem, DesignSystemHost, INSTRUCTIONS, RULES, RULE_IDS, TokenIndex, VERSION, applyFixes, attachDocs, buildModel, checkSource, configJsonSchema, createServer, defineConfig, extractComponents, findVariantDefinitions, fingerprint, formatDiagnostics, loadConfig, loadDesignSystem, loadTokens, mergeTokens, parseCssTokens, parseDesignMd, parseDoc, parseDtcgTokens, searchComponents, serveStdio, toDesignMd, toDtcg };
@@ -1,5 +1,5 @@
1
- import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Et as SCRIPT_FILE, Ft as truncate, It as unique, J as ConfigError, Lt as defaultProjectConfig, Mt as closest, Nt as json, Pt as plural, X as DEFAULT_SEVERITY, bt as detectProject, ct as loadConfig, d as descriptionOf, dt as formatPx, f as searchComponents, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, kt as pathOf, mt as LIBRARY_IDS, n as DesignSystemHost, ot as excludedBy, pt as LIBRARIES, q as CONFIG_FILES, r as buildModel, rt as RULE_IDS, tt as LEGACY_CONFIG_FILES, u as buildSearchIndex, v as NAME, xt as findDesignSystemPackage, y as VERSION, z as TOKEN_CATEGORIES } from "./design-system-rl__N6tw.js";
2
- import { i as projectDirOf, n as hasProjectMarker, r as loadTarget, t as findProjectRoot } from "./workspace-INef0Yqt.js";
1
+ import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Et as SCRIPT_FILE, Ft as truncate, It as unique, J as ConfigError, Lt as defaultProjectConfig, Mt as closest, Nt as json, Pt as plural, X as DEFAULT_SEVERITY, bt as detectProject, ct as loadConfig, d as descriptionOf, dt as formatPx, f as searchComponents, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, kt as pathOf, mt as LIBRARY_IDS, n as DesignSystemHost, ot as excludedBy, pt as LIBRARIES, q as CONFIG_FILES, r as buildModel, rt as RULE_IDS, tt as LEGACY_CONFIG_FILES, u as buildSearchIndex, v as NAME, xt as findDesignSystemPackage, y as VERSION, z as TOKEN_CATEGORIES } from "./design-system-7wZoJsMm.js";
2
+ import { i as projectDirOf, n as hasProjectMarker, r as loadTarget, t as findProjectRoot } from "./workspace-C5pMS6Xv.js";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -19,8 +19,11 @@ function explainProject(ds, cwd, indent = "") {
19
19
  const out = (line = "") => lines.push(line ? `${indent}${line}` : "");
20
20
  out("How the design system was found");
21
21
  for (const step of packageSteps(config, cwd)) out(` ${(step.accepted ? "found" : "no").padEnd(6)} ${step.candidate}: ${step.reason}`);
22
- if (config.configured) out(` components come from ${config.inherits && config.designSystemPackage ? display(cwd, config.designSystemPackage.config) : config.configFile ? `${display(cwd, config.configFile)}${over(config, cwd)}` : "--components"}, so zero config did not look`);
23
- else {
22
+ if (config.configured) {
23
+ out(` components come from ${config.inherits && config.designSystemPackage ? display(cwd, config.designSystemPackage.config) : config.configFile ? `${display(cwd, config.configFile)}${over(config, cwd)}` : "--components"}, so zero config did not look`);
24
+ const unpublished = unpublishedComponents(ds);
25
+ if (unpublished) out(` ${unpublished}`);
26
+ } else {
24
27
  const steps = [];
25
28
  detectProject(config.root, config.tsconfig, steps, config.designSystemPackage);
26
29
  for (const step of steps) out(` ${(step.accepted ? "found" : "no").padEnd(6)} ${step.candidate}: ${step.reason}`);
@@ -85,6 +88,22 @@ function explainProject(ds, cwd, indent = "") {
85
88
  }
86
89
  return lines;
87
90
  }
91
+ /**
92
+ * Why a project that takes on the config of a design-system package installed in node_modules
93
+ * knows none of its components: the package publishes neither their sources nor their type
94
+ * declarations, or `"libraries": false` turns off the declarations it publishes. The one message
95
+ * for it, where a project without components would otherwise be told to check the "components"
96
+ * globs. Undefined otherwise.
97
+ */
98
+ function unpublishedComponents(ds) {
99
+ const declared = ds.config.designSystemPackage;
100
+ if (!declared?.installed || !ds.config.inherits || ds.model.components.length) return void 0;
101
+ const tokens = ds.model.tokens.length ? " The token rules still check against the tokens it publishes." : "";
102
+ const name = pathOf(declared.name);
103
+ const see = "See https://onsystem.vercel.app/docs/design-system-packages#what-apps-get";
104
+ if (ds.config.librariesOff?.some((library) => library.package === declared.name)) return `${name} publishes no component sources, and "libraries": false in the config turns off the type declarations it publishes, so its components are unknown here and the component rules did not run: set "libraries" to [], which reads its declarations and no library, or remove it.${tokens} ${see}`;
105
+ return `${name} publishes neither its component sources nor their type declarations, so its components are unknown here and the component rules did not run: ask its team to add its sources to "files" in its package.json, or to publish type declarations.${tokens} ${see}`;
106
+ }
88
107
  /** Why a library the project depends on is not read: `left out by "libraries" in onsystem.config.json`. */
89
108
  function librariesLeftOut(config, cwd) {
90
109
  return `left out by "libraries" in ${config.configFile ? display(cwd, config.configFile) : "the config"}`;
@@ -562,8 +581,12 @@ function listTail(listed) {
562
581
  if (listed.omitted) lines.push("", `${listed.omitted.toLocaleString("en-US")} more not shown: pass a higher \`limit\`, or a \`query\`.`);
563
582
  return lines;
564
583
  }
565
- /** The shared explanation when a project has no design-system components to show. */
566
- function noComponentsMessage(root, project, monorepo = false) {
584
+ /**
585
+ * The shared explanation when a project has no design-system components to show. `reason`, when
586
+ * known, says why instead ({@link unpublishedComponents}).
587
+ */
588
+ function noComponentsMessage(root, project, monorepo = false, reason) {
589
+ if (reason) return answer([reason]);
567
590
  if (monorepo) return answer([`No design-system components found in any project of this monorepo (root: ${root}). Check the "components" globs in the config. Run \`onsystem inspect\` there to see what was looked at.`]);
568
591
  return answer([`No design-system components found${project ? ` in ${pathOf(project)}` : ""}. Check the "components" globs in the config (root: ${root}).`]);
569
592
  }
@@ -571,7 +594,7 @@ function noComponentsMessage(root, project, monorepo = false) {
571
594
  function renderComponentList(ds, project, filter = {}) {
572
595
  const roots = ds.roots();
573
596
  const where = project ? ` in ${pathOf(project)}` : "";
574
- if (!roots.length) return noComponentsMessage(ds.root, project);
597
+ if (!roots.length) return noComponentsMessage(ds.root, project, false, unpublishedComponents(ds));
575
598
  const listed = listComponents(roots, (c) => c, filter);
576
599
  if (!listed.matched) return answer([`No components match ${filterText(filter)}${where} (${roots.length} in all). Call list_components without filters to see them.`]);
577
600
  const marks = new Marks();
@@ -686,7 +709,7 @@ function renderAmbiguous(name, entries) {
686
709
  }
687
710
  /** get_component's answer for a name no component has, with the nearest and all of them. */
688
711
  function renderNotFound(name, guess, names, where = "") {
689
- return answer([`No component named ${json(name)}${where}.` + (guess ? ` Did you mean ${json(guess)}?` : "") + ` Available: ${names.join(", ")}.`]);
712
+ return answer([`No component named ${json(name)}${where}.` + (guess ? ` Did you mean ${json(guess)}?` : "") + (names.length ? ` Available: ${names.join(", ")}.` : "")]);
690
713
  }
691
714
  /**
692
715
  * The build-with-design-system prompt: `task`, the steps, and what the design system has, its
@@ -1552,7 +1575,7 @@ function register(serve, { getDesignSystem, getProjects }) {
1552
1575
  const { ds } = found;
1553
1576
  const component = ds.getComponent(name);
1554
1577
  if (!component) {
1555
- if (!ds.roots().length) return refuse(noComponentsMessage(ds.root, found.project?.dir));
1578
+ if (!ds.roots().length) return refuse(noComponentsMessage(ds.root, found.project?.dir, false, unpublishedComponents(ds)));
1556
1579
  const names = ds.roots().map((c) => c.name);
1557
1580
  return refuse(renderNotFound(name, ds.suggestComponent(name), names));
1558
1581
  }
@@ -1,4 +1,4 @@
1
- import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Tt as workspacePatterns, ct as loadConfig, i as componentFiles, lt as loadConfigWith, nt as PROJECT_MARKERS, st as hasOwnConfig, vt as PackageFinder, wt as workspacePackageDirs, xt as findDesignSystemPackage } from "./design-system-rl__N6tw.js";
1
+ import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Tt as workspacePatterns, ct as loadConfig, i as componentFiles, lt as loadConfigWith, nt as PROJECT_MARKERS, st as hasOwnConfig, vt as PackageFinder, wt as workspacePackageDirs, xt as findDesignSystemPackage } from "./design-system-7wZoJsMm.js";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import os from "node:os";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onsystem",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "mcpName": "io.github.dgesteves/onsystem",
5
5
  "description": "Keeps coding agents on your design system: catches palette colors, native elements and APIs your system lacks, at every edit and pull request, with the fix. Set it up once in your design-system package; every app in your monorepo gets it.",
6
6
  "keywords": [