@honeydeck/honeydeck 0.3.0 → 0.4.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/package.json
CHANGED
package/src/layouts/SPEC.md
CHANGED
|
@@ -180,7 +180,7 @@ Building the future of presentations.`,
|
|
|
180
180
|
}
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
`mdx` is required on `LayoutDemo` and is the single source for both the live visual preview and the copyable snippet shown in the layouts docs tab. Honeydeck compiles this MDX with the same slide MDX compiler family, extracts frontmatter/title/steps from it, and renders the resulting slide through the active layout map. Honeydeck statically crawls analyzable active layout maps at build time and discovers colocated `demo` exports from layout modules. Dynamic maps, computed entries, non-static imports, and demos whose `mdx` value is not a static string may be skipped with warnings. If no static MDX demo is discovered, the layout still appears in the reference pages with a "No demo MDX provided" hint.
|
|
183
|
+
`mdx` is required on `LayoutDemo` and is the single source for both the live visual preview and the copyable snippet shown in the layouts docs tab. Honeydeck compiles this MDX with the same slide MDX compiler family, extracts frontmatter/title/steps from it, and renders the resulting slide through the active layout map. Honeydeck statically crawls analyzable active layout maps at build time and discovers colocated `demo` exports from layout modules. Analyzable map entries include direct static imports, named imports from layout barrels, spread static default imports, and static member references into an imported layout map such as `Default: defaultLayouts.Default`. Dynamic maps, computed entries, non-static imports, and demos whose `mdx` value is not a static string may be skipped with warnings. If no static MDX demo is discovered, the layout still appears in the reference pages with a "No demo MDX provided" hint.
|
|
184
184
|
|
|
185
185
|
### TwoCol Slot Components
|
|
186
186
|
|
|
@@ -168,37 +168,18 @@ function crawlLayoutMapFile(
|
|
|
168
168
|
continue;
|
|
169
169
|
|
|
170
170
|
const layoutName = getLayoutName(property);
|
|
171
|
-
|
|
172
|
-
if (!layoutName || !localName) continue;
|
|
171
|
+
if (!layoutName) continue;
|
|
173
172
|
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const modulePath = resolveImportedModule(
|
|
183
|
-
mapPath,
|
|
184
|
-
binding.moduleSpecifier,
|
|
185
|
-
context.packageRoot,
|
|
186
|
-
);
|
|
187
|
-
if (!modulePath) {
|
|
188
|
-
context.warnings.push(
|
|
189
|
-
`Could not resolve layout module "${binding.moduleSpecifier}" for layout "${layoutName}".`,
|
|
190
|
-
);
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
context.watchedFiles.add(modulePath);
|
|
195
|
-
const publicModuleSpecifier = toPublicSpecifier({
|
|
196
|
-
entryPath: context.entryPath,
|
|
197
|
-
packageRoot: context.packageRoot,
|
|
173
|
+
const reference = resolveLayoutModuleReference({
|
|
174
|
+
property,
|
|
175
|
+
layoutName,
|
|
176
|
+
bindings,
|
|
198
177
|
mapPath,
|
|
199
|
-
|
|
200
|
-
originalSpecifier: binding.moduleSpecifier,
|
|
178
|
+
context,
|
|
201
179
|
});
|
|
180
|
+
if (!reference) continue;
|
|
181
|
+
|
|
182
|
+
const { modulePath, publicModuleSpecifier } = reference;
|
|
202
183
|
|
|
203
184
|
let demoMetadata: StaticDemoMetadata | undefined;
|
|
204
185
|
try {
|
|
@@ -372,13 +353,303 @@ function getLayoutName(
|
|
|
372
353
|
return null;
|
|
373
354
|
}
|
|
374
355
|
|
|
375
|
-
|
|
376
|
-
|
|
356
|
+
type LayoutModuleReference = Pick<
|
|
357
|
+
DiscoveredLayoutDemo,
|
|
358
|
+
"modulePath" | "publicModuleSpecifier"
|
|
359
|
+
>;
|
|
360
|
+
|
|
361
|
+
function resolveLayoutModuleReference({
|
|
362
|
+
property,
|
|
363
|
+
layoutName,
|
|
364
|
+
bindings,
|
|
365
|
+
mapPath,
|
|
366
|
+
context,
|
|
367
|
+
}: {
|
|
368
|
+
property: ts.PropertyAssignment | ts.ShorthandPropertyAssignment;
|
|
369
|
+
layoutName: string;
|
|
370
|
+
bindings: Map<string, ImportBinding>;
|
|
371
|
+
mapPath: string;
|
|
372
|
+
context: CrawlContext;
|
|
373
|
+
}): LayoutModuleReference | null {
|
|
374
|
+
const value = ts.isShorthandPropertyAssignment(property)
|
|
375
|
+
? property.name
|
|
376
|
+
: unwrapExpression(property.initializer);
|
|
377
|
+
|
|
378
|
+
if (ts.isIdentifier(value)) {
|
|
379
|
+
return resolveImportedLayoutReference(
|
|
380
|
+
value.text,
|
|
381
|
+
layoutName,
|
|
382
|
+
bindings,
|
|
383
|
+
mapPath,
|
|
384
|
+
context,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (ts.isPropertyAccessExpression(value)) {
|
|
389
|
+
return resolveLayoutMapMemberReference(
|
|
390
|
+
value,
|
|
391
|
+
layoutName,
|
|
392
|
+
bindings,
|
|
393
|
+
mapPath,
|
|
394
|
+
context,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
context.warnings.push(
|
|
399
|
+
`Layout "${layoutName}" is not backed by a static import; demo auto-discovery skipped.`,
|
|
400
|
+
);
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function resolveImportedLayoutReference(
|
|
405
|
+
localName: string,
|
|
406
|
+
layoutName: string,
|
|
407
|
+
bindings: Map<string, ImportBinding>,
|
|
408
|
+
mapPath: string,
|
|
409
|
+
context: CrawlContext,
|
|
410
|
+
): LayoutModuleReference | null {
|
|
411
|
+
const binding = bindings.get(localName);
|
|
412
|
+
if (!binding) {
|
|
413
|
+
context.warnings.push(
|
|
414
|
+
`Layout "${layoutName}" is not backed by a static import; demo auto-discovery skipped.`,
|
|
415
|
+
);
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const importedModulePath = resolveImportedModule(
|
|
420
|
+
mapPath,
|
|
421
|
+
binding.moduleSpecifier,
|
|
422
|
+
context.packageRoot,
|
|
423
|
+
);
|
|
424
|
+
if (!importedModulePath) {
|
|
425
|
+
context.warnings.push(
|
|
426
|
+
`Could not resolve layout module "${binding.moduleSpecifier}" for layout "${layoutName}".`,
|
|
427
|
+
);
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
context.watchedFiles.add(importedModulePath);
|
|
432
|
+
const modulePath =
|
|
433
|
+
binding.importedName === "default"
|
|
434
|
+
? importedModulePath
|
|
435
|
+
: (resolveNamedExportModulePath(
|
|
436
|
+
importedModulePath,
|
|
437
|
+
binding.importedName,
|
|
438
|
+
context.packageRoot,
|
|
439
|
+
context.watchedFiles,
|
|
440
|
+
) ?? importedModulePath);
|
|
441
|
+
context.watchedFiles.add(modulePath);
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
modulePath,
|
|
445
|
+
publicModuleSpecifier: toPublicSpecifier({
|
|
446
|
+
entryPath: context.entryPath,
|
|
447
|
+
packageRoot: context.packageRoot,
|
|
448
|
+
mapPath,
|
|
449
|
+
modulePath,
|
|
450
|
+
originalSpecifier: binding.moduleSpecifier,
|
|
451
|
+
}),
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function resolveLayoutMapMemberReference(
|
|
456
|
+
memberExpression: ts.PropertyAccessExpression,
|
|
457
|
+
layoutName: string,
|
|
458
|
+
bindings: Map<string, ImportBinding>,
|
|
459
|
+
mapPath: string,
|
|
460
|
+
context: CrawlContext,
|
|
461
|
+
): LayoutModuleReference | null {
|
|
462
|
+
const mapIdentifier = unwrapExpression(memberExpression.expression);
|
|
463
|
+
if (
|
|
464
|
+
!ts.isIdentifier(mapIdentifier) ||
|
|
465
|
+
!ts.isIdentifier(memberExpression.name)
|
|
466
|
+
) {
|
|
467
|
+
context.warnings.push(
|
|
468
|
+
`Layout "${layoutName}" is not backed by a static import; demo auto-discovery skipped.`,
|
|
469
|
+
);
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const binding = bindings.get(mapIdentifier.text);
|
|
474
|
+
if (!binding) {
|
|
475
|
+
context.warnings.push(
|
|
476
|
+
`Layout "${layoutName}" references layout map "${mapIdentifier.text}" without a static import; demo auto-discovery skipped.`,
|
|
477
|
+
);
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (binding.importedName !== "default") {
|
|
482
|
+
context.warnings.push(
|
|
483
|
+
`Layout "${layoutName}" references layout map "${mapIdentifier.text}", but only default-imported layout maps can be inspected.`,
|
|
484
|
+
);
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const memberMapPath = resolveImportedModule(
|
|
489
|
+
mapPath,
|
|
490
|
+
binding.moduleSpecifier,
|
|
491
|
+
context.packageRoot,
|
|
492
|
+
);
|
|
493
|
+
if (!memberMapPath) {
|
|
494
|
+
context.warnings.push(
|
|
495
|
+
`Could not resolve layout map "${binding.moduleSpecifier}" for layout "${layoutName}".`,
|
|
496
|
+
);
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const memberName = memberExpression.name.text;
|
|
501
|
+
const lookupContext = {
|
|
502
|
+
...context,
|
|
503
|
+
visitedMaps: new Set<string>(),
|
|
504
|
+
};
|
|
505
|
+
const reference = crawlLayoutMapFile(memberMapPath, lookupContext).find(
|
|
506
|
+
(demo) => demo.layoutName === memberName,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
if (!reference) {
|
|
510
|
+
context.warnings.push(
|
|
511
|
+
`Could not statically find layout "${memberName}" in layout map "${binding.moduleSpecifier}" for layout "${layoutName}".`,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return reference ?? null;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function resolveNamedExportModulePath(
|
|
519
|
+
modulePath: string,
|
|
520
|
+
exportedName: string,
|
|
521
|
+
packageRoot: string,
|
|
522
|
+
watchedFiles?: Set<string>,
|
|
523
|
+
visited = new Set<string>(),
|
|
377
524
|
): string | null {
|
|
378
|
-
|
|
525
|
+
const visitKey = `${modulePath}#${exportedName}`;
|
|
526
|
+
if (visited.has(visitKey)) return null;
|
|
527
|
+
visited.add(visitKey);
|
|
528
|
+
watchedFiles?.add(modulePath);
|
|
529
|
+
|
|
530
|
+
let sourceFile: ts.SourceFile;
|
|
531
|
+
try {
|
|
532
|
+
sourceFile = parseFile(modulePath);
|
|
533
|
+
} catch {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
for (const statement of sourceFile.statements) {
|
|
538
|
+
if (!ts.isExportDeclaration(statement)) continue;
|
|
539
|
+
|
|
540
|
+
if (!statement.exportClause) {
|
|
541
|
+
if (
|
|
542
|
+
!statement.moduleSpecifier ||
|
|
543
|
+
!ts.isStringLiteral(statement.moduleSpecifier)
|
|
544
|
+
)
|
|
545
|
+
continue;
|
|
546
|
+
|
|
547
|
+
const starModulePath = resolveImportedModule(
|
|
548
|
+
modulePath,
|
|
549
|
+
statement.moduleSpecifier.text,
|
|
550
|
+
packageRoot,
|
|
551
|
+
);
|
|
552
|
+
if (!starModulePath) continue;
|
|
553
|
+
watchedFiles?.add(starModulePath);
|
|
554
|
+
const resolved = resolveNamedExportModulePath(
|
|
555
|
+
starModulePath,
|
|
556
|
+
exportedName,
|
|
557
|
+
packageRoot,
|
|
558
|
+
watchedFiles,
|
|
559
|
+
visited,
|
|
560
|
+
);
|
|
561
|
+
if (resolved) return resolved;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (!ts.isNamedExports(statement.exportClause)) continue;
|
|
566
|
+
|
|
567
|
+
for (const element of statement.exportClause.elements) {
|
|
568
|
+
if (element.name.text !== exportedName) continue;
|
|
569
|
+
|
|
570
|
+
if (
|
|
571
|
+
statement.moduleSpecifier &&
|
|
572
|
+
ts.isStringLiteral(statement.moduleSpecifier)
|
|
573
|
+
) {
|
|
574
|
+
const importedModulePath = resolveImportedModule(
|
|
575
|
+
modulePath,
|
|
576
|
+
statement.moduleSpecifier.text,
|
|
577
|
+
packageRoot,
|
|
578
|
+
);
|
|
579
|
+
if (!importedModulePath) return null;
|
|
580
|
+
watchedFiles?.add(importedModulePath);
|
|
581
|
+
|
|
582
|
+
const importedName = element.propertyName?.text ?? element.name.text;
|
|
583
|
+
if (importedName === "default") return importedModulePath;
|
|
584
|
+
|
|
585
|
+
return (
|
|
586
|
+
resolveNamedExportModulePath(
|
|
587
|
+
importedModulePath,
|
|
588
|
+
importedName,
|
|
589
|
+
packageRoot,
|
|
590
|
+
watchedFiles,
|
|
591
|
+
visited,
|
|
592
|
+
) ?? importedModulePath
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const localName = element.propertyName?.text ?? element.name.text;
|
|
597
|
+
const binding = collectImportBindings(sourceFile).get(localName);
|
|
598
|
+
if (!binding) return modulePath;
|
|
599
|
+
|
|
600
|
+
const importedModulePath = resolveImportedModule(
|
|
601
|
+
modulePath,
|
|
602
|
+
binding.moduleSpecifier,
|
|
603
|
+
packageRoot,
|
|
604
|
+
);
|
|
605
|
+
if (!importedModulePath) return null;
|
|
606
|
+
if (binding.importedName === "default") return importedModulePath;
|
|
607
|
+
|
|
608
|
+
watchedFiles?.add(importedModulePath);
|
|
609
|
+
return (
|
|
610
|
+
resolveNamedExportModulePath(
|
|
611
|
+
importedModulePath,
|
|
612
|
+
binding.importedName,
|
|
613
|
+
packageRoot,
|
|
614
|
+
watchedFiles,
|
|
615
|
+
visited,
|
|
616
|
+
) ?? importedModulePath
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
379
620
|
|
|
380
|
-
const
|
|
381
|
-
|
|
621
|
+
for (const statement of sourceFile.statements) {
|
|
622
|
+
if (!hasExportModifier(statement)) continue;
|
|
623
|
+
if (
|
|
624
|
+
(ts.isFunctionDeclaration(statement) ||
|
|
625
|
+
ts.isClassDeclaration(statement)) &&
|
|
626
|
+
statement.name?.text === exportedName
|
|
627
|
+
) {
|
|
628
|
+
return modulePath;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (ts.isVariableStatement(statement)) {
|
|
632
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
633
|
+
if (
|
|
634
|
+
ts.isIdentifier(declaration.name) &&
|
|
635
|
+
declaration.name.text === exportedName
|
|
636
|
+
)
|
|
637
|
+
return modulePath;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function hasExportModifier(node: ts.Node): boolean {
|
|
646
|
+
return (
|
|
647
|
+
ts.canHaveModifiers(node) &&
|
|
648
|
+
(ts
|
|
649
|
+
.getModifiers(node)
|
|
650
|
+
?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ??
|
|
651
|
+
false)
|
|
652
|
+
);
|
|
382
653
|
}
|
|
383
654
|
|
|
384
655
|
function resolveImportedModule(
|