@astryxdesign/cli 0.7.2-canary.f16dc9f → 0.7.2-canary.f2b60a0
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/README.md +15 -14
- package/api/build/help/help.mjs +1 -1
- 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 +420 -167
- package/api/component/integration-export-name.test.mjs +644 -0
- package/api/component/list/list.mjs +42 -4
- package/api/docs/_adapter.d.mts +11 -0
- package/api/docs/_adapter.mjs +244 -1
- package/api/docs/integrationDocs.test.mjs +124 -0
- package/api/doctor/doctor.mjs +27 -3
- package/api/doctor/doctor.test.mjs +43 -0
- package/api/doctor/theme-checks.mjs +1 -1
- package/api/gap-report/gap-report.mjs +67 -4
- package/api/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- package/api/init/run/run.mjs +2 -2
- package/api/integration/_adapter.d.mts +12 -0
- package/api/integration/_adapter.mjs +54 -0
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +8 -4
- package/api/integration/add-contribution.test.mjs +4 -2
- package/api/integration/authoring-checks.mjs +11 -2
- package/api/integration/authoring-checks.test.mjs +39 -0
- package/api/integration/authoring-checks.type.d.mts +9 -0
- package/api/integration/authoring-checks.type.mjs +14 -1
- package/api/integration/check/check.d.mts +96 -0
- package/api/integration/check/check.mjs +357 -0
- package/api/integration/check/check.test.mjs +489 -0
- package/api/integration/integration-check.type.d.mts +118 -0
- package/api/integration/integration-check.type.mjs +84 -0
- package/api/integration/integrationCheck.doc.d.mts +10 -0
- package/api/integration/integrationCheck.doc.mjs +73 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +36 -5
- package/api/integration/pack-check.mjs +61 -107
- package/api/integration/pack-check.test.mjs +359 -0
- package/api/integration/validate-integration.mjs +8 -0
- package/api/integration/validate-integration.test.mjs +11 -0
- package/api/integration/validate-integration.type.d.mts +8 -0
- package/api/integration/validate-integration.type.mjs +12 -1
- package/api/integration/validateIntegration.doc.mjs +17 -3
- package/api/search/search.d.mts +22 -6
- package/api/search/search.domain-priority.test.mjs +108 -18
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +141 -25
- 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 +64 -0
- package/api/theme/build/build.mjs +180 -24
- package/api/theme/themeBuild.doc.mjs +4 -4
- 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/README.md +10 -0
- package/assets/docs/author-a-theme.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +3 -3
- package/assets/docs/styling-overview.doc.mjs +1 -1
- package/assets/docs/theme.doc.dense.mjs +1 -1
- package/assets/docs/theme.doc.mjs +1 -1
- package/assets/docs/theme.doc.zh.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +11 -1
- package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-topic.doc.mjs +10 -7
- package/assets/docs/tree/agent-guidance.doc.mjs +2 -2
- package/assets/docs/tree/block-template.doc.mjs +8 -20
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/check-the-props.doc.mjs +196 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +51 -5
- package/assets/docs/tree/checks.doc.mjs +24 -16
- package/assets/docs/tree/codemods.doc.mjs +4 -4
- package/assets/docs/tree/component-family.doc.mjs +8 -6
- package/assets/docs/tree/component-imports.doc.mjs +10 -2
- package/assets/docs/tree/components.doc.mjs +1 -1
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +7 -18
- package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
- package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
- package/assets/docs/tree/docs.doc.mjs +3 -3
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +6 -6
- package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
- package/assets/docs/tree/generate-a-palette.doc.mjs +5 -3
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +1 -1
- package/assets/docs/tree/integrations.doc.mjs +8 -0
- package/assets/docs/tree/links.doc.mjs +6 -5
- package/assets/docs/tree/migration-setup.doc.mjs +1 -0
- package/assets/docs/tree/migration-verification.doc.mjs +0 -1
- package/assets/docs/tree/migration.doc.mjs +0 -1
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +4 -8
- package/assets/docs/tree/publishing.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +37 -21
- package/assets/docs/tree/replace-a-core-component.doc.mjs +4 -6
- package/assets/docs/tree/replace-a-core-template.doc.mjs +6 -8
- package/assets/docs/tree/sections-and-placement.doc.mjs +4 -6
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +4 -2
- package/assets/docs/tree/short-and-findable.doc.mjs +6 -5
- package/assets/docs/tree/single-component.doc.mjs +15 -6
- package/assets/docs/tree/start-a-template.doc.mjs +3 -3
- package/assets/docs/tree/subcomponent.doc.mjs +6 -6
- package/assets/docs/tree/template-assets.doc.mjs +2 -2
- package/assets/docs/tree/template-doc-overview.doc.mjs +4 -15
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-icons.doc.mjs +1 -1
- package/assets/docs/tree/template-images-media.doc.mjs +1 -1
- package/assets/docs/tree/template-styles.doc.mjs +2 -2
- package/assets/docs/tree/templates.doc.mjs +1 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +3 -3
- package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
- package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
- package/assets/docs/tree/troubleshooting.doc.mjs +13 -5
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- package/assets/docs/tree/upgrading.doc.mjs +3 -3
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +3 -1
- package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
- package/assets/docs/tree/versioning.doc.mjs +7 -6
- package/assets/docs/tree/write-the-template-file.doc.mjs +4 -3
- package/assets/docs/use-a-theme.doc.mjs +2 -2
- 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/assets/theme.template.ts +1 -1
- package/authoring/codemod/codemod.doc.mjs +7 -3
- package/authoring/config/config.doc.mjs +9 -6
- package/authoring/config/type.ts +4 -2
- package/authoring/discover/discover.doc.mjs +1 -1
- package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
- package/authoring/doctypes/base/type.ts +6 -0
- package/authoring/doctypes/command/command.doc.mjs +14 -12
- package/authoring/doctypes/component/component.doc.mjs +31 -28
- package/authoring/doctypes/component/type.ts +13 -8
- package/authoring/doctypes/enum/enum.doc.mjs +7 -4
- package/authoring/doctypes/function/function.doc.mjs +9 -8
- package/authoring/doctypes/hook/hook.doc.mjs +5 -4
- package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
- package/authoring/doctypes/reference/reference.doc.mjs +11 -10
- package/authoring/doctypes/schema/schema.doc.mjs +9 -9
- package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
- package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
- package/authoring/doctypes/template/parse.d.mts +6 -2
- package/authoring/doctypes/template/parse.mjs +3 -1
- package/authoring/doctypes/template/template.doc.mjs +3 -3
- package/authoring/doctypes/template/type.ts +22 -0
- package/authoring/doctypes/theme/theme.doc.mjs +2 -2
- package/authoring/gap-report/gap-report.doc.mjs +2 -2
- package/authoring/identity/identity.doc.mjs +3 -3
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/integration.doc.mjs +12 -4
- package/authoring/integration/parse.test.mjs +10 -0
- package/authoring/integration/schema.d.mts +2 -0
- package/authoring/integration/schema.mjs +1 -0
- package/authoring/integration/type.ts +4 -0
- package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
- package/clients/cli/commands/build-theme.mjs +1 -1
- package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
- package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
- package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
- package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
- package/clients/cli/commands/doctor-integration.test.mjs +837 -0
- package/clients/cli/commands/doctor.mjs +190 -0
- package/clients/cli/commands/integration-check.doc.mjs +85 -0
- package/clients/cli/commands/integration-check.test.mjs +267 -0
- package/clients/cli/commands/integration.doc.mjs +6 -2
- package/clients/cli/commands/integration.mjs +107 -1
- package/clients/cli/commands/setup-nudge.test.mjs +79 -1
- package/clients/cli/commands/swizzle.mjs +5 -3
- package/clients/cli/commands/swizzle.routing.test.mjs +18 -1
- package/clients/cli/commands/text-json-parity.test.mjs +5 -0
- package/clients/cli/commands/theme-build.doc.mjs +2 -2
- package/clients/cli/index.mjs +30 -5
- package/clients/cli/lib/component-format.mjs +5 -1
- package/foundation/agent-docs/agent-doc-state.d.mts +14 -0
- package/foundation/agent-docs/agent-doc-state.mjs +38 -0
- package/foundation/agent-docs/agent-docs.d.mts +2 -1
- package/foundation/agent-docs/agent-docs.mjs +8 -6
- package/foundation/config/project.mjs +82 -0
- package/foundation/discovery/authoring-self-docs.mjs +2 -1
- package/foundation/discovery/component-discovery.d.mts +190 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +864 -56
- 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/doc-types.d.mts +157 -0
- package/foundation/discovery/doc-types.mjs +1224 -0
- package/foundation/discovery/doc-types.test.mjs +652 -0
- package/foundation/discovery/docs-discovery.d.mts +81 -0
- package/foundation/discovery/docs-discovery.mjs +57 -0
- package/foundation/discovery/integration-doc-files.d.mts +22 -0
- package/foundation/discovery/integration-doc-files.mjs +231 -0
- package/foundation/discovery/integration-doc-files.test.mjs +246 -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/discovery/package-tsconfig.d.mts +14 -0
- package/foundation/discovery/package-tsconfig.mjs +228 -0
- package/foundation/discovery/package-tsconfig.test.mjs +210 -0
- package/foundation/discovery/project-typescript.d.mts +23 -0
- package/foundation/discovery/project-typescript.mjs +61 -0
- package/foundation/discovery/prop-drift.d.mts +278 -0
- package/foundation/discovery/prop-drift.mjs +2263 -0
- package/foundation/discovery/prop-drift.test.mjs +2219 -0
- package/foundation/discovery/template-adapter.mjs +74 -5
- package/foundation/discovery/type-match.d.mts +53 -0
- package/foundation/discovery/type-match.mjs +660 -0
- package/foundation/discovery/type-match.test.mjs +169 -0
- package/foundation/discovery/type-text.d.mts +68 -0
- package/foundation/discovery/type-text.mjs +658 -0
- package/foundation/discovery/type-text.test.mjs +196 -0
- package/foundation/discovery/unresolved-references.d.mts +62 -0
- package/foundation/discovery/unresolved-references.mjs +790 -0
- package/foundation/discovery/unresolved-references.test.mjs +567 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +11 -1
- package/foundation/doc-compiler/inputs.mjs +23 -0
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/links.d.mts +9 -5
- package/foundation/doc-compiler/links.mjs +4 -2
- package/foundation/integrations/contribution-inventory.mjs +6 -1
- package/foundation/response/response-types.doc.mjs +7 -2
- package/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +10 -10
- package/scripts/postinstall.mjs +3 -3
|
@@ -75,14 +75,75 @@ async function placedGuides(route, seen = new Set()) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
describe('search domain priority — the rule', () => {
|
|
78
|
-
it('gives a doc no priority for a
|
|
78
|
+
it('gives a doc no priority for a heading match or a one-word keyword', () => {
|
|
79
79
|
const topic = {
|
|
80
80
|
domain: 'doc',
|
|
81
81
|
name: 'typography/font-setup',
|
|
82
|
-
keywords: ['
|
|
82
|
+
keywords: ['size'],
|
|
83
83
|
titles: ['Font Setup', 'Font Sizes'],
|
|
84
84
|
};
|
|
85
85
|
expect(priority('font size', topic)).toBe(0);
|
|
86
|
+
expect(priority('size', topic)).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('gives a doc priority when the query is one of its declared keywords', () => {
|
|
90
|
+
const guide = {
|
|
91
|
+
domain: 'doc',
|
|
92
|
+
name: 'guides/quality',
|
|
93
|
+
titles: ['Is my code good?'],
|
|
94
|
+
keywords: ['code review', 'review my code'],
|
|
95
|
+
_declared: ['code review', 'review my code'],
|
|
96
|
+
};
|
|
97
|
+
expect(priority('code review', guide)).toBe(1);
|
|
98
|
+
expect(priority('Code Reviews', guide)).toBe(1);
|
|
99
|
+
expect(priority('review my code', guide)).toBe(1);
|
|
100
|
+
// A section's keywords are its headings and code terms.
|
|
101
|
+
const section = {
|
|
102
|
+
...guide,
|
|
103
|
+
name: 'before-you-ship',
|
|
104
|
+
_topic: 'guides/quality',
|
|
105
|
+
_section: 'before-you-ship',
|
|
106
|
+
};
|
|
107
|
+
expect(priority('code review', section)).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("gives a doc no priority for a code term or table cell it didn't declare", () => {
|
|
111
|
+
// A typed doc's keywords hold the first column of its tables and the code
|
|
112
|
+
// terms its text mentions; `missing_field` reads as two words.
|
|
113
|
+
const reference = {
|
|
114
|
+
domain: 'doc',
|
|
115
|
+
name: 'cli/api/functions/validate-integration',
|
|
116
|
+
titles: ['validateIntegration'],
|
|
117
|
+
keywords: ['validate-integration', 'missing_field', 'file required'],
|
|
118
|
+
_declared: [],
|
|
119
|
+
_topic: 'cli/api/functions/validate-integration',
|
|
120
|
+
};
|
|
121
|
+
expect(priority('missing field', reference)).toBe(0);
|
|
122
|
+
expect(priority('file required', reference)).toBe(0);
|
|
123
|
+
expect(
|
|
124
|
+
priority('missing field', {...reference, _declared: ['missing field']}),
|
|
125
|
+
).toBe(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('keeps a component the query names exactly above a doc that declares it', async () => {
|
|
129
|
+
const tokens = tokenizeQuery('empty state');
|
|
130
|
+
const component = {domain: 'component', name: 'EmptyState'};
|
|
131
|
+
const doc = {
|
|
132
|
+
domain: 'doc',
|
|
133
|
+
name: 'illustrations',
|
|
134
|
+
keywords: ['empty state'],
|
|
135
|
+
_declared: ['empty state'],
|
|
136
|
+
};
|
|
137
|
+
const c = scoreQuery('empty state', tokens, component);
|
|
138
|
+
const d = scoreQuery('empty state', tokens, doc);
|
|
139
|
+
expect(
|
|
140
|
+
domainPriority('empty state', tokens, /** @type {any} */ (component), c),
|
|
141
|
+
).toBe(1);
|
|
142
|
+
expect(
|
|
143
|
+
domainPriority('empty state', tokens, /** @type {any} */ (doc), d),
|
|
144
|
+
).toBe(1);
|
|
145
|
+
// Both have priority, so the stronger match wins: the exact name.
|
|
146
|
+
expect(c?.score).toBeGreaterThan(d?.score ?? 0);
|
|
86
147
|
});
|
|
87
148
|
|
|
88
149
|
it('gives a doc priority when the query names it', () => {
|
|
@@ -190,32 +251,61 @@ describe('search domain priority — the rule', () => {
|
|
|
190
251
|
|
|
191
252
|
describe('search domain priority — real docs and components', () => {
|
|
192
253
|
it(
|
|
193
|
-
'finds
|
|
254
|
+
'finds a guide that declares the whole phrase above a component that matches one word',
|
|
194
255
|
async () => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
r => r.domain === '
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
256
|
+
const results = (await search('font size', {cwd})).data.results;
|
|
257
|
+
expect(results[0]).toMatchObject({domain: 'doc'});
|
|
258
|
+
expect(results[0].name).toMatch(/^typography(?:\/|$)/);
|
|
259
|
+
// The component the phrase points to stays near the top.
|
|
260
|
+
expect(
|
|
261
|
+
results
|
|
262
|
+
.slice(0, 3)
|
|
263
|
+
.some(r => r.domain === 'component' && r.name === 'Text'),
|
|
264
|
+
).toBe(true);
|
|
265
|
+
},
|
|
266
|
+
SLOW,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
it(
|
|
270
|
+
'keeps a component above a reference doc whose table or code terms hold the phrase',
|
|
271
|
+
async () => {
|
|
272
|
+
for (const q of ['missing field', 'file required']) {
|
|
273
|
+
const [top] = (await search(q, {cwd})).data.results;
|
|
274
|
+
expect(top, q).toMatchObject({domain: 'component', name: 'Field'});
|
|
206
275
|
}
|
|
207
276
|
},
|
|
208
277
|
SLOW,
|
|
209
278
|
);
|
|
210
279
|
|
|
211
280
|
it(
|
|
212
|
-
'
|
|
281
|
+
'opens the guide that answers a declared phrase, not one that only mentions it',
|
|
282
|
+
async () => {
|
|
283
|
+
const [layers] = (await search('cascade layers', {cwd})).data.results;
|
|
284
|
+
expect(layers).toMatchObject({
|
|
285
|
+
domain: 'doc',
|
|
286
|
+
name: 'migration/migration-setup',
|
|
287
|
+
});
|
|
288
|
+
const [keyboard] = (await search('keyboard navigation', {cwd})).data
|
|
289
|
+
.results;
|
|
290
|
+
expect(keyboard?.name).not.toBe('migration/migration-verification');
|
|
291
|
+
},
|
|
292
|
+
SLOW,
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
it(
|
|
296
|
+
'keeps a component above docs that match only by a heading, with scores unchanged',
|
|
213
297
|
async () => {
|
|
214
298
|
const results = (await search('font size', {cwd})).data.results;
|
|
215
|
-
const
|
|
216
|
-
r => r.domain === '
|
|
299
|
+
const text = results.findIndex(
|
|
300
|
+
r => r.domain === 'component' && r.name === 'Text',
|
|
217
301
|
);
|
|
218
|
-
expect(
|
|
302
|
+
expect(text).toBeGreaterThanOrEqual(0);
|
|
303
|
+
// A doc below Text outscores it on text alone: priority moved it, not the score.
|
|
304
|
+
expect(
|
|
305
|
+
results
|
|
306
|
+
.slice(text + 1)
|
|
307
|
+
.some(r => r.domain === 'doc' && r.score > results[text].score),
|
|
308
|
+
).toBe(true);
|
|
219
309
|
},
|
|
220
310
|
SLOW,
|
|
221
311
|
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file A reader looking for the check a build runs finds
|
|
5
|
+
* `astryx integration check` first.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {describe, expect, it} from 'vitest';
|
|
9
|
+
import {search} from './search.mjs';
|
|
10
|
+
|
|
11
|
+
describe('search finds integration check for CI', () => {
|
|
12
|
+
it.each([
|
|
13
|
+
'ci',
|
|
14
|
+
'continuous integration',
|
|
15
|
+
'typecheck integration',
|
|
16
|
+
'integration check',
|
|
17
|
+
])('`%s` ranks it first', async query => {
|
|
18
|
+
const {results} = (await search(query)).data;
|
|
19
|
+
expect(results[0], JSON.stringify(results.slice(0, 3))).toMatchObject({
|
|
20
|
+
domain: 'doc',
|
|
21
|
+
name: 'cli/commands/integration-check',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
package/api/search/search.mjs
CHANGED
|
@@ -57,9 +57,10 @@
|
|
|
57
57
|
* Between domains, ranking adds one rule ahead of the score: domain priority
|
|
58
58
|
* (see {@link domainPriority}). A component, hook, template, or theme whose
|
|
59
59
|
* name or keyword a query word hits ranks ahead of every doc the reader did not
|
|
60
|
-
* ask for by name or
|
|
61
|
-
*
|
|
62
|
-
* query holds,
|
|
60
|
+
* ask for by name, title, or a declared phrase, so `font size` finds `Text`
|
|
61
|
+
* before a token page whose heading holds the phrase. A doc the query names,
|
|
62
|
+
* whose title the query holds, or that declares the whole query as a keyword
|
|
63
|
+
* keeps its place.
|
|
63
64
|
*
|
|
64
65
|
* Description and guidance are separate tiers on purpose. A component's own
|
|
65
66
|
* one-line description saying "notification" is a claim about what it IS; the
|
|
@@ -81,11 +82,20 @@
|
|
|
81
82
|
|
|
82
83
|
import {withListingCache} from '../../foundation/fs/listing-cache.mjs';
|
|
83
84
|
import {readDocView} from '../../foundation/doc-compiler/read.mjs';
|
|
84
|
-
import {
|
|
85
|
+
import {
|
|
86
|
+
discoverExternalPackages,
|
|
87
|
+
findCoreDir,
|
|
88
|
+
} from '../../foundation/fs/paths.mjs';
|
|
85
89
|
import {
|
|
86
90
|
discoverComponents,
|
|
87
91
|
discoverValidIntegrationComponents,
|
|
92
|
+
docStem,
|
|
93
|
+
familyMember,
|
|
88
94
|
findComponentReadme,
|
|
95
|
+
forEachIntegrationDoc,
|
|
96
|
+
integrationMemberEntry,
|
|
97
|
+
integrationRecordHold,
|
|
98
|
+
isIntegrationMember,
|
|
89
99
|
resolveImportPath,
|
|
90
100
|
resolveIntegrationImportPath,
|
|
91
101
|
CORE_PACKAGE,
|
|
@@ -97,6 +107,7 @@ import {
|
|
|
97
107
|
import {
|
|
98
108
|
loadComponentReplacements,
|
|
99
109
|
loadIntegrationsSafely,
|
|
110
|
+
resolveLegacyExternalDoc,
|
|
100
111
|
} from '../component/_adapter.mjs';
|
|
101
112
|
import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
|
|
102
113
|
import {discoverTemplates, extractComponents} from '../template/template.mjs';
|
|
@@ -128,6 +139,9 @@ import {setResultCoverage} from './coverage.mjs';
|
|
|
128
139
|
* @property {string[]} [aliases] - Other names the candidate answers to, scored
|
|
129
140
|
* with the same name signals: a theme's display name.
|
|
130
141
|
* @property {string[]} [keywords]
|
|
142
|
+
* @property {string[]} [_declared] - A doc's own declared keywords: what its
|
|
143
|
+
* author lists in `keywords`, without the code terms and table cells its
|
|
144
|
+
* other keywords add.
|
|
131
145
|
* @property {string[]} [weakKeywords]
|
|
132
146
|
* @property {string} [description]
|
|
133
147
|
* @property {string[]} [prose]
|
|
@@ -149,6 +163,9 @@ import {setResultCoverage} from './coverage.mjs';
|
|
|
149
163
|
* @property {'page'|'block'} [_kind]
|
|
150
164
|
* @property {string} [_resultName]
|
|
151
165
|
* @property {string} [_commandName]
|
|
166
|
+
* @property {'file'|'doc'} [_hold] - How an integration component holds its
|
|
167
|
+
* name, for the owner rule.
|
|
168
|
+
* @property {string} [_docPath] - An integration component's doc.
|
|
152
169
|
*/
|
|
153
170
|
|
|
154
171
|
/**
|
|
@@ -500,19 +517,20 @@ const STRONG_TOKEN_SCORE = 70;
|
|
|
500
517
|
* typography/font-setup, `side panel` is "Side panels");
|
|
501
518
|
* - 1: a word of the query is the topic's name (`illustration` in a longer
|
|
502
519
|
* question is the illustrations guide);
|
|
520
|
+
* - 1: a query of two or more words is one of the topic's own declared
|
|
521
|
+
* keywords, whatever the plural (`code review` on a guide that declares it);
|
|
503
522
|
* - 1: the query holds one of the doc's titles whole (`resizable side panels`
|
|
504
523
|
* names "Side panels"; `light dark mode button` names "Light/Dark Mode");
|
|
505
524
|
* - 1: the doc matches every word of the query and one of them is a word of
|
|
506
525
|
* its title (`switch to a dark theme` and "Use a theme").
|
|
507
526
|
* A section answers to its topic's name.
|
|
508
527
|
*
|
|
509
|
-
* A doc that matched only by
|
|
528
|
+
* A doc that matched only by a heading holding the query, by one word, or by
|
|
510
529
|
* words spread through its text has no priority. Docs are split into many
|
|
511
|
-
* small topics,
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
* above the component. Broad reference pages match every word of many queries
|
|
530
|
+
* small topics, each with its own headings, so a common phrase such as
|
|
531
|
+
* `font size` hits a heading exactly (170) while the component the reader is
|
|
532
|
+
* after matches one word by keyword (`Text`, 98). Ranked on text alone, every
|
|
533
|
+
* split adds another doc above the component. Broad reference pages match every word of many queries
|
|
516
534
|
* in their text the same way. The score stays the text-match strength the
|
|
517
535
|
* result reports, so callers that gate on it (`build`) see the same numbers.
|
|
518
536
|
*
|
|
@@ -538,6 +556,20 @@ export function domainPriority(term, tokens, candidate, hit) {
|
|
|
538
556
|
if (named.some(n => samePhrase(term, n))) {
|
|
539
557
|
return 2;
|
|
540
558
|
}
|
|
559
|
+
// A query of two or more words that is one of the doc's own declared
|
|
560
|
+
// keywords, read as words whatever the plural, is the author naming the
|
|
561
|
+
// doc for that phrase: `code review` on a guide that declares it. Only
|
|
562
|
+
// what the author declared counts: the code terms and table cells a
|
|
563
|
+
// doc's other keywords add (`missing_field`) name what it mentions, not
|
|
564
|
+
// what it is for. One word is left out too, and a section declares
|
|
565
|
+
// nothing.
|
|
566
|
+
if (
|
|
567
|
+
phraseWords(term).length >= 2 &&
|
|
568
|
+
candidate._section == null &&
|
|
569
|
+
(candidate._declared ?? []).some(k => samePhrase(term, String(k)))
|
|
570
|
+
) {
|
|
571
|
+
return 1;
|
|
572
|
+
}
|
|
541
573
|
if (
|
|
542
574
|
words.some(
|
|
543
575
|
w =>
|
|
@@ -1079,7 +1111,11 @@ async function gatherCoreComponents(coreDir) {
|
|
|
1079
1111
|
/** @type {string[]} */
|
|
1080
1112
|
let guidance = [];
|
|
1081
1113
|
if (readme && readme.endsWith('.doc.mjs')) {
|
|
1082
|
-
const
|
|
1114
|
+
const loaded = await loadModuleDoc(readme);
|
|
1115
|
+
// A member documented in its family's doc (VStack in Stack's doc) is
|
|
1116
|
+
// indexed from its own entry: the family's keywords and guidance
|
|
1117
|
+
// describe the family, not the member.
|
|
1118
|
+
const doc = familyMember(loaded, comp) ?? loaded;
|
|
1083
1119
|
if (doc) {
|
|
1084
1120
|
keywords = Array.isArray(doc.keywords) ? doc.keywords : [];
|
|
1085
1121
|
description = doc.usage?.description || doc.description || '';
|
|
@@ -1105,20 +1141,57 @@ async function gatherCoreComponents(coreDir) {
|
|
|
1105
1141
|
* usage/description from each component's .doc.mjs, same as core. Without
|
|
1106
1142
|
* this, an integration component is invisible to `search`/`build` even
|
|
1107
1143
|
* though `component --list`/`component <Name>` already resolve it — the two
|
|
1108
|
-
* discovery paths silently disagreed.
|
|
1144
|
+
* discovery paths silently disagreed. Also returns, per name, the docs that
|
|
1145
|
+
* hold it by their file name and the loadable docs that only record it.
|
|
1109
1146
|
* @param {import('../../foundation/integrations/integrations.mjs').LoadedIntegration[]} loadedIntegrations
|
|
1110
|
-
* @returns {Promise<Candidate[]>}
|
|
1147
|
+
* @returns {Promise<{candidates: Candidate[], fileNamed: Map<string, Set<string>>, documented: Map<string, Set<string>>}>}
|
|
1111
1148
|
*/
|
|
1112
1149
|
async function gatherIntegrationComponents(loadedIntegrations) {
|
|
1113
1150
|
/** @type {Candidate[]} */
|
|
1114
1151
|
const candidates = [];
|
|
1152
|
+
// What the owner rule reads: the docs that hold a name by their file name,
|
|
1153
|
+
// loadable or not and hidden ones too, and the loadable docs that only
|
|
1154
|
+
// record it.
|
|
1155
|
+
/** @type {Map<string, Set<string>>} */
|
|
1156
|
+
const fileNamed = new Map();
|
|
1157
|
+
/** @type {Map<string, Set<string>>} */
|
|
1158
|
+
const documented = new Map();
|
|
1159
|
+
/** @param {Map<string, Set<string>>} map @param {string} name @param {string} docPath */
|
|
1160
|
+
const hold = (map, name, docPath) =>
|
|
1161
|
+
map.set(name, (map.get(name) ?? new Set()).add(docPath));
|
|
1115
1162
|
for (const integration of loadedIntegrations) {
|
|
1116
|
-
const {components} =
|
|
1163
|
+
const {components, discovered} =
|
|
1164
|
+
await discoverValidIntegrationComponents(integration);
|
|
1165
|
+
const renamed = new Set(
|
|
1166
|
+
discovered
|
|
1167
|
+
.filter(rec => integrationRecordHold(rec) === 'doc')
|
|
1168
|
+
.filter(rec => !isIntegrationMember(rec))
|
|
1169
|
+
.map(rec => rec.docPath),
|
|
1170
|
+
);
|
|
1171
|
+
forEachIntegrationDoc(integration.components, (docPath, stem) => {
|
|
1172
|
+
if (!renamed.has(docPath)) hold(fileNamed, stem, docPath);
|
|
1173
|
+
});
|
|
1117
1174
|
for (const rec of components) {
|
|
1118
|
-
const
|
|
1175
|
+
const loaded = await loadModuleDoc(rec.docPath);
|
|
1176
|
+
// A family member is indexed from its own entry: the family's keywords
|
|
1177
|
+
// and guidance describe the family.
|
|
1178
|
+
const member = isIntegrationMember(rec);
|
|
1179
|
+
const doc = member ? integrationMemberEntry(loaded, rec) : loaded;
|
|
1180
|
+
// A doc that names another export of its file still answers to the
|
|
1181
|
+
// file's name, as `component <file name>` does.
|
|
1182
|
+
const fileName = docStem(rec.docPath);
|
|
1183
|
+
if (integrationRecordHold(rec) === 'doc') {
|
|
1184
|
+
hold(documented, rec.name, rec.docPath);
|
|
1185
|
+
}
|
|
1186
|
+
if (renamed.has(rec.docPath) && !member) {
|
|
1187
|
+
hold(documented, fileName, rec.docPath);
|
|
1188
|
+
}
|
|
1119
1189
|
candidates.push({
|
|
1120
1190
|
domain: 'component',
|
|
1121
1191
|
name: rec.name,
|
|
1192
|
+
aliases: !member && fileName !== rec.name ? [fileName] : [],
|
|
1193
|
+
_hold: integrationRecordHold(rec),
|
|
1194
|
+
_docPath: rec.docPath,
|
|
1122
1195
|
keywords: doc && Array.isArray(doc.keywords) ? doc.keywords : [],
|
|
1123
1196
|
description: doc ? doc.usage?.description || doc.description || '' : '',
|
|
1124
1197
|
guidance: guidanceFrom(doc),
|
|
@@ -1138,12 +1211,12 @@ async function gatherIntegrationComponents(loadedIntegrations) {
|
|
|
1138
1211
|
packageName: rec.package,
|
|
1139
1212
|
},
|
|
1140
1213
|
rec.name,
|
|
1141
|
-
|
|
1214
|
+
loaded?.import,
|
|
1142
1215
|
),
|
|
1143
1216
|
});
|
|
1144
1217
|
}
|
|
1145
1218
|
}
|
|
1146
|
-
return candidates;
|
|
1219
|
+
return {candidates, fileNamed, documented};
|
|
1147
1220
|
}
|
|
1148
1221
|
|
|
1149
1222
|
/**
|
|
@@ -1155,11 +1228,12 @@ async function gatherIntegrationComponents(loadedIntegrations) {
|
|
|
1155
1228
|
*/
|
|
1156
1229
|
async function gatherComponents(coreDir, cwd) {
|
|
1157
1230
|
const loadedIntegrations = await loadIntegrationsSafely(cwd);
|
|
1158
|
-
const [core,
|
|
1231
|
+
const [core, gathered, replacements] = await Promise.all([
|
|
1159
1232
|
gatherCoreComponents(coreDir),
|
|
1160
1233
|
gatherIntegrationComponents(loadedIntegrations),
|
|
1161
1234
|
loadComponentReplacements(coreDir, loadedIntegrations),
|
|
1162
1235
|
]);
|
|
1236
|
+
const {candidates: integrations, fileNamed, documented} = gathered;
|
|
1163
1237
|
// An active replacement answers to the Core name it replaces, so a search
|
|
1164
1238
|
// for that name finds the replacement and not the Core original
|
|
1165
1239
|
// (spec:AST-035 FR11).
|
|
@@ -1170,9 +1244,43 @@ async function gatherComponents(coreDir, cwd) {
|
|
|
1170
1244
|
active.target,
|
|
1171
1245
|
]),
|
|
1172
1246
|
);
|
|
1247
|
+
// The owner rule, as component detail applies it: a candidate `component
|
|
1248
|
+
// <name>` does not open names its package and follows the owner, and a
|
|
1249
|
+
// file-name alias that opens another owner is dropped.
|
|
1250
|
+
const coreNames = new Set(core.map(candidate => candidate.name));
|
|
1251
|
+
/** @param {string} name @param {Candidate} candidate */
|
|
1252
|
+
const opens = (name, candidate) =>
|
|
1253
|
+
!coreNames.has(name) &&
|
|
1254
|
+
findComponentReadme(coreDir, name) == null &&
|
|
1255
|
+
[...(fileNamed.get(name) ?? [])].every(doc => doc === candidate._docPath) &&
|
|
1256
|
+
[...(documented.get(name) ?? [])].every(doc => doc === candidate._docPath);
|
|
1257
|
+
// A legacy docs package keeps an exact name over an export name a doc records.
|
|
1258
|
+
/** @type {ReturnType<typeof discoverExternalPackages> | undefined} */
|
|
1259
|
+
let externals;
|
|
1260
|
+
/** @param {string} name */
|
|
1261
|
+
const legacyDocuments = name =>
|
|
1262
|
+
(externals ??= discoverExternalPackages(cwd)).some(
|
|
1263
|
+
ext => resolveLegacyExternalDoc(ext, name) != null,
|
|
1264
|
+
);
|
|
1265
|
+
const ruled = integrations.map(candidate => {
|
|
1266
|
+
const aliases = (candidate.aliases ?? []).filter(alias =>
|
|
1267
|
+
opens(alias, candidate),
|
|
1268
|
+
);
|
|
1269
|
+
return candidate._hold === 'doc' &&
|
|
1270
|
+
(!opens(candidate.name, candidate) || legacyDocuments(candidate.name))
|
|
1271
|
+
? {
|
|
1272
|
+
...candidate,
|
|
1273
|
+
aliases,
|
|
1274
|
+
_command: `astryx component ${candidate.name} --package ${candidate._package}`,
|
|
1275
|
+
}
|
|
1276
|
+
: {...candidate, aliases};
|
|
1277
|
+
});
|
|
1173
1278
|
return [
|
|
1174
1279
|
...core.filter(candidate => !replacements.forTarget(candidate.name)),
|
|
1175
|
-
...
|
|
1280
|
+
...[
|
|
1281
|
+
...ruled.filter(candidate => !candidate._command),
|
|
1282
|
+
...ruled.filter(candidate => candidate._command),
|
|
1283
|
+
].map(candidate => {
|
|
1176
1284
|
const target = targetOf.get(`${candidate._package}\0${candidate.name}`);
|
|
1177
1285
|
if (target != null) {
|
|
1178
1286
|
return {...candidate, aliases: [...(candidate.aliases ?? []), target]};
|
|
@@ -1225,11 +1333,11 @@ export async function componentKeywords(coreDir, cwd) {
|
|
|
1225
1333
|
.flat()
|
|
1226
1334
|
.map(async name => {
|
|
1227
1335
|
const readme = findComponentReadme(coreDir, name);
|
|
1228
|
-
const
|
|
1336
|
+
const loaded =
|
|
1229
1337
|
readme && readme.endsWith('.doc.mjs')
|
|
1230
1338
|
? await loadModuleDoc(readme)
|
|
1231
1339
|
: null;
|
|
1232
|
-
return {name, keywords: keywordsOf(
|
|
1340
|
+
return {name, keywords: keywordsOf(familyMember(loaded, name) ?? loaded)};
|
|
1233
1341
|
});
|
|
1234
1342
|
const loadedIntegrations = await loadIntegrationsSafely(cwd);
|
|
1235
1343
|
const replacements = await loadComponentReplacements(
|
|
@@ -1239,10 +1347,13 @@ export async function componentKeywords(coreDir, cwd) {
|
|
|
1239
1347
|
const integrations = loadedIntegrations.map(async integration => {
|
|
1240
1348
|
const {components} = await discoverValidIntegrationComponents(integration);
|
|
1241
1349
|
return Promise.all(
|
|
1242
|
-
components.map(async rec =>
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1350
|
+
components.map(async rec => {
|
|
1351
|
+
const loaded = await loadModuleDoc(rec.docPath);
|
|
1352
|
+
return {
|
|
1353
|
+
name: rec.name,
|
|
1354
|
+
keywords: keywordsOf(integrationMemberEntry(loaded, rec) ?? loaded),
|
|
1355
|
+
};
|
|
1356
|
+
}),
|
|
1246
1357
|
);
|
|
1247
1358
|
});
|
|
1248
1359
|
return [
|
|
@@ -1416,6 +1527,10 @@ async function gatherDocs(cwd) {
|
|
|
1416
1527
|
...defined,
|
|
1417
1528
|
...codeTerms({content}),
|
|
1418
1529
|
],
|
|
1530
|
+
_declared: [
|
|
1531
|
+
...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
|
|
1532
|
+
...(Array.isArray(node.keywords) ? node.keywords : []),
|
|
1533
|
+
],
|
|
1419
1534
|
description: node.summary || '',
|
|
1420
1535
|
prose: sectionProse({title: node.title, content}),
|
|
1421
1536
|
titles: [node.title],
|
|
@@ -1552,6 +1667,7 @@ function topicCandidates(
|
|
|
1552
1667
|
...(doc?.title || title ? [doc?.title || title] : []),
|
|
1553
1668
|
...(Array.isArray(doc?.keywords) ? doc.keywords : []),
|
|
1554
1669
|
],
|
|
1670
|
+
_declared: Array.isArray(doc?.keywords) ? doc.keywords : [],
|
|
1555
1671
|
description: doc?.description || summary,
|
|
1556
1672
|
prose: split
|
|
1557
1673
|
? sections.map(section => section.title).filter(Boolean)
|
|
@@ -616,6 +616,42 @@ describe('search leaf — a theme is found by its names; its description is pros
|
|
|
616
616
|
);
|
|
617
617
|
});
|
|
618
618
|
|
|
619
|
+
describe('search leaf — components whose docs live in a family folder', () => {
|
|
620
|
+
it("indexes a family member from its own entry, not the family's keywords", async () => {
|
|
621
|
+
// HStack and VStack are documented in Stack's doc, whose keywords
|
|
622
|
+
// (horizontal, vertical, row, column, ...) describe Stack.
|
|
623
|
+
const reasons = async (/** @type {string} */ query) =>
|
|
624
|
+
Object.fromEntries(
|
|
625
|
+
(await search(query, {cwd, type: 'component'})).data.results.map(r => [
|
|
626
|
+
r.name,
|
|
627
|
+
r.reason,
|
|
628
|
+
]),
|
|
629
|
+
);
|
|
630
|
+
const column = await reasons('column');
|
|
631
|
+
expect(column.HStack ?? '').not.toMatch(/keyword/);
|
|
632
|
+
expect(column.VStack ?? '').not.toMatch(/keyword/);
|
|
633
|
+
expect(column.Stack).toMatch(/keyword/);
|
|
634
|
+
const vertical = (await search('vertical', {cwd, type: 'component'})).data.results.map(
|
|
635
|
+
r => r.name,
|
|
636
|
+
);
|
|
637
|
+
expect(vertical.indexOf('VStack')).toBeGreaterThanOrEqual(0);
|
|
638
|
+
if (vertical.includes('HStack')) {
|
|
639
|
+
expect(vertical.indexOf('VStack')).toBeLessThan(vertical.indexOf('HStack'));
|
|
640
|
+
}
|
|
641
|
+
}, SLOW);
|
|
642
|
+
|
|
643
|
+
it('ranks VStack, HStack and Heading first for their own names, with their own import', async () => {
|
|
644
|
+
for (const name of ['VStack', 'HStack', 'Heading']) {
|
|
645
|
+
const r = await search(name.toLowerCase(), {cwd, type: 'component'});
|
|
646
|
+
expect(r.data.results[0]).toMatchObject({
|
|
647
|
+
domain: 'component',
|
|
648
|
+
name,
|
|
649
|
+
import: `@astryxdesign/core/${name}`,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
}, SLOW);
|
|
653
|
+
});
|
|
654
|
+
|
|
619
655
|
describe('search leaf — integration components', () => {
|
|
620
656
|
/**
|
|
621
657
|
* A minimal consumer project: a stub `@astryxdesign/core` (so `findCoreDir`
|
|
@@ -21,8 +21,10 @@ import {checkGhCli} from '../_github.mjs';
|
|
|
21
21
|
import {Project} from '../../../foundation/config/project.mjs';
|
|
22
22
|
import {
|
|
23
23
|
CORE_PACKAGE,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
docStem,
|
|
25
|
+
findComponentReadme,
|
|
26
|
+
findIntegrationComponent,
|
|
27
|
+
unscopedComponentOwners,
|
|
26
28
|
} from '../../../foundation/discovery/component-discovery.mjs';
|
|
27
29
|
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
28
30
|
import {AstryxError, writeFailed} from '../../error.mjs';
|
|
@@ -126,33 +128,38 @@ async function loadConfigSafely(cwd) {
|
|
|
126
128
|
|
|
127
129
|
/**
|
|
128
130
|
* Build the set of OWNER packages that provide a component named `name` across
|
|
129
|
-
* core + every loaded integration.
|
|
131
|
+
* core + every loaded integration. Core holds every name it documents, but has
|
|
132
|
+
* source to copy (`sourceDir`) only for a name with its own folder.
|
|
130
133
|
* @param {string} coreDir
|
|
131
134
|
* @param {Array<{name: string, components?: string, issuesUrl?: string}>} loadedIntegrations
|
|
132
135
|
* @param {string} name
|
|
133
136
|
* @param {string|undefined} coreIssuesUrl
|
|
134
|
-
* @returns {Array<{package: string, sourceDir: string|null, ownerPackage: string, issuesUrl: string|undefined}>}
|
|
137
|
+
* @returns {Array<{package: string, sourceDir: string|null, docPath: string|null, ownerPackage: string, issuesUrl: string|undefined, hold: string}>}
|
|
135
138
|
*/
|
|
136
139
|
function resolveOwners(coreDir, loadedIntegrations, name, coreIssuesUrl) {
|
|
137
140
|
const owners = [];
|
|
138
141
|
const coreComponentDir = path.join(coreDir, 'src', name);
|
|
139
|
-
|
|
142
|
+
const coreFolder = fs.existsSync(coreComponentDir);
|
|
143
|
+
if (coreFolder || findComponentReadme(coreDir, name)) {
|
|
140
144
|
owners.push({
|
|
141
145
|
package: CORE_PACKAGE,
|
|
142
|
-
sourceDir: coreComponentDir,
|
|
146
|
+
sourceDir: coreFolder ? coreComponentDir : null,
|
|
147
|
+
docPath: null,
|
|
143
148
|
ownerPackage: CORE_PACKAGE,
|
|
144
149
|
issuesUrl: coreIssuesUrl || DEFAULT_ISSUES_URL,
|
|
150
|
+
hold: 'core',
|
|
145
151
|
});
|
|
146
152
|
}
|
|
147
153
|
for (const integration of loadedIntegrations) {
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
150
|
-
const sourcePath = findIntegrationComponentSource(integration, name);
|
|
154
|
+
const found = findIntegrationComponent(integration, name);
|
|
155
|
+
if (!found) continue;
|
|
151
156
|
owners.push({
|
|
152
157
|
package: integration.name,
|
|
153
|
-
sourceDir: sourcePath ? path.dirname(sourcePath) : null,
|
|
158
|
+
sourceDir: found.sourcePath ? path.dirname(found.sourcePath) : null,
|
|
159
|
+
docPath: found.docPath,
|
|
154
160
|
ownerPackage: integration.name,
|
|
155
161
|
issuesUrl: integration.issuesUrl,
|
|
162
|
+
hold: found.hold,
|
|
156
163
|
});
|
|
157
164
|
}
|
|
158
165
|
return owners;
|
|
@@ -222,25 +229,32 @@ export async function swizzleCopy(component, options = {}) {
|
|
|
222
229
|
: undefined;
|
|
223
230
|
// An active integration replacement answers to the Core name it replaces,
|
|
224
231
|
// bare or qualified by its own package (spec:AST-035 FR11): copy it under its
|
|
225
|
-
//
|
|
232
|
+
// doc's file name, the folder it has always gone to. `--package
|
|
233
|
+
// @astryxdesign/core` still copies the Core original.
|
|
226
234
|
if (pkg !== CORE_PACKAGE) {
|
|
227
235
|
const replacement = await resolveComponentReplacement(
|
|
228
236
|
coreDir,
|
|
229
237
|
loadedIntegrations,
|
|
230
238
|
dirName,
|
|
231
239
|
);
|
|
240
|
+
const copyName = replacement ? docStem(replacement.docPath) : null;
|
|
232
241
|
if (
|
|
233
242
|
replacement &&
|
|
234
|
-
|
|
243
|
+
copyName &&
|
|
244
|
+
(!pkg || (pkg === replacement.package && copyName !== dirName))
|
|
235
245
|
) {
|
|
236
|
-
return swizzleCopy(
|
|
246
|
+
return swizzleCopy(copyName, {
|
|
237
247
|
...options,
|
|
238
248
|
package: replacement.package,
|
|
239
249
|
});
|
|
240
250
|
}
|
|
241
251
|
}
|
|
242
252
|
|
|
243
|
-
const
|
|
253
|
+
const found = resolveOwners(coreDir, loadedIntegrations, dirName, coreIssuesUrl);
|
|
254
|
+
// The owner rule, then a Core name without its own folder drops out.
|
|
255
|
+
const allOwners = (pkg ? found : unscopedComponentOwners(found)).filter(
|
|
256
|
+
owner => owner.package !== CORE_PACKAGE || owner.sourceDir != null,
|
|
257
|
+
);
|
|
244
258
|
|
|
245
259
|
if (allOwners.length === 0) {
|
|
246
260
|
throw new AstryxError(
|