@astryxdesign/cli 0.7.2-canary.caec7ca → 0.7.2-canary.cbb7fa3
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/api/component/_adapter.d.mts +41 -1
- package/api/component/_adapter.mjs +93 -7
- package/api/component/component.mjs +22 -5
- package/api/component/component.test.mjs +80 -0
- package/api/component/integration-export-name.test.mjs +644 -0
- package/api/component/list/list.mjs +42 -4
- package/api/gap-report/gap-report.mjs +67 -4
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +7 -3
- package/api/integration/pack-check.mjs +61 -107
- package/api/integration/pack-check.test.mjs +359 -0
- package/api/search/search.d.mts +9 -0
- package/api/search/search.mjs +108 -16
- package/api/search/search.test.mjs +36 -0
- package/api/swizzle/copy/copy.mjs +28 -14
- package/api/theme/build/build.family.test.mjs +16 -0
- package/api/theme/build/build.mjs +77 -18
- package/api/theme/themeBuild.doc.mjs +3 -3
- package/assets/codemods/transforms/next/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
- package/assets/codemods/transforms/next/index.mjs +11 -1
- package/assets/codemods/transforms/next/migrate-list-edge-compensation-to-edge.mjs +198 -0
- package/assets/docs/tree/add-a-component.doc.mjs +10 -0
- package/assets/docs/tree/checks.doc.mjs +1 -1
- package/assets/docs/tree/component-family.doc.mjs +5 -1
- package/assets/docs/tree/component-imports.doc.mjs +8 -0
- package/assets/docs/tree/single-component.doc.mjs +1 -1
- package/assets/docs/tree/troubleshooting.doc.mjs +9 -1
- package/assets/docs/tree/versioning.doc.mjs +1 -0
- package/assets/docs/working-with-ai.doc.mjs +43 -4
- package/authoring/doctypes/component/component.doc.mjs +2 -2
- package/authoring/doctypes/component/type.ts +6 -3
- package/clients/cli/commands/build-theme.path-safety.test.mjs +84 -0
- package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
- package/clients/cli/commands/theme-build.doc.mjs +2 -2
- package/foundation/discovery/component-discovery.d.mts +172 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +863 -55
- package/foundation/discovery/component-discovery.test.mjs +96 -0
- package/foundation/discovery/component-replacement.mjs +18 -0
- package/foundation/discovery/component-replacement.test.mjs +40 -0
- package/foundation/discovery/module-exports.d.mts +27 -0
- package/foundation/discovery/module-exports.mjs +356 -31
- package/foundation/discovery/module-exports.test.mjs +172 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +4 -0
- package/foundation/doc-compiler/inputs.mjs +23 -0
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/integrations/contribution-inventory.mjs +6 -1
- package/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +9 -9
|
@@ -35,6 +35,14 @@ export const docs = {
|
|
|
35
35
|
type: 'prose',
|
|
36
36
|
text: 'Add writes the entry only when package.json already has an `exports` map, so start every package with `"exports": {}`.',
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
type: 'prose',
|
|
40
|
+
text: 'A file does not have to be named after its component. When `Slides.tsx` exports `AcmeCarousel` and nothing named `Slides`, keep the doc beside it as `Slides.doc.mjs` and set its `name` to `AcmeCarousel`: apps list, search and open the component by that name, and `integration verify` checks that its import exports `AcmeCarousel`. A file that exports its own name keeps that name. A package that names its files in kebab case works the same way: `acme-gallery.doc.mjs` beside `acme-gallery.tsx` sets `name: \'AcmeGallery\'`.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'prose',
|
|
44
|
+
text: "The file name still opens the doc. A name that Core or another doc's file already has stays theirs, and so does a Core name your package replaces: with `Badge.doc.mjs` naming `AcmeBadge`, `astryx component Badge` opens Core's `Badge`, and `--package` with your package name reaches yours.",
|
|
45
|
+
},
|
|
38
46
|
],
|
|
39
47
|
},
|
|
40
48
|
{
|
|
@@ -56,7 +56,7 @@ export default {
|
|
|
56
56
|
style: 'unordered',
|
|
57
57
|
items: [
|
|
58
58
|
'Keep `props` on the top-level doc. Private implementation helpers do not need entries.',
|
|
59
|
-
'
|
|
59
|
+
'Name the doc after the source file beside it, such as `AcmeCarousel.doc.mjs` beside `AcmeCarousel.tsx`, and change both in the same pull request.',
|
|
60
60
|
'If the module later exposes several related public exports, adapt this doc with {@link generic:component-family}.',
|
|
61
61
|
],
|
|
62
62
|
},
|
|
@@ -67,7 +67,7 @@ export const docs = {
|
|
|
67
67
|
rows: [
|
|
68
68
|
[
|
|
69
69
|
'`component_export_missing`: `Component "AcmeCarousel" advertises import "@acme/astryx-widgets/components/AcmeCarousel", but that packed module does not export "AcmeCarousel".`',
|
|
70
|
-
"package.json has no `exports` map, or the module does not export the name. Keep `export function AcmeCarousel`, and add the entry: `npm pkg set 'exports[./components/AcmeCarousel]=./components/AcmeCarousel.tsx'`.",
|
|
70
|
+
"package.json has no `exports` map, or the module does not export the name. Keep `export function AcmeCarousel`, and add the entry: `npm pkg set 'exports[./components/AcmeCarousel]=./components/AcmeCarousel.tsx'`. When the quoted name is a file name, such as `acme-carousel`, set the doc's `name` to the component the file exports. A type-only export does not count.",
|
|
71
71
|
],
|
|
72
72
|
[
|
|
73
73
|
'`template_export_missing`: `Template "acme-dashboard" public import "@acme/astryx-widgets/templates/acme-dashboard" does not have a default export.`',
|
|
@@ -125,6 +125,14 @@ export const docs = {
|
|
|
125
125
|
type: 'table',
|
|
126
126
|
headers: ['Symptom', 'Fix'],
|
|
127
127
|
rows: [
|
|
128
|
+
[
|
|
129
|
+
'`Error: No component named "AcmeCarousel"` for a component your package documents.',
|
|
130
|
+
"Its doc still names its file. Set `name` to the export, such as `name: 'AcmeCarousel'` in `acme-carousel.doc.mjs`. A family member is listed only when the module its family doc imports exports it ({@link generic:component-family}).",
|
|
131
|
+
],
|
|
132
|
+
[
|
|
133
|
+
'`npx astryx component Dialog` opens Core\'s `Dialog`, not yours.',
|
|
134
|
+
"A name that Core, a legacy docs package, or another package's file already has stays theirs. Run `npx astryx component Dialog --package @acme/astryx-widgets`, or give your component a name of its own.",
|
|
135
|
+
],
|
|
128
136
|
[
|
|
129
137
|
'`Error: Template "dashboard" is ambiguous — narrow it with --type and/or --package.`',
|
|
130
138
|
"Your template id matches a Core id. Rename it, such as `acme-dashboard`, or set `replaces: 'dashboard'` to take its place. `npx astryx doctor integration templates` warns about this before you publish.",
|
|
@@ -140,6 +140,7 @@ export const docs = {
|
|
|
140
140
|
'Published 0.6.3 and earlier reject a template that sets `replaces` or `keywords`, drop that template, and hide every doc topic your package ships. Published 0.6.4 and 0.6.5 still drop a template that sets `keywords`.',
|
|
141
141
|
'A stable CLI before 0.6.4 cannot read a docs section, a section `id`, or a theme folder that `integration add theme` writes. It can then hide every doc topic your package ships.',
|
|
142
142
|
'Stable 0.6.3 still loads your components, but 0.6.0 cannot read the component docs that `integration add component` writes: `component AcmeCarousel` fails there.',
|
|
143
|
+
'An app on an older CLI lists a component whose doc names another export of its file under the file name, and lists no family members, so `component AcmeCarousel` fails there.',
|
|
143
144
|
],
|
|
144
145
|
},
|
|
145
146
|
{
|
|
@@ -44,7 +44,7 @@ export const docs = {
|
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
type: 'prose',
|
|
47
|
-
text:
|
|
47
|
+
text: "By default this creates `AGENTS.md` (the tool-agnostic standard most agents read). To target a specific tool's file instead:",
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
type: 'code',
|
|
@@ -182,21 +182,60 @@ astryx docs principles --dense`,
|
|
|
182
182
|
},
|
|
183
183
|
{
|
|
184
184
|
type: 'prose',
|
|
185
|
-
text: 'Add the server to your MCP config file.
|
|
185
|
+
text: 'Add the server to your MCP config file. The entry is a remote HTTP server, and the exact shape differs per client, so use the snippet for the tool you run. An entry with an unrecognized transport is skipped with a warning rather than an error, which reads as a server that connects but exposes no tools.',
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
type: 'code',
|
|
189
189
|
lang: 'json',
|
|
190
|
-
label: '
|
|
190
|
+
label: 'Claude Code (.mcp.json)',
|
|
191
191
|
code: `{
|
|
192
192
|
"mcpServers": {
|
|
193
193
|
"astryx": {
|
|
194
|
-
"type": "
|
|
194
|
+
"type": "http",
|
|
195
195
|
"url": "https://astryx.atmeta.com/mcp"
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}`,
|
|
199
199
|
},
|
|
200
|
+
{
|
|
201
|
+
type: 'prose',
|
|
202
|
+
text: 'Claude Code can also write that entry from the terminal:',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
type: 'code',
|
|
206
|
+
lang: 'bash',
|
|
207
|
+
code: `claude mcp add --transport http astryx https://astryx.atmeta.com/mcp`,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
type: 'code',
|
|
211
|
+
lang: 'json',
|
|
212
|
+
label: 'Cline (cline_mcp_settings.json) - type is streamableHttp',
|
|
213
|
+
code: `{
|
|
214
|
+
"mcpServers": {
|
|
215
|
+
"astryx": {
|
|
216
|
+
"type": "streamableHttp",
|
|
217
|
+
"url": "https://astryx.atmeta.com/mcp"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}`,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: 'code',
|
|
224
|
+
lang: 'json',
|
|
225
|
+
label:
|
|
226
|
+
'Cursor (.cursor/mcp.json) - remote servers take no type field',
|
|
227
|
+
code: `{
|
|
228
|
+
"mcpServers": {
|
|
229
|
+
"astryx": {
|
|
230
|
+
"url": "https://astryx.atmeta.com/mcp"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}`,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
type: 'prose',
|
|
237
|
+
text: 'Claude Desktop is the exception: claude_desktop_config.json holds local stdio servers, so a remote server is added through Settings, then Connectors, then Add custom connector, pasting the URL above.',
|
|
238
|
+
},
|
|
200
239
|
{
|
|
201
240
|
type: 'prose',
|
|
202
241
|
text: 'Once connected, your AI tool can search for components by natural language (e.g. "dropdown menu", "success message") and retrieve full documentation without any manual CLI invocation. The server uses the same keyword index from component docs, so search quality improves automatically as component documentation is updated.',
|
|
@@ -30,7 +30,7 @@ export const doc = {
|
|
|
30
30
|
name: 'name',
|
|
31
31
|
type: 'string',
|
|
32
32
|
description:
|
|
33
|
-
"Stable machine identity and directory name without the Astryx prefix, PascalCase. e.g. 'Button', 'TextInput', 'AppShell'. Change `displayName`, not `name`, to edit the visible label; registry URLs derive from this identity by default.",
|
|
33
|
+
"Stable machine identity and directory name without the Astryx prefix, PascalCase. e.g. 'Button', 'TextInput', 'AppShell'. For an integration component it is the export apps import: the doc sits beside the file that exports it, and when that file exports nothing under its own name, `name` may name the export it does have. Change `displayName`, not `name`, to edit the visible label; registry URLs derive from this identity by default.",
|
|
34
34
|
required: true,
|
|
35
35
|
},
|
|
36
36
|
{
|
|
@@ -187,7 +187,7 @@ export const doc = {
|
|
|
187
187
|
name: 'components',
|
|
188
188
|
type: '(ComponentEntry | ComponentRef)[]',
|
|
189
189
|
description:
|
|
190
|
-
'MultiComponentDoc variant (required there): one entry per public component/hook exported from the directory. Each entry is a full ComponentEntry (inline: name, displayName, description, props | params+returns) or a name-only ComponentRef pointing at a sibling {Name}.doc.mjs.',
|
|
190
|
+
'MultiComponentDoc variant (required there): one entry per public component/hook exported from the directory. Each entry is a full ComponentEntry (inline: name, displayName, description, props | params+returns) or a name-only ComponentRef pointing at a sibling {Name}.doc.mjs. In an integration, each full entry the module named by `import` exports is a component under its own name.',
|
|
191
191
|
},
|
|
192
192
|
{
|
|
193
193
|
name: 'subComponentOf',
|
|
@@ -33,9 +33,12 @@ export interface ComponentBaseDoc extends AuthoredDocGraphFields {
|
|
|
33
33
|
type?: 'component';
|
|
34
34
|
/**
|
|
35
35
|
* Stable machine identity and directory name without the Astryx prefix,
|
|
36
|
-
* PascalCase (for example `Button`, `Table`, or `AppShell`).
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* PascalCase (for example `Button`, `Table`, or `AppShell`). For an
|
|
37
|
+
* integration component it is the export apps import: the doc sits beside
|
|
38
|
+
* the file that exports it, and when that file exports nothing under its own
|
|
39
|
+
* name, `name` may name the export it does have. Do not change this to edit
|
|
40
|
+
* the visible label; use `displayName` for that. Registry URLs derive from
|
|
41
|
+
* this value by default.
|
|
39
42
|
*/
|
|
40
43
|
name: string;
|
|
41
44
|
/** Human-readable display name with spaces between words, used by the
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* - Multi-file write is atomic: if one file fails, none are left behind.
|
|
10
10
|
* - An `--out` escape, or an output directory that cannot be created, fails
|
|
11
11
|
* with a registered error code.
|
|
12
|
+
* - Every output stays inside the working directory: an absolute `--out`
|
|
13
|
+
* path or a theme file outside it fails before anything is written, while
|
|
14
|
+
* absolute paths inside it build, also through a symlinked spelling.
|
|
12
15
|
*
|
|
13
16
|
* Runs the CLI in-process (shared runCli harness) against a tiny synthetic theme file.
|
|
14
17
|
*/
|
|
@@ -129,6 +132,68 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
129
132
|
expect(fs.existsSync(path.join(tmpDir, 'escaped.css'))).toBe(false);
|
|
130
133
|
});
|
|
131
134
|
|
|
135
|
+
it('reports an absolute --out path outside the project as ERR_PATH_TRAVERSAL, with --check too', async () => {
|
|
136
|
+
const project = path.join(tmpDir, 'project');
|
|
137
|
+
const outside = path.join(tmpDir, 'outside');
|
|
138
|
+
fs.mkdirSync(outside, {recursive: true});
|
|
139
|
+
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
140
|
+
const out = path.join(outside, 'ocean.css');
|
|
141
|
+
|
|
142
|
+
for (const extra of [[], ['--check']]) {
|
|
143
|
+
const {result, envelope} = await buildJson(
|
|
144
|
+
[path.relative(project, themeFile), '--out', out, ...extra],
|
|
145
|
+
project,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result.code).toBe(1);
|
|
149
|
+
expect(envelope.code).toBe(ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
150
|
+
}
|
|
151
|
+
expect(fs.readdirSync(outside)).toEqual([]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('reports a theme file outside the project as ERR_PATH_TRAVERSAL, because its outputs go beside it', async () => {
|
|
155
|
+
const project = path.join(tmpDir, 'project');
|
|
156
|
+
fs.mkdirSync(project, {recursive: true});
|
|
157
|
+
const shared = path.join(tmpDir, 'shared');
|
|
158
|
+
const themeFile = writeTheme(shared, 'ocean');
|
|
159
|
+
|
|
160
|
+
for (const file of [themeFile, path.relative(project, themeFile)]) {
|
|
161
|
+
const {result, envelope} = await buildJson([file], project);
|
|
162
|
+
|
|
163
|
+
expect(result.code).toBe(1);
|
|
164
|
+
expect(envelope.code).toBe(ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
165
|
+
}
|
|
166
|
+
expect(fs.readdirSync(shared)).toEqual(['theme.mjs']);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('builds absolute paths inside the project, also when they reach it through a symlink', async () => {
|
|
170
|
+
const project = path.join(tmpDir, 'project');
|
|
171
|
+
const themeFile = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
172
|
+
const alias = path.join(tmpDir, 'alias');
|
|
173
|
+
fs.symlinkSync(project, alias, 'dir');
|
|
174
|
+
|
|
175
|
+
for (const [args, written] of [
|
|
176
|
+
[
|
|
177
|
+
[themeFile, '--out', path.join(project, 'dist', 'direct.css')],
|
|
178
|
+
path.join(project, 'dist', 'direct.css'),
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
[themeFile, '--out', path.join(alias, 'dist', 'aliased.css')],
|
|
182
|
+
path.join(project, 'dist', 'aliased.css'),
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
[path.join(alias, 'themes', 'theme.mjs')],
|
|
186
|
+
path.join(project, 'themes', 'ocean.css'),
|
|
187
|
+
],
|
|
188
|
+
]) {
|
|
189
|
+
const {result, envelope} = await buildJson(args, project);
|
|
190
|
+
|
|
191
|
+
expect(result.code).toBe(0);
|
|
192
|
+
expect(envelope.type).toBe('theme.build');
|
|
193
|
+
expect(fs.existsSync(written)).toBe(true);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
132
197
|
it('reports an --out path that escapes through a symlink as ERR_PATH_TRAVERSAL', async () => {
|
|
133
198
|
const project = path.join(tmpDir, 'project');
|
|
134
199
|
const outside = path.join(tmpDir, 'outside');
|
|
@@ -177,4 +242,23 @@ describe('theme build output-path failures carry registered codes', () => {
|
|
|
177
242
|
code: ERROR_CODES.ERR_PATH_TRAVERSAL,
|
|
178
243
|
});
|
|
179
244
|
});
|
|
245
|
+
|
|
246
|
+
it('themeBuild() rejects an output outside cwd: an absolute out path, or the default beside a theme file outside cwd', async () => {
|
|
247
|
+
const project = path.join(tmpDir, 'project');
|
|
248
|
+
const inside = writeTheme(path.join(project, 'themes'), 'ocean');
|
|
249
|
+
const shared = path.join(tmpDir, 'shared');
|
|
250
|
+
const outsideTheme = writeTheme(shared, 'ocean');
|
|
251
|
+
const outDir = path.join(tmpDir, 'out');
|
|
252
|
+
|
|
253
|
+
for (const [file, options] of [
|
|
254
|
+
[path.relative(project, inside), {out: path.join(outDir, 'ocean.css')}],
|
|
255
|
+
[outsideTheme, {}],
|
|
256
|
+
]) {
|
|
257
|
+
await expect(
|
|
258
|
+
themeBuild(file, options, {cwd: project}),
|
|
259
|
+
).rejects.toMatchObject({code: ERROR_CODES.ERR_PATH_TRAVERSAL});
|
|
260
|
+
}
|
|
261
|
+
expect(fs.existsSync(outDir)).toBe(false);
|
|
262
|
+
expect(fs.readdirSync(shared)).toEqual(['theme.mjs']);
|
|
263
|
+
});
|
|
180
264
|
});
|
|
@@ -126,7 +126,10 @@ describe('theme build emits a live TextInput selector (#4109)', () => {
|
|
|
126
126
|
`};\n`,
|
|
127
127
|
);
|
|
128
128
|
|
|
129
|
-
const result = await runCli(
|
|
129
|
+
const result = await runCli(
|
|
130
|
+
['theme', 'build', themeFile, '-o', outFile],
|
|
131
|
+
tmpDir,
|
|
132
|
+
);
|
|
130
133
|
const css = fs.readFileSync(outFile, 'utf8');
|
|
131
134
|
|
|
132
135
|
expect(result.status).toBe(0);
|
|
@@ -148,7 +151,7 @@ describe('theme build emits a live TextInput selector (#4109)', () => {
|
|
|
148
151
|
`};\n`,
|
|
149
152
|
);
|
|
150
153
|
|
|
151
|
-
await runCli(['theme', 'build', themeFile, '-o', outFile]);
|
|
154
|
+
await runCli(['theme', 'build', themeFile, '-o', outFile], tmpDir);
|
|
152
155
|
const css = fs.readFileSync(outFile, 'utf8');
|
|
153
156
|
|
|
154
157
|
expect(css).toContain('.astryx-text-input');
|
|
@@ -30,7 +30,7 @@ export const doc = {
|
|
|
30
30
|
{
|
|
31
31
|
flag: '--family',
|
|
32
32
|
description:
|
|
33
|
-
'Build the selected extension family into one keyed CSS, JS, and declaration set. Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
|
|
33
|
+
'Build the selected extension family into one keyed CSS, JS, and declaration set, written beside the family root theme file, which must be inside the working directory (ERR_PATH_TRAVERSAL). Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
flag: '--family-key <key>',
|
|
@@ -41,7 +41,7 @@ export const doc = {
|
|
|
41
41
|
flag: '-o, --out <path>',
|
|
42
42
|
param: 'options.out',
|
|
43
43
|
description:
|
|
44
|
-
'Output CSS file path; the .js and .d.ts are written beside it (default: <theme name>.css next to the source).
|
|
44
|
+
'Output CSS file path, relative or absolute; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). Every output must resolve inside the working directory, the defaults beside the source too (ERR_PATH_TRAVERSAL). Single theme only: refused with more than one file or with --family (ERR_THEME_INVALID)',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
flag: '--icons-specifier <specifier>',
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
// @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* The entry a family doc gives `name` in its `components`, when the doc is not
|
|
6
|
+
* the component's own (Stack's doc for VStack); null otherwise.
|
|
7
|
+
* @param {any} docs - a loaded component doc
|
|
8
|
+
* @param {string} name - bare component name
|
|
9
|
+
* @returns {any}
|
|
10
|
+
*/
|
|
11
|
+
export function familyMember(docs: any, name: string): any;
|
|
4
12
|
/**
|
|
5
13
|
* Auto-discover components by scanning for Astryx*.tsx files in core/src/.
|
|
14
|
+
* A component is listed when a doc documents it: a doc in its folder, or, for a
|
|
15
|
+
* component folder with no doc of its own, the doc `findComponentReadme` finds
|
|
16
|
+
* elsewhere (Heading's doc in the Text folder; HStack and VStack as members of
|
|
17
|
+
* Stack's doc).
|
|
6
18
|
*
|
|
7
19
|
* Returns an ordered Record where:
|
|
8
20
|
* - Grouped components use the group name as key: `'Buttons': ['Button', 'IconButton']`
|
|
@@ -103,19 +115,81 @@ export function discoverExternalComponentsGrouped(docsDir: string): Record<strin
|
|
|
103
115
|
* @returns {string | null}
|
|
104
116
|
*/
|
|
105
117
|
export function findExternalComponentDoc(docsDir: string, name: string): string | null;
|
|
118
|
+
/** @param {string} docPath */
|
|
119
|
+
export function docStem(docPath: string): string;
|
|
120
|
+
/**
|
|
121
|
+
* Each family doc under an integration's components root that names an
|
|
122
|
+
* import, with the file the import resolves to: null when it resolves to none,
|
|
123
|
+
* such as build output that does not exist yet.
|
|
124
|
+
*
|
|
125
|
+
* @param {{name: string, components?: string, issuesUrl?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
126
|
+
* @returns {Array<{docPath: string, module: string | null}>}
|
|
127
|
+
*/
|
|
128
|
+
export function familyImports(integration: {
|
|
129
|
+
name: string;
|
|
130
|
+
components?: string;
|
|
131
|
+
issuesUrl?: string;
|
|
132
|
+
__packageDir?: string;
|
|
133
|
+
__packageExports?: Record<string, unknown> | null;
|
|
134
|
+
}): Array<{
|
|
135
|
+
docPath: string;
|
|
136
|
+
module: string | null;
|
|
137
|
+
}>;
|
|
138
|
+
/**
|
|
139
|
+
* Whether a discovered integration record is a member its family doc lists,
|
|
140
|
+
* decided from the files, not from whether the doc loads.
|
|
141
|
+
*
|
|
142
|
+
* @param {{name: string, docPath: string|null}} record
|
|
143
|
+
* @returns {boolean}
|
|
144
|
+
*/
|
|
145
|
+
export function isIntegrationMember(record: {
|
|
146
|
+
name: string;
|
|
147
|
+
docPath: string | null;
|
|
148
|
+
}): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* The entry a family doc gives a member that discovery lists under its own
|
|
151
|
+
* name, or null when the record is the doc's own component or the loaded doc
|
|
152
|
+
* has no entry for it.
|
|
153
|
+
*
|
|
154
|
+
* @param {any} doc the doc loaded from `record.docPath`
|
|
155
|
+
* @param {{name: string, docPath: string|null}} record
|
|
156
|
+
* @returns {any}
|
|
157
|
+
*/
|
|
158
|
+
export function integrationMemberEntry(doc: any, record: {
|
|
159
|
+
name: string;
|
|
160
|
+
docPath: string | null;
|
|
161
|
+
}): any;
|
|
162
|
+
/**
|
|
163
|
+
* Visit every component doc under an integration's components root, hidden
|
|
164
|
+
* ones included, in discovery order, with its stem.
|
|
165
|
+
* @param {string | undefined} componentsDir
|
|
166
|
+
* @param {(docPath: string, stem: string) => void} visit
|
|
167
|
+
*/
|
|
168
|
+
export function forEachIntegrationDoc(componentsDir: string | undefined, visit: (docPath: string, stem: string) => void): void;
|
|
106
169
|
/**
|
|
107
170
|
* Discover ownership records for the components contributed by a single loaded
|
|
108
171
|
* integration. Scans the integration's resolved `components` dir for same-stem
|
|
109
|
-
* doc files and records each with owner package + issuesUrl + sourcePath.
|
|
172
|
+
* doc files and records each with owner package + issuesUrl + sourcePath. A doc
|
|
173
|
+
* that names a differently named export of its file is recorded under that
|
|
174
|
+
* export, and each family member it lists, under the member's name, unless the
|
|
175
|
+
* name is taken: every doc's file name (a hidden doc's too), a Core name a doc
|
|
176
|
+
* in the package replaces, and a name an earlier doc records.
|
|
110
177
|
*
|
|
111
|
-
* @param {{name: string, components?: string, issuesUrl?: string}} integration
|
|
178
|
+
* @param {{name: string, components?: string, issuesUrl?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
112
179
|
* a single entry from `loadedIntegrations` (lib/integrations.mjs)
|
|
180
|
+
* @param {{documentedNames?: boolean}} [options] `documentedNames: false`
|
|
181
|
+
* records each doc under its file name alone, without reading the names
|
|
182
|
+
* docs document: for a caller that needs only what each doc says
|
|
113
183
|
* @returns {Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>}
|
|
114
184
|
*/
|
|
115
185
|
export function discoverIntegrationComponents(integration: {
|
|
116
186
|
name: string;
|
|
117
187
|
components?: string;
|
|
118
188
|
issuesUrl?: string;
|
|
189
|
+
__packageDir?: string;
|
|
190
|
+
__packageExports?: Record<string, unknown> | null;
|
|
191
|
+
}, { documentedNames }?: {
|
|
192
|
+
documentedNames?: boolean;
|
|
119
193
|
}): Array<{
|
|
120
194
|
name: string;
|
|
121
195
|
package: string;
|
|
@@ -129,11 +203,16 @@ export function discoverIntegrationComponents(integration: {
|
|
|
129
203
|
* Invalid metadata or a missing same-stem source removes only that component;
|
|
130
204
|
* callers can report the returned errors while retaining valid siblings. A
|
|
131
205
|
* component whose doc sets `replaces` carries the value as written; the
|
|
132
|
-
* component replacement resolver decides what it means.
|
|
206
|
+
* component replacement resolver decides what it means. A family member is
|
|
207
|
+
* read through its family's doc: it never carries the family's `replaces`, and
|
|
208
|
+
* a family doc that fails to load is reported once. A doc that fails to load
|
|
209
|
+
* is reported under its file name: what it says cannot be trusted.
|
|
133
210
|
*
|
|
134
211
|
* @param {{name: string, components?: string, issuesUrl?: string}} integration
|
|
212
|
+
* @param {{documentedNames?: boolean}} [options] as for
|
|
213
|
+
* {@link discoverIntegrationComponents}
|
|
135
214
|
* @returns {Promise<{
|
|
136
|
-
* components: Array<{name: string, package: string, docPath: string, sourcePath: string, issuesUrl: string|undefined, group: string|null, replaces?: unknown}>,
|
|
215
|
+
* components: Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null, replaces?: unknown}>,
|
|
137
216
|
* discovered: Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>,
|
|
138
217
|
* errors: Array<{name: string, message: string}>,
|
|
139
218
|
* }>}
|
|
@@ -142,12 +221,14 @@ export function discoverValidIntegrationComponents(integration: {
|
|
|
142
221
|
name: string;
|
|
143
222
|
components?: string;
|
|
144
223
|
issuesUrl?: string;
|
|
224
|
+
}, options?: {
|
|
225
|
+
documentedNames?: boolean;
|
|
145
226
|
}): Promise<{
|
|
146
227
|
components: Array<{
|
|
147
228
|
name: string;
|
|
148
229
|
package: string;
|
|
149
230
|
docPath: string;
|
|
150
|
-
sourcePath: string;
|
|
231
|
+
sourcePath: string | null;
|
|
151
232
|
issuesUrl: string | undefined;
|
|
152
233
|
group: string | null;
|
|
153
234
|
replaces?: unknown;
|
|
@@ -165,30 +246,103 @@ export function discoverValidIntegrationComponents(integration: {
|
|
|
165
246
|
message: string;
|
|
166
247
|
}>;
|
|
167
248
|
}>;
|
|
249
|
+
/**
|
|
250
|
+
* How an owner holds a component name, which the owner rule
|
|
251
|
+
* ({@link unscopedComponentOwners}) reads: `core` when Core documents it;
|
|
252
|
+
* `file` when an integration doc named after its file is recorded under it;
|
|
253
|
+
* `doc` when only a doc records it: an export name, a member, or the file name
|
|
254
|
+
* of a doc recorded under another export.
|
|
255
|
+
* @typedef {'core' | 'file' | 'doc'} NameHold
|
|
256
|
+
*/
|
|
257
|
+
/**
|
|
258
|
+
* How a discovered integration record holds its name.
|
|
259
|
+
* @param {{name: string, docPath: string|null}} record
|
|
260
|
+
* @returns {'file' | 'doc'}
|
|
261
|
+
*/
|
|
262
|
+
export function integrationRecordHold(record: {
|
|
263
|
+
name: string;
|
|
264
|
+
docPath: string | null;
|
|
265
|
+
}): "file" | "doc";
|
|
266
|
+
/**
|
|
267
|
+
* The integration component a name finds, as discovery records it, and its
|
|
268
|
+
* hold: the same-stem doc first, then a doc that records the name as an export
|
|
269
|
+
* or a member.
|
|
270
|
+
*
|
|
271
|
+
* @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
272
|
+
* @param {string} name
|
|
273
|
+
* @returns {{docPath: string, sourcePath: string|null, hold: 'file' | 'doc'} | null}
|
|
274
|
+
*/
|
|
275
|
+
export function findIntegrationComponent(integration: {
|
|
276
|
+
name?: string;
|
|
277
|
+
components?: string;
|
|
278
|
+
__packageDir?: string;
|
|
279
|
+
__packageExports?: Record<string, unknown> | null;
|
|
280
|
+
}, name: string): {
|
|
281
|
+
docPath: string;
|
|
282
|
+
sourcePath: string | null;
|
|
283
|
+
hold: "file" | "doc";
|
|
284
|
+
} | null;
|
|
285
|
+
/**
|
|
286
|
+
* The name discovery records a component doc under: the export its `name`
|
|
287
|
+
* documents when the doc is recorded under it, else the doc's file name.
|
|
288
|
+
*
|
|
289
|
+
* @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
290
|
+
* @param {string} docPath
|
|
291
|
+
* @returns {string}
|
|
292
|
+
*/
|
|
293
|
+
export function recordedDocName(integration: {
|
|
294
|
+
name?: string;
|
|
295
|
+
components?: string;
|
|
296
|
+
__packageDir?: string;
|
|
297
|
+
__packageExports?: Record<string, unknown> | null;
|
|
298
|
+
}, docPath: string): string;
|
|
168
299
|
/**
|
|
169
300
|
* Find an integration component's doc file by name within a loaded
|
|
170
301
|
* integration's resolved `components` dir. Honors the same-stem convention
|
|
171
|
-
* (`{Name}.doc.{ts,mjs,js}`), preferring `.ts` → `.mjs` → `.js
|
|
302
|
+
* (`{Name}.doc.{ts,mjs,js}`), preferring `.ts` → `.mjs` → `.js`, and finds a
|
|
303
|
+
* doc that records the component under another name: a differently named
|
|
304
|
+
* export of its file, or a member its family doc lists.
|
|
172
305
|
*
|
|
173
|
-
* @param {{components?: string}} integration
|
|
306
|
+
* @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
174
307
|
* @param {string} name bare component name (no `XDS`/`Astryx` prefix)
|
|
175
308
|
* @returns {string|null}
|
|
176
309
|
*/
|
|
177
310
|
export function findIntegrationComponentDoc(integration: {
|
|
311
|
+
name?: string;
|
|
178
312
|
components?: string;
|
|
313
|
+
__packageDir?: string;
|
|
314
|
+
__packageExports?: Record<string, unknown> | null;
|
|
179
315
|
}, name: string): string | null;
|
|
180
316
|
/**
|
|
181
|
-
* Find an integration component's swizzleable source file
|
|
182
|
-
*
|
|
317
|
+
* Find an integration component's swizzleable source file by name within a
|
|
318
|
+
* loaded integration's resolved `components` dir: the same-stem `{Name}.tsx`
|
|
319
|
+
* of its doc, or, for a family member, the file that declares it. Returns null
|
|
183
320
|
* when the integration ships docs without source.
|
|
184
321
|
*
|
|
185
|
-
* @param {{components?: string}} integration
|
|
322
|
+
* @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
|
|
186
323
|
* @param {string} name bare component name
|
|
187
324
|
* @returns {string|null}
|
|
188
325
|
*/
|
|
189
326
|
export function findIntegrationComponentSource(integration: {
|
|
327
|
+
name?: string;
|
|
190
328
|
components?: string;
|
|
329
|
+
__packageDir?: string;
|
|
330
|
+
__packageExports?: Record<string, unknown> | null;
|
|
191
331
|
}, name: string): string | null;
|
|
332
|
+
/**
|
|
333
|
+
* The owner rule: an owner that holds a name only because a doc records it
|
|
334
|
+
* (`hold: 'doc'`) yields to Core and to every file-named owner; `--package`
|
|
335
|
+
* still reaches it. Surfaces resolve a replacement first, then this rule, then
|
|
336
|
+
* which docs load.
|
|
337
|
+
*
|
|
338
|
+
* @template {{hold?: string}} T
|
|
339
|
+
* @param {T[]} owners each with its {@link NameHold}; an owner without one
|
|
340
|
+
* counts as Core's or file-named
|
|
341
|
+
* @returns {T[]}
|
|
342
|
+
*/
|
|
343
|
+
export function unscopedComponentOwners<T extends {
|
|
344
|
+
hold?: string;
|
|
345
|
+
}>(owners: T[]): T[];
|
|
192
346
|
/**
|
|
193
347
|
* Build a flat list of ownership records for ALL discoverable components —
|
|
194
348
|
* core (built-in) plus every loaded integration. This is the authoritative
|
|
@@ -217,3 +371,11 @@ export function discoverOwnedComponents(coreDir: string, loadedIntegrations?: Ar
|
|
|
217
371
|
}>;
|
|
218
372
|
/** The owner package name for built-in (core) components. */
|
|
219
373
|
export const CORE_PACKAGE: import("../../authoring/index.js").ProviderId;
|
|
374
|
+
/**
|
|
375
|
+
* How an owner holds a component name, which the owner rule
|
|
376
|
+
* ({@link unscopedComponentOwners}) reads: `core` when Core documents it;
|
|
377
|
+
* `file` when an integration doc named after its file is recorded under it;
|
|
378
|
+
* `doc` when only a doc records it: an export name, a member, or the file name
|
|
379
|
+
* of a doc recorded under another export.
|
|
380
|
+
*/
|
|
381
|
+
export type NameHold = "core" | "file" | "doc";
|