@duro-app/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +91 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/list.d.ts +4 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +38 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/lookup.d.ts +13 -0
- package/dist/commands/lookup.d.ts.map +1 -0
- package/dist/commands/lookup.js +52 -0
- package/dist/commands/lookup.js.map +1 -0
- package/dist/commands/manifest.d.ts +5 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +33 -0
- package/dist/commands/manifest.js.map +1 -0
- package/dist/commands/mcp.d.ts +61 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +155 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/format.d.ts +13 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +186 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +46 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +86 -0
- package/dist/manifest.js.map +1 -0
- package/dist/registry-types.d.ts +93 -0
- package/dist/registry-types.d.ts.map +1 -0
- package/dist/registry-types.js +2 -0
- package/dist/registry-types.js.map +1 -0
- package/dist/registry.d.ts +32 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +57 -0
- package/dist/registry.js.map +1 -0
- package/dist/search.d.ts +16 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +75 -0
- package/dist/search.js.map +1 -0
- package/package.json +73 -0
- package/registry.json +6991 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Frederic Rousseau
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @duro-app/cli
|
|
2
|
+
|
|
3
|
+
Machine-queryable docs for the Duro design system. One lookup verb over a
|
|
4
|
+
generated registry: component props and usage guidance, runnable recipes,
|
|
5
|
+
design tokens, and the critical rules — as human output, `--json`, or an MCP
|
|
6
|
+
server. Zero runtime dependencies.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
npx @duro-app/cli manifest --json # the one-call agent bootstrap
|
|
10
|
+
npx @duro-app/cli Button # props, usage, example, related
|
|
11
|
+
npx @duro-app/cli Select --part Root
|
|
12
|
+
npx @duro-app/cli login-form --source-only > src/LoginForm.tsx
|
|
13
|
+
npx @duro-app/cli spacing # token scale + the deep import to copy
|
|
14
|
+
npx @duro-app/cli rules # critical rules + what the lint plugin enforces
|
|
15
|
+
npx @duro-app/cli "tags that wrap" # free text falls back to a need-search
|
|
16
|
+
npx @duro-app/cli list
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Install for the short bin: `pnpm add -D @duro-app/cli` → `duro Button`.
|
|
20
|
+
|
|
21
|
+
Exit codes: `0` ok · `1` not found (with "did you mean") · `2` usage error ·
|
|
22
|
+
`3` unreadable/incompatible registry.
|
|
23
|
+
|
|
24
|
+
## MCP server
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
claude mcp add duro -- npx -y -p @duro-app/cli -p @modelcontextprotocol/sdk duro mcp
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Tools: `duro_lookup` (same fallback-to-search behavior as the CLI),
|
|
31
|
+
`duro_list`, `duro_manifest`. Resources: `duro://component/<Name>`,
|
|
32
|
+
`duro://recipe/<name>`. Prompt: `duro/build-ui` primes a session with the
|
|
33
|
+
critical rules and the component index. The SDK is an optional peer — plain
|
|
34
|
+
CLI use never loads it.
|
|
35
|
+
|
|
36
|
+
## The registry
|
|
37
|
+
|
|
38
|
+
`registry.json` is generated from the monorepo sources by
|
|
39
|
+
`scripts/build-registry.mjs` (ts-morph, build-time only) and committed: it
|
|
40
|
+
aggregates the 57 `*.meta.ts` files, per-component prop tables extracted from
|
|
41
|
+
the TypeScript source, recipe sources rewritten to publishable imports, the
|
|
42
|
+
token scales, and the lint-rule metadata. It is a pure function of the source
|
|
43
|
+
tree — sorted keys, no timestamps — so `--check` (run in prebuild, CI, and
|
|
44
|
+
the amont pre-push hook) is a byte comparison. The same script maintains the
|
|
45
|
+
generated regions of the repo's CLAUDE.md (`--write-docs` / `--check-docs`).
|
|
46
|
+
|
|
47
|
+
The CLI and `@duro-app/ui` are released in lockstep; a version mismatch with
|
|
48
|
+
a locally installed ui prints a one-line stderr warning.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { loadRegistry } from './registry.js';
|
|
4
|
+
import { runLookup } from './commands/lookup.js';
|
|
5
|
+
import { runList } from './commands/list.js';
|
|
6
|
+
import { runManifest, cliVersion } from './commands/manifest.js';
|
|
7
|
+
function emit(result, json) {
|
|
8
|
+
if (json) {
|
|
9
|
+
process.stdout.write(JSON.stringify(result.data, null, 2) + '\n');
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
process.stdout.write(result.text + '\n');
|
|
13
|
+
}
|
|
14
|
+
process.exit(result.exitCode ?? 0);
|
|
15
|
+
}
|
|
16
|
+
function usageError(message) {
|
|
17
|
+
process.stderr.write(`${message}\nUsage: duro <name|list|manifest|mcp> [flags] — duro manifest for details\n`);
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
async function main() {
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = parseArgs({
|
|
24
|
+
allowPositionals: true,
|
|
25
|
+
options: {
|
|
26
|
+
json: { type: 'boolean', default: false },
|
|
27
|
+
part: { type: 'string' },
|
|
28
|
+
props: { type: 'boolean', default: false },
|
|
29
|
+
'source-only': { type: 'boolean', default: false },
|
|
30
|
+
'no-color': { type: 'boolean', default: false },
|
|
31
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
32
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
usageError(`duro: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
}
|
|
39
|
+
const { values, positionals } = parsed;
|
|
40
|
+
if (values.version) {
|
|
41
|
+
process.stdout.write(cliVersion() + '\n');
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
const registry = loadRegistry();
|
|
45
|
+
// Version-skew guard: the registry documents the lockstep-released ui
|
|
46
|
+
// version; warn (stderr only) when a locally installed ui differs.
|
|
47
|
+
if (!values.json) {
|
|
48
|
+
try {
|
|
49
|
+
const { createRequire } = await import('node:module');
|
|
50
|
+
const require = createRequire(process.cwd() + '/');
|
|
51
|
+
const uiPkg = require('@duro-app/ui/package.json');
|
|
52
|
+
const own = cliVersion();
|
|
53
|
+
if (uiPkg.version && own !== '0.0.0' && uiPkg.version !== own) {
|
|
54
|
+
process.stderr.write(`duro: docs are for @duro-app/ui@${own}, you have ${uiPkg.version} — npm i -D @duro-app/cli@${uiPkg.version}\n`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// No local ui — nothing to compare.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const [first, ...rest] = positionals;
|
|
62
|
+
if (values.help || first === undefined) {
|
|
63
|
+
emit(runManifest(registry), values.json);
|
|
64
|
+
}
|
|
65
|
+
if (first === 'manifest') {
|
|
66
|
+
emit(runManifest(registry), values.json);
|
|
67
|
+
}
|
|
68
|
+
if (first === 'list') {
|
|
69
|
+
if (rest.length > 1)
|
|
70
|
+
usageError('duro list takes at most one kind');
|
|
71
|
+
emit(runList(registry, rest[0]), values.json);
|
|
72
|
+
}
|
|
73
|
+
if (first === 'mcp') {
|
|
74
|
+
const { runMcp } = await import('./commands/mcp.js');
|
|
75
|
+
await runMcp(registry);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Everything else is a lookup — multiple positionals become a search query.
|
|
79
|
+
const query = [first, ...rest].join(' ');
|
|
80
|
+
emit(runLookup(registry, query, {
|
|
81
|
+
part: values.part,
|
|
82
|
+
propsOnly: values.props,
|
|
83
|
+
sourceOnly: values['source-only'],
|
|
84
|
+
}), values.json);
|
|
85
|
+
}
|
|
86
|
+
main().catch((error) => {
|
|
87
|
+
const exitCode = error.exitCode ?? 1;
|
|
88
|
+
process.stderr.write(`duro: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
89
|
+
process.exit(exitCode);
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAI9C,wBAAgB,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAkCxE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { renderList } from '../format.js';
|
|
2
|
+
const KINDS = new Set(['components', 'recipes', 'tokens']);
|
|
3
|
+
export function runList(registry, kind) {
|
|
4
|
+
if (kind !== undefined && !KINDS.has(kind)) {
|
|
5
|
+
return {
|
|
6
|
+
text: `duro list: unknown kind "${kind}" — expected components | recipes | tokens`,
|
|
7
|
+
data: { kind: 'usage-error' },
|
|
8
|
+
exitCode: 2,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const data = [
|
|
12
|
+
...(!kind || kind === 'components'
|
|
13
|
+
? Object.entries(registry.components).map(([key, entry]) => ({
|
|
14
|
+
name: key,
|
|
15
|
+
kind: entry.kind,
|
|
16
|
+
importPath: entry.importPath,
|
|
17
|
+
description: entry.meta?.description ?? null,
|
|
18
|
+
}))
|
|
19
|
+
: []),
|
|
20
|
+
...(!kind || kind === 'recipes'
|
|
21
|
+
? Object.entries(registry.recipes).map(([key, entry]) => ({
|
|
22
|
+
name: key,
|
|
23
|
+
kind: 'recipe',
|
|
24
|
+
description: entry.meta.description,
|
|
25
|
+
}))
|
|
26
|
+
: []),
|
|
27
|
+
...(!kind || kind === 'tokens'
|
|
28
|
+
? Object.entries(registry.tokens.groups).map(([key, group]) => ({
|
|
29
|
+
name: key,
|
|
30
|
+
kind: 'tokens',
|
|
31
|
+
importPath: group.importPath,
|
|
32
|
+
description: `${group.entries.length} tokens`,
|
|
33
|
+
}))
|
|
34
|
+
: []),
|
|
35
|
+
];
|
|
36
|
+
return { text: renderList(registry, kind), data };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAA;AAGvC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAA;AAE1D,MAAM,UAAU,OAAO,CAAC,QAAkB,EAAE,IAAa;IACvD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO;YACL,IAAI,EAAE,4BAA4B,IAAI,4CAA4C;YAClF,IAAI,EAAE,EAAC,IAAI,EAAE,aAAa,EAAC;YAC3B,QAAQ,EAAE,CAAC;SACZ,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAG;QACX,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,KAAK,YAAY;YAChC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzD,IAAI,EAAE,GAAG;gBACT,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW,IAAI,IAAI;aAC7C,CAAC,CAAC;YACL,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,KAAK,SAAS;YAC7B,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;gBACtD,IAAI,EAAE,GAAG;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW;aACpC,CAAC,CAAC;YACL,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ;YAC5B,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5D,IAAI,EAAE,GAAG;gBACT,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,WAAW,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,SAAS;aAC9C,CAAC,CAAC;YACL,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;IACD,OAAO,EAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,EAAC,CAAA;AACjD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Registry } from '../registry-types.js';
|
|
2
|
+
export interface LookupOptions {
|
|
3
|
+
part?: string;
|
|
4
|
+
propsOnly?: boolean;
|
|
5
|
+
sourceOnly?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface CommandResult {
|
|
8
|
+
text: string;
|
|
9
|
+
data: unknown;
|
|
10
|
+
exitCode?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function runLookup(registry: Registry, query: string, options: LookupOptions): CommandResult;
|
|
13
|
+
//# sourceMappingURL=lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/commands/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAWlD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,GACrB,aAAa,CAoDf"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { lookup } from '../registry.js';
|
|
2
|
+
import { search, suggestions } from '../search.js';
|
|
3
|
+
import { renderComponent, renderIcons, renderRecipe, renderRules, renderTokenGroup, } from '../format.js';
|
|
4
|
+
export function runLookup(registry, query, options) {
|
|
5
|
+
const hit = lookup(registry, query);
|
|
6
|
+
if (hit) {
|
|
7
|
+
switch (hit.kind) {
|
|
8
|
+
case 'component':
|
|
9
|
+
return {
|
|
10
|
+
text: renderComponent(registry, hit.key, hit.entry, {
|
|
11
|
+
part: options.part,
|
|
12
|
+
propsOnly: options.propsOnly,
|
|
13
|
+
}),
|
|
14
|
+
data: { kind: hit.kind, key: hit.key, entry: hit.entry },
|
|
15
|
+
};
|
|
16
|
+
case 'recipe':
|
|
17
|
+
return {
|
|
18
|
+
text: renderRecipe(hit.entry, { sourceOnly: options.sourceOnly }),
|
|
19
|
+
data: { kind: hit.kind, key: hit.key, entry: hit.entry },
|
|
20
|
+
};
|
|
21
|
+
case 'tokens':
|
|
22
|
+
return {
|
|
23
|
+
text: renderTokenGroup(hit.key, hit.entry),
|
|
24
|
+
data: { kind: hit.kind, key: hit.key, entry: hit.entry },
|
|
25
|
+
};
|
|
26
|
+
case 'icons':
|
|
27
|
+
return { text: renderIcons(hit.entry), data: { kind: hit.kind, entry: hit.entry } };
|
|
28
|
+
case 'rules':
|
|
29
|
+
return { text: renderRules(hit.entry), data: { kind: hit.kind, entry: hit.entry } };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Not a name: try full-text search over the metas.
|
|
33
|
+
const hits = search(registry, query);
|
|
34
|
+
if (hits.length > 0) {
|
|
35
|
+
const width = Math.max(...hits.map((searchHit) => searchHit.key.length)) + 2;
|
|
36
|
+
const text = [
|
|
37
|
+
`No exact match for "${query}" — closest by usage:`,
|
|
38
|
+
...hits.map((searchHit) => ` ${searchHit.key.padEnd(width)}${searchHit.kind === 'recipe' ? '(recipe) ' : ''}${searchHit.description}`),
|
|
39
|
+
'',
|
|
40
|
+
`duro ${hits[0].key} for details`,
|
|
41
|
+
].join('\n');
|
|
42
|
+
return { text, data: { kind: 'search', query, hits } };
|
|
43
|
+
}
|
|
44
|
+
const didYouMean = suggestions(registry, query);
|
|
45
|
+
const hint = didYouMean.length > 0 ? ` Did you mean: ${didYouMean.join(', ')}?` : '';
|
|
46
|
+
return {
|
|
47
|
+
text: `duro: nothing found for "${query}".${hint} Try: duro list`,
|
|
48
|
+
data: { kind: 'not-found', query, suggestions: didYouMean },
|
|
49
|
+
exitCode: 1,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/commands/lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AAE9C,wBAAgB,UAAU,IAAI,MAAM,CAWnC;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAoB7D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { buildManifest, COMMANDS } from '../manifest.js';
|
|
3
|
+
export function cliVersion() {
|
|
4
|
+
try {
|
|
5
|
+
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
6
|
+
return pkg.version ?? '0.0.0';
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return '0.0.0';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function runManifest(registry) {
|
|
13
|
+
const manifest = buildManifest(registry, cliVersion());
|
|
14
|
+
const text = [
|
|
15
|
+
`duro — ${manifest.description}`,
|
|
16
|
+
'',
|
|
17
|
+
`USAGE ${manifest.usage}`,
|
|
18
|
+
'',
|
|
19
|
+
...COMMANDS.map((command) => {
|
|
20
|
+
const flags = command.flags.map((flag) => `--${flag.name}`).join(' ');
|
|
21
|
+
return [
|
|
22
|
+
` duro ${command.name === 'lookup' ? '<name>' : command.name}${flags ? ` [${flags}]` : ''}`,
|
|
23
|
+
` ${command.summary}`,
|
|
24
|
+
...command.examples.map((example) => ` $ ${example}`),
|
|
25
|
+
].join('\n');
|
|
26
|
+
}),
|
|
27
|
+
'',
|
|
28
|
+
`GLOBAL ${manifest.globalFlags.map((flag) => `--${flag.name}`).join(' · ')}`,
|
|
29
|
+
`NAMES ${manifest.enums.names.length} — duro list`,
|
|
30
|
+
].join('\n');
|
|
31
|
+
return { text, data: manifest };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AAEpC,OAAO,EAAC,aAAa,EAAE,QAAQ,EAAC,MAAM,gBAAgB,CAAA;AAGtD,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAGrE,CAAA;QACD,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAA;IAChB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAkB;IAC5C,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAA;IACtD,MAAM,IAAI,GAAG;QACX,UAAU,QAAQ,CAAC,WAAW,EAAE;QAChC,EAAE;QACF,UAAU,QAAQ,CAAC,KAAK,EAAE;QAC1B,EAAE;QACF,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACrE,OAAO;gBACL,UAAU,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7F,SAAS,OAAO,CAAC,OAAO,EAAE;gBAC1B,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,OAAO,EAAE,CAAC;aAC3D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACd,CAAC,CAAC;QACF,EAAE;QACF,WAAW,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QAC7E,WAAW,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,cAAc;KACrD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACZ,OAAO,EAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAC,CAAA;AAC/B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Registry } from '../registry-types.js';
|
|
2
|
+
/** The three MCP tools, JSON Schemas hand-written (no zod dependency). */
|
|
3
|
+
export declare function toolDefinitions(registry: Registry): ({
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
name: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
part: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
sourceOnly: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
kind?: undefined;
|
|
22
|
+
};
|
|
23
|
+
required: string[];
|
|
24
|
+
};
|
|
25
|
+
} | {
|
|
26
|
+
name: string;
|
|
27
|
+
description: string;
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: string;
|
|
30
|
+
properties: {
|
|
31
|
+
kind: {
|
|
32
|
+
type: string;
|
|
33
|
+
enum: string[];
|
|
34
|
+
};
|
|
35
|
+
name?: undefined;
|
|
36
|
+
part?: undefined;
|
|
37
|
+
sourceOnly?: undefined;
|
|
38
|
+
};
|
|
39
|
+
required?: undefined;
|
|
40
|
+
};
|
|
41
|
+
} | {
|
|
42
|
+
name: string;
|
|
43
|
+
description: string;
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: string;
|
|
46
|
+
properties: {
|
|
47
|
+
name?: undefined;
|
|
48
|
+
part?: undefined;
|
|
49
|
+
sourceOnly?: undefined;
|
|
50
|
+
kind?: undefined;
|
|
51
|
+
};
|
|
52
|
+
required?: undefined;
|
|
53
|
+
};
|
|
54
|
+
})[];
|
|
55
|
+
export declare function callTool(registry: Registry, name: string, args: Record<string, unknown>): {
|
|
56
|
+
text: string;
|
|
57
|
+
data: unknown;
|
|
58
|
+
isError: boolean;
|
|
59
|
+
};
|
|
60
|
+
export declare function runMcp(registry: Registry): Promise<void>;
|
|
61
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAYlD,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuCjD;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;EAkBvF;AAED,wBAAsB,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG9D"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { buildManifest } from '../manifest.js';
|
|
2
|
+
import { runLookup } from './lookup.js';
|
|
3
|
+
import { runList } from './list.js';
|
|
4
|
+
import { cliVersion } from './manifest.js';
|
|
5
|
+
import { renderComponent, renderRecipe } from '../format.js';
|
|
6
|
+
const INSTALL_HINT = `duro mcp needs @modelcontextprotocol/sdk (an optional peer).
|
|
7
|
+
npm i -D @modelcontextprotocol/sdk
|
|
8
|
+
or run it without installing:
|
|
9
|
+
npx -y -p @duro-app/cli -p @modelcontextprotocol/sdk duro mcp`;
|
|
10
|
+
/** The three MCP tools, JSON Schemas hand-written (no zod dependency). */
|
|
11
|
+
export function toolDefinitions(registry) {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
name: 'duro_lookup',
|
|
15
|
+
description: 'Docs for anything in the Duro design system by name: a component (props, usage, example), a recipe (runnable source), a token group, "icons", or "rules". Free text falls back to a ranked search over usage metadata — pass what you need, e.g. "tags that wrap".',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
name: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Component/recipe/token-group name, "icons", "rules", or free text to search',
|
|
22
|
+
},
|
|
23
|
+
part: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Narrow a compound component to one part (e.g. Root)',
|
|
26
|
+
},
|
|
27
|
+
sourceOnly: { type: 'boolean', description: 'Recipes: return just the source file' },
|
|
28
|
+
},
|
|
29
|
+
required: ['name'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'duro_list',
|
|
34
|
+
description: `One-line index of everything documented (${Object.keys(registry.components).length} components, ${Object.keys(registry.recipes).length} recipes, token groups).`,
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
kind: { type: 'string', enum: ['components', 'recipes', 'tokens'] },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'duro_manifest',
|
|
44
|
+
description: 'The duro command spec plus all valid lookup names.',
|
|
45
|
+
inputSchema: { type: 'object', properties: {} },
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
export function callTool(registry, name, args) {
|
|
50
|
+
switch (name) {
|
|
51
|
+
case 'duro_lookup': {
|
|
52
|
+
const result = runLookup(registry, String(args.name ?? ''), {
|
|
53
|
+
part: typeof args.part === 'string' ? args.part : undefined,
|
|
54
|
+
sourceOnly: args.sourceOnly === true,
|
|
55
|
+
});
|
|
56
|
+
return { text: result.text, data: result.data, isError: result.exitCode === 1 };
|
|
57
|
+
}
|
|
58
|
+
case 'duro_list': {
|
|
59
|
+
const result = runList(registry, typeof args.kind === 'string' ? args.kind : undefined);
|
|
60
|
+
return { text: result.text, data: result.data, isError: result.exitCode !== undefined };
|
|
61
|
+
}
|
|
62
|
+
case 'duro_manifest':
|
|
63
|
+
return { text: '', data: buildManifest(registry, cliVersion()), isError: false };
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(`unknown tool ${name}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function runMcp(registry) {
|
|
69
|
+
let sdkServer, sdkStdio, sdkTypes;
|
|
70
|
+
try {
|
|
71
|
+
sdkServer = await import('@modelcontextprotocol/sdk/server/index.js');
|
|
72
|
+
sdkStdio = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
73
|
+
sdkTypes = await import('@modelcontextprotocol/sdk/types.js');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
process.stderr.write(INSTALL_HINT + '\n');
|
|
77
|
+
process.exit(2);
|
|
78
|
+
}
|
|
79
|
+
const server = new sdkServer.Server({ name: 'duro', version: cliVersion() }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
80
|
+
server.setRequestHandler(sdkTypes.ListToolsRequestSchema, () => ({
|
|
81
|
+
tools: toolDefinitions(registry),
|
|
82
|
+
}));
|
|
83
|
+
server.setRequestHandler(sdkTypes.CallToolRequestSchema, (request) => {
|
|
84
|
+
const { text, data, isError } = callTool(registry, request.params.name, (request.params.arguments ?? {}));
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: 'text', text: text || JSON.stringify(data, null, 2) }],
|
|
87
|
+
structuredContent: data,
|
|
88
|
+
isError,
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
server.setRequestHandler(sdkTypes.ListResourcesRequestSchema, () => ({
|
|
92
|
+
resources: [
|
|
93
|
+
...Object.keys(registry.components).map((name) => ({
|
|
94
|
+
uri: `duro://component/${encodeURIComponent(name)}`,
|
|
95
|
+
name: `${name} docs`,
|
|
96
|
+
mimeType: 'text/plain',
|
|
97
|
+
})),
|
|
98
|
+
...Object.keys(registry.recipes).map((name) => ({
|
|
99
|
+
uri: `duro://recipe/${name}`,
|
|
100
|
+
name: `${name} recipe`,
|
|
101
|
+
mimeType: 'text/plain',
|
|
102
|
+
})),
|
|
103
|
+
],
|
|
104
|
+
}));
|
|
105
|
+
server.setRequestHandler(sdkTypes.ReadResourceRequestSchema, (request) => {
|
|
106
|
+
const uri = request.params.uri;
|
|
107
|
+
const componentMatch = /^duro:\/\/component\/(.+)$/.exec(uri);
|
|
108
|
+
const recipeMatch = /^duro:\/\/recipe\/(.+)$/.exec(uri);
|
|
109
|
+
let text = null;
|
|
110
|
+
if (componentMatch) {
|
|
111
|
+
const key = decodeURIComponent(componentMatch[1]);
|
|
112
|
+
const entry = registry.components[key];
|
|
113
|
+
if (entry)
|
|
114
|
+
text = renderComponent(registry, key, entry);
|
|
115
|
+
}
|
|
116
|
+
else if (recipeMatch) {
|
|
117
|
+
const entry = registry.recipes[recipeMatch[1]];
|
|
118
|
+
if (entry)
|
|
119
|
+
text = renderRecipe(entry);
|
|
120
|
+
}
|
|
121
|
+
if (text === null)
|
|
122
|
+
throw new Error(`unknown resource ${uri}`);
|
|
123
|
+
return { contents: [{ uri, mimeType: 'text/plain', text }] };
|
|
124
|
+
});
|
|
125
|
+
server.setRequestHandler(sdkTypes.ListPromptsRequestSchema, () => ({
|
|
126
|
+
prompts: [
|
|
127
|
+
{
|
|
128
|
+
name: 'duro/build-ui',
|
|
129
|
+
description: 'Prime a session with the Duro critical rules and component index',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
}));
|
|
133
|
+
server.setRequestHandler(sdkTypes.GetPromptRequestSchema, (request) => {
|
|
134
|
+
if (request.params.name !== 'duro/build-ui') {
|
|
135
|
+
throw new Error(`unknown prompt ${request.params.name}`);
|
|
136
|
+
}
|
|
137
|
+
const componentIndex = Object.entries(registry.components)
|
|
138
|
+
.map(([key, entry]) => `${key}: ${entry.meta?.description ?? entry.kind}`)
|
|
139
|
+
.join('\n');
|
|
140
|
+
return {
|
|
141
|
+
messages: [
|
|
142
|
+
{
|
|
143
|
+
role: 'user',
|
|
144
|
+
content: {
|
|
145
|
+
type: 'text',
|
|
146
|
+
text: `You are building UI with the Duro design system. Follow these rules:\n\n${registry.rules.critical}\n\nAvailable components:\n${componentIndex}\n\nQuery details with the duro_lookup tool.`,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
const transport = new sdkStdio.StdioServerTransport();
|
|
153
|
+
await server.connect(transport);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAA;AACrC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAA;AACxC,OAAO,EAAC,eAAe,EAAE,YAAY,EAAC,MAAM,cAAc,CAAA;AAE1D,MAAM,YAAY,GAAG;;;gEAG2C,CAAA;AAEhE,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,QAAkB;IAChD,OAAO;QACL;YACE,IAAI,EAAE,aAAa;YACnB,WAAW,EACT,oQAAoQ;YACtQ,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,6EAA6E;qBAChF;oBACD,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,qDAAqD;qBACnE;oBACD,UAAU,EAAE,EAAC,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sCAAsC,EAAC;iBACnF;gBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;aACnB;SACF;QACD;YACE,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,4CAA4C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,MAAM,gBAAgB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,0BAA0B;YAC9K,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAC;iBAClE;aACF;SACF;QACD;YACE,IAAI,EAAE,eAAe;YACrB,WAAW,EAAE,oDAAoD;YACjE,WAAW,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAC;SAC9C;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,QAAkB,EAAE,IAAY,EAAE,IAA6B;IACtF,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE;gBAC1D,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBAC3D,UAAU,EAAE,IAAI,CAAC,UAAU,KAAK,IAAI;aACrC,CAAC,CAAA;YACF,OAAO,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAC,CAAA;QAC/E,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;YACvF,OAAO,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAC,CAAA;QACvF,CAAC;QACD,KAAK,eAAe;YAClB,OAAO,EAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAC,CAAA;QAChF;YACE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAA;IAC3C,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,QAAkB;IAC7C,IAAI,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAA;IACjC,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAA;QACrE,QAAQ,GAAG,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAA;QACpE,QAAQ,GAAG,MAAM,MAAM,CAAC,oCAAoC,CAAC,CAAA;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;QACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,CACjC,EAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAC,EACrC,EAAC,YAAY,EAAE,EAAC,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAC,EAAC,CACxD,CAAA;IAED,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/D,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC;KACjC,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC,OAAO,EAAE,EAAE;QACnE,MAAM,EAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAC,GAAG,QAAQ,CACpC,QAAQ,EACR,OAAO,CAAC,MAAM,CAAC,IAAI,EACnB,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAA4B,CAC5D,CAAA;QACD,OAAO;YACL,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAC,CAAC;YACtE,iBAAiB,EAAE,IAAI;YACvB,OAAO;SACR,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;QACnE,SAAS,EAAE;YACT,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACjD,GAAG,EAAE,oBAAoB,kBAAkB,CAAC,IAAI,CAAC,EAAE;gBACnD,IAAI,EAAE,GAAG,IAAI,OAAO;gBACpB,QAAQ,EAAE,YAAY;aACvB,CAAC,CAAC;YACH,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC9C,GAAG,EAAE,iBAAiB,IAAI,EAAE;gBAC5B,IAAI,EAAE,GAAG,IAAI,SAAS;gBACtB,QAAQ,EAAE,YAAY;aACvB,CAAC,CAAC;SACJ;KACF,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,yBAAyB,EAAE,CAAC,OAAO,EAAE,EAAE;QACvE,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAA;QAC9B,MAAM,cAAc,GAAG,4BAA4B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC7D,MAAM,WAAW,GAAG,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACvD,IAAI,IAAI,GAAkB,IAAI,CAAA;QAC9B,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;YACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;YACtC,IAAI,KAAK;gBAAE,IAAI,GAAG,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;QACzD,CAAC;aAAM,IAAI,WAAW,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;YAC9C,IAAI,KAAK;gBAAE,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;QACvC,CAAC;QACD,IAAI,IAAI,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAA;QAC7D,OAAO,EAAC,QAAQ,EAAE,CAAC,EAAC,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAC,CAAC,EAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;QACjE,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,eAAe;gBACrB,WAAW,EAAE,kEAAkE;aAChF;SACF;KACF,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,sBAAsB,EAAE,CAAC,OAAO,EAAE,EAAE;QACpE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;QAC1D,CAAC;QACD,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;aACvD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,CAAC,IAAI,EAAE,WAAW,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;aACzE,IAAI,CAAC,IAAI,CAAC,CAAA;QACb,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,2EAA2E,QAAQ,CAAC,KAAK,CAAC,QAAQ,8BAA8B,cAAc,8CAA8C;qBACnM;iBACF;aACF;SACF,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,SAAS,GAAG,IAAI,QAAQ,CAAC,oBAAoB,EAAE,CAAA;IACrD,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;AACjC,CAAC"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentEntry, IconRegistry, RecipeEntry, Registry, RulesRegistry, TokenGroup } from './registry-types.js';
|
|
2
|
+
export declare function renderComponent(registry: Registry, key: string, entry: ComponentEntry, options?: {
|
|
3
|
+
part?: string;
|
|
4
|
+
propsOnly?: boolean;
|
|
5
|
+
}): string;
|
|
6
|
+
export declare function renderRecipe(entry: RecipeEntry, options?: {
|
|
7
|
+
sourceOnly?: boolean;
|
|
8
|
+
}): string;
|
|
9
|
+
export declare function renderTokenGroup(key: string, group: TokenGroup): string;
|
|
10
|
+
export declare function renderIcons(icons: IconRegistry): string;
|
|
11
|
+
export declare function renderRules(rules: RulesRegistry): string;
|
|
12
|
+
export declare function renderList(registry: Registry, kind?: string): string;
|
|
13
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EAGZ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,UAAU,EACX,MAAM,qBAAqB,CAAA;AAgE5B,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAM,GACjD,MAAM,CAyDR;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,GAAE;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAM,GAAG,MAAM,CAS7F;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAuBvE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAUvD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAWxD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAgCpE"}
|