@krapwoo/ds-viewer 0.14.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +249 -3
- package/config/index.ts +3 -0
- package/dist/cli/config.js +9 -1
- package/dist/cli/doctor.js +452 -19
- package/dist/cli/explain.js +26 -0
- package/dist/cli/staticPage.js +99 -10
- package/dist/cli/tokenDefinition.js +125 -0
- package/dist/native/catalog/catalogNavigation.js +27 -11
- package/dist/native/catalog/pageApi.js +557 -26
- package/dist/native/catalog/tokenDefinition.js +205 -0
- package/native/catalog/CatalogSearchInput.tsx +18 -3
- package/native/catalog/CatalogShell.tsx +318 -46
- package/native/catalog/CatalogSidebar.tsx +37 -1
- package/native/catalog/FamilyPage.tsx +567 -137
- package/native/catalog/GroupPage.tsx +230 -0
- package/native/catalog/JumpRow.tsx +81 -0
- package/native/catalog/ReferenceDetails.tsx +144 -16
- package/native/catalog/SectionBlock.tsx +49 -5
- package/native/catalog/SourceReferencePicker.tsx +180 -0
- package/native/catalog/StructuredTokenPage.tsx +592 -0
- package/native/catalog/Swatch.tsx +29 -2
- package/native/catalog/TokenLayouts.tsx +30 -9
- package/native/catalog/catalogNavigation.ts +30 -10
- package/native/catalog/catalogReferences.ts +212 -0
- package/native/catalog/familyModel.ts +10 -4
- package/native/catalog/index.ts +22 -0
- package/native/catalog/pageApi.ts +598 -23
- package/native/catalog/tokenDefinition.ts +430 -0
- package/native/catalog/types.ts +77 -5
- package/package.json +1 -1
- package/starter-kit/components/AnimatedChevron/AnimatedChevron.catalog.tsx +1 -0
- package/starter-kit/components/Avatar/Avatar.catalog.tsx +1 -0
- package/starter-kit/components/BottomSheet/BottomSheet.catalog.tsx +1 -0
- package/starter-kit/components/ButtonGroup/ButtonGroup.catalog.tsx +1 -0
- package/starter-kit/components/Divider/Divider.catalog.tsx +1 -0
- package/starter-kit/components/Pill/Pill.catalog.tsx +1 -0
- package/starter-kit/components/TopNav/TopNav.catalog.tsx +1 -0
package/dist/cli/staticPage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { readStaticTokenDefinition } from "./tokenDefinition.js";
|
|
3
4
|
const TRANSPILE_OPTIONS = {
|
|
4
5
|
compilerOptions: { jsx: ts.JsxEmit.ReactJSX, module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
|
|
5
6
|
reportDiagnostics: true,
|
|
@@ -58,21 +59,26 @@ function readStaticPage(file) {
|
|
|
58
59
|
return { file, checkable: false, viewerImports };
|
|
59
60
|
const page = { file, checkable: true, viewerImports };
|
|
60
61
|
const idProp = findProp(pageObject, 'id');
|
|
62
|
+
const titleProp = findProp(pageObject, 'title');
|
|
61
63
|
const componentProp = findProp(pageObject, 'component');
|
|
62
64
|
const groupProp = findProp(pageObject, 'group');
|
|
63
65
|
const specimenSizeProp = findProp(pageObject, 'specimenSize');
|
|
64
66
|
const tokenGalleryProp = findProp(pageObject, 'tokenGallery');
|
|
65
67
|
if (idProp)
|
|
66
68
|
page.id = stringLiteral(resolve(idProp.initializer, consts, new Set()));
|
|
69
|
+
if (titleProp)
|
|
70
|
+
page.title = stringLiteral(resolve(titleProp.initializer, consts, new Set()));
|
|
67
71
|
if (componentProp)
|
|
68
72
|
page.component = stringLiteral(resolve(componentProp.initializer, consts, new Set()));
|
|
69
73
|
if (groupProp)
|
|
70
74
|
page.group = stringLiteral(resolve(groupProp.initializer, consts, new Set()));
|
|
71
75
|
if (specimenSizeProp)
|
|
72
76
|
page.specimenSize = stringLiteral(resolve(specimenSizeProp.initializer, consts, new Set()));
|
|
77
|
+
page.hasExplicitTokenGallery = tokenGalleryProp !== undefined;
|
|
73
78
|
if (tokenGalleryProp)
|
|
74
79
|
page.tokenGallery = resolve(tokenGalleryProp.initializer, consts, new Set()).kind === ts.SyntaxKind.TrueKeyword;
|
|
75
80
|
const tokenSectionsProp = findProp(pageObject, 'tokenSections');
|
|
81
|
+
page.hasExplicitTokenSections = tokenSectionsProp !== undefined;
|
|
76
82
|
if (tokenSectionsProp) {
|
|
77
83
|
const list = resolve(tokenSectionsProp.initializer, consts, new Set());
|
|
78
84
|
const items = ts.isArrayLiteralExpression(list) ? list.elements.map((element) => resolve(element, consts, new Set())) : [list];
|
|
@@ -201,6 +207,7 @@ function readStaticPage(file) {
|
|
|
201
207
|
}
|
|
202
208
|
}
|
|
203
209
|
const membersProp = findProp(pageObject, 'members');
|
|
210
|
+
page.hasMembers = membersProp !== undefined;
|
|
204
211
|
if (membersProp) {
|
|
205
212
|
const members = readFamilyMembers(membersProp.initializer, consts, gridNames, sourceFile);
|
|
206
213
|
if (!members)
|
|
@@ -209,6 +216,7 @@ function readStaticPage(file) {
|
|
|
209
216
|
page.members = members;
|
|
210
217
|
}
|
|
211
218
|
const wholeProp = findProp(pageObject, 'whole');
|
|
219
|
+
page.hasWhole = wholeProp !== undefined;
|
|
212
220
|
if (wholeProp) {
|
|
213
221
|
const whole = stringLiteral(resolve(wholeProp.initializer, consts, new Set()));
|
|
214
222
|
if (whole === undefined)
|
|
@@ -217,6 +225,7 @@ function readStaticPage(file) {
|
|
|
217
225
|
page.whole = whole;
|
|
218
226
|
}
|
|
219
227
|
const sectionsProp = findProp(pageObject, 'sections');
|
|
228
|
+
page.hasSections = sectionsProp !== undefined;
|
|
220
229
|
if (sectionsProp) {
|
|
221
230
|
const sectionsRead = readSections(sectionsProp.initializer, consts);
|
|
222
231
|
if (!sectionsRead)
|
|
@@ -235,11 +244,46 @@ function readStaticPage(file) {
|
|
|
235
244
|
const relatedPagesProp = findProp(pageObject, 'relatedPages');
|
|
236
245
|
if (relatedPagesProp)
|
|
237
246
|
page.relatedPages = readRelatedPages(relatedPagesProp.initializer, consts);
|
|
247
|
+
page.hasAnatomy = findProp(pageObject, 'anatomy') !== undefined;
|
|
248
|
+
const navigationProp = findProp(pageObject, 'navigation');
|
|
249
|
+
if (navigationProp) {
|
|
250
|
+
const resolved = resolve(navigationProp.initializer, consts, new Set());
|
|
251
|
+
if (ts.isStringLiteral(resolved) && (resolved.text === 'browse' || resolved.text === 'reference')) {
|
|
252
|
+
page.navigation = resolved.text;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
page.checkable = false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const standaloneProp = findProp(pageObject, 'standalone');
|
|
259
|
+
if (standaloneProp)
|
|
260
|
+
page.standalone = stringLiteral(resolve(standaloneProp.initializer, consts, new Set()));
|
|
261
|
+
const tokenDefinitionProp = findProp(pageObject, 'tokenDefinition');
|
|
262
|
+
page.hasTokenDefinition = tokenDefinitionProp !== undefined;
|
|
263
|
+
if (tokenDefinitionProp) {
|
|
264
|
+
const tokenDefinition = readStaticTokenDefinition(tokenDefinitionProp.initializer, consts);
|
|
265
|
+
if (!tokenDefinition)
|
|
266
|
+
page.checkable = false;
|
|
267
|
+
else {
|
|
268
|
+
page.tokenDefinition = tokenDefinition;
|
|
269
|
+
// #61 geometry parity (design R4): the same titles/wide flags `buildCatalogSections` derives
|
|
270
|
+
// at runtime from `tokenDefinition.sections` — only when no separate `tokenSections` already
|
|
271
|
+
// set them (an explicit conflict is reported on its own terms by `cli/doctor.ts`; whichever
|
|
272
|
+
// geometry already won here doesn't change that).
|
|
273
|
+
if (page.tokenSectionTitles === undefined) {
|
|
274
|
+
page.tokenSectionTitles = tokenDefinition.sections.map((section) => section.title);
|
|
275
|
+
page.tokenSectionWide = tokenDefinition.sections.map((section) => section.wide ?? false);
|
|
276
|
+
page.tokenGallery = true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
238
280
|
return page;
|
|
239
281
|
}
|
|
240
282
|
/** A literal array of string literals (or same-file `const` references to one) — undefined for
|
|
241
|
-
* anything else, including an array containing even one non-literal element.
|
|
242
|
-
|
|
283
|
+
* anything else, including an array containing even one non-literal element. Exported for
|
|
284
|
+
* `cli/tokenDefinition.ts`'s own static read of a tokenDefinition section's `aliases` (#64 B5),
|
|
285
|
+
* the same leniency every other string-literal-array field here already gets. */
|
|
286
|
+
export function readStringLiteralArray(node, consts) {
|
|
243
287
|
const resolved = resolve(node, consts, new Set());
|
|
244
288
|
if (!ts.isArrayLiteralExpression(resolved))
|
|
245
289
|
return undefined;
|
|
@@ -295,15 +339,19 @@ function readFamilyMembers(node, consts, gridNames, sourceFile) {
|
|
|
295
339
|
if (!comparison)
|
|
296
340
|
return undefined;
|
|
297
341
|
}
|
|
342
|
+
const anatomyProp = findProp(resolvedElement, 'anatomy');
|
|
343
|
+
const memberIds = anatomyProp ? readAnatomyMemberIds(anatomyProp.initializer, consts) : undefined;
|
|
298
344
|
members.push({
|
|
299
345
|
id,
|
|
300
346
|
component,
|
|
301
347
|
parent,
|
|
302
348
|
aliases,
|
|
303
349
|
composedOf,
|
|
350
|
+
hasComposedOf: composedOfProp !== undefined,
|
|
304
351
|
hasWhenToUse: findProp(resolvedElement, 'whenToUse') !== undefined,
|
|
305
352
|
hasA11y: findProp(resolvedElement, 'a11y') !== undefined,
|
|
306
353
|
hasExamples: Boolean(variants.items?.length) || Boolean(states.items?.length) || Boolean(comparison),
|
|
354
|
+
anatomy: memberIds ? { memberIds } : undefined,
|
|
307
355
|
});
|
|
308
356
|
}
|
|
309
357
|
return members;
|
|
@@ -339,9 +387,9 @@ function readSections(node, consts) {
|
|
|
339
387
|
}
|
|
340
388
|
return sections;
|
|
341
389
|
}
|
|
342
|
-
/** A literal array of `ComposedOfEntry`-shaped object literals, read down to
|
|
343
|
-
* (
|
|
344
|
-
*
|
|
390
|
+
/** A literal array of `ComposedOfEntry`-shaped object literals, read down to `component` and a
|
|
391
|
+
* literal `relationship` (a non-literal one is left off the entry; `role` is authored prose
|
|
392
|
+
* `doctor` never needs). Undefined — not an empty array — for anything that isn't itself a
|
|
345
393
|
* literal array, or whose element isn't an object literal with a literal `component`: `doctor`
|
|
346
394
|
* must never mistake "couldn't read this" for "author declared no composition here". */
|
|
347
395
|
function readComposedOf(node, consts) {
|
|
@@ -356,10 +404,51 @@ function readComposedOf(node, consts) {
|
|
|
356
404
|
const component = propertyStringLiteral(resolvedElement, 'component', consts);
|
|
357
405
|
if (component === undefined)
|
|
358
406
|
return undefined;
|
|
359
|
-
|
|
407
|
+
const relationship = propertyStringLiteral(resolvedElement, 'relationship', consts);
|
|
408
|
+
entries.push(relationship === undefined ? { component } : { component, relationship });
|
|
360
409
|
}
|
|
361
410
|
return entries;
|
|
362
411
|
}
|
|
412
|
+
/** #63 A4/A10: `CatalogMember.anatomy`/`CatalogPageInput.anatomy`'s own `groups[].items[]`, read
|
|
413
|
+
* down to just the literal `member` ids a `{ member: string }` item names — an `{ other: string }`
|
|
414
|
+
* item is skipped (it never names a descendant), and `description`/`picture` are never read (design
|
|
415
|
+
* parity with `readComposedOf`/`readRelatedPages`: `doctor`'s own rule never needs them). Undefined
|
|
416
|
+
* — never a partial read — for anything that isn't itself a literal `{ groups: [...] }` object with
|
|
417
|
+
* a literal `groups` array of literal `{ items: [...] }` objects whose own `items` array is itself
|
|
418
|
+
* literal; an `other`/`description`-shaped item still reads fine even though its own `member` (if
|
|
419
|
+
* any) is simply absent from the result. */
|
|
420
|
+
function readAnatomyMemberIds(node, consts) {
|
|
421
|
+
const resolved = resolve(node, consts, new Set());
|
|
422
|
+
if (!ts.isObjectLiteralExpression(resolved))
|
|
423
|
+
return undefined;
|
|
424
|
+
const groupsProp = findProp(resolved, 'groups');
|
|
425
|
+
if (!groupsProp)
|
|
426
|
+
return undefined;
|
|
427
|
+
const groupsResolved = resolve(groupsProp.initializer, consts, new Set());
|
|
428
|
+
if (!ts.isArrayLiteralExpression(groupsResolved))
|
|
429
|
+
return undefined;
|
|
430
|
+
const memberIds = [];
|
|
431
|
+
for (const groupElement of groupsResolved.elements) {
|
|
432
|
+
const group = resolve(groupElement, consts, new Set());
|
|
433
|
+
if (!ts.isObjectLiteralExpression(group))
|
|
434
|
+
return undefined;
|
|
435
|
+
const itemsProp = findProp(group, 'items');
|
|
436
|
+
if (!itemsProp)
|
|
437
|
+
return undefined;
|
|
438
|
+
const itemsResolved = resolve(itemsProp.initializer, consts, new Set());
|
|
439
|
+
if (!ts.isArrayLiteralExpression(itemsResolved))
|
|
440
|
+
return undefined;
|
|
441
|
+
for (const itemElement of itemsResolved.elements) {
|
|
442
|
+
const item = resolve(itemElement, consts, new Set());
|
|
443
|
+
if (!ts.isObjectLiteralExpression(item))
|
|
444
|
+
return undefined;
|
|
445
|
+
const member = propertyStringLiteral(item, 'member', consts);
|
|
446
|
+
if (member !== undefined)
|
|
447
|
+
memberIds.push(member);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return memberIds;
|
|
451
|
+
}
|
|
363
452
|
/** A literal array of `RelatedPage`-shaped object literals, read down to just `page` (design
|
|
364
453
|
* parity with `readComposedOf`: `note` is authored prose `doctor` never needs). Undefined — not
|
|
365
454
|
* an empty array — for anything that isn't itself a literal array, or whose element isn't an
|
|
@@ -433,7 +522,7 @@ function topLevelConsts(sourceFile) {
|
|
|
433
522
|
* reference to its own initializer (same pattern as `cli/tokens.ts`'s `readExpression`) — returns
|
|
434
523
|
* the resolved expression node itself, not a value, so callers can pattern-match on its kind
|
|
435
524
|
* (array/object literal, call expression, …). `seen` guards a reference cycle. */
|
|
436
|
-
function resolve(node, consts, seen) {
|
|
525
|
+
export function resolve(node, consts, seen) {
|
|
437
526
|
if (ts.isAsExpression(node) || ts.isParenthesizedExpression(node) || ts.isSatisfiesExpression(node)) {
|
|
438
527
|
return resolve(node.expression, consts, seen);
|
|
439
528
|
}
|
|
@@ -444,7 +533,7 @@ function resolve(node, consts, seen) {
|
|
|
444
533
|
}
|
|
445
534
|
return node;
|
|
446
535
|
}
|
|
447
|
-
function stringLiteral(node) {
|
|
536
|
+
export function stringLiteral(node) {
|
|
448
537
|
return ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) ? node.text : undefined;
|
|
449
538
|
}
|
|
450
539
|
/** `true`/`false` only from a known `true`/`false` literal; any other expression (a variable, a
|
|
@@ -464,11 +553,11 @@ function literalBooleanOrUnknown(node, consts) {
|
|
|
464
553
|
function propertyName(name) {
|
|
465
554
|
return ts.isIdentifier(name) || ts.isStringLiteral(name) ? name.text : name.getText();
|
|
466
555
|
}
|
|
467
|
-
function findProp(obj, name) {
|
|
556
|
+
export function findProp(obj, name) {
|
|
468
557
|
const prop = obj.properties.find((p) => ts.isPropertyAssignment(p) && propertyName(p.name) === name);
|
|
469
558
|
return prop;
|
|
470
559
|
}
|
|
471
|
-
function propertyStringLiteral(obj, name, consts) {
|
|
560
|
+
export function propertyStringLiteral(obj, name, consts) {
|
|
472
561
|
const prop = findProp(obj, name);
|
|
473
562
|
return prop ? stringLiteral(resolve(prop.initializer, consts, new Set())) : undefined;
|
|
474
563
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static, literal-only reader for `CatalogPageInput.tokenDefinition` — the same posture
|
|
3
|
+
* `cli/staticPage.ts`'s own `readStaticPage` already uses for every other field: no module
|
|
4
|
+
* resolution, no execution, a same-file `const` chase only (`resolve`, reused from
|
|
5
|
+
* `cli/staticPage.ts`). Reads structural metadata only (`kind`, section `id`/`title`/`desc`/`wide`,
|
|
6
|
+
* and each entry's own literal `id`, for duplicate detection) — never an entry's real value, which
|
|
7
|
+
* stays an opaque runtime import (design R4).
|
|
8
|
+
*/
|
|
9
|
+
import * as ts from 'typescript';
|
|
10
|
+
import { findProp, propertyStringLiteral, readStringLiteralArray, resolve } from "./staticPage.js";
|
|
11
|
+
import { canonicalTokenTitle, isTokenKind } from "../native/catalog/tokenDefinition.js";
|
|
12
|
+
/** Reads `node` (a `tokenDefinition:` property initializer) down to `kind` and each section's
|
|
13
|
+
* `id`/`title`/`desc`/`wide`/entry-ids. Returns `undefined` — never a partial read — when `kind`
|
|
14
|
+
* isn't a literal, `sections` isn't a literal array, or any one section isn't a literal object
|
|
15
|
+
* with a literal `id` and `title`: the same "never a partial read" contract every other array
|
|
16
|
+
* reader in `cli/staticPage.ts` already follows (`readComposedOf`, `readSections`, …). An entry
|
|
17
|
+
* whose own `id` isn't literal is simply omitted from `entryIds` — informational leniency, since
|
|
18
|
+
* only the *structural* shape (kind/sections/ids) needs to be checkable, never an entry's real
|
|
19
|
+
* value (design R4: "no blanket uncheckability of otherwise literal page geometry"). */
|
|
20
|
+
export function readStaticTokenDefinition(node, consts) {
|
|
21
|
+
const resolved = resolve(node, consts, new Set());
|
|
22
|
+
if (!ts.isObjectLiteralExpression(resolved))
|
|
23
|
+
return undefined;
|
|
24
|
+
const kind = propertyStringLiteral(resolved, 'kind', consts);
|
|
25
|
+
if (kind === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
const sectionsProp = findProp(resolved, 'sections');
|
|
28
|
+
if (!sectionsProp)
|
|
29
|
+
return undefined;
|
|
30
|
+
const sectionsResolved = resolve(sectionsProp.initializer, consts, new Set());
|
|
31
|
+
if (!ts.isArrayLiteralExpression(sectionsResolved))
|
|
32
|
+
return undefined;
|
|
33
|
+
const sections = [];
|
|
34
|
+
for (const element of sectionsResolved.elements) {
|
|
35
|
+
const sectionResolved = resolve(element, consts, new Set());
|
|
36
|
+
if (!ts.isObjectLiteralExpression(sectionResolved))
|
|
37
|
+
return undefined;
|
|
38
|
+
const id = propertyStringLiteral(sectionResolved, 'id', consts);
|
|
39
|
+
const title = propertyStringLiteral(sectionResolved, 'title', consts);
|
|
40
|
+
if (id === undefined || title === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
const desc = propertyStringLiteral(sectionResolved, 'desc', consts);
|
|
43
|
+
const wideProp = findProp(sectionResolved, 'wide');
|
|
44
|
+
const wide = wideProp ? resolve(wideProp.initializer, consts, new Set()).kind === ts.SyntaxKind.TrueKeyword : false;
|
|
45
|
+
const entryIds = [];
|
|
46
|
+
let entriesOpaque = false;
|
|
47
|
+
const entriesProp = findProp(sectionResolved, 'entries');
|
|
48
|
+
if (entriesProp) {
|
|
49
|
+
const entriesResolved = resolve(entriesProp.initializer, consts, new Set());
|
|
50
|
+
if (ts.isArrayLiteralExpression(entriesResolved)) {
|
|
51
|
+
for (const entryElement of entriesResolved.elements) {
|
|
52
|
+
const entryResolved = resolve(entryElement, consts, new Set());
|
|
53
|
+
if (!ts.isObjectLiteralExpression(entryResolved)) {
|
|
54
|
+
entriesOpaque = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const entryId = propertyStringLiteral(entryResolved, 'id', consts);
|
|
58
|
+
if (entryId !== undefined)
|
|
59
|
+
entryIds.push(entryId);
|
|
60
|
+
else
|
|
61
|
+
entriesOpaque = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
entriesOpaque = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// #64 B1: this section's own sub-groups, read the same lenient way as its own entries above.
|
|
69
|
+
const groups = [];
|
|
70
|
+
let groupsOpaque = false;
|
|
71
|
+
const groupsProp = findProp(sectionResolved, 'groups');
|
|
72
|
+
if (groupsProp) {
|
|
73
|
+
const groupsResolved = resolve(groupsProp.initializer, consts, new Set());
|
|
74
|
+
if (ts.isArrayLiteralExpression(groupsResolved)) {
|
|
75
|
+
for (const groupElement of groupsResolved.elements) {
|
|
76
|
+
const groupResolved = resolve(groupElement, consts, new Set());
|
|
77
|
+
if (!ts.isObjectLiteralExpression(groupResolved)) {
|
|
78
|
+
groupsOpaque = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const groupId = propertyStringLiteral(groupResolved, 'id', consts);
|
|
82
|
+
const groupTitle = propertyStringLiteral(groupResolved, 'title', consts);
|
|
83
|
+
if (groupId === undefined || groupTitle === undefined) {
|
|
84
|
+
groupsOpaque = true;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const groupEntryIds = [];
|
|
88
|
+
let groupEntriesOpaque = false;
|
|
89
|
+
const groupEntriesProp = findProp(groupResolved, 'entries');
|
|
90
|
+
if (groupEntriesProp) {
|
|
91
|
+
const groupEntriesResolved = resolve(groupEntriesProp.initializer, consts, new Set());
|
|
92
|
+
if (ts.isArrayLiteralExpression(groupEntriesResolved)) {
|
|
93
|
+
for (const entryElement of groupEntriesResolved.elements) {
|
|
94
|
+
const entryResolved = resolve(entryElement, consts, new Set());
|
|
95
|
+
if (!ts.isObjectLiteralExpression(entryResolved)) {
|
|
96
|
+
groupEntriesOpaque = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const entryId = propertyStringLiteral(entryResolved, 'id', consts);
|
|
100
|
+
if (entryId !== undefined)
|
|
101
|
+
groupEntryIds.push(entryId);
|
|
102
|
+
else
|
|
103
|
+
groupEntriesOpaque = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
groupEntriesOpaque = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
groups.push({ id: groupId, title: groupTitle, entryIds: groupEntryIds, entriesOpaque: groupEntriesOpaque });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
groupsOpaque = true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// #64 B5: this section's own literal `aliases`, when every element resolves as a literal
|
|
118
|
+
// string — omitted (never a penalty) otherwise.
|
|
119
|
+
const aliasesProp = findProp(sectionResolved, 'aliases');
|
|
120
|
+
const aliases = aliasesProp ? readStringLiteralArray(aliasesProp.initializer, consts) : undefined;
|
|
121
|
+
sections.push({ id, title, desc, wide, entryIds, entriesOpaque, groups, groupsOpaque, ...(aliases !== undefined ? { aliases } : null) });
|
|
122
|
+
}
|
|
123
|
+
return { kind, sections };
|
|
124
|
+
}
|
|
125
|
+
export { canonicalTokenTitle, isTokenKind };
|
|
@@ -132,23 +132,37 @@ export function resolveActiveFromHash(hash, order) {
|
|
|
132
132
|
return idFromHash(hash, order);
|
|
133
133
|
}
|
|
134
134
|
/** Every member id other than the whole, every member alias, and every family page's own
|
|
135
|
-
* (page-level) alias, mapped to where it resolves (spec §5.3 "Old addresses").
|
|
136
|
-
* `
|
|
135
|
+
* (page-level) alias, mapped to where it resolves (spec §5.3 "Old addresses"). #64 B5: also, a
|
|
136
|
+
* tokenDefinition page's own page-level aliases (`tokenDefinitionAliases`) and each of its
|
|
137
|
+
* sections' own `aliases` — the same routing a family page's aliases already get, extended to
|
|
138
|
+
* tokenDefinition pages. A page with neither contributes nothing. */
|
|
137
139
|
export function aliasTargets(sections) {
|
|
138
140
|
const targets = new Map();
|
|
139
141
|
for (const section of sections) {
|
|
140
142
|
const family = section.family;
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
if (family) {
|
|
144
|
+
for (const member of family.members) {
|
|
145
|
+
if (member.id === family.whole) {
|
|
146
|
+
// C3: the whole's own id already resolves via `order` (it equals the page's own id), but
|
|
147
|
+
// its own aliases were never registered anywhere — routing one to the page, with no member
|
|
148
|
+
// focus (unlike a non-whole member's alias, which focuses that member).
|
|
149
|
+
for (const alias of member.aliases ?? [])
|
|
150
|
+
targets.set(alias, { pageId: section.id });
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
targets.set(member.id, { pageId: section.id, memberId: member.id });
|
|
154
|
+
for (const alias of member.aliases ?? [])
|
|
155
|
+
targets.set(alias, { pageId: section.id, memberId: member.id });
|
|
156
|
+
}
|
|
157
|
+
for (const alias of family.aliases ?? [])
|
|
158
|
+
targets.set(alias, { pageId: section.id });
|
|
149
159
|
}
|
|
150
|
-
for (const alias of
|
|
160
|
+
for (const alias of section.tokenDefinitionAliases ?? [])
|
|
151
161
|
targets.set(alias, { pageId: section.id });
|
|
162
|
+
for (const tdSection of section.tokenDefinition?.sections ?? []) {
|
|
163
|
+
for (const alias of tdSection.aliases ?? [])
|
|
164
|
+
targets.set(alias, { pageId: section.id, sectionId: tdSection.id });
|
|
165
|
+
}
|
|
152
166
|
}
|
|
153
167
|
return targets;
|
|
154
168
|
}
|
|
@@ -171,6 +185,8 @@ export function resolveRoute(hash, order, aliases) {
|
|
|
171
185
|
if (target) {
|
|
172
186
|
if (target.memberId !== undefined)
|
|
173
187
|
return { kind: 'member', pageId: target.pageId, memberId: target.memberId };
|
|
188
|
+
if (target.sectionId !== undefined)
|
|
189
|
+
return { kind: 'page', id: target.pageId, sectionId: target.sectionId };
|
|
174
190
|
return { kind: 'page', id: target.pageId };
|
|
175
191
|
}
|
|
176
192
|
return { kind: 'notFound', raw: decoded };
|