onsystem 0.10.1 → 0.10.2
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 +6 -9
- package/dist/{config-BbTW1jKU.d.ts → config-JaNAiR9Z.d.ts} +6 -8
- package/dist/{design-system-CPH4C7-1.js → design-system-rl__N6tw.js} +102 -102
- 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 +17 -51
- package/dist/index.js +2 -2
- package/dist/{stdio-BK2kMN1x.js → stdio-CkczPt_1.js} +152 -50
- package/dist/{workspace-BINqxi7p.js → workspace-INef0Yqt.js} +10 -19
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { C as TOKEN_CATEGORIES, D as VariantInfo, E as TokenCategory, O as ImportMapping, S as SourceLocation, T as Token, _ as DocSection, a as ResolvedRule, b as PropInfo, c as configJsonSchema, d as CheckResult, f as ComponentDocs, g as Diagnostic, h as DesignSystemModel, i as ResolvedConfig, l as defineConfig, m as CompoundVariantInfo, n as LoadConfigOptions, o as RuleId, p as ComponentInfo, r as RULE_IDS, s as RuleOptions, t as Config, u as loadConfig, v as ExampleInfo, w as TextEdit, x as Severity, y as InheritedProps } from "./config-
|
|
1
|
+
import { C as TOKEN_CATEGORIES, D as VariantInfo, E as TokenCategory, O as ImportMapping, S as SourceLocation, T as Token, _ as DocSection, a as ResolvedRule, b as PropInfo, c as configJsonSchema, d as CheckResult, f as ComponentDocs, g as Diagnostic, h as DesignSystemModel, i as ResolvedConfig, l as defineConfig, m as CompoundVariantInfo, n as LoadConfigOptions, o as RuleId, p as ComponentInfo, r as RULE_IDS, s as RuleOptions, t as Config, u as loadConfig, v as ExampleInfo, w as TextEdit, x as Severity, y as InheritedProps } from "./config-JaNAiR9Z.js";
|
|
2
2
|
import ts from "typescript";
|
|
3
3
|
import { Oklch } from "culori";
|
|
4
4
|
import { McpServer, Transport } from "@modelcontextprotocol/server";
|
|
5
|
-
//#region src/lint/analyze.d.ts
|
|
6
5
|
interface ImportBinding {
|
|
7
6
|
source: string;
|
|
8
7
|
/** Imported name, `default`, or `*` for namespace imports. */
|
|
@@ -37,8 +36,6 @@ interface Analysis {
|
|
|
37
36
|
classStrings: ClassString[];
|
|
38
37
|
styles: StyleObject[];
|
|
39
38
|
}
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/modules.d.ts
|
|
42
39
|
/** Where an import points: a file relative to the root, or `{}` for a declared module not on disk. */
|
|
43
40
|
interface ModuleLocation {
|
|
44
41
|
file?: string;
|
|
@@ -83,8 +80,6 @@ declare class ModuleResolver {
|
|
|
83
80
|
exportsOf(file: string): ModuleExports | undefined;
|
|
84
81
|
private createResolver;
|
|
85
82
|
}
|
|
86
|
-
//#endregion
|
|
87
|
-
//#region src/tokens/roles.d.ts
|
|
88
83
|
/**
|
|
89
84
|
* What a color utility paints, which decides the tokens that fit it: `text-*`
|
|
90
85
|
* wants a foreground, `bg-*` a surface, `border-*` a border color.
|
|
@@ -100,8 +95,6 @@ type ColorRole = 'text' | 'surface' | 'line';
|
|
|
100
95
|
* palette (`color-red-500`).
|
|
101
96
|
*/
|
|
102
97
|
type ScopedFamily = string;
|
|
103
|
-
//#endregion
|
|
104
|
-
//#region src/tokens/css.d.ts
|
|
105
98
|
/**
|
|
106
99
|
* Reads design tokens from CSS custom properties, the way shadcn/ui and
|
|
107
100
|
* Tailwind v4 projects define them:
|
|
@@ -114,14 +107,10 @@ export declare function parseCssTokens(css: string, file: string): {
|
|
|
114
107
|
tokens: Token[];
|
|
115
108
|
warnings: string[];
|
|
116
109
|
};
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region src/tokens/design-md.d.ts
|
|
119
110
|
export declare function parseDesignMd(text: string, file: string): {
|
|
120
111
|
tokens: Token[];
|
|
121
112
|
warnings: string[];
|
|
122
113
|
};
|
|
123
|
-
//#endregion
|
|
124
|
-
//#region src/tokens/dtcg.d.ts
|
|
125
114
|
interface DtcgOptions {
|
|
126
115
|
/** Prefix for generated custom property names: `ds` → `--ds-color-primary`. */
|
|
127
116
|
prefix?: string;
|
|
@@ -130,8 +119,6 @@ export declare function parseDtcgTokens(text: string, file: string, options?: Dt
|
|
|
130
119
|
tokens: Token[];
|
|
131
120
|
warnings: string[];
|
|
132
121
|
};
|
|
133
|
-
//#endregion
|
|
134
|
-
//#region src/tokens/export.d.ts
|
|
135
122
|
/**
|
|
136
123
|
* Writes the tokens onsystem found as a DTCG 2025.10 file or a DESIGN.md. Both are grouped by
|
|
137
124
|
* category; reading the DTCG file back gives the same tokens, with the same names.
|
|
@@ -158,8 +145,6 @@ export declare function toDtcg(tokens: readonly Token[]): Exported;
|
|
|
158
145
|
export declare function toDesignMd(tokens: readonly Token[], options: {
|
|
159
146
|
name: string;
|
|
160
147
|
}): Exported;
|
|
161
|
-
//#endregion
|
|
162
|
-
//#region src/tokens/index.d.ts
|
|
163
148
|
interface TokenSource {
|
|
164
149
|
file: string;
|
|
165
150
|
/** DTCG only: prefix for generated custom property names. */
|
|
@@ -281,8 +266,6 @@ export declare class TokenIndex {
|
|
|
281
266
|
/** px value of a token-like length string, resolving `var()` against the token set. */
|
|
282
267
|
toPx(value: string): number | undefined;
|
|
283
268
|
}
|
|
284
|
-
//#endregion
|
|
285
|
-
//#region src/lint/target.d.ts
|
|
286
269
|
/** What the lint rules need to know about the design system. */
|
|
287
270
|
declare class LintTarget {
|
|
288
271
|
readonly model: DesignSystemModel;
|
|
@@ -378,8 +361,6 @@ declare class LintTarget {
|
|
|
378
361
|
/** Component names for "did you mean" suggestions. */
|
|
379
362
|
names(): string[];
|
|
380
363
|
}
|
|
381
|
-
//#endregion
|
|
382
|
-
//#region src/lint/context.d.ts
|
|
383
364
|
interface Report {
|
|
384
365
|
start: number;
|
|
385
366
|
end: number;
|
|
@@ -440,12 +421,8 @@ type Resolution =
|
|
|
440
421
|
{
|
|
441
422
|
kind: 'external';
|
|
442
423
|
};
|
|
443
|
-
//#endregion
|
|
444
|
-
//#region src/lint/rules/index.d.ts
|
|
445
424
|
/** Every rule, in the order they run. */
|
|
446
425
|
export declare const RULES: readonly Rule[];
|
|
447
|
-
//#endregion
|
|
448
|
-
//#region src/lint/format.d.ts
|
|
449
426
|
type OutputFormat = 'pretty' | 'json' | 'github' | 'sarif';
|
|
450
427
|
/**
|
|
451
428
|
* Renders check results:
|
|
@@ -468,8 +445,6 @@ export declare function formatDiagnostics(results: CheckResult[], format: Output
|
|
|
468
445
|
*/
|
|
469
446
|
uri?: ((file: string) => string) | undefined;
|
|
470
447
|
}): string;
|
|
471
|
-
//#endregion
|
|
472
|
-
//#region src/lint/index.d.ts
|
|
473
448
|
interface CheckOptions {
|
|
474
449
|
/** File name used in results and to pick TSX vs JSX parsing. Default `snippet.tsx`. */
|
|
475
450
|
filename?: string | undefined;
|
|
@@ -483,8 +458,6 @@ interface CheckOptions {
|
|
|
483
458
|
export declare function checkSource(code: string, target: LintTarget, options: CheckOptions): CheckResult;
|
|
484
459
|
/** Applies non-overlapping fixes from the end of the file backwards. */
|
|
485
460
|
export declare function applyFixes(code: string, diagnostics: Diagnostic[]): string;
|
|
486
|
-
//#endregion
|
|
487
|
-
//#region src/search/bm25.d.ts
|
|
488
461
|
/**
|
|
489
462
|
* Okapi BM25 over weighted fields (BM25F-style: field weights scale term
|
|
490
463
|
* frequency and document length). Small, dependency-free and deterministic,
|
|
@@ -512,8 +485,6 @@ declare class Bm25Index<T> {
|
|
|
512
485
|
get size(): number;
|
|
513
486
|
search(query: Map<string, number>, limit?: number): Bm25Hit<T>[];
|
|
514
487
|
}
|
|
515
|
-
//#endregion
|
|
516
|
-
//#region src/search/index.d.ts
|
|
517
488
|
interface SearchHit {
|
|
518
489
|
component: ComponentInfo;
|
|
519
490
|
score: number;
|
|
@@ -530,8 +501,6 @@ interface SearchHit {
|
|
|
530
501
|
* which is not an answer to give as the best match.
|
|
531
502
|
*/
|
|
532
503
|
export declare function searchComponents(index: Bm25Index<ComponentInfo>, query: string, limit?: number): SearchHit[];
|
|
533
|
-
//#endregion
|
|
534
|
-
//#region src/util/log.d.ts
|
|
535
504
|
/**
|
|
536
505
|
* Diagnostics go to stderr: in stdio mode stdout carries JSON-RPC and must
|
|
537
506
|
* never receive anything else.
|
|
@@ -541,8 +510,6 @@ interface Logger {
|
|
|
541
510
|
warn(message: string): void;
|
|
542
511
|
error(message: string): void;
|
|
543
512
|
}
|
|
544
|
-
//#endregion
|
|
545
|
-
//#region src/design-system.d.ts
|
|
546
513
|
/** A loaded design system: the model plus the indexes the tools query. */
|
|
547
514
|
export declare class DesignSystem {
|
|
548
515
|
readonly model: DesignSystemModel;
|
|
@@ -659,6 +626,11 @@ export declare class DesignSystemHost {
|
|
|
659
626
|
constructor(config: ResolvedConfig, options?: HostOptions);
|
|
660
627
|
get(): Promise<DesignSystem>;
|
|
661
628
|
reload(): Promise<DesignSystem>;
|
|
629
|
+
/**
|
|
630
|
+
* `build`, dropped if it fails with no earlier model to serve instead (a first build that
|
|
631
|
+
* fails), so the next `get()` builds again.
|
|
632
|
+
*/
|
|
633
|
+
private kept;
|
|
662
634
|
onChange(listener: (ds: DesignSystem) => void): () => void;
|
|
663
635
|
/** Watches the component, token and docs locations, the config and the tsconfig chain. */
|
|
664
636
|
watch(): void;
|
|
@@ -677,8 +649,6 @@ export declare class DesignSystemHost {
|
|
|
677
649
|
private reloadConfig;
|
|
678
650
|
private build;
|
|
679
651
|
}
|
|
680
|
-
//#endregion
|
|
681
|
-
//#region src/extract/components.d.ts
|
|
682
652
|
interface ExtractComponentsOptions {
|
|
683
653
|
root: string;
|
|
684
654
|
/** Absolute paths of the component source files. */
|
|
@@ -708,8 +678,6 @@ interface ExtractComponentsResult {
|
|
|
708
678
|
* `Tabs.List` via static members or `Object.assign`).
|
|
709
679
|
*/
|
|
710
680
|
export declare function extractComponents(options: ExtractComponentsOptions): ExtractComponentsResult;
|
|
711
|
-
//#endregion
|
|
712
|
-
//#region src/extract/cva.d.ts
|
|
713
681
|
/** A `cva()` (class-variance-authority) or `tv()` (tailwind-variants) definition. */
|
|
714
682
|
interface VariantDefinition {
|
|
715
683
|
/** The variable it is assigned to, e.g. `buttonVariants`. */
|
|
@@ -721,8 +689,6 @@ interface VariantDefinition {
|
|
|
721
689
|
}
|
|
722
690
|
/** Finds every `const x = cva(base, { variants, defaultVariants })` in a file. */
|
|
723
691
|
export declare function findVariantDefinitions(sourceFile: ts.SourceFile): Map<string, VariantDefinition>;
|
|
724
|
-
//#endregion
|
|
725
|
-
//#region src/extract/docs.d.ts
|
|
726
692
|
interface ParsedDoc {
|
|
727
693
|
file: string;
|
|
728
694
|
title?: string;
|
|
@@ -739,8 +705,6 @@ interface ParsedDoc {
|
|
|
739
705
|
export declare function parseDoc(text: string, file: string): ParsedDoc;
|
|
740
706
|
/** Attaches docs and their examples to components. Returns warnings for unmatched pages. */
|
|
741
707
|
export declare function attachDocs(components: ComponentInfo[], docs: ParsedDoc[]): string[];
|
|
742
|
-
//#endregion
|
|
743
|
-
//#region src/catalog.d.ts
|
|
744
708
|
/** One project's design system at a workspace root. */
|
|
745
709
|
interface ServedProject {
|
|
746
710
|
ds: DesignSystem;
|
|
@@ -809,8 +773,6 @@ declare class Catalog {
|
|
|
809
773
|
query?: string | undefined;
|
|
810
774
|
}): CatalogToken[];
|
|
811
775
|
}
|
|
812
|
-
//#endregion
|
|
813
|
-
//#region src/server/index.d.ts
|
|
814
776
|
export declare const INSTRUCTIONS = "This server describes the project's design system: its React components (props, variants, parts), design tokens and usage docs, and lints UI code against it.\nWhen writing or changing UI in this project:\n1. Find components with search_components (by intent) or list_components.\n2. Read get_component before using a component: use only the props and variant values it lists.\n3. Style with tokens from get_tokens, never with hex/rgb colors, arbitrary px values or Tailwind's default palette. chart-* and sidebar-* tokens are for charts and the sidebar only: for a status color the theme has no token for (success, warning, info), add a token or keep the Tailwind color as check_ui says, and never use a chart-* or sidebar-* token for it.\n4. Run check_ui on every snippet or file you write and fix all errors before you finish.\nIn a monorepo served from its root, pass `path` (the file you are editing) to list_components, get_component, search_components and get_tokens, and `path` or `filename` to check_ui, so each answer comes from that file's own project.";
|
|
815
777
|
interface CreateServerOptions {
|
|
816
778
|
/**
|
|
@@ -827,8 +789,6 @@ interface CreateServerOptions {
|
|
|
827
789
|
}
|
|
828
790
|
/** Builds the MCP server: five tools, two resources and one prompt. Transport-agnostic. */
|
|
829
791
|
export declare function createServer(options: CreateServerOptions): McpServer;
|
|
830
|
-
//#endregion
|
|
831
|
-
//#region src/server/stdio.d.ts
|
|
832
792
|
interface ServeOptions extends LoadConfigOptions {
|
|
833
793
|
cwd: string;
|
|
834
794
|
cache: boolean;
|
|
@@ -860,12 +820,18 @@ interface ServerHandle {
|
|
|
860
820
|
* project marker, or the monorepo root above that, served as if the server had been
|
|
861
821
|
* started there, except that a config there is read only when it is JSON: a prompt can
|
|
862
822
|
* choose the path, so a JavaScript or TypeScript config is not run. Each root found is
|
|
863
|
-
* loaded once and kept; a request without an absolute path is answered from
|
|
864
|
-
* found so far.
|
|
823
|
+
* loaded once and kept, by its real path; a request without an absolute path is answered from
|
|
824
|
+
* the one root found so far. The same holds for a `--root` (or a client's root) that is no
|
|
825
|
+
* project but holds projects in its folders, such as a folder of repositories, except that only
|
|
826
|
+
* an absolute path in that folder is answered, from the project in it that holds the file (never
|
|
827
|
+
* from a folder outside or above it), and a file in none of them from that folder, as before.
|
|
828
|
+
* Links are resolved: a path, or the project it leads to, that is really outside the folder is
|
|
829
|
+
* refused, a workspace in it serves only its packages that are really in it, and a project in it
|
|
830
|
+
* that a link replaces while the server runs is not built again from where the link leads.
|
|
831
|
+
*
|
|
832
|
+
* A load that fails, such as on an invalid config, is not kept: the next request loads
|
|
833
|
+
* again, so fixing the config is enough, also when it was invalid at startup.
|
|
865
834
|
*/
|
|
866
835
|
export declare function serveStdio(options: ServeOptions): Promise<ServerHandle>;
|
|
867
|
-
//#endregion
|
|
868
|
-
//#region src/version.d.ts
|
|
869
836
|
export declare const VERSION: string;
|
|
870
|
-
//#endregion
|
|
871
837
|
export { type CheckResult, type ComponentDocs, type ComponentInfo, type CompoundVariantInfo, type Config, type DesignSystemModel, type Diagnostic, type DocSection, type ExampleInfo, type HostOptions, type InheritedProps, type LoadConfigOptions, type LoadOptions, type OutputFormat, type PropInfo, RULE_IDS, type ResolvedConfig, type RuleId, type Severity, type SourceLocation, type TOKEN_CATEGORIES, type TextEdit, type Token, type TokenCategory, type VariantInfo, configJsonSchema, defineConfig, loadConfig };
|
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,
|
|
2
|
-
import { n as INSTRUCTIONS, r as createServer, t as serveStdio } from "./stdio-
|
|
1
|
+
import { F as loadTokens, G as parseDoc, H as parseDesignMd, I as mergeTokens, K as extractComponents, L as toDesignMd, N as RULES, P as TokenIndex, R as toDtcg, U as parseCssTokens, V as parseDtcgTokens, W as attachDocs, _ as formatDiagnostics, a as fingerprint, at as defineConfig, ct as loadConfig, f as searchComponents, ft as findVariantDefinitions, it as configJsonSchema, m as checkSource, n as DesignSystemHost, o as loadDesignSystem, p as applyFixes, r as buildModel, rt as RULE_IDS, t as DesignSystem, y as VERSION } from "./design-system-rl__N6tw.js";
|
|
2
|
+
import { n as INSTRUCTIONS, r as createServer, t as serveStdio } from "./stdio-CkczPt_1.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 {
|
|
2
|
-
import { n as hasProjectMarker, r as loadTarget, t as findProjectRoot } from "./workspace-
|
|
1
|
+
import { At as relativePath, Ct as isWorkspaceRoot, Dt as isInside, Et as SCRIPT_FILE, Ft as truncate, It as unique, J as ConfigError, Lt as defaultProjectConfig, Mt as closest, Nt as json, Pt as plural, X as DEFAULT_SEVERITY, bt as detectProject, ct as loadConfig, d as descriptionOf, dt as formatPx, f as searchComponents, gt as notReadYet, h as uncheckedNotice, ht as libraryTitle, kt as pathOf, mt as LIBRARY_IDS, n as DesignSystemHost, ot as excludedBy, pt as LIBRARIES, q as CONFIG_FILES, r as buildModel, rt as RULE_IDS, tt as LEGACY_CONFIG_FILES, u as buildSearchIndex, v as NAME, xt as findDesignSystemPackage, y as VERSION, z as TOKEN_CATEGORIES } from "./design-system-rl__N6tw.js";
|
|
2
|
+
import { i as projectDirOf, n as hasProjectMarker, r as loadTarget, t as findProjectRoot } from "./workspace-INef0Yqt.js";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -7,7 +7,7 @@ import * as z from "zod";
|
|
|
7
7
|
import fsp from "node:fs/promises";
|
|
8
8
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
|
|
9
9
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
10
|
-
|
|
10
|
+
// src/explain.ts
|
|
11
11
|
/**
|
|
12
12
|
* What `inspect --explain` says about one project: every candidate zero config looked
|
|
13
13
|
* at and why each was taken or not, the config it resolved to, tokens whose value did
|
|
@@ -151,8 +151,7 @@ function packageSteps(config, cwd) {
|
|
|
151
151
|
reason: `${declared.name} (${declared.installed ? "installed" : "a workspace package"}) declares ${display(cwd, declared.config)}, which this project ${config.configFile === declared.config ? "takes on" : `reads under ${display(cwd, config.configFile ?? "")}`}, with its paths from ${display(cwd, declared.dir)}${lookup.others.length ? `; ${lookup.others.join(", ")} also declare one, not read: one config is taken on, from the package the code imports most` : ""}${skipped}`
|
|
152
152
|
}];
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
//#region src/not-read.ts
|
|
154
|
+
// src/not-read.ts
|
|
156
155
|
const LIMITS_DOCS = "https://onsystem.vercel.app/docs/how-it-works#limits";
|
|
157
156
|
/**
|
|
158
157
|
* What `check` and `check_ui` could not read, said after the results as the notice for missing
|
|
@@ -228,8 +227,7 @@ function listOf$2(items, max = 3) {
|
|
|
228
227
|
if (items.length <= max) return `${items.slice(0, -1).join(", ")} and ${items.at(-1) ?? ""}`;
|
|
229
228
|
return `${items.slice(0, max).join(", ")} and ${items.length - max} more`;
|
|
230
229
|
}
|
|
231
|
-
|
|
232
|
-
//#region src/server/render.ts
|
|
230
|
+
// src/server/render.ts
|
|
233
231
|
/**
|
|
234
232
|
* Tool results are read by models, so they are rendered as compact Markdown:
|
|
235
233
|
* fewer tokens than JSON, and the same information. The JSON is still sent
|
|
@@ -1080,8 +1078,7 @@ function renderCheck(result, capped = capDiagnostics(result, Infinity), notices
|
|
|
1080
1078
|
lines.push("", result.errorCount ? "Fix every error, then run check_ui again." : "Only warnings: fix them if the design system has an equivalent.", ...after);
|
|
1081
1079
|
return answer(lines);
|
|
1082
1080
|
}
|
|
1083
|
-
|
|
1084
|
-
//#region src/server/index.ts
|
|
1081
|
+
// src/server/index.ts
|
|
1085
1082
|
const INSTRUCTIONS = `This server describes the project's design system: its React components (props, variants, parts), design tokens and usage docs, and lints UI code against it.
|
|
1086
1083
|
When writing or changing UI in this project:
|
|
1087
1084
|
1. Find components with search_components (by intent) or list_components.
|
|
@@ -1822,8 +1819,7 @@ function register(serve, { getDesignSystem, getProjects }) {
|
|
|
1822
1819
|
return prompted("Build UI with the design system", renderPrompt(task, "catalog" in found ? [...new Map(found.catalog.roots().map((e) => [e.component.name, e.component])).values()] : found.ds.roots(), "catalog" in found ? found.catalog.getTokens().map((t) => t.token) : found.ds.tokens, "catalog" in found ? found.set.dirs : void 0));
|
|
1823
1820
|
});
|
|
1824
1821
|
}
|
|
1825
|
-
|
|
1826
|
-
//#region src/catalog.ts
|
|
1822
|
+
// src/catalog.ts
|
|
1827
1823
|
/**
|
|
1828
1824
|
* Every project's components and tokens in one list, for a workspace root when no path
|
|
1829
1825
|
* picks a project. A component used by several projects (a design-system package) is
|
|
@@ -1965,8 +1961,7 @@ function packageOwner(root) {
|
|
|
1965
1961
|
return name;
|
|
1966
1962
|
};
|
|
1967
1963
|
}
|
|
1968
|
-
|
|
1969
|
-
//#region src/server/projects.ts
|
|
1964
|
+
// src/server/projects.ts
|
|
1970
1965
|
/**
|
|
1971
1966
|
* A workspace root's projects for the MCP server: one `DesignSystemHost` per project,
|
|
1972
1967
|
* created on first use and watched from then on, and the merged catalog, rebuilt when a
|
|
@@ -2025,6 +2020,7 @@ var WorkspaceProjects = class {
|
|
|
2025
2020
|
host = new DesignSystemHost(project.config, {
|
|
2026
2021
|
cache: this.options.cache,
|
|
2027
2022
|
logger: this.options.logger,
|
|
2023
|
+
build: this.options.build,
|
|
2028
2024
|
loadConfig: () => this.workspace.configFor(project)
|
|
2029
2025
|
});
|
|
2030
2026
|
host.onChange(() => this.options.onChange?.());
|
|
@@ -2033,8 +2029,7 @@ var WorkspaceProjects = class {
|
|
|
2033
2029
|
return host;
|
|
2034
2030
|
}
|
|
2035
2031
|
};
|
|
2036
|
-
|
|
2037
|
-
//#region src/server/stdio.ts
|
|
2032
|
+
// src/server/stdio.ts
|
|
2038
2033
|
/**
|
|
2039
2034
|
* Starts the server (on stdio unless another transport is given), for clients on
|
|
2040
2035
|
* either protocol era: a 2026-07-28 client (stateless, `server/discover`) and a
|
|
@@ -2054,8 +2049,17 @@ var WorkspaceProjects = class {
|
|
|
2054
2049
|
* project marker, or the monorepo root above that, served as if the server had been
|
|
2055
2050
|
* started there, except that a config there is read only when it is JSON: a prompt can
|
|
2056
2051
|
* choose the path, so a JavaScript or TypeScript config is not run. Each root found is
|
|
2057
|
-
* loaded once and kept; a request without an absolute path is answered from
|
|
2058
|
-
* found so far.
|
|
2052
|
+
* loaded once and kept, by its real path; a request without an absolute path is answered from
|
|
2053
|
+
* the one root found so far. The same holds for a `--root` (or a client's root) that is no
|
|
2054
|
+
* project but holds projects in its folders, such as a folder of repositories, except that only
|
|
2055
|
+
* an absolute path in that folder is answered, from the project in it that holds the file (never
|
|
2056
|
+
* from a folder outside or above it), and a file in none of them from that folder, as before.
|
|
2057
|
+
* Links are resolved: a path, or the project it leads to, that is really outside the folder is
|
|
2058
|
+
* refused, a workspace in it serves only its packages that are really in it, and a project in it
|
|
2059
|
+
* that a link replaces while the server runs is not built again from where the link leads.
|
|
2060
|
+
*
|
|
2061
|
+
* A load that fails, such as on an invalid config, is not kept: the next request loads
|
|
2062
|
+
* again, so fixing the config is enough, also when it was invalid at startup.
|
|
2059
2063
|
*/
|
|
2060
2064
|
function serveStdio$1(options) {
|
|
2061
2065
|
const { logger } = options;
|
|
@@ -2076,15 +2080,28 @@ function serveStdio$1(options) {
|
|
|
2076
2080
|
/**
|
|
2077
2081
|
* The project at `root` (the working directory when undefined), or a monorepo root's
|
|
2078
2082
|
* projects. `jsonConfigOnly` for a root found from a tool call's path: its configs, and those
|
|
2079
|
-
* of the projects in it, are read only when they are JSON, never run.
|
|
2083
|
+
* of the projects in it, are read only when they are JSON, never run. `within`: the folder of
|
|
2084
|
+
* projects it was found in, for the error on a config that is not JSON, and outside which no
|
|
2085
|
+
* workspace package is served, and no project or package is built.
|
|
2080
2086
|
*/
|
|
2081
|
-
const load = async (root, jsonConfigOnly = false) => {
|
|
2087
|
+
const load = async (root, jsonConfigOnly = false, within) => {
|
|
2082
2088
|
const configOptions = {
|
|
2083
2089
|
...options,
|
|
2084
2090
|
root,
|
|
2085
|
-
jsonConfigOnly
|
|
2091
|
+
jsonConfigOnly,
|
|
2092
|
+
jsonConfigWithin: jsonConfigOnly ? within?.dir : void 0
|
|
2086
2093
|
};
|
|
2087
|
-
const
|
|
2094
|
+
const target = await loadTarget(configOptions);
|
|
2095
|
+
const { config } = target;
|
|
2096
|
+
let { workspace } = target;
|
|
2097
|
+
if (workspace && within) {
|
|
2098
|
+
const outside = workspace.projects.filter((p) => !isInside(within.real, realPath(p.root)));
|
|
2099
|
+
if (outside.length) {
|
|
2100
|
+
logger.warn(`${pathOf(workspace.root)} names workspace packages outside ${pathOf(within.dir)}, the folder the server answers for (links resolved), which it leaves out: ${outside.map((p) => pathOf(p.root)).join(", ")}. To include them, start the server with --root ${pathOf(workspace.root)}.`);
|
|
2101
|
+
workspace = workspace.without(outside.map((p) => p.root));
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
const build = within ? buildWithin(within) : void 0;
|
|
2088
2105
|
for (const text of config.deprecations ?? []) logger.warn(text);
|
|
2089
2106
|
for (const text of (workspace ? workspace.warnings : config.warnings) ?? []) logger.warn(text);
|
|
2090
2107
|
if (workspace) {
|
|
@@ -2092,7 +2109,8 @@ function serveStdio$1(options) {
|
|
|
2092
2109
|
cache: options.cache,
|
|
2093
2110
|
watch: options.watch,
|
|
2094
2111
|
logger,
|
|
2095
|
-
onChange: notify
|
|
2112
|
+
onChange: notify,
|
|
2113
|
+
build
|
|
2096
2114
|
});
|
|
2097
2115
|
logger.info(`monorepo root: ${workspace.projects.length} of ${workspace.projects.length + workspace.others.length} workspace packages have a design system${workspace.projects.length ? ` (${workspace.projects.map((p) => p.dir).join(", ")})` : ""}`);
|
|
2098
2116
|
closers.push(() => {
|
|
@@ -2104,7 +2122,8 @@ function serveStdio$1(options) {
|
|
|
2104
2122
|
const host = new DesignSystemHost(config, {
|
|
2105
2123
|
cache: options.cache,
|
|
2106
2124
|
logger,
|
|
2107
|
-
loadConfig: () => loadConfig(configOptions)
|
|
2125
|
+
loadConfig: () => loadConfig(configOptions),
|
|
2126
|
+
build
|
|
2108
2127
|
});
|
|
2109
2128
|
host.onChange(notify);
|
|
2110
2129
|
if (options.watch) host.watch();
|
|
@@ -2116,44 +2135,85 @@ function serveStdio$1(options) {
|
|
|
2116
2135
|
});
|
|
2117
2136
|
return { host };
|
|
2118
2137
|
};
|
|
2138
|
+
/**
|
|
2139
|
+
* The roots loaded or loading, by `key` (the real path of `dir`, for a root found from a path).
|
|
2140
|
+
* One that fails is logged and dropped, so the next request loads it again, and until then it
|
|
2141
|
+
* is no project found.
|
|
2142
|
+
*/
|
|
2143
|
+
const found = /* @__PURE__ */ new Map();
|
|
2144
|
+
const loadOnce = (key, dir, start) => {
|
|
2145
|
+
const known = found.get(key);
|
|
2146
|
+
if (known) return known.served;
|
|
2147
|
+
const served = start();
|
|
2148
|
+
found.set(key, {
|
|
2149
|
+
dir,
|
|
2150
|
+
served
|
|
2151
|
+
});
|
|
2152
|
+
served.catch((error) => {
|
|
2153
|
+
logger.error(error.message);
|
|
2154
|
+
if (found.get(key)?.served === served) found.delete(key);
|
|
2155
|
+
});
|
|
2156
|
+
return served;
|
|
2157
|
+
};
|
|
2119
2158
|
let started = false;
|
|
2120
2159
|
/** One project for every request: the one at `root`, or the working directory. */
|
|
2121
2160
|
const serveOne = (root) => {
|
|
2122
2161
|
if (started) return;
|
|
2123
2162
|
started = true;
|
|
2124
|
-
const
|
|
2125
|
-
served
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
decide(() => served);
|
|
2163
|
+
const dir = path.resolve(options.cwd, root ?? ".");
|
|
2164
|
+
const served = () => loadOnce(dir, dir, () => load(root));
|
|
2165
|
+
served();
|
|
2166
|
+
decide(served);
|
|
2129
2167
|
};
|
|
2130
|
-
/**
|
|
2131
|
-
|
|
2168
|
+
/**
|
|
2169
|
+
* Each request's project, from the absolute path it names. `within`: a folder that is no
|
|
2170
|
+
* project but holds some (the `--root` or client root the server was started for), which
|
|
2171
|
+
* answers for a file in none of them. Then only a path in it is answered, from a project in
|
|
2172
|
+
* it: never from a folder outside or above it, links resolved, nor from a relative path, which
|
|
2173
|
+
* the tools would read against whichever project answers.
|
|
2174
|
+
*/
|
|
2175
|
+
const serveByPath = (within) => {
|
|
2132
2176
|
if (started) return;
|
|
2133
2177
|
started = true;
|
|
2134
|
-
|
|
2135
|
-
|
|
2178
|
+
const folder = within === void 0 ? void 0 : {
|
|
2179
|
+
dir: within,
|
|
2180
|
+
real: realPath(within)
|
|
2181
|
+
};
|
|
2182
|
+
const where = within ? `${pathOf(within)} holds no project of its own, only projects in its folders` : `${options.cwd} holds no project and the client reported no roots`;
|
|
2183
|
+
logger.info(`${where}: finding each request's project from the absolute path it names`);
|
|
2184
|
+
const askForPath = `Pass \`path\` (for check_ui with \`code\`, \`filename\`) as the absolute path of the file you are editing, and it answers from that file's project. Or start the server with --root <project folder>.`;
|
|
2136
2185
|
decide(async (target) => {
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
if (!
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
return served;
|
|
2186
|
+
const named = target !== void 0 && target !== "";
|
|
2187
|
+
if (within && named && !path.isAbsolute(target)) throw new ProjectNotFoundError(`onsystem cannot tell which project ${pathOf(target)} is in: it is a relative path, and ${pathOf(within)} holds no project of its own, only projects in its folders. ${askForPath}`);
|
|
2188
|
+
if (named && path.isAbsolute(target)) {
|
|
2189
|
+
const file = path.resolve(target);
|
|
2190
|
+
if (within && !isInside(within, file)) throw new ProjectNotFoundError(`${pathOf(target)} is outside ${pathOf(within)}, the folder the server answers for. Pass the absolute path of a file inside it, or start the server with --root <project folder>.`);
|
|
2191
|
+
const up = findProjectRoot(file);
|
|
2192
|
+
const dir = within && !(up && isInside(within, up)) ? projectDirOf(file, within) : up;
|
|
2193
|
+
if (!dir) throw new ProjectNotFoundError(`No project at or above ${pathOf(target)}: no folder from there up holds a package.json, components.json, project.json or onsystem config (the home folder and the filesystem root do not count). Pass the absolute path of a file in your project.`);
|
|
2194
|
+
const real = realPath(dir);
|
|
2195
|
+
if (folder && !(isInside(folder.real, real) && isInside(folder.real, realPath(file)))) throw new ProjectNotFoundError(`${pathOf(target)} leads outside ${pathOf(folder.dir)}, the folder the server answers for, through a link. Pass the absolute path of a file inside it, or start the server with --root <project folder>.`);
|
|
2196
|
+
if (!found.has(real)) logger.info(`using ${dir}, the project of ${target}`);
|
|
2197
|
+
return loadOnce(real, dir, () => load(dir, dir !== within, folder));
|
|
2150
2198
|
}
|
|
2151
2199
|
const [only, ...others] = found.values();
|
|
2152
|
-
if (only && !others.length) return only;
|
|
2153
|
-
throw new ProjectNotFoundError(only ? `Requests have named files in ${found.size} projects (${[...found.
|
|
2200
|
+
if (only && !others.length) return only.served;
|
|
2201
|
+
throw new ProjectNotFoundError(only ? `Requests have named files in ${found.size} projects (${[...found.values()].map((f) => pathOf(f.dir)).join(", ")}). Pass \`path\` (for check_ui with \`code\`, \`filename\`) as the absolute path of the file you are editing, so the answer comes from its project.` : within ? `onsystem cannot tell which project to answer for: ${pathOf(within)} holds no project of its own, only projects in its folders. ${askForPath}` : `onsystem cannot tell which project to answer for: it was started in ${options.cwd}, which holds no project, and the client reported no workspace roots. ${askForPath}`);
|
|
2154
2202
|
});
|
|
2155
2203
|
};
|
|
2156
|
-
|
|
2204
|
+
/** Flags that describe the project themselves: the folder they apply to is the one served. */
|
|
2205
|
+
const described = options.config !== void 0 || Boolean(options.components?.length || options.tokens?.length || options.docs?.length);
|
|
2206
|
+
/**
|
|
2207
|
+
* The project at `root` (the working directory when undefined). A folder that is no project but
|
|
2208
|
+
* holds projects (a folder of repositories, which `--root ${workspaceFolder}` names when the
|
|
2209
|
+
* editor has their parent open) would mix their tokens: each request is answered from its path.
|
|
2210
|
+
*/
|
|
2211
|
+
const serveAt = (root) => {
|
|
2212
|
+
const dir = root === void 0 ? void 0 : path.resolve(options.cwd, root);
|
|
2213
|
+
if (dir !== void 0 && !described && holdsOnlyProjects(dir)) serveByPath(dir);
|
|
2214
|
+
else serveOne(root);
|
|
2215
|
+
};
|
|
2216
|
+
if (options.root !== void 0 || options.config !== void 0 || [...CONFIG_FILES, ...LEGACY_CONFIG_FILES].some((file) => fs.existsSync(path.join(options.cwd, file))) || hasProjectMarker(options.cwd)) serveAt(options.root);
|
|
2157
2217
|
/**
|
|
2158
2218
|
* When nothing else says where the project is: the working directory when flags
|
|
2159
2219
|
* (`--components`, `--tokens`, `--docs`) describe it, else each request's path.
|
|
@@ -2189,7 +2249,7 @@ function serveStdio$1(options) {
|
|
|
2189
2249
|
return;
|
|
2190
2250
|
}
|
|
2191
2251
|
if (root !== options.cwd) logger.info(`using client root ${root}`);
|
|
2192
|
-
|
|
2252
|
+
serveAt(root);
|
|
2193
2253
|
});
|
|
2194
2254
|
};
|
|
2195
2255
|
return server;
|
|
@@ -2206,6 +2266,49 @@ function serveStdio$1(options) {
|
|
|
2206
2266
|
await handle.close();
|
|
2207
2267
|
} });
|
|
2208
2268
|
}
|
|
2269
|
+
/**
|
|
2270
|
+
* `buildModel` for a project found in the folder `within`, which refuses to build it once its
|
|
2271
|
+
* folder leads outside `within` (a project or workspace package replaced by a link while the
|
|
2272
|
+
* server watches it): that rebuild fails, so the model built from inside the folder stays.
|
|
2273
|
+
*/
|
|
2274
|
+
function buildWithin(within) {
|
|
2275
|
+
return async (config, options) => {
|
|
2276
|
+
if (!isInside(within.real, realPath(config.root))) throw new ConfigError(`${pathOf(config.root)} now leads outside ${pathOf(within.dir)}, the folder the server answers for, through a link: it is not read from there.`);
|
|
2277
|
+
return buildModel(config, options);
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Whether `dir` is no project (no project marker, no workspace file) but a folder in it, or in one
|
|
2282
|
+
* of its folders, is one. Dot folders and `node_modules` are not looked in.
|
|
2283
|
+
*/
|
|
2284
|
+
function holdsOnlyProjects(dir) {
|
|
2285
|
+
if (hasProjectMarker(dir) || isWorkspaceRoot(dir)) return false;
|
|
2286
|
+
const folders = (from) => {
|
|
2287
|
+
try {
|
|
2288
|
+
return fs.readdirSync(from, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => path.join(from, e.name));
|
|
2289
|
+
} catch {
|
|
2290
|
+
return [];
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
const below = folders(dir);
|
|
2294
|
+
return below.some(hasProjectMarker) || below.some((f) => folders(f).some(hasProjectMarker));
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Where `p` really is: links resolved, in the letter case the file system keeps
|
|
2298
|
+
* (`realpathSync.native`; plain `realpathSync` keeps the case given on macOS). A path that does
|
|
2299
|
+
* not exist yet, such as the `filename` of new code, from the nearest folder above it that does.
|
|
2300
|
+
*/
|
|
2301
|
+
function realPath(p) {
|
|
2302
|
+
const rest = [];
|
|
2303
|
+
for (let dir = path.resolve(p);;) try {
|
|
2304
|
+
return path.join(fs.realpathSync.native(dir), ...rest);
|
|
2305
|
+
} catch {
|
|
2306
|
+
const parent = path.dirname(dir);
|
|
2307
|
+
if (parent === dir) return path.resolve(p);
|
|
2308
|
+
rest.unshift(path.basename(dir));
|
|
2309
|
+
dir = parent;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2209
2312
|
/** First `file://` root the client reports, if it supports roots (deprecated in 2026-07-28). */
|
|
2210
2313
|
async function clientRoot(server) {
|
|
2211
2314
|
if (!server.server.getClientCapabilities()?.roots) return void 0;
|
|
@@ -2217,5 +2320,4 @@ async function clientRoot(server) {
|
|
|
2217
2320
|
return;
|
|
2218
2321
|
}
|
|
2219
2322
|
}
|
|
2220
|
-
//#endregion
|
|
2221
2323
|
export { notReadNotices as a, missingDependencies as i, INSTRUCTIONS as n, explainProject as o, createServer as r, librariesLeftOut as s, serveStdio$1 as t };
|
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* What marks the folder a file belongs to: the nearest one at or above it with a config,
|
|
8
|
-
* a components.json, a package.json or an Nx project.json. The Claude Code hook finds a
|
|
9
|
-
* file's project the same way, so `check` from a workspace root and the hook agree on
|
|
10
|
-
* every file.
|
|
11
|
-
*/
|
|
12
|
-
const PROJECT_MARKERS = [
|
|
13
|
-
...CONFIG_FILES,
|
|
14
|
-
...LEGACY_CONFIG_FILES,
|
|
15
|
-
"components.json",
|
|
16
|
-
"package.json",
|
|
17
|
-
"project.json"
|
|
18
|
-
];
|
|
5
|
+
// src/workspace.ts
|
|
19
6
|
/**
|
|
20
7
|
* A monorepo seen from its root: every workspace package with a design system (an app
|
|
21
8
|
* that uses one, from source or a published library, or the design-system package
|
|
@@ -23,7 +10,7 @@ const PROJECT_MARKERS = [
|
|
|
23
10
|
* any file in it. Without a design system at the root, `check`, `inspect` and the MCP
|
|
24
11
|
* server work from here, each file checked by its own project's design system.
|
|
25
12
|
*/
|
|
26
|
-
var Workspace = class {
|
|
13
|
+
var Workspace = class Workspace {
|
|
27
14
|
root;
|
|
28
15
|
source;
|
|
29
16
|
projects;
|
|
@@ -71,6 +58,10 @@ var Workspace = class {
|
|
|
71
58
|
}
|
|
72
59
|
return project;
|
|
73
60
|
}
|
|
61
|
+
/** The workspace without the projects at `roots`. */
|
|
62
|
+
without(roots) {
|
|
63
|
+
return new Workspace(this.root, this.source, this.projects.filter((p) => !roots.includes(p.root)), this.others, this.configFile, this.options, this.rootConfig, this.otherConfigs);
|
|
64
|
+
}
|
|
74
65
|
rootProject() {
|
|
75
66
|
return {
|
|
76
67
|
root: this.root,
|
|
@@ -148,7 +139,8 @@ function loadProjectConfig(dir, workspaceRoot, rootConfigFile, options) {
|
|
|
148
139
|
const shared = {
|
|
149
140
|
cwd: options.cwd,
|
|
150
141
|
includeDesignSystem: options.includeDesignSystem,
|
|
151
|
-
jsonConfigOnly: options.jsonConfigOnly
|
|
142
|
+
jsonConfigOnly: options.jsonConfigOnly,
|
|
143
|
+
jsonConfigWithin: options.jsonConfigWithin
|
|
152
144
|
};
|
|
153
145
|
const packages = new PackageFinder(dir);
|
|
154
146
|
if (!rootConfigFile || hasOwnConfig(dir)) return loadConfigWith({
|
|
@@ -224,5 +216,4 @@ function isDirectory(target) {
|
|
|
224
216
|
return false;
|
|
225
217
|
}
|
|
226
218
|
}
|
|
227
|
-
|
|
228
|
-
export { hasProjectMarker as n, loadTarget as r, findProjectRoot as t };
|
|
219
|
+
export { projectDirOf as i, hasProjectMarker as n, loadTarget as r, findProjectRoot as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onsystem",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
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": [
|