@jskit-ai/kernel 0.1.66 → 0.1.67

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.
@@ -70,7 +70,13 @@ function normalizeAppRouteOutletTarget({
70
70
  });
71
71
  }
72
72
 
73
- function discoverRouteMetaOutletTargetsFromVueSource(source = "", { context = "shell layout" } = {}) {
73
+ function discoverRouteMetaOutletTargetsFromVueSource(
74
+ source = "",
75
+ {
76
+ context = "shell layout",
77
+ enforceSingleDefault = true
78
+ } = {}
79
+ ) {
74
80
  const sourceText = String(source || "");
75
81
  const resolvedContext = normalizeText(context) || "shell layout";
76
82
  const targetById = new Map();
@@ -113,12 +119,14 @@ function discoverRouteMetaOutletTargetsFromVueSource(source = "", { context = "s
113
119
  throw new Error(`${resolvedContext} contains duplicate route meta placement target "${normalizedTarget.id}".`);
114
120
  }
115
121
  if (normalizedTarget.default === true) {
116
- if (defaultTargetId && defaultTargetId !== normalizedTarget.id) {
122
+ if (enforceSingleDefault === true && defaultTargetId && defaultTargetId !== normalizedTarget.id) {
117
123
  throw new Error(
118
124
  `${resolvedContext} defines multiple default route meta placement targets: "${defaultTargetId}" and "${normalizedTarget.id}".`
119
125
  );
120
126
  }
121
- defaultTargetId = normalizedTarget.id;
127
+ if (!defaultTargetId) {
128
+ defaultTargetId = normalizedTarget.id;
129
+ }
122
130
  }
123
131
  targetById.set(normalizedTarget.id, normalizedTarget);
124
132
  }
@@ -422,9 +430,14 @@ async function resolveSemanticPlacementTargetFromApp({
422
430
  throw new Error(`${resolvedContext} could not resolve a default semantic placement target.`);
423
431
  }
424
432
 
425
- async function discoverShellOutletTargetsFromApp({ appRoot, sourceRoot = "src" } = {}) {
433
+ async function collectAppSourceShellOutletTargets({
434
+ appRoot,
435
+ sourceRoot = "src",
436
+ enforceSingleDefault = true,
437
+ context = "discoverShellOutletTargetsFromApp"
438
+ } = {}) {
426
439
  const resolvedAppRoot = resolveRequiredAppRoot(appRoot, {
427
- context: "discoverShellOutletTargetsFromApp"
440
+ context
428
441
  });
429
442
 
430
443
  const sourceDirectory = path.resolve(resolvedAppRoot, String(sourceRoot || "src"));
@@ -442,12 +455,14 @@ async function discoverShellOutletTargetsFromApp({ appRoot, sourceRoot = "src" }
442
455
 
443
456
  const discoveredShellOutlets = source.includes("<ShellOutlet")
444
457
  ? discoverShellOutletTargetsFromVueSource(source, {
445
- context: relativePath
458
+ context: relativePath,
459
+ enforceSingleDefault
446
460
  })
447
461
  : { targets: [], defaultTargetId: "" };
448
462
  const discoveredRouteMetaOutlets = source.includes("<route")
449
463
  ? discoverRouteMetaOutletTargetsFromVueSource(source, {
450
- context: relativePath
464
+ context: relativePath,
465
+ enforceSingleDefault
451
466
  })
452
467
  : { targets: [], defaultTargetId: "" };
453
468
  const discoveredTargets = [
@@ -473,18 +488,37 @@ async function discoverShellOutletTargetsFromApp({ appRoot, sourceRoot = "src" }
473
488
  normalizeShellOutletTargetId(discoveredRouteMetaOutlets.defaultTargetId)
474
489
  ].filter(Boolean);
475
490
  for (const discoveredDefaultTargetId of discoveredDefaultTargetIds) {
476
- if (defaultTargetId && discoveredDefaultTargetId !== defaultTargetId) {
491
+ if (enforceSingleDefault === true && defaultTargetId && discoveredDefaultTargetId !== defaultTargetId) {
477
492
  throw new Error(
478
493
  `Multiple default ShellOutlet targets found in app source: "${defaultTargetId}" (${defaultTargetSource}) and ` +
479
494
  `"${discoveredDefaultTargetId}" (${relativePath}).`
480
495
  );
481
496
  }
482
497
 
483
- defaultTargetId = discoveredDefaultTargetId;
484
- defaultTargetSource = relativePath;
498
+ if (!defaultTargetId) {
499
+ defaultTargetId = discoveredDefaultTargetId;
500
+ defaultTargetSource = relativePath;
501
+ }
485
502
  }
486
503
  }
487
504
 
505
+ return Object.freeze({
506
+ resolvedAppRoot,
507
+ targetById,
508
+ defaultTargetId
509
+ });
510
+ }
511
+
512
+ async function discoverShellOutletTargetsFromApp({ appRoot, sourceRoot = "src" } = {}) {
513
+ const discoveredAppTargets = await collectAppSourceShellOutletTargets({
514
+ appRoot,
515
+ sourceRoot,
516
+ enforceSingleDefault: true,
517
+ context: "discoverShellOutletTargetsFromApp"
518
+ });
519
+ const resolvedAppRoot = discoveredAppTargets.resolvedAppRoot;
520
+ const targetById = new Map(discoveredAppTargets.targetById);
521
+
488
522
  const packageTargets = await collectInstalledPackageOutletTargets(resolvedAppRoot);
489
523
  for (const target of packageTargets) {
490
524
  if (!targetById.has(target.id)) {
@@ -496,13 +530,34 @@ async function discoverShellOutletTargetsFromApp({ appRoot, sourceRoot = "src" }
496
530
  const normalizedTargets = targets.map((target) =>
497
531
  Object.freeze({
498
532
  ...target,
499
- default: target.id === defaultTargetId
533
+ default: target.id === discoveredAppTargets.defaultTargetId
500
534
  })
501
535
  );
502
536
 
503
537
  return Object.freeze({
504
538
  targets: Object.freeze(normalizedTargets),
505
- defaultTargetId
539
+ defaultTargetId: discoveredAppTargets.defaultTargetId
540
+ });
541
+ }
542
+
543
+ async function discoverShellOutletSourcePathsFromApp({ appRoot, sourceRoot = "src" } = {}) {
544
+ const discoveredAppTargets = await collectAppSourceShellOutletTargets({
545
+ appRoot,
546
+ sourceRoot,
547
+ enforceSingleDefault: false,
548
+ context: "discoverShellOutletSourcePathsFromApp"
549
+ });
550
+ const targetById = new Map(discoveredAppTargets.targetById);
551
+
552
+ const packageTargets = await collectInstalledPackageOutletTargets(discoveredAppTargets.resolvedAppRoot);
553
+ for (const target of packageTargets) {
554
+ if (!targetById.has(target.id)) {
555
+ targetById.set(target.id, target);
556
+ }
557
+ }
558
+
559
+ return Object.freeze({
560
+ targets: Object.freeze([...targetById.values()].sort((left, right) => left.id.localeCompare(right.id)))
506
561
  });
507
562
  }
508
563
 
@@ -550,6 +605,7 @@ async function resolveShellOutletPlacementTargetFromApp({ appRoot, placement = "
550
605
 
551
606
  export {
552
607
  discoverPlacementTopologyFromApp,
608
+ discoverShellOutletSourcePathsFromApp,
553
609
  discoverShellOutletTargetsFromApp,
554
610
  resolveSemanticPlacementTargetFromApp,
555
611
  resolveShellOutletPlacementTargetFromApp
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import test from "node:test";
6
6
  import {
7
+ discoverShellOutletSourcePathsFromApp,
7
8
  discoverShellOutletTargetsFromApp,
8
9
  resolveShellOutletPlacementTargetFromApp
9
10
  } from "./shellOutlets.js";
@@ -296,6 +297,36 @@ test("resolveShellOutletPlacementTargetFromApp throws when multiple default outl
296
297
  });
297
298
  });
298
299
 
300
+ test("discoverShellOutletSourcePathsFromApp ignores default conflicts while keeping source paths", async () => {
301
+ await withTempApp(async (appRoot) => {
302
+ await writeFileInApp(
303
+ appRoot,
304
+ "src/components/ShellLayout.vue",
305
+ `<template>
306
+ <div>
307
+ <ShellOutlet target="shell-layout:primary-menu" default />
308
+ <ShellOutlet target="shell-layout:primary-bottom-nav" default />
309
+ </div>
310
+ </template>
311
+ `
312
+ );
313
+
314
+ const discovered = await discoverShellOutletSourcePathsFromApp({ appRoot });
315
+ assert.deepEqual(discovered.targets, [
316
+ {
317
+ id: "shell-layout:primary-bottom-nav",
318
+ default: true,
319
+ sourcePath: "src/components/ShellLayout.vue"
320
+ },
321
+ {
322
+ id: "shell-layout:primary-menu",
323
+ default: true,
324
+ sourcePath: "src/components/ShellLayout.vue"
325
+ }
326
+ ]);
327
+ });
328
+ });
329
+
299
330
  test("resolveShellOutletPlacementTargetFromApp requires appRoot", async () => {
300
331
  await assert.rejects(
301
332
  () =>