@astryxdesign/cli 0.7.2-canary.db3a2e7 → 0.7.2-canary.ebcc404
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 +27 -1
- package/api/component/_adapter.mjs +45 -7
- package/api/component/component.mjs +12 -3
- package/api/component/component.test.mjs +420 -167
- package/api/component/integration-export-name.test.mjs +419 -0
- package/api/component/list/list.mjs +42 -4
- package/api/gap-report/gap-report.mjs +66 -4
- package/api/integration/pack-check.mjs +13 -104
- package/api/integration/pack-check.test.mjs +112 -0
- package/api/search/search.d.mts +9 -0
- package/api/search/search.mjs +90 -10
- package/api/search/search.test.mjs +36 -0
- package/api/swizzle/copy/copy.mjs +28 -14
- package/api/template/template.test.mjs +120 -3
- 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/component-imports.doc.mjs +8 -0
- package/assets/docs/working-with-ai.doc.mjs +43 -4
- package/assets/templates/blocks/components/Drawer/DrawerForm.doc.mjs +24 -0
- package/assets/templates/blocks/components/Drawer/DrawerForm.tsx +62 -0
- package/assets/templates/blocks/components/Drawer/DrawerScrim.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerScrim.tsx +68 -0
- package/assets/templates/blocks/components/Drawer/DrawerShowcase.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerShowcase.tsx +41 -0
- package/assets/templates/blocks/components/Drawer/DrawerStart.doc.mjs +21 -0
- package/assets/templates/blocks/components/Drawer/DrawerStart.tsx +41 -0
- package/assets/templates/blocks/components/Drawer/DrawerWidths.doc.mjs +22 -0
- package/assets/templates/blocks/components/Drawer/DrawerWidths.tsx +54 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.tsx +73 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.doc.mjs +22 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.tsx +49 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.doc.mjs +21 -0
- package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.tsx +42 -0
- package/assets/templates/pages/detail-page/page.tsx +3 -3
- package/assets/templates/pages/table-inbox/page.tsx +10 -8
- package/authoring/doctypes/base/type.ts +6 -0
- package/authoring/doctypes/component/component.doc.mjs +3 -3
- 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/clients/cli/lib/component-format.mjs +5 -1
- package/foundation/discovery/component-discovery.d.mts +95 -6
- package/foundation/discovery/component-discovery.export-names.test.mjs +293 -0
- package/foundation/discovery/component-discovery.mjs +524 -49
- package/foundation/discovery/component-discovery.test.mjs +96 -0
- package/foundation/discovery/component-replacement.mjs +18 -0
- package/foundation/discovery/component-replacement.test.mjs +20 -0
- package/foundation/discovery/module-exports.d.mts +21 -0
- package/foundation/discovery/module-exports.mjs +343 -31
- package/foundation/discovery/module-exports.test.mjs +172 -0
- package/foundation/discovery/template-adapter.mjs +74 -5
- package/foundation/doc-compiler/doc-loads.test.mjs +5 -1
- package/foundation/doc-compiler/inputs.mjs +23 -0
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +10 -10
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @input Controlled open state for a modal Drawer
|
|
7
|
+
* @output A Drawer whose DrawerHeader renders a close button because it receives onOpenChange
|
|
8
|
+
* @position Copyable DrawerHeader example
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {useState} from 'react';
|
|
12
|
+
import {Drawer, DrawerHeader} from '@astryxdesign/core/Drawer';
|
|
13
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
+
import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
15
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
16
|
+
|
|
17
|
+
export default function DrawerHeaderWithCloseButton() {
|
|
18
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Button label="Open drawer" onClick={() => setIsOpen(true)} />
|
|
23
|
+
<Drawer
|
|
24
|
+
isOpen={isOpen}
|
|
25
|
+
onOpenChange={setIsOpen}
|
|
26
|
+
label="Details"
|
|
27
|
+
width={360}>
|
|
28
|
+
<Layout
|
|
29
|
+
header={<DrawerHeader title="Details" onOpenChange={setIsOpen} />}
|
|
30
|
+
content={
|
|
31
|
+
<LayoutContent>
|
|
32
|
+
<Text type="body">
|
|
33
|
+
Passing onOpenChange renders the close button, which calls it
|
|
34
|
+
with false.
|
|
35
|
+
</Text>
|
|
36
|
+
</LayoutContent>
|
|
37
|
+
}
|
|
38
|
+
/>
|
|
39
|
+
</Drawer>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -600,9 +600,9 @@ export default function DetailPage2Template() {
|
|
|
600
600
|
}
|
|
601
601
|
end={!isNarrow && showSidePanel ? <RightPanel /> : undefined}
|
|
602
602
|
/>
|
|
603
|
-
{/* Mobile: the side
|
|
604
|
-
|
|
605
|
-
|
|
603
|
+
{/* Mobile: the side-panel content uses the full viewport so order details
|
|
604
|
+
are not squeezed beside narrow page chrome. Drawer remains available
|
|
605
|
+
for side-overlay flows that intentionally preserve page context. */}
|
|
606
606
|
<Dialog
|
|
607
607
|
variant="fullscreen"
|
|
608
608
|
isOpen={isNarrow && isPanelDialogOpen}
|
|
@@ -1652,8 +1652,8 @@ const COMPOSER_TOOLS: {
|
|
|
1652
1652
|
/**
|
|
1653
1653
|
* The compose sheet.
|
|
1654
1654
|
*
|
|
1655
|
-
* **`BottomSheet hasScrim={false}
|
|
1656
|
-
*
|
|
1655
|
+
* **`BottomSheet hasScrim={false}` keeps this bottom-edge composer non-modal.**
|
|
1656
|
+
* With the scrim off the sheet opens with `show()` rather than
|
|
1657
1657
|
* `showModal()`, so it never enters the top layer, never makes the page inert
|
|
1658
1658
|
* and never locks body scroll: the inbox behind it stays live. That matters
|
|
1659
1659
|
* more here than anywhere else in this template, because writing a message is
|
|
@@ -1661,12 +1661,14 @@ const COMPOSER_TOOLS: {
|
|
|
1661
1661
|
* actually said, copy an order number out of a thread. A modal composer
|
|
1662
1662
|
* answers that with "close me first".
|
|
1663
1663
|
*
|
|
1664
|
-
* The trade is
|
|
1665
|
-
* edge,
|
|
1666
|
-
*
|
|
1667
|
-
*
|
|
1668
|
-
*
|
|
1669
|
-
*
|
|
1664
|
+
* The trade is bottom-edge geometry, and it is worth naming. A non-modal
|
|
1665
|
+
* `Drawer` keeps page context live from an inline edge, but it is a full-height
|
|
1666
|
+
* side panel; this composer intentionally rises from the bottom. A sheet's
|
|
1667
|
+
* positioner is `inset-inline: 0` with `justify-content: center`, and there is
|
|
1668
|
+
* no anchor, side or width prop. So this cannot be docked to the inline-end
|
|
1669
|
+
* corner the way a `Dialog` can be. `Dialog` takes a static `position` and would
|
|
1670
|
+
* put the window in that corner — but `Dialog` is always modal, so buying the
|
|
1671
|
+
* corner costs the live inbox.
|
|
1670
1672
|
* `ChatComposerDrawer` is a false friend: despite the name it is an in-flow
|
|
1671
1673
|
* disclosure tray for a ChatComposer's attachments, with no positioning of its
|
|
1672
1674
|
* own.
|
|
@@ -383,6 +383,12 @@ export interface ComponentPropDoc {
|
|
|
383
383
|
* ```
|
|
384
384
|
*/
|
|
385
385
|
slotElements?: ComponentSlotElement[];
|
|
386
|
+
/** Lifecycle marker for a prop outside the stable compatibility promise
|
|
387
|
+
* (spec:AST-017 FR21). Set `'experimental'` on a stable component's
|
|
388
|
+
* optional `experimental` prop object, and say in `description` that its
|
|
389
|
+
* contents may change or be removed in a patch release. Omit for stable
|
|
390
|
+
* props. */
|
|
391
|
+
stability?: 'experimental';
|
|
386
392
|
}
|
|
387
393
|
|
|
388
394
|
/**
|
|
@@ -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
|
{
|
|
@@ -56,7 +56,7 @@ export const doc = {
|
|
|
56
56
|
name: 'replaces',
|
|
57
57
|
type: 'string',
|
|
58
58
|
description:
|
|
59
|
-
|
|
59
|
+
'Integration components only: the exact `name` of the Core ComponentDoc this component takes over for unqualified lookup, so every app that loads the integration gets it from component detail, component lists, search, `swizzle <Name>`, and issue routing; `swizzle --list` keeps listing Core names. The Core original stays reachable with `--package @astryxdesign/core`. It takes effect only when the package\'s peer range starts at the release that applies it, `"@astryxdesign/cli": ">=0.6.7"` or later; without such a range the component keeps its own name and `doctor integration components` warns. Set it only to intentionally own a Core identity; give an alternative or variant its own name instead.',
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
name: 'keywords',
|
|
@@ -181,7 +181,7 @@ export const doc = {
|
|
|
181
181
|
name: 'props',
|
|
182
182
|
type: 'ComponentPropDoc[]',
|
|
183
183
|
description:
|
|
184
|
-
'SingleComponentDoc variant (required there): all public props for the one primary component. Each prop is {name, type, description, default?, required?, slotElements?}. Skip styling props like xstyle/className/style. Also present on SubComponentDoc.',
|
|
184
|
+
'SingleComponentDoc variant (required there): all public props for the one primary component. Each prop is {name, type, description, default?, required?, slotElements?, stability?}. Skip styling props like xstyle/className/style. Also present on SubComponentDoc.',
|
|
185
185
|
},
|
|
186
186
|
{
|
|
187
187
|
name: 'components',
|
|
@@ -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>',
|
|
@@ -68,8 +68,12 @@ function formatPropsTable(props) {
|
|
|
68
68
|
for (const p of props) {
|
|
69
69
|
const def = p.default ? `\`${mdCell(p.default)}\`` : '-';
|
|
70
70
|
const req = p.required ? ' **(required)**' : '';
|
|
71
|
+
// The text projection of the prop doc's `stability` field: an
|
|
72
|
+
// experimental prop is outside the stable compatibility promise.
|
|
73
|
+
const stability =
|
|
74
|
+
p.stability === 'experimental' ? ' **(experimental)**' : '';
|
|
71
75
|
lines.push(
|
|
72
|
-
`| \`${mdCell(p.name)}\` | \`${mdCell(p.type)}\` | ${def} | ${mdCell(p.description)}${req} |`,
|
|
76
|
+
`| \`${mdCell(p.name)}\` | \`${mdCell(p.type)}\` | ${def} | ${mdCell(p.description)}${req}${stability} |`,
|
|
73
77
|
);
|
|
74
78
|
}
|
|
75
79
|
return lines.join('\n');
|
|
@@ -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,36 @@ 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
|
+
* Visit every component doc under an integration's components root, hidden
|
|
122
|
+
* ones included, in discovery order, with its stem.
|
|
123
|
+
* @param {string | undefined} componentsDir
|
|
124
|
+
* @param {(docPath: string, stem: string) => void} visit
|
|
125
|
+
*/
|
|
126
|
+
export function forEachIntegrationDoc(componentsDir: string | undefined, visit: (docPath: string, stem: string) => void): void;
|
|
106
127
|
/**
|
|
107
128
|
* Discover ownership records for the components contributed by a single loaded
|
|
108
129
|
* integration. Scans the integration's resolved `components` dir for same-stem
|
|
109
|
-
* doc files and records each with owner package + issuesUrl + sourcePath.
|
|
130
|
+
* doc files and records each with owner package + issuesUrl + sourcePath. A doc
|
|
131
|
+
* that names a differently named export of its file is recorded under that
|
|
132
|
+
* export unless the name is taken: every doc's file name (a hidden doc's too),
|
|
133
|
+
* a Core name a doc in the package replaces, and a name an earlier doc records.
|
|
110
134
|
*
|
|
111
135
|
* @param {{name: string, components?: string, issuesUrl?: string}} integration
|
|
112
136
|
* a single entry from `loadedIntegrations` (lib/integrations.mjs)
|
|
137
|
+
* @param {{documentedNames?: boolean}} [options] `documentedNames: false`
|
|
138
|
+
* records each doc under its file name alone, without reading the names
|
|
139
|
+
* docs document: for a caller that needs only what each doc says
|
|
113
140
|
* @returns {Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>}
|
|
114
141
|
*/
|
|
115
142
|
export function discoverIntegrationComponents(integration: {
|
|
116
143
|
name: string;
|
|
117
144
|
components?: string;
|
|
118
145
|
issuesUrl?: string;
|
|
146
|
+
}, { documentedNames }?: {
|
|
147
|
+
documentedNames?: boolean;
|
|
119
148
|
}): Array<{
|
|
120
149
|
name: string;
|
|
121
150
|
package: string;
|
|
@@ -129,9 +158,12 @@ export function discoverIntegrationComponents(integration: {
|
|
|
129
158
|
* Invalid metadata or a missing same-stem source removes only that component;
|
|
130
159
|
* callers can report the returned errors while retaining valid siblings. A
|
|
131
160
|
* component whose doc sets `replaces` carries the value as written; the
|
|
132
|
-
* component replacement resolver decides what it means.
|
|
161
|
+
* component replacement resolver decides what it means. A doc that fails to
|
|
162
|
+
* load is reported under its file name: what it says cannot be trusted.
|
|
133
163
|
*
|
|
134
164
|
* @param {{name: string, components?: string, issuesUrl?: string}} integration
|
|
165
|
+
* @param {{documentedNames?: boolean}} [options] as for
|
|
166
|
+
* {@link discoverIntegrationComponents}
|
|
135
167
|
* @returns {Promise<{
|
|
136
168
|
* components: Array<{name: string, package: string, docPath: string, sourcePath: string, issuesUrl: string|undefined, group: string|null, replaces?: unknown}>,
|
|
137
169
|
* discovered: Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>,
|
|
@@ -142,6 +174,8 @@ export function discoverValidIntegrationComponents(integration: {
|
|
|
142
174
|
name: string;
|
|
143
175
|
components?: string;
|
|
144
176
|
issuesUrl?: string;
|
|
177
|
+
}, options?: {
|
|
178
|
+
documentedNames?: boolean;
|
|
145
179
|
}): Promise<{
|
|
146
180
|
components: Array<{
|
|
147
181
|
name: string;
|
|
@@ -165,10 +199,43 @@ export function discoverValidIntegrationComponents(integration: {
|
|
|
165
199
|
message: string;
|
|
166
200
|
}>;
|
|
167
201
|
}>;
|
|
202
|
+
/**
|
|
203
|
+
* How an owner holds a component name, which the owner rule
|
|
204
|
+
* ({@link unscopedComponentOwners}) reads: `core` when Core documents it;
|
|
205
|
+
* `file` when an integration doc named after its file is recorded under it;
|
|
206
|
+
* `doc` when only a doc records it: an export name, or the file name of a doc
|
|
207
|
+
* recorded under another export.
|
|
208
|
+
* @typedef {'core' | 'file' | 'doc'} NameHold
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* How a discovered integration record holds its name.
|
|
212
|
+
* @param {{name: string, docPath: string|null}} record
|
|
213
|
+
* @returns {'file' | 'doc'}
|
|
214
|
+
*/
|
|
215
|
+
export function integrationRecordHold(record: {
|
|
216
|
+
name: string;
|
|
217
|
+
docPath: string | null;
|
|
218
|
+
}): "file" | "doc";
|
|
219
|
+
/**
|
|
220
|
+
* The integration component a name finds, as discovery records it, and its
|
|
221
|
+
* hold: the same-stem doc first, then a doc that records the name.
|
|
222
|
+
*
|
|
223
|
+
* @param {{components?: string}} integration
|
|
224
|
+
* @param {string} name
|
|
225
|
+
* @returns {{docPath: string, sourcePath: string|null, hold: 'file' | 'doc'} | null}
|
|
226
|
+
*/
|
|
227
|
+
export function findIntegrationComponent(integration: {
|
|
228
|
+
components?: string;
|
|
229
|
+
}, name: string): {
|
|
230
|
+
docPath: string;
|
|
231
|
+
sourcePath: string | null;
|
|
232
|
+
hold: "file" | "doc";
|
|
233
|
+
} | null;
|
|
168
234
|
/**
|
|
169
235
|
* Find an integration component's doc file by name within a loaded
|
|
170
236
|
* integration's resolved `components` dir. Honors the same-stem convention
|
|
171
|
-
* (`{Name}.doc.{ts,mjs,js}`), preferring `.ts` → `.mjs` → `.js
|
|
237
|
+
* (`{Name}.doc.{ts,mjs,js}`), preferring `.ts` → `.mjs` → `.js`, and finds a
|
|
238
|
+
* doc that records the component under a differently named export of its file.
|
|
172
239
|
*
|
|
173
240
|
* @param {{components?: string}} integration
|
|
174
241
|
* @param {string} name bare component name (no `XDS`/`Astryx` prefix)
|
|
@@ -178,9 +245,9 @@ export function findIntegrationComponentDoc(integration: {
|
|
|
178
245
|
components?: string;
|
|
179
246
|
}, name: string): string | null;
|
|
180
247
|
/**
|
|
181
|
-
* Find an integration component's swizzleable source file
|
|
182
|
-
*
|
|
183
|
-
* when the integration ships docs without source.
|
|
248
|
+
* Find an integration component's swizzleable source file by name within a
|
|
249
|
+
* loaded integration's resolved `components` dir: the same-stem `{Name}.tsx`
|
|
250
|
+
* of its doc. Returns null when the integration ships docs without source.
|
|
184
251
|
*
|
|
185
252
|
* @param {{components?: string}} integration
|
|
186
253
|
* @param {string} name bare component name
|
|
@@ -189,6 +256,20 @@ export function findIntegrationComponentDoc(integration: {
|
|
|
189
256
|
export function findIntegrationComponentSource(integration: {
|
|
190
257
|
components?: string;
|
|
191
258
|
}, name: string): string | null;
|
|
259
|
+
/**
|
|
260
|
+
* The owner rule: an owner that holds a name only because a doc records it
|
|
261
|
+
* (`hold: 'doc'`) yields to Core and to every file-named owner; `--package`
|
|
262
|
+
* still reaches it. Surfaces resolve a replacement first, then this rule, then
|
|
263
|
+
* which docs load.
|
|
264
|
+
*
|
|
265
|
+
* @template {{hold?: string}} T
|
|
266
|
+
* @param {T[]} owners each with its {@link NameHold}; an owner without one
|
|
267
|
+
* counts as Core's or file-named
|
|
268
|
+
* @returns {T[]}
|
|
269
|
+
*/
|
|
270
|
+
export function unscopedComponentOwners<T extends {
|
|
271
|
+
hold?: string;
|
|
272
|
+
}>(owners: T[]): T[];
|
|
192
273
|
/**
|
|
193
274
|
* Build a flat list of ownership records for ALL discoverable components —
|
|
194
275
|
* core (built-in) plus every loaded integration. This is the authoritative
|
|
@@ -217,3 +298,11 @@ export function discoverOwnedComponents(coreDir: string, loadedIntegrations?: Ar
|
|
|
217
298
|
}>;
|
|
218
299
|
/** The owner package name for built-in (core) components. */
|
|
219
300
|
export const CORE_PACKAGE: import("../../authoring/index.js").ProviderId;
|
|
301
|
+
/**
|
|
302
|
+
* How an owner holds a component name, which the owner rule
|
|
303
|
+
* ({@link unscopedComponentOwners}) reads: `core` when Core documents it;
|
|
304
|
+
* `file` when an integration doc named after its file is recorded under it;
|
|
305
|
+
* `doc` when only a doc records it: an export name, or the file name of a doc
|
|
306
|
+
* recorded under another export.
|
|
307
|
+
*/
|
|
308
|
+
export type NameHold = "core" | "file" | "doc";
|