@astryxdesign/cli 0.7.12-canary.f543ec7 → 0.7.12
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/CHANGELOG.md +107 -0
- package/api/build/blocks/blocks.d.mts +41 -0
- package/api/build/blocks/blocks.mjs +118 -68
- package/api/component/__fixtures__/resolutions/modules.txt +3 -3
- package/api/docs/_adapter.d.mts +22 -0
- package/api/docs/_adapter.mjs +81 -3
- package/api/docs/docs.doc.mjs +5 -5
- package/api/docs/docs.type.d.mts +21 -0
- package/api/docs/docs.type.mjs +11 -0
- package/api/docs/list/list.d.mts +5 -1
- package/api/docs/list/list.mjs +72 -6
- package/api/docs/node/node.mjs +2 -40
- package/api/doctor/doctor.d.mts +4 -4
- package/api/doctor/doctor.mjs +248 -78
- package/api/icon/_authoring.mjs +8 -1
- package/api/icon/add/add.mjs +45 -5
- package/api/icon/icon.type.d.mts +24 -0
- package/api/icon/icon.type.mjs +6 -0
- package/api/icon/iconAdd.doc.mjs +1 -1
- package/api/icon/map/map.mjs +11 -4
- package/api/index.d.mts +1 -0
- package/api/index.mjs +1 -0
- package/api/integration/add-helpers.d.mts +5 -1
- package/api/integration/add-helpers.mjs +1 -1
- package/api/render/_adapter.mjs +9 -0
- package/api/render/render.doc.mjs +3 -3
- package/api/render/render.type.d.mts +1 -1
- package/api/render/render.type.mjs +1 -1
- package/api/search/ORDERING.md +3 -0
- package/api/search/_adapter.d.mts +32 -3
- package/api/search/_adapter.mjs +50 -22
- package/api/search/_fit-order.d.mts +47 -0
- package/api/search/_fit-order.mjs +257 -0
- package/api/search/_main-searcher.mjs +15 -5
- package/api/search/exportSearchIndex.doc.d.mts +10 -0
- package/api/search/exportSearchIndex.doc.mjs +58 -0
- package/api/search/rankSearchIndex.doc.d.mts +10 -0
- package/api/search/rankSearchIndex.doc.mjs +79 -0
- package/api/search/search-index.d.mts +44 -0
- package/api/search/search-index.mjs +270 -0
- package/api/search/search.type.d.mts +28 -0
- package/api/search/search.type.mjs +17 -0
- package/api/swizzle/_moduleGraph.d.mts +7 -0
- package/api/swizzle/_moduleGraph.mjs +191 -83
- package/api/swizzle/copy/copy.mjs +28 -6
- package/api/template/skeleton/skeleton.mjs +1 -1
- package/api/template/template.mjs +1 -0
- package/assets/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +1 -1
- package/assets/codemods/transforms/v0.1.0/index.mjs +1 -1
- package/assets/discover/official-packages.json +16 -0
- package/assets/docs/styling-overview.doc.mjs +3 -3
- package/assets/docs/tree/icon-sets.doc.mjs +4 -4
- package/assets/docs/tree/render-pages.doc.mjs +3 -3
- package/assets/docs/tree/tailwind.doc.mjs +41 -1
- package/assets/docs/tree/type-scale.doc.mjs +19 -2
- package/assets/templates/blocks/components/Hooks/useInputStatusIconHookUsage.tsx +1 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.tsx +56 -0
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.doc.mjs +2 -2
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.tsx +66 -10
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.tsx +39 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.tsx +83 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.tsx +41 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.tsx +97 -0
- package/assets/templates/pages/table-filter/page.tsx +15 -23
- package/assets/templates/pages/table-grouped/page.tsx +30 -30
- package/assets/templates/pages/table-inbox/page.tsx +3 -6
- package/assets/templates/themes/butter/butterTheme.ts +1 -1
- package/assets/templates/themes/gothic/gothicTheme.ts +16 -0
- package/assets/templates/themes/matcha/matchaTheme.ts +1 -1
- package/assets/templates/themes/neutral/neutralTheme.ts +6 -0
- package/assets/templates/themes/y2k/y2kPaletteRefs.generated.ts +1 -1
- package/assets/templates/themes/y2k/y2kTheme.ts +1 -1
- package/clients/cli/commands/docs.doc.mjs +5 -1
- package/clients/cli/commands/docs.mjs +30 -2
- package/clients/cli/commands/experimental/icon.mjs +25 -4
- package/clients/cli/commands/experimental-render.doc.mjs +1 -1
- package/clients/cli/commands/template.mjs +204 -181
- package/clients/cli/lib/resolve-theme.mjs +16 -7
- package/foundation/discovery/component-discovery.mjs +1 -1
- package/foundation/discovery/hook-discovery.mjs +31 -8
- package/foundation/discovery/icon-set-writer.d.mts +21 -5
- package/foundation/discovery/icon-set-writer.mjs +114 -13
- package/foundation/discovery/template-adapter.mjs +2 -2
- package/foundation/doc-compiler/import.d.mts +5 -1
- package/foundation/doc-compiler/import.mjs +40 -6
- package/foundation/fs/import-url.d.mts +20 -0
- package/foundation/fs/import-url.mjs +22 -0
- package/foundation/fs/module-loader.d.mts +24 -3
- package/foundation/fs/module-loader.mjs +69 -47
- package/foundation/render/charts.d.mts +59 -0
- package/foundation/render/charts.mjs +216 -0
- package/foundation/render/embedded-imports.d.mts +7 -7
- package/foundation/render/embedded-imports.mjs +63 -54
- package/foundation/render/page-host.d.mts +4 -0
- package/foundation/render/page-host.mjs +45 -0
- package/foundation/render/page-worker.mjs +350 -57
- package/foundation/render/render-page.d.mts +11 -6
- package/foundation/render/render-page.mjs +164 -54
- package/foundation/render/ts-paths.d.mts +63 -0
- package/foundation/render/ts-paths.mjs +138 -0
- package/foundation/response/error-codes.doc.mjs +3 -3
- package/foundation/response/error-codes.mjs +3 -2
- package/foundation/text/levenshtein.d.mts +6 -1
- package/foundation/text/levenshtein.mjs +25 -9
- package/foundation/text/suggest-order.d.mts +8 -1
- package/foundation/text/suggest-order.mjs +72 -6
- package/foundation/xle/registry-core.mjs +1 -1
- package/package.json +7 -6
package/api/icon/_authoring.mjs
CHANGED
|
@@ -691,6 +691,7 @@ async function redeclareIconSet(
|
|
|
691
691
|
);
|
|
692
692
|
result.data.icons = set.data.icons.length;
|
|
693
693
|
result.data.updated = true;
|
|
694
|
+
if (where.moduleUpdate) result.data.moduleUpdate = where.moduleUpdate;
|
|
694
695
|
// A look the icons' own library draws comes from that library, so its
|
|
695
696
|
// command runs as printed; any other look comes from a module the
|
|
696
697
|
// author names.
|
|
@@ -1388,6 +1389,7 @@ async function refreshIconSet(
|
|
|
1388
1389
|
);
|
|
1389
1390
|
result.data.icons = icons.length;
|
|
1390
1391
|
result.data.refreshed = true;
|
|
1392
|
+
if (where.moduleUpdate) result.data.moduleUpdate = where.moduleUpdate;
|
|
1391
1393
|
// Every entry an exact name gives, kept or new, so a refresh lists
|
|
1392
1394
|
// them for review again.
|
|
1393
1395
|
const byName = Object.fromEntries(
|
|
@@ -1542,7 +1544,7 @@ export async function addIconDefinition(name, options) {
|
|
|
1542
1544
|
throw error;
|
|
1543
1545
|
}
|
|
1544
1546
|
}
|
|
1545
|
-
|
|
1547
|
+
const result = receipt(
|
|
1546
1548
|
'icon-definition',
|
|
1547
1549
|
doc.name,
|
|
1548
1550
|
{
|
|
@@ -1559,5 +1561,10 @@ export async function addIconDefinition(name, options) {
|
|
|
1559
1561
|
files,
|
|
1560
1562
|
dryRun,
|
|
1561
1563
|
);
|
|
1564
|
+
// Only a new definition with an icon writes the data file.
|
|
1565
|
+
if (icon != null && where.moduleUpdate) {
|
|
1566
|
+
result.data.moduleUpdate = where.moduleUpdate;
|
|
1567
|
+
}
|
|
1568
|
+
return result;
|
|
1562
1569
|
});
|
|
1563
1570
|
}
|
package/api/icon/add/add.mjs
CHANGED
|
@@ -147,6 +147,15 @@ export async function iconAdd(exportName, options) {
|
|
|
147
147
|
);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
+
// An export name cannot start with a digit, and an id the author gives has
|
|
151
|
+
// no library prefix to keep.
|
|
152
|
+
if (givenId != null && /^[0-9]/u.test(givenId)) {
|
|
153
|
+
throw new AstryxError(
|
|
154
|
+
`The icon id "${givenId}" starts with a digit, and an export name cannot. Pass --id with an id that starts with a letter.`,
|
|
155
|
+
undefined,
|
|
156
|
+
ERROR_CODES.ERR_INVALID_ARGUMENT,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
150
159
|
if (all) {
|
|
151
160
|
return authoring(() => addVariantForAll({cwd, from, selector, variant}));
|
|
152
161
|
}
|
|
@@ -386,6 +395,8 @@ export async function iconAdd(exportName, options) {
|
|
|
386
395
|
map,
|
|
387
396
|
icons,
|
|
388
397
|
};
|
|
398
|
+
const dataChanged =
|
|
399
|
+
JSON.stringify(next) !== JSON.stringify(set.data) || where.moduleRenamed;
|
|
389
400
|
/** @type {string[]} */
|
|
390
401
|
const files = [];
|
|
391
402
|
// Every write happens inside this try, so any failure undoes the ones
|
|
@@ -413,7 +424,7 @@ export async function iconAdd(exportName, options) {
|
|
|
413
424
|
.join('/'),
|
|
414
425
|
);
|
|
415
426
|
}
|
|
416
|
-
if (
|
|
427
|
+
if (dataChanged) {
|
|
417
428
|
writeIconSetData(set, next, {
|
|
418
429
|
extraDefinitions: created ? [created.name] : [],
|
|
419
430
|
});
|
|
@@ -433,10 +444,12 @@ export async function iconAdd(exportName, options) {
|
|
|
433
444
|
}
|
|
434
445
|
throw error;
|
|
435
446
|
}
|
|
436
|
-
|
|
447
|
+
// A renamed package's new name alone is not an added icon.
|
|
448
|
+
const added =
|
|
437
449
|
JSON.stringify(next) !== JSON.stringify(set.data) ||
|
|
438
450
|
created != null ||
|
|
439
451
|
generatedChanged;
|
|
452
|
+
const changed = dataChanged || added;
|
|
440
453
|
// A ./ file publishes only when package.json `files` lists it; the
|
|
441
454
|
// author adds it (architecture:cli-surface INV16).
|
|
442
455
|
const note =
|
|
@@ -457,6 +470,8 @@ export async function iconAdd(exportName, options) {
|
|
|
457
470
|
...(created ? {newDefinition: created.name} : {}),
|
|
458
471
|
files,
|
|
459
472
|
changed,
|
|
473
|
+
added,
|
|
474
|
+
...(where.moduleUpdate ? {moduleUpdate: where.moduleUpdate} : {}),
|
|
460
475
|
...(note ? {note} : {}),
|
|
461
476
|
},
|
|
462
477
|
};
|
|
@@ -638,8 +653,27 @@ async function addVariantForAll({cwd, from, selector, variant}) {
|
|
|
638
653
|
);
|
|
639
654
|
// Only icons from the same package as the variant module can have a
|
|
640
655
|
// variant there: an icon from your own `./` file, or from another library,
|
|
641
|
-
// is left as it is and never listed as missing.
|
|
656
|
+
// is left as it is and never listed as missing. Some libraries ship each
|
|
657
|
+
// look as its own package (Font Awesome's solid and regular packages): an
|
|
658
|
+
// icon from another package meets its variant only under the same export
|
|
659
|
+
// name and of the same kind, a component or data for the adapter. Once
|
|
660
|
+
// one icon of a package meets its twin, that package is a look package of
|
|
661
|
+
// the variant module, so its other icons are listed as missing.
|
|
642
662
|
const variantPackage = iconModulePackage(source.module);
|
|
663
|
+
/** @param {{module: string, source: string, adapt: boolean}} origin */
|
|
664
|
+
const otherPackageTwin = origin => {
|
|
665
|
+
if (origin.module.startsWith('.')) return false;
|
|
666
|
+
const twin = byLibraryExport.get(origin.source);
|
|
667
|
+
return twin != null && twin.adapt === origin.adapt;
|
|
668
|
+
};
|
|
669
|
+
const lookPackages = new Set(
|
|
670
|
+
set.data.icons.flatMap(icon => {
|
|
671
|
+
const origin = origins.get(icon.export);
|
|
672
|
+
return origin && otherPackageTwin(origin)
|
|
673
|
+
? [iconModulePackage(origin.module)]
|
|
674
|
+
: [];
|
|
675
|
+
}),
|
|
676
|
+
);
|
|
643
677
|
const generated = generatedModulePath(set, packageDir);
|
|
644
678
|
/** @type {import('../../../foundation/discovery/icon-set-writer.mjs').GeneratedIcon[]} */
|
|
645
679
|
const toWrite = [];
|
|
@@ -653,7 +687,11 @@ async function addVariantForAll({cwd, from, selector, variant}) {
|
|
|
653
687
|
const own = [];
|
|
654
688
|
const icons = set.data.icons.map(icon => {
|
|
655
689
|
const origin = origins.get(icon.export);
|
|
656
|
-
if (
|
|
690
|
+
if (
|
|
691
|
+
origin &&
|
|
692
|
+
iconModulePackage(origin.module) !== variantPackage &&
|
|
693
|
+
!lookPackages.has(iconModulePackage(origin.module))
|
|
694
|
+
) {
|
|
657
695
|
return icon;
|
|
658
696
|
}
|
|
659
697
|
const librarySource =
|
|
@@ -742,7 +780,8 @@ async function addVariantForAll({cwd, from, selector, variant}) {
|
|
|
742
780
|
const files = [];
|
|
743
781
|
const written = writeGeneratedIcons(packageDir, generated, next, toWrite);
|
|
744
782
|
files.push(...written.files);
|
|
745
|
-
const dataChanged =
|
|
783
|
+
const dataChanged =
|
|
784
|
+
JSON.stringify(next) !== JSON.stringify(set.data) || where.moduleRenamed;
|
|
746
785
|
if (dataChanged) {
|
|
747
786
|
try {
|
|
748
787
|
writeIconSetData(set, next);
|
|
@@ -766,6 +805,7 @@ async function addVariantForAll({cwd, from, selector, variant}) {
|
|
|
766
805
|
missing,
|
|
767
806
|
files,
|
|
768
807
|
changed: dataChanged || written.changed,
|
|
808
|
+
...(where.moduleUpdate ? {moduleUpdate: where.moduleUpdate} : {}),
|
|
769
809
|
},
|
|
770
810
|
};
|
|
771
811
|
}
|
package/api/icon/icon.type.d.mts
CHANGED
|
@@ -223,6 +223,10 @@ export type IconMapManyResponse = {
|
|
|
223
223
|
changed: boolean;
|
|
224
224
|
}>;
|
|
225
225
|
changed: boolean;
|
|
226
|
+
moduleUpdate?: {
|
|
227
|
+
previous: string;
|
|
228
|
+
module: string;
|
|
229
|
+
} | undefined;
|
|
226
230
|
files?: string[] | undefined;
|
|
227
231
|
};
|
|
228
232
|
};
|
|
@@ -239,6 +243,10 @@ export type IconMapResponse = {
|
|
|
239
243
|
icon: string | null;
|
|
240
244
|
previous: string | null;
|
|
241
245
|
changed: boolean;
|
|
246
|
+
moduleUpdate?: {
|
|
247
|
+
previous: string;
|
|
248
|
+
module: string;
|
|
249
|
+
} | undefined;
|
|
242
250
|
files?: string[] | undefined;
|
|
243
251
|
};
|
|
244
252
|
};
|
|
@@ -303,6 +311,10 @@ export type IconAddVariantsResponse = {
|
|
|
303
311
|
missing: string[];
|
|
304
312
|
files: string[];
|
|
305
313
|
changed: boolean;
|
|
314
|
+
moduleUpdate?: {
|
|
315
|
+
previous: string;
|
|
316
|
+
module: string;
|
|
317
|
+
} | undefined;
|
|
306
318
|
};
|
|
307
319
|
};
|
|
308
320
|
/**
|
|
@@ -323,6 +335,11 @@ export type IconAddResponse = {
|
|
|
323
335
|
newDefinition?: string | undefined;
|
|
324
336
|
files: string[];
|
|
325
337
|
changed: boolean;
|
|
338
|
+
added: boolean;
|
|
339
|
+
moduleUpdate?: {
|
|
340
|
+
previous: string;
|
|
341
|
+
module: string;
|
|
342
|
+
} | undefined;
|
|
326
343
|
note?: string | undefined;
|
|
327
344
|
};
|
|
328
345
|
};
|
|
@@ -557,6 +574,13 @@ export type IconNewData = {
|
|
|
557
574
|
* - What the author does next, including each change the package needs that the CLI never makes itself (its entry, peers, scripts), as text to copy.
|
|
558
575
|
*/
|
|
559
576
|
notes?: string[] | undefined;
|
|
577
|
+
/**
|
|
578
|
+
* - On a refresh, an update, or a new definition with `icon`: the data file's `module` before and after this command, when the package was renamed and the command writes its new name.
|
|
579
|
+
*/
|
|
580
|
+
moduleUpdate?: {
|
|
581
|
+
previous: string;
|
|
582
|
+
module: string;
|
|
583
|
+
} | undefined;
|
|
560
584
|
};
|
|
561
585
|
/**
|
|
562
586
|
* `astryx --json experimental icon new-set <name>`.
|
package/api/icon/icon.type.mjs
CHANGED
|
@@ -156,6 +156,7 @@
|
|
|
156
156
|
* @property {string} data.file - The data file, relative to the package.
|
|
157
157
|
* @property {Array<{definition: string, icon: string, previous: string | null, changed: boolean}>} data.entries - Each pair, in order, and what it replaced.
|
|
158
158
|
* @property {boolean} data.changed - Whether the file was written.
|
|
159
|
+
* @property {{previous: string, module: string}} [data.moduleUpdate] - The data file's `module` before and after this command, when the package was renamed and the command wrote its new name.
|
|
159
160
|
* @property {string[]} [data.files] - The generated module and its types, when its theme map was rewritten.
|
|
160
161
|
*/
|
|
161
162
|
|
|
@@ -172,6 +173,7 @@
|
|
|
172
173
|
* @property {string | null} data.icon - The id of the icon now mapped (the icon may be named by any name `map` accepts); null after `remove`.
|
|
173
174
|
* @property {string | null} data.previous - The icon id it replaced.
|
|
174
175
|
* @property {boolean} data.changed - Whether the file was written.
|
|
176
|
+
* @property {{previous: string, module: string}} [data.moduleUpdate] - The data file's `module` before and after this command, when the package was renamed and the command wrote its new name.
|
|
175
177
|
* @property {string[]} [data.files] - The generated module and its types, when its theme map was rewritten.
|
|
176
178
|
*/
|
|
177
179
|
|
|
@@ -207,6 +209,7 @@
|
|
|
207
209
|
* @property {string[]} data.missing - Ids of icons `from` has no export for; they keep no such variant.
|
|
208
210
|
* @property {string[]} data.files - Files written, relative to the package.
|
|
209
211
|
* @property {boolean} data.changed
|
|
212
|
+
* @property {{previous: string, module: string}} [data.moduleUpdate] - The data file's `module` before and after this command, when the package was renamed and the command wrote its new name.
|
|
210
213
|
*/
|
|
211
214
|
|
|
212
215
|
/**
|
|
@@ -223,6 +226,8 @@
|
|
|
223
226
|
* @property {string} [data.newDefinition] - The set's own definition the command wrote.
|
|
224
227
|
* @property {string[]} data.files - Files written, relative to the package.
|
|
225
228
|
* @property {boolean} data.changed - Whether anything was written.
|
|
229
|
+
* @property {boolean} data.added - Whether the icon, its variant, its `map` entry, or a new definition was added. False when the set already had them, even when `changed` is true because the command wrote a renamed package's new name.
|
|
230
|
+
* @property {{previous: string, module: string}} [data.moduleUpdate] - The data file's `module` before and after this command, when the package was renamed and the command wrote its new name.
|
|
226
231
|
* @property {string} [data.note] - What the package still needs, as text to copy: the entry line that exports the icon from the set's module, and the package.json `files` that publishes a `./` source.
|
|
227
232
|
*/
|
|
228
233
|
|
|
@@ -332,6 +337,7 @@
|
|
|
332
337
|
* @property {Array<{definition: string, icon: string}>} [mapped] - With `from`: the `map` entries an icon with the definition's exact name gives (on a refresh, every such entry, kept or new); check each.
|
|
333
338
|
* @property {Array<{definition: string, icon: string, word: string}>} [suggestions] - With `from`: for each unmapped Astryx definition, up to three icons it might map to, best first: the definition's name in another spelling or as another name of an icon, ids that start with it, an id or another name that is one of its words, then two-word ids that are a one-word word sized, shaped, or numbered (`grid-2x2`); never a negated form, the opposite direction, or an icon already mapped. `word` is the name or word that matched. Never written.
|
|
334
339
|
* @property {string[]} [notes] - What the author does next, including each change the package needs that the CLI never makes itself (its entry, peers, scripts), as text to copy.
|
|
340
|
+
* @property {{previous: string, module: string}} [moduleUpdate] - On a refresh, an update, or a new definition with `icon`: the data file's `module` before and after this command, when the package was renamed and the command writes its new name.
|
|
335
341
|
*/
|
|
336
342
|
|
|
337
343
|
/**
|
package/api/icon/iconAdd.doc.mjs
CHANGED
|
@@ -119,7 +119,7 @@ export const doc = {
|
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
code: 'ERR_INVALID_ARGUMENT',
|
|
122
|
-
when: "the export is data rather than a component, a new definition is named like an Astryx one, or the variant is not one the set declares, is the look each icon's own export already draws, or is no look word of a library whose export names encode looks",
|
|
122
|
+
when: "the export is data rather than a component, a new definition is named like an Astryx one, or the variant is not one the set declares, is the look each icon's own export already draws, or is no look word of a library whose export names encode looks, or `id` starts with a digit",
|
|
123
123
|
},
|
|
124
124
|
{code: 'ERR_FILE_EXISTS', when: 'another icon already has the id'},
|
|
125
125
|
{
|
package/api/icon/map/map.mjs
CHANGED
|
@@ -77,7 +77,8 @@ export async function iconMap(definition, icon, options = {}) {
|
|
|
77
77
|
ERROR_CODES.ERR_MISSING_ARGUMENT,
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
|
-
const {packageDir, set} =
|
|
80
|
+
const {packageDir, set, moduleRenamed, moduleUpdate} =
|
|
81
|
+
await findLocalIconSet(cwd, selector);
|
|
81
82
|
// The docs' kebab id (`eye-slash`) names the same definition, and a
|
|
82
83
|
// library's other name for an icon (`trash-2`) names the same icon.
|
|
83
84
|
definition = definitionNameOf(definition, mappableDefinitions(set));
|
|
@@ -112,7 +113,8 @@ export async function iconMap(definition, icon, options = {}) {
|
|
|
112
113
|
}
|
|
113
114
|
map[definition] = /** @type {string} */ (icon);
|
|
114
115
|
}
|
|
115
|
-
const changed =
|
|
116
|
+
const changed =
|
|
117
|
+
(remove ? previous != null : previous !== icon) || moduleRenamed;
|
|
116
118
|
/** @type {string[]} */
|
|
117
119
|
const files = [];
|
|
118
120
|
if (changed) {
|
|
@@ -139,6 +141,7 @@ export async function iconMap(definition, icon, options = {}) {
|
|
|
139
141
|
icon: remove ? null : /** @type {string} */ (icon),
|
|
140
142
|
previous,
|
|
141
143
|
changed,
|
|
144
|
+
...(moduleUpdate ? {moduleUpdate} : {}),
|
|
142
145
|
...(files.length > 0 ? {files} : {}),
|
|
143
146
|
},
|
|
144
147
|
};
|
|
@@ -161,7 +164,10 @@ async function mapMany({cwd, selector, pairs}) {
|
|
|
161
164
|
ERROR_CODES.ERR_MISSING_ARGUMENT,
|
|
162
165
|
);
|
|
163
166
|
}
|
|
164
|
-
const {packageDir, set} = await findLocalIconSet(
|
|
167
|
+
const {packageDir, set, moduleRenamed, moduleUpdate} = await findLocalIconSet(
|
|
168
|
+
cwd,
|
|
169
|
+
selector,
|
|
170
|
+
);
|
|
165
171
|
const known = mappableDefinitions(set);
|
|
166
172
|
const ids = set.data.icons.map(each => each.id);
|
|
167
173
|
const map = {...(set.data.map ?? {})};
|
|
@@ -204,7 +210,7 @@ async function mapMany({cwd, selector, pairs}) {
|
|
|
204
210
|
changed: previous !== id,
|
|
205
211
|
});
|
|
206
212
|
}
|
|
207
|
-
const changed = entries.some(each => each.changed);
|
|
213
|
+
const changed = entries.some(each => each.changed) || moduleRenamed;
|
|
208
214
|
/** @type {string[]} */
|
|
209
215
|
const files = [];
|
|
210
216
|
if (changed) {
|
|
@@ -228,6 +234,7 @@ async function mapMany({cwd, selector, pairs}) {
|
|
|
228
234
|
file: path.relative(packageDir, set.dataPath).split(path.sep).join('/'),
|
|
229
235
|
entries,
|
|
230
236
|
changed,
|
|
237
|
+
...(moduleUpdate ? {moduleUpdate} : {}),
|
|
231
238
|
...(files.length > 0 ? {files} : {}),
|
|
232
239
|
},
|
|
233
240
|
};
|
package/api/index.d.mts
CHANGED
|
@@ -47,6 +47,7 @@ export * from "./integration/authoring-checks.type.mjs";
|
|
|
47
47
|
export * from "./integration/integration-check.type.mjs";
|
|
48
48
|
export type Logger = import("./logger.mjs").Logger;
|
|
49
49
|
export { themeBuild, themeAdd, themeRemove, themeUse, themeEject, themeTemplate, themeList, themeListAvailable, themeListCopySources, themeTargets, themePaletteGenerate, generateTonalPalette, listThemes } from "./theme/theme.mjs";
|
|
50
|
+
export { exportSearchIndex, rankSearchIndex } from "./search/search-index.mjs";
|
|
50
51
|
export { integrationAdd, integrationAddAgentDoc, integrationAddCodemod, integrationAddComponent, integrationAddDoc, integrationAddTemplate } from "./integration/add-contribution.mjs";
|
|
51
52
|
export { validateIntegration, summarizeIssues } from "./integration/validate-integration.mjs";
|
|
52
53
|
export { integrationTemplateConflicts, integrationComponentConflicts, integrationDocConflicts } from "./integration/authoring-checks.mjs";
|
package/api/index.mjs
CHANGED
|
@@ -48,6 +48,7 @@ export {
|
|
|
48
48
|
} from './theme/theme.mjs';
|
|
49
49
|
export {hook} from './hook/hook.mjs';
|
|
50
50
|
export {search} from './search/search.mjs';
|
|
51
|
+
export {exportSearchIndex, rankSearchIndex} from './search/search-index.mjs';
|
|
51
52
|
export {build} from './build/build.mjs';
|
|
52
53
|
export {swizzle} from './swizzle/swizzle.mjs';
|
|
53
54
|
export {gapReport} from './gap-report/gap-report.mjs';
|
|
@@ -92,7 +92,7 @@ export function loadCurrentManifest(manifestFile: string, manifestExists: boolea
|
|
|
92
92
|
* @param {string} manifestPath
|
|
93
93
|
* @param {string[]} files
|
|
94
94
|
* @param {boolean} dryRun
|
|
95
|
-
* @returns {{type: 'integration.add', data: Omit<import('./integration-authoring.type.mjs').IntegrationAddData, 'kind'> & {kind: K, icons?: number, refreshed?: boolean, updated?: boolean, renamed?: Array<{from: string, to: string}>, moved?: Array<{id: string, from: string, to: string}>, licenseNoticeMissing?: {library: string, path: string}, mapped?: Array<{definition: string, icon: string}>, suggestions?: Array<{definition: string, icon: string, word: string}>, notes?: string[]}}}
|
|
95
|
+
* @returns {{type: 'integration.add', data: Omit<import('./integration-authoring.type.mjs').IntegrationAddData, 'kind'> & {kind: K, icons?: number, refreshed?: boolean, updated?: boolean, renamed?: Array<{from: string, to: string}>, moved?: Array<{id: string, from: string, to: string}>, licenseNoticeMissing?: {library: string, path: string}, mapped?: Array<{definition: string, icon: string}>, suggestions?: Array<{definition: string, icon: string, word: string}>, notes?: string[], moduleUpdate?: {previous: string, module: string}}}}
|
|
96
96
|
*/
|
|
97
97
|
export function receipt<K extends import("./integration-authoring.type.mjs").IntegrationAddData["kind"] | import("../icon/icon.type.mjs").IconNewData["kind"]>(kind: K, name: string, root: {
|
|
98
98
|
path: string;
|
|
@@ -127,6 +127,10 @@ export function receipt<K extends import("./integration-authoring.type.mjs").Int
|
|
|
127
127
|
word: string;
|
|
128
128
|
}>;
|
|
129
129
|
notes?: string[];
|
|
130
|
+
moduleUpdate?: {
|
|
131
|
+
previous: string;
|
|
132
|
+
module: string;
|
|
133
|
+
};
|
|
130
134
|
};
|
|
131
135
|
};
|
|
132
136
|
/** @param {string} slug */
|
|
@@ -486,7 +486,7 @@ export async function loadCurrentManifest(manifestFile, manifestExists) {
|
|
|
486
486
|
* @param {string} manifestPath
|
|
487
487
|
* @param {string[]} files
|
|
488
488
|
* @param {boolean} dryRun
|
|
489
|
-
* @returns {{type: 'integration.add', data: Omit<import('./integration-authoring.type.mjs').IntegrationAddData, 'kind'> & {kind: K, icons?: number, refreshed?: boolean, updated?: boolean, renamed?: Array<{from: string, to: string}>, moved?: Array<{id: string, from: string, to: string}>, licenseNoticeMissing?: {library: string, path: string}, mapped?: Array<{definition: string, icon: string}>, suggestions?: Array<{definition: string, icon: string, word: string}>, notes?: string[]}}}
|
|
489
|
+
* @returns {{type: 'integration.add', data: Omit<import('./integration-authoring.type.mjs').IntegrationAddData, 'kind'> & {kind: K, icons?: number, refreshed?: boolean, updated?: boolean, renamed?: Array<{from: string, to: string}>, moved?: Array<{id: string, from: string, to: string}>, licenseNoticeMissing?: {library: string, path: string}, mapped?: Array<{definition: string, icon: string}>, suggestions?: Array<{definition: string, icon: string, word: string}>, notes?: string[], moduleUpdate?: {previous: string, module: string}}}}
|
|
490
490
|
*/
|
|
491
491
|
export function receipt(kind, name, root, manifestPath, files, dryRun) {
|
|
492
492
|
return {
|
package/api/render/_adapter.mjs
CHANGED
|
@@ -548,6 +548,15 @@ export function reverseToFile({cwd, input, output, overwrite}) {
|
|
|
548
548
|
...(missing.length > 0 ? {missing} : {}),
|
|
549
549
|
};
|
|
550
550
|
}
|
|
551
|
+
// A file a render wrote, whose source block was taken out on the way
|
|
552
|
+
// (sanitizers and editors strip <script> elements), says so.
|
|
553
|
+
if (!isAstryxMarkup(html) && /<meta name="generator" content="Astryx[ "]/.test(html)) {
|
|
554
|
+
throw new AstryxError(
|
|
555
|
+
`${shown(cwd, input)} was rendered by Astryx, but its embedded source was removed (a sanitizer or an editor strips <script> blocks) and it has no Astryx components to rebuild from; nothing was written. Render the page again from its source and send the new file as is.`,
|
|
556
|
+
[],
|
|
557
|
+
ERROR_CODES.ERR_RENDER_SOURCE_DAMAGED,
|
|
558
|
+
);
|
|
559
|
+
}
|
|
551
560
|
if (!isAstryxMarkup(html)) {
|
|
552
561
|
throw new AstryxError(
|
|
553
562
|
`${shown(cwd, input)} is not an Astryx page: it has no embedded source and no Astryx components to rebuild from. To start an Astryx page like it, run astryx build "<what the page is>".`,
|
|
@@ -96,12 +96,12 @@ export const doc = {
|
|
|
96
96
|
{code: 'ERR_RENDER_BROWSER_API', when: 'the page reads a browser-only API, such as window or document, while rendering'},
|
|
97
97
|
{code: 'ERR_RENDER_ASSET_TOO_LARGE', when: 'a local file the page uses is over 8 MB'},
|
|
98
98
|
{code: 'ERR_RENDER_TIMEOUT', when: 'the page does not finish rendering within 60 seconds'},
|
|
99
|
-
{code: 'ERR_RENDER_FAILED', when: 'the page throws while rendering for any other reason'},
|
|
99
|
+
{code: 'ERR_RENDER_FAILED', when: 'the page throws while rendering for any other reason, or Node itself crashes (a crash signal, or a crash status on Windows) or is stopped by a signal while it renders the page (the message names the Node version and path)'},
|
|
100
100
|
{
|
|
101
101
|
code: 'ERR_RENDER_NOT_ASTRYX',
|
|
102
|
-
when: 'reversing a file with no embedded source and no Astryx markup; the suggestion is `astryx build "<what the page is>"`',
|
|
102
|
+
when: 'reversing a file a render did not write, with no embedded source and no Astryx markup; the suggestion is `astryx build "<what the page is>"`',
|
|
103
103
|
},
|
|
104
|
-
{code: 'ERR_RENDER_SOURCE_DAMAGED', when: 'reversing a file whose embedded source is damaged, lists a path twice, carries a module, stylesheet or JSON file the page does not reach through its imports, or an image, font or media file whose bytes are not one, or carries a file a render never embeds (a dotfile or dot-folder, `node_modules`, a package manifest, lockfile or tool configuration, or any kind but a page module, stylesheet, JSON or image)'},
|
|
104
|
+
{code: 'ERR_RENDER_SOURCE_DAMAGED', when: 'reversing a file a render wrote whose embedded source was removed (and that has no Astryx components to rebuild from), a file whose embedded source is damaged, lists a path twice, carries a module, stylesheet or JSON file the page does not reach through its imports, or an image, font or media file whose bytes are not one, or carries a file a render never embeds (a dotfile or dot-folder, `node_modules`, a package manifest, lockfile or tool configuration, or any kind but a page module, stylesheet, JSON or image)'},
|
|
105
105
|
{code: 'ERR_PATH_TRAVERSAL', when: 'a file would be written outside the current folder (symlinks followed), or through a symbolic link: `out`, or with `reverse` the page or an embedded file; or, with `reverse`, a file other than the page would go into a folder that already exists'},
|
|
106
106
|
],
|
|
107
107
|
examples: [
|
|
@@ -31,7 +31,7 @@ export type RenderWarning = {
|
|
|
31
31
|
/**
|
|
32
32
|
* - What kind of problem.
|
|
33
33
|
*/
|
|
34
|
-
code: "dropped-children" | "remote-asset" | "missing-asset" | "page-output" | "page-script" | "no-astryx-styles" | "chart-size" | "not-embedded";
|
|
34
|
+
code: "dropped-children" | "remote-asset" | "missing-asset" | "page-output" | "page-script" | "no-astryx-styles" | "chart-size" | "not-embedded" | "node-crash";
|
|
35
35
|
/**
|
|
36
36
|
* - What happened and what to do, in one sentence.
|
|
37
37
|
*/
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* author meant.
|
|
23
23
|
* @experimental May change or be removed in a patch release.
|
|
24
24
|
* @typedef {object} RenderWarning
|
|
25
|
-
* @property {'dropped-children' | 'remote-asset' | 'missing-asset' | 'page-output' | 'page-script' | 'no-astryx-styles' | 'chart-size' | 'not-embedded'} code - What kind of problem.
|
|
25
|
+
* @property {'dropped-children' | 'remote-asset' | 'missing-asset' | 'page-output' | 'page-script' | 'no-astryx-styles' | 'chart-size' | 'not-embedded' | 'node-crash'} code - What kind of problem.
|
|
26
26
|
* @property {string} message - What happened and what to do, in one sentence.
|
|
27
27
|
*/
|
|
28
28
|
|
package/api/search/ORDERING.md
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
`spec:AST-083` owns search's result order; this file describes the data that implements it. Inside one score band, a searcher's static weights decide the order. Matches with the same score move together as one block, placed by the learned position of the block's first match: moving each one on its own breaks search's existing rules for tied matches. Inside a block, the lexical ranker's order holds, except below the first result: there a block of one domain puts a part of the first result first (its name words start with the first result's; at most two parts lead across an answer), then the match whose matched words are rarer in the catalog. Matches never cross bands or kinds. There is no model runtime: every number is a plain static value read at load time.
|
|
8
8
|
|
|
9
|
+
After that order, one fit pass reads the first ten results (`_fit-order.mjs`). The first result stays. The others split into runs of one band and kind. Inside a run, each match gets a fit to the whole query from build's block-picker match (`../build/blocks/blocks.mjs` with `blocks.weights.json`) over the match's own docs: its name words, display name or title, description, and keywords. A match whose fit is under 1.7 times the run's median fit moves, in order, after the others in its run, unless its name spells two or more of the query's words. Such a match never moves down; it can still move up when others move after it. A run whose median fit is zero or less keeps its order. A query that is an entry's whole name skips the pass, as it skips the learned order. The pass always reads ten places before the limit cuts the answer. At the default limit, and at any limit of 10 or more, the set of results stays the same. A smaller limit shows the top of the new order, so it can show a different result. Build's loader (`loadBlockPicker` in `../build/_adapter.mjs`) reads and checks the block-picker weights for both commands. When the file is missing, unreadable, or not the picker's shape, the loader returns null and the pass keeps the order. The pass reads the weights only when a run holds two or more matches, and it decodes only the matrix rows that the query's words and those matches' docs use, so a search does not pay for the whole catalog.
|
|
10
|
+
|
|
9
11
|
## Files
|
|
10
12
|
|
|
11
13
|
| File | What it holds |
|
|
12
14
|
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
15
|
| `tree.weights.json` | The root classifier: for each query word or word pair, one number per class (component, hook, doc, template, theme, none). The query's tokens vote, and a topic searcher takes the query only when its class wins by the floor in `tree.mjs`. |
|
|
14
16
|
| `_main-searcher.weights.json`, `_doc-searcher.weights.json`, `_template-searcher.weights.json` | One weight, mean and spread per signal for each searcher (see `SIGNALS` and `PER_DOMAIN` in `_features.mjs`). A match's place inside its band is the weighted sum of its standardized signals. |
|
|
17
|
+
| `../build/blocks/blocks.weights.json` | The block picker's vocabulary, idf and two projection matrices, shared with build. The fit pass reads them through build's loader; search ships no copy. |
|
|
15
18
|
| `_cards.json` | One card per Core component and hook: what it is, the words people use for it, and its main props, written from the item's own source. Cards feed one signal, word overlap with the card. |
|
|
16
19
|
|
|
17
20
|
The signals are counts over the query and each entry's own text: the lexical score and rank, BM25 over the entry's name, docs text and card, how many query words the entry covers, whether the query holds the entry's name, and the classifier's guess of the domain.
|
|
@@ -16,7 +16,7 @@ export function requireSearchCoreDir(cwd: string): string;
|
|
|
16
16
|
* in the CLI's searches. A caller that runs its own operation shares it.
|
|
17
17
|
*
|
|
18
18
|
* @param {{cwd?: string, type?: string}} options
|
|
19
|
-
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null}>}
|
|
19
|
+
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null, coreSkippedCode: string | null}>}
|
|
20
20
|
*/
|
|
21
21
|
export function gatherCandidates(options: {
|
|
22
22
|
cwd?: string;
|
|
@@ -27,7 +27,28 @@ export function gatherCandidates(options: {
|
|
|
27
27
|
searchedComponents: boolean;
|
|
28
28
|
outside: boolean;
|
|
29
29
|
coreSkipped: string | null;
|
|
30
|
+
coreSkippedCode: string | null;
|
|
30
31
|
}>;
|
|
32
|
+
/**
|
|
33
|
+
* The Core a search of `type` reads, and the domains it gathers. `astryx docs`
|
|
34
|
+
* reads docs without @astryxdesign/core, and `astryx theme list` reads themes
|
|
35
|
+
* without it, so a docs or themes search never looks for Core. Every other
|
|
36
|
+
* domain reads Core: the project's, else the answering release's own,
|
|
37
|
+
* installed into the per-user cache on first need (spec AST-081 FR3). A Core
|
|
38
|
+
* domain asked for by name throws the lookup's coded error. An open search
|
|
39
|
+
* without Core gathers the docs and themes alone and records why in
|
|
40
|
+
* `coreSkipped`; it never fails silently.
|
|
41
|
+
* @param {string | undefined} type
|
|
42
|
+
* @param {(cwd: string) => string} lookup finds the Core folder, or throws
|
|
43
|
+
* @param {string} cwd
|
|
44
|
+
* @returns {{coreDir: string | null, coreSkipped: string | null, coreSkippedCode: string | null, wants: (domain: string) => boolean}}
|
|
45
|
+
*/
|
|
46
|
+
export function resolveSearchCore(type: string | undefined, lookup: (cwd: string) => string, cwd: string): {
|
|
47
|
+
coreDir: string | null;
|
|
48
|
+
coreSkipped: string | null;
|
|
49
|
+
coreSkippedCode: string | null;
|
|
50
|
+
wants: (domain: string) => boolean;
|
|
51
|
+
};
|
|
31
52
|
/**
|
|
32
53
|
* gatherCandidates, reused within one process: a later search in the same
|
|
33
54
|
* folder, for the same `type`, ranks the candidates an earlier search
|
|
@@ -46,8 +67,10 @@ export function gatherCandidatesReused(options: {
|
|
|
46
67
|
type?: string;
|
|
47
68
|
}): Promise<Gathered>;
|
|
48
69
|
/**
|
|
49
|
-
* The search tree's static data, read once: the entry cards
|
|
50
|
-
* weights file.
|
|
70
|
+
* The search tree's static data, read once: the entry cards, each node's
|
|
71
|
+
* weights file, and build's block-picker weights for the fit pass. Build's
|
|
72
|
+
* loader (loadBlockPicker) reads and checks the picker weights, only when the
|
|
73
|
+
* pass first needs them.
|
|
51
74
|
*
|
|
52
75
|
* These files are static, reviewed data that only order matches inside a score
|
|
53
76
|
* band; ORDERING.md beside them says what each holds and how
|
|
@@ -89,6 +112,12 @@ export type SearchData = {
|
|
|
89
112
|
* The template searcher's weights.
|
|
90
113
|
*/
|
|
91
114
|
template: SearcherWeights;
|
|
115
|
+
/**
|
|
116
|
+
* Build's block-picker weights, which the fit pass (_fit-order.mjs) reads;
|
|
117
|
+
* null when build's loader finds the file absent, unreadable, or the wrong
|
|
118
|
+
* shape.
|
|
119
|
+
*/
|
|
120
|
+
picker: import("../build/blocks/blocks.mjs").BlockPickerWeights | null;
|
|
92
121
|
};
|
|
93
122
|
export type SearchCatalog = Awaited<ReturnType<typeof gatherCandidates>> & {
|
|
94
123
|
data: SearchData;
|
package/api/search/_adapter.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from '../../foundation/fs/read-record.mjs';
|
|
17
17
|
import {isOutsideProject} from '../../foundation/env/project-scope.mjs';
|
|
18
18
|
import {coreDirOrThrow} from '../error.mjs';
|
|
19
|
+
import {loadBlockPicker} from '../build/_adapter.mjs';
|
|
19
20
|
import {CORELESS_DOMAINS} from './_query.mjs';
|
|
20
21
|
import {gatherComponents} from './_gather-components.mjs';
|
|
21
22
|
import {gatherHooks} from './_gather-hooks.mjs';
|
|
@@ -43,46 +44,62 @@ export function requireSearchCoreDir(cwd) {
|
|
|
43
44
|
* in the CLI's searches. A caller that runs its own operation shares it.
|
|
44
45
|
*
|
|
45
46
|
* @param {{cwd?: string, type?: string}} options
|
|
46
|
-
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null}>}
|
|
47
|
+
* @returns {Promise<{all: Candidate[], components: Candidate[], searchedComponents: boolean, outside: boolean, coreSkipped: string | null, coreSkippedCode: string | null}>}
|
|
47
48
|
*/
|
|
48
49
|
export function gatherCandidates(options) {
|
|
49
50
|
return withListingCache(() => gatherDomains(options));
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
+
* The Core a search of `type` reads, and the domains it gathers. `astryx docs`
|
|
55
|
+
* reads docs without @astryxdesign/core, and `astryx theme list` reads themes
|
|
56
|
+
* without it, so a docs or themes search never looks for Core. Every other
|
|
57
|
+
* domain reads Core: the project's, else the answering release's own,
|
|
58
|
+
* installed into the per-user cache on first need (spec AST-081 FR3). A Core
|
|
59
|
+
* domain asked for by name throws the lookup's coded error. An open search
|
|
60
|
+
* without Core gathers the docs and themes alone and records why in
|
|
61
|
+
* `coreSkipped`; it never fails silently.
|
|
62
|
+
* @param {string | undefined} type
|
|
63
|
+
* @param {(cwd: string) => string} lookup finds the Core folder, or throws
|
|
64
|
+
* @param {string} cwd
|
|
65
|
+
* @returns {{coreDir: string | null, coreSkipped: string | null, coreSkippedCode: string | null, wants: (domain: string) => boolean}}
|
|
54
66
|
*/
|
|
55
|
-
|
|
56
|
-
const {cwd = process.cwd(), type} = options;
|
|
57
|
-
// `astryx docs` reads docs without @astryxdesign/core, and `astryx theme
|
|
58
|
-
// list` reads themes without it (bundled themes need no project), so a
|
|
59
|
-
// search of either must too. Every other domain reads core: asked for by
|
|
60
|
-
// name, it is an error without core; an open search then covers the docs
|
|
61
|
-
// and themes alone.
|
|
67
|
+
export function resolveSearchCore(type, lookup, cwd) {
|
|
62
68
|
const needsCore = !type || !CORELESS_DOMAINS.includes(type);
|
|
63
|
-
// A domain that reads Core gets it as `component` does: the project's, else
|
|
64
|
-
// the answering release's own, installed into the per-user cache on first
|
|
65
|
-
// need (spec AST-081 FR3). Asked for by name, a Core that cannot be had is
|
|
66
|
-
// the error; an open search answers from the rest and says what it skipped
|
|
67
|
-
// and why, never silently.
|
|
68
69
|
/** @type {string | null} */
|
|
69
70
|
let coreDir = null;
|
|
70
71
|
/** @type {string | null} */
|
|
71
72
|
let coreSkipped = null;
|
|
73
|
+
/** @type {string | null} */
|
|
74
|
+
let coreSkippedCode = null;
|
|
72
75
|
if (needsCore && type) {
|
|
73
|
-
coreDir = coreDirOrThrow(
|
|
76
|
+
coreDir = coreDirOrThrow(lookup, cwd);
|
|
74
77
|
} else if (needsCore) {
|
|
75
78
|
try {
|
|
76
|
-
coreDir =
|
|
79
|
+
coreDir = lookup(cwd);
|
|
77
80
|
} catch (error) {
|
|
78
81
|
coreSkipped = /** @type {Error} */ (error).message;
|
|
82
|
+
const code = /** @type {any} */ (error)?.code;
|
|
83
|
+
coreSkippedCode = typeof code === 'string' ? code : null;
|
|
79
84
|
}
|
|
80
85
|
}
|
|
86
|
+
/** @param {string} domain */
|
|
87
|
+
const wants = domain =>
|
|
88
|
+
(!type && (coreDir != null || CORELESS_DOMAINS.includes(domain))) ||
|
|
89
|
+
type === domain;
|
|
90
|
+
return {coreDir, coreSkipped, coreSkippedCode, wants};
|
|
91
|
+
}
|
|
81
92
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
/**
|
|
94
|
+
* @param {{cwd?: string, type?: string}} options
|
|
95
|
+
*/
|
|
96
|
+
async function gatherDomains(options) {
|
|
97
|
+
const {cwd = process.cwd(), type} = options;
|
|
98
|
+
const {coreDir, coreSkipped, coreSkippedCode, wants} = resolveSearchCore(
|
|
99
|
+
type,
|
|
100
|
+
requireSearchCoreDir,
|
|
101
|
+
cwd,
|
|
102
|
+
);
|
|
86
103
|
const [components, hooks, docTopics, templates, themes] = await Promise.all([
|
|
87
104
|
wants('component')
|
|
88
105
|
? gatherComponents(/** @type {string} */ (coreDir), cwd)
|
|
@@ -103,6 +120,7 @@ async function gatherDomains(options) {
|
|
|
103
120
|
searchedComponents: wants('component'),
|
|
104
121
|
outside,
|
|
105
122
|
coreSkipped,
|
|
123
|
+
coreSkippedCode,
|
|
106
124
|
};
|
|
107
125
|
}
|
|
108
126
|
|
|
@@ -150,6 +168,10 @@ export function gatherCandidatesReused(options) {
|
|
|
150
168
|
* @property {SearcherWeights} main The main searcher's weights.
|
|
151
169
|
* @property {SearcherWeights} doc The doc searcher's weights.
|
|
152
170
|
* @property {SearcherWeights} template The template searcher's weights.
|
|
171
|
+
* @property {import('../build/blocks/blocks.mjs').BlockPickerWeights | null} picker
|
|
172
|
+
* Build's block-picker weights, which the fit pass (_fit-order.mjs) reads;
|
|
173
|
+
* null when build's loader finds the file absent, unreadable, or the wrong
|
|
174
|
+
* shape.
|
|
153
175
|
* @typedef {Awaited<ReturnType<typeof gatherCandidates>> & {data: SearchData}} SearchCatalog
|
|
154
176
|
*/
|
|
155
177
|
|
|
@@ -157,8 +179,10 @@ export function gatherCandidatesReused(options) {
|
|
|
157
179
|
let searchData = null;
|
|
158
180
|
|
|
159
181
|
/**
|
|
160
|
-
* The search tree's static data, read once: the entry cards
|
|
161
|
-
* weights file.
|
|
182
|
+
* The search tree's static data, read once: the entry cards, each node's
|
|
183
|
+
* weights file, and build's block-picker weights for the fit pass. Build's
|
|
184
|
+
* loader (loadBlockPicker) reads and checks the picker weights, only when the
|
|
185
|
+
* pass first needs them.
|
|
162
186
|
*
|
|
163
187
|
* These files are static, reviewed data that only order matches inside a score
|
|
164
188
|
* band; ORDERING.md beside them says what each holds and how
|
|
@@ -176,6 +200,10 @@ export function loadSearchData() {
|
|
|
176
200
|
main: read('./_main-searcher.weights.json'),
|
|
177
201
|
doc: read('./_doc-searcher.weights.json'),
|
|
178
202
|
template: read('./_template-searcher.weights.json'),
|
|
203
|
+
// Read on first use: most searches never reach the fit pass.
|
|
204
|
+
get picker() {
|
|
205
|
+
return loadBlockPicker();
|
|
206
|
+
},
|
|
179
207
|
};
|
|
180
208
|
}
|
|
181
209
|
return searchData;
|