@jskit-ai/ui-generator 0.1.16 → 0.1.18

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.
@@ -3,6 +3,7 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import test from "node:test";
6
+ import { readLocalLinkItemComponentSource } from "@jskit-ai/shell-web/server/support/localLinkItemScaffolds";
6
7
  import { runGeneratorSubcommand } from "../src/server/subcommands/addSubpages.js";
7
8
 
8
9
  async function withTempApp(run) {
@@ -45,17 +46,8 @@ export default function getPlacements() {
45
46
  path.join(appRoot, "packages", "main", "src", "client", "providers", "MainClientProvider.js"),
46
47
  `const mainClientComponents = [];
47
48
 
48
- function registerMainClientComponent(componentToken, resolveComponent) {
49
- const token = String(componentToken || "").trim();
50
- if (!token || typeof resolveComponent !== "function") {
51
- return;
52
- }
53
- mainClientComponents.push(
54
- Object.freeze({
55
- token,
56
- resolveComponent
57
- })
58
- );
49
+ function registerMainClientComponent(token, resolveComponent) {
50
+ mainClientComponents.push({ token, resolveComponent });
59
51
  }
60
52
 
61
53
  class MainClientProvider {}
@@ -105,14 +97,21 @@ test("ui-generator add-subpages derives the default target from an index-route p
105
97
 
106
98
  assert.deepEqual(result.touchedFiles, [
107
99
  "packages/main/src/client/providers/MainClientProvider.js",
100
+ "src/components/menus/TabLinkItem.vue",
108
101
  "src/components/SectionContainerShell.vue",
109
- "src/components/TabLinkItem.vue",
110
102
  `src/pages/${targetFile}`
111
103
  ]);
112
104
 
113
105
  const pageSource = await readPageFile(appRoot, targetFile);
114
- assert.match(pageSource, /<ShellOutlet host="practice" position="sub-pages" \/>/);
106
+ assert.match(
107
+ pageSource,
108
+ /<ShellOutlet target="practice:sub-pages" default-link-component-token="local\.main\.ui\.tab-link-item" \/>/
109
+ );
115
110
  assert.match(pageSource, /<RouterView \/>/);
111
+ assert.equal(
112
+ await readFile(path.join(appRoot, "src", "components", "menus", "TabLinkItem.vue"), "utf8"),
113
+ await readLocalLinkItemComponentSource("local.main.ui.tab-link-item")
114
+ );
116
115
  });
117
116
  });
118
117
 
@@ -131,7 +130,10 @@ test("ui-generator add-subpages derives the default target from a dynamic file-r
131
130
  });
132
131
 
133
132
  const pageSource = await readPageFile(appRoot, targetFile);
134
- assert.match(pageSource, /<ShellOutlet host="contacts-contact-id" position="sub-pages" \/>/);
133
+ assert.match(
134
+ pageSource,
135
+ /<ShellOutlet target="contacts-contact-id:sub-pages" default-link-component-token="local\.main\.ui\.tab-link-item" \/>/
136
+ );
135
137
  });
136
138
  });
137
139
 
@@ -150,28 +152,31 @@ test("ui-generator add-subpages derives the default target from a nested route p
150
152
  });
151
153
 
152
154
  const pageSource = await readPageFile(appRoot, targetFile);
153
- assert.match(pageSource, /<ShellOutlet host="catalog-products" position="sub-pages" \/>/);
155
+ assert.match(
156
+ pageSource,
157
+ /<ShellOutlet target="catalog-products:sub-pages" default-link-component-token="local\.main\.ui\.tab-link-item" \/>/
158
+ );
154
159
  });
155
160
  });
156
161
 
157
- test("ui-generator add-subpages supports explicit target host shorthand", async () => {
162
+ test("ui-generator add-subpages rejects explicit target shorthand without a position", async () => {
158
163
  await withTempApp(async (appRoot) => {
159
164
  await writeAppFixture(appRoot);
160
165
 
161
166
  const targetFile = "w/[workspaceSlug]/admin/practice/index.vue";
162
167
  await writePageFile(appRoot, targetFile);
163
168
 
164
- await runGeneratorSubcommand({
165
- appRoot,
166
- subcommand: "add-subpages",
167
- args: [targetFile],
168
- options: {
169
- target: "practice-hub"
170
- }
171
- });
172
-
173
- const pageSource = await readPageFile(appRoot, targetFile);
174
- assert.match(pageSource, /<ShellOutlet host="practice-hub" position="sub-pages" \/>/);
169
+ await assert.rejects(
170
+ runGeneratorSubcommand({
171
+ appRoot,
172
+ subcommand: "add-subpages",
173
+ args: [targetFile],
174
+ options: {
175
+ target: "practice-hub"
176
+ }
177
+ }),
178
+ /option "target" must be a target in "host:position" format/
179
+ );
175
180
  });
176
181
  });
177
182
 
@@ -192,7 +197,10 @@ test("ui-generator add-subpages supports explicit target host:position", async (
192
197
  });
193
198
 
194
199
  const pageSource = await readPageFile(appRoot, targetFile);
195
- assert.match(pageSource, /<ShellOutlet host="practice-hub" position="secondary-tabs" \/>/);
200
+ assert.match(
201
+ pageSource,
202
+ /<ShellOutlet target="practice-hub:secondary-tabs" default-link-component-token="local\.main\.ui\.tab-link-item" \/>/
203
+ );
196
204
  });
197
205
  });
198
206
 
@@ -209,8 +217,9 @@ test("ui-generator add-subpages does not rewrite existing scaffold support compo
209
217
  customSectionShellSource,
210
218
  "utf8"
211
219
  );
220
+ await mkdir(path.join(appRoot, "src", "components", "menus"), { recursive: true });
212
221
  await writeFile(
213
- path.join(appRoot, "src", "components", "TabLinkItem.vue"),
222
+ path.join(appRoot, "src", "components", "menus", "TabLinkItem.vue"),
214
223
  customTabLinkSource,
215
224
  "utf8"
216
225
  );
@@ -233,7 +242,7 @@ test("ui-generator add-subpages does not rewrite existing scaffold support compo
233
242
  customSectionShellSource
234
243
  );
235
244
  assert.equal(
236
- await readFile(path.join(appRoot, "src", "components", "TabLinkItem.vue"), "utf8"),
245
+ await readFile(path.join(appRoot, "src", "components", "menus", "TabLinkItem.vue"), "utf8"),
237
246
  customTabLinkSource
238
247
  );
239
248
  });
@@ -299,7 +308,7 @@ test("ui-generator add-subpages validates target format", async () => {
299
308
  target: "practice:"
300
309
  }
301
310
  }),
302
- /option "target" must be "host" or "host:position"/
311
+ /option "target" must be a target in "host:position" format/
303
312
  );
304
313
  });
305
314
  });
@@ -31,8 +31,12 @@ async function writeShellLayout(appRoot, source = "") {
31
31
  source ||
32
32
  `<template>
33
33
  <div>
34
- <ShellOutlet host="shell-layout" position="top-right" />
35
- <ShellOutlet host="shell-layout" position="primary-menu" default />
34
+ <ShellOutlet target="shell-layout:top-right" />
35
+ <ShellOutlet
36
+ target="shell-layout:primary-menu"
37
+ default
38
+ default-link-component-token="local.main.ui.surface-aware-menu-link-item"
39
+ />
36
40
  </div>
37
41
  </template>
38
42
  `
@@ -57,16 +61,44 @@ test("buildUiPageTemplateContext resolves link placement from default app ShellO
57
61
  targetFile: "admin/reports/index.vue",
58
62
  options: {}
59
63
  });
60
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "shell-layout");
61
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "primary-menu");
62
- assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "users.web.shell.surface-aware-menu-link-item");
64
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "shell-layout:primary-menu");
65
+ assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.surface-aware-menu-link-item");
63
66
  assert.equal(context.__JSKIT_UI_LINK_WORKSPACE_SUFFIX__, "/reports");
64
67
  assert.equal(context.__JSKIT_UI_LINK_NON_WORKSPACE_SUFFIX__, "/reports");
68
+ assert.equal(context.__JSKIT_UI_LINK_WHEN_LINE__, "");
65
69
  assert.equal(context.__JSKIT_UI_LINK_TO_PROP_LINE__, "");
66
70
  assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_ID__, "ui-generator.page.admin.reports.link");
67
71
  });
68
72
  });
69
73
 
74
+ test("buildUiPageTemplateContext derives an auth guard from an authenticated surface policy", async () => {
75
+ await withTempApp(async (appRoot) => {
76
+ await writeConfig(
77
+ appRoot,
78
+ `export const config = {
79
+ surfaceAccessPolicies: {
80
+ authenticated: {
81
+ requireAuth: true
82
+ }
83
+ },
84
+ surfaceDefinitions: {
85
+ app: { id: "app", pagesRoot: "app", enabled: true, accessPolicyId: "authenticated" }
86
+ }
87
+ };
88
+ `
89
+ );
90
+ await writeShellLayout(appRoot);
91
+
92
+ const context = await buildUiPageTemplateContext({
93
+ appRoot,
94
+ targetFile: "app/reports/index.vue",
95
+ options: {}
96
+ });
97
+
98
+ assert.equal(context.__JSKIT_UI_LINK_WHEN_LINE__, " when: ({ auth }) => Boolean(auth?.authenticated)\n");
99
+ });
100
+ });
101
+
70
102
  test("buildUiPageTemplateContext supports explicit link placement override", async () => {
71
103
  await withTempApp(async (appRoot) => {
72
104
  await writeConfig(
@@ -87,8 +119,8 @@ test("buildUiPageTemplateContext supports explicit link placement override", asy
87
119
  "link-placement": "shell-layout:top-right"
88
120
  }
89
121
  });
90
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "top-right");
91
- assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "users.web.shell.surface-aware-menu-link-item");
122
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "shell-layout:top-right");
123
+ assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.surface-aware-menu-link-item");
92
124
  });
93
125
  });
94
126
 
@@ -133,7 +165,11 @@ test("buildUiPageTemplateContext supports explicit package outlet link placement
133
165
  ui: {
134
166
  placements: {
135
167
  outlets: [
136
- { host: "workspace-tools", position: "primary-menu", source: "src/client/components/UsersWorkspaceToolsWidget.vue" }
168
+ {
169
+ target: "workspace-tools:primary-menu",
170
+ defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
171
+ source: "src/client/components/UsersWorkspaceToolsWidget.vue"
172
+ }
137
173
  ]
138
174
  }
139
175
  }
@@ -149,8 +185,7 @@ test("buildUiPageTemplateContext supports explicit package outlet link placement
149
185
  "link-placement": "workspace-tools:primary-menu"
150
186
  }
151
187
  });
152
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "workspace-tools");
153
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "primary-menu");
188
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "workspace-tools:primary-menu");
154
189
  });
155
190
  });
156
191
 
@@ -226,7 +261,7 @@ test("buildUiPageTemplateContext infers subpage link placement, tab token, and l
226
261
  `<template>
227
262
  <SectionContainerShell>
228
263
  <template #tabs>
229
- <ShellOutlet host="contact-view" position="sub-pages" />
264
+ <ShellOutlet target="contact-view:sub-pages" />
230
265
  </template>
231
266
  <RouterView />
232
267
  </SectionContainerShell>
@@ -240,8 +275,7 @@ test("buildUiPageTemplateContext infers subpage link placement, tab token, and l
240
275
  options: {}
241
276
  });
242
277
 
243
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "contact-view");
244
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "sub-pages");
278
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "contact-view:sub-pages");
245
279
  assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.tab-link-item");
246
280
  assert.equal(context.__JSKIT_UI_LINK_TO_PROP_LINE__, " to: \"./notes\",\n");
247
281
  });
@@ -265,7 +299,7 @@ test("buildUiPageTemplateContext inherits a file-route parent host for deeper de
265
299
  `<template>
266
300
  <SectionContainerShell>
267
301
  <template #tabs>
268
- <ShellOutlet host="contact-view" position="sub-pages" />
302
+ <ShellOutlet target="contact-view:sub-pages" />
269
303
  </template>
270
304
  <RouterView />
271
305
  </SectionContainerShell>
@@ -279,8 +313,7 @@ test("buildUiPageTemplateContext inherits a file-route parent host for deeper de
279
313
  options: {}
280
314
  });
281
315
 
282
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "contact-view");
283
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "sub-pages");
316
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "contact-view:sub-pages");
284
317
  assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.tab-link-item");
285
318
  assert.equal(context.__JSKIT_UI_LINK_TO_PROP_LINE__, " to: \"./notes/history\",\n");
286
319
  });
@@ -304,7 +337,7 @@ test("buildUiPageTemplateContext infers subpage link placement from an index-rou
304
337
  `<template>
305
338
  <SectionContainerShell>
306
339
  <template #tabs>
307
- <ShellOutlet host="catalog" position="sub-pages" />
340
+ <ShellOutlet target="catalog:sub-pages" />
308
341
  </template>
309
342
  <RouterView />
310
343
  </SectionContainerShell>
@@ -318,8 +351,7 @@ test("buildUiPageTemplateContext infers subpage link placement from an index-rou
318
351
  options: {}
319
352
  });
320
353
 
321
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "catalog");
322
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "sub-pages");
354
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "catalog:sub-pages");
323
355
  assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.tab-link-item");
324
356
  assert.equal(context.__JSKIT_UI_LINK_TO_PROP_LINE__, " to: \"./products\",\n");
325
357
  });
@@ -343,7 +375,7 @@ test("buildUiPageTemplateContext finds the nearest index-route parent host", asy
343
375
  `<template>
344
376
  <SectionContainerShell>
345
377
  <template #tabs>
346
- <ShellOutlet host="catalog" position="sub-pages" />
378
+ <ShellOutlet target="catalog:sub-pages" />
347
379
  </template>
348
380
  <RouterView />
349
381
  </SectionContainerShell>
@@ -356,7 +388,7 @@ test("buildUiPageTemplateContext finds the nearest index-route parent host", asy
356
388
  `<template>
357
389
  <SectionContainerShell>
358
390
  <template #tabs>
359
- <ShellOutlet host="catalog-products" position="sub-pages" />
391
+ <ShellOutlet target="catalog-products:sub-pages" />
360
392
  </template>
361
393
  <RouterView />
362
394
  </SectionContainerShell>
@@ -370,8 +402,7 @@ test("buildUiPageTemplateContext finds the nearest index-route parent host", asy
370
402
  options: {}
371
403
  });
372
404
 
373
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "catalog-products");
374
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "sub-pages");
405
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "catalog-products:sub-pages");
375
406
  assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.tab-link-item");
376
407
  assert.equal(context.__JSKIT_UI_LINK_TO_PROP_LINE__, " to: \"./variants\",\n");
377
408
  });
@@ -395,7 +426,7 @@ test("buildUiPageTemplateContext infers subpage link placement from an index rou
395
426
  `<template>
396
427
  <SectionContainerShell>
397
428
  <template #tabs>
398
- <ShellOutlet host="customer-view" position="sub-pages" />
429
+ <ShellOutlet target="customer-view:sub-pages" />
399
430
  </template>
400
431
  <RouterView />
401
432
  </SectionContainerShell>
@@ -409,8 +440,7 @@ test("buildUiPageTemplateContext infers subpage link placement from an index rou
409
440
  options: {}
410
441
  });
411
442
 
412
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_HOST__, "customer-view");
413
- assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_POSITION__, "sub-pages");
443
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_TARGET__, "customer-view:sub-pages");
414
444
  assert.equal(context.__JSKIT_UI_LINK_COMPONENT_TOKEN__, "local.main.ui.tab-link-item");
415
445
  assert.equal(context.__JSKIT_UI_LINK_TO_PROP_LINE__, " to: \"./pets\",\n");
416
446
  assert.equal(context.__JSKIT_UI_LINK_WORKSPACE_SUFFIX__, "/customers/[customerId]/pets");
@@ -468,11 +498,35 @@ test("buildUiPageTemplateContext rejects target files with a leading src segment
468
498
  targetFile: "src/components/ReportsPanel.vue",
469
499
  options: {}
470
500
  }),
471
- /must be relative to src\/pages\/, without a leading src\/ segment/
501
+ /must be relative to src\/pages\/ or start with src\/pages\/:/
472
502
  );
473
503
  });
474
504
  });
475
505
 
506
+ test("buildUiPageTemplateContext accepts target files with a src/pages prefix", async () => {
507
+ await withTempApp(async (appRoot) => {
508
+ await writeConfig(
509
+ appRoot,
510
+ `export const config = {
511
+ surfaceDefinitions: {
512
+ admin: { id: "admin", pagesRoot: "admin", enabled: true }
513
+ }
514
+ };
515
+ `
516
+ );
517
+ await writeShellLayout(appRoot);
518
+
519
+ const context = await buildUiPageTemplateContext({
520
+ appRoot,
521
+ targetFile: "src/pages/admin/reports/index.vue",
522
+ options: {}
523
+ });
524
+
525
+ assert.equal(context.__JSKIT_UI_LINK_PLACEMENT_ID__, "ui-generator.page.admin.reports.link");
526
+ assert.equal(context.__JSKIT_UI_LINK_WORKSPACE_SUFFIX__, "/reports");
527
+ });
528
+ });
529
+
476
530
  test("buildUiPageTemplateContext fails when the target file matches no surface", async () => {
477
531
  await withTempApp(async (appRoot) => {
478
532
  await writeConfig(
@@ -546,7 +600,7 @@ test("buildUiPageTemplateContext validates link placement format", async () => {
546
600
  "link-placement": "invalid-placement"
547
601
  }
548
602
  }),
549
- /option "placement" must be in "host:position" format/
603
+ /option "placement" must be a target in "host:position" format/
550
604
  );
551
605
  });
552
606
  });
@@ -23,8 +23,12 @@ async function writeAppFixture(appRoot) {
23
23
  path.join(appRoot, "src", "components", "ShellLayout.vue"),
24
24
  `<template>
25
25
  <div>
26
- <ShellOutlet host="shell-layout" position="primary-menu" default />
27
- <ShellOutlet host="shell-layout" position="top-right" />
26
+ <ShellOutlet
27
+ target="shell-layout:primary-menu"
28
+ default
29
+ default-link-component-token="local.main.ui.surface-aware-menu-link-item"
30
+ />
31
+ <ShellOutlet target="shell-layout:top-right" />
28
32
  </div>
29
33
  </template>
30
34
  `,
@@ -34,7 +38,7 @@ async function writeAppFixture(appRoot) {
34
38
  path.join(appRoot, "src", "pages", "admin", "workspace", "settings", "index.vue"),
35
39
  `<template>
36
40
  <section>
37
- <ShellOutlet host="admin-settings" position="forms" />
41
+ <ShellOutlet target="admin-settings:forms" />
38
42
  </section>
39
43
  </template>
40
44
  `,
@@ -55,17 +59,8 @@ export default function getPlacements() {
55
59
  path.join(appRoot, "packages", "main", "src", "client", "providers", "MainClientProvider.js"),
56
60
  `const mainClientComponents = [];
57
61
 
58
- function registerMainClientComponent(componentToken, resolveComponent) {
59
- const token = String(componentToken || "").trim();
60
- if (!token || typeof resolveComponent !== "function") {
61
- return;
62
- }
63
- mainClientComponents.push(
64
- Object.freeze({
65
- token,
66
- resolveComponent
67
- })
68
- );
62
+ function registerMainClientComponent(token, resolveComponent) {
63
+ mainClientComponents.push({ token, resolveComponent });
69
64
  }
70
65
 
71
66
  class MainClientProvider {}
@@ -104,8 +99,7 @@ test("ui-generator placed-element subcommand creates component and outlet placem
104
99
 
105
100
  const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
106
101
  assert.match(placementSource, /id: "ui-generator\.element\.ops-panel"/);
107
- assert.match(placementSource, /host: "shell-layout"/);
108
- assert.match(placementSource, /position: "top-right"/);
102
+ assert.match(placementSource, /target: "shell-layout:top-right"/);
109
103
  assert.match(placementSource, /componentToken: "local\.main\.ui\.element\.ops-panel"/);
110
104
  });
111
105
  });
@@ -125,8 +119,7 @@ test("ui-generator placed-element subcommand supports explicit placement overrid
125
119
  });
126
120
 
127
121
  const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
128
- assert.match(placementSource, /host: "shell-layout"/);
129
- assert.match(placementSource, /position: "primary-menu"/);
122
+ assert.match(placementSource, /target: "shell-layout:primary-menu"/);
130
123
  });
131
124
  });
132
125
 
@@ -40,7 +40,7 @@ import { computed } from "vue";
40
40
  subcommand: "outlet",
41
41
  args: [targetFile],
42
42
  options: {
43
- target: "contact-view"
43
+ target: "contact-view:sub-pages"
44
44
  }
45
45
  });
46
46
 
@@ -48,7 +48,7 @@ import { computed } from "vue";
48
48
 
49
49
  const output = await readFile(targetPath, "utf8");
50
50
  assert.match(output, /import ShellOutlet from "@jskit-ai\/shell-web\/client\/components\/ShellOutlet";/);
51
- assert.match(output, /<ShellOutlet host="contact-view" position="sub-pages" \/>/);
51
+ assert.match(output, /<ShellOutlet target="contact-view:sub-pages" \/>/);
52
52
  assert.doesNotMatch(output, /RouterView/);
53
53
  assert.doesNotMatch(output, /jskit:ui-generator\.outlet:/);
54
54
 
@@ -57,7 +57,7 @@ import { computed } from "vue";
57
57
  subcommand: "outlet",
58
58
  args: [targetFile],
59
59
  options: {
60
- target: "contact-view"
60
+ target: "contact-view:sub-pages"
61
61
  }
62
62
  });
63
63
 
@@ -79,7 +79,7 @@ import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";
79
79
 
80
80
  <template>
81
81
  <section>
82
- <ShellOutlet host="contact-view" position="sub-pages" />
82
+ <ShellOutlet target="contact-view:sub-pages" />
83
83
  </section>
84
84
  </template>
85
85
  `,
@@ -91,12 +91,12 @@ import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";
91
91
  subcommand: "outlet",
92
92
  args: [targetFile],
93
93
  options: {
94
- target: "contact-view"
94
+ target: "contact-view:sub-pages"
95
95
  }
96
96
  });
97
97
 
98
98
  const output = await readFile(targetPath, "utf8");
99
- assert.equal((output.match(/<ShellOutlet host="contact-view" position="sub-pages" \/>/g) || []).length, 1);
99
+ assert.equal((output.match(/<ShellOutlet target="contact-view:sub-pages" \/>/g) || []).length, 1);
100
100
  assert.equal(
101
101
  (output.match(/import ShellOutlet from "@jskit-ai\/shell-web\/client\/components\/ShellOutlet";/g) || []).length,
102
102
  1
@@ -124,7 +124,7 @@ test("ui-generator outlet creates script setup when missing", async () => {
124
124
  subcommand: "outlet",
125
125
  args: [targetFile],
126
126
  options: {
127
- target: "contact-view"
127
+ target: "contact-view:sub-pages"
128
128
  }
129
129
  });
130
130
 
@@ -161,7 +161,7 @@ test("ui-generator outlet inserts generated script after existing route block",
161
161
  subcommand: "outlet",
162
162
  args: [targetFile],
163
163
  options: {
164
- target: "contact-view"
164
+ target: "contact-view:sub-pages"
165
165
  }
166
166
  });
167
167
 
@@ -203,12 +203,12 @@ test("ui-generator outlet keeps indentation when injected into nested template b
203
203
  subcommand: "outlet",
204
204
  args: [targetFile],
205
205
  options: {
206
- target: "contact-view"
206
+ target: "contact-view:sub-pages"
207
207
  }
208
208
  });
209
209
 
210
210
  const output = await readFile(targetPath, "utf8");
211
- assert.match(output, /\n\s{2}<\/section>\n\s{2}<ShellOutlet host="contact-view" position="sub-pages" \/>\n<\/template>/);
211
+ assert.match(output, /\n\s{2}<\/section>\n\s{2}<ShellOutlet target="contact-view:sub-pages" \/>\n<\/template>/);
212
212
  assert.match(output, /<template v-else-if="view\.isLoading">\n\s*<v-skeleton-loader type="heading, text@2, article" \/>\n\s*<\/template>/);
213
213
  assert.doesNotMatch(output, /jskit:ui-generator\.outlet:/);
214
214
  });
@@ -226,11 +226,11 @@ test("ui-generator outlet rejects unsupported options", async () => {
226
226
  runGeneratorSubcommand({
227
227
  appRoot,
228
228
  subcommand: "outlet",
229
- args: [targetFile],
230
- options: {
231
- target: "contact-view",
232
- bogus: "routed"
233
- }
229
+ args: [targetFile],
230
+ options: {
231
+ target: "contact-view:sub-pages",
232
+ bogus: "routed"
233
+ }
234
234
  }),
235
235
  /ui-generator outlet received unsupported option: --bogus\./
236
236
  );
@@ -255,7 +255,7 @@ test("ui-generator outlet supports explicit target host:position", async () => {
255
255
  });
256
256
 
257
257
  const output = await readFile(targetPath, "utf8");
258
- assert.match(output, /<ShellOutlet host="customer-view" position="summary-actions" \/>/);
258
+ assert.match(output, /<ShellOutlet target="customer-view:summary-actions" \/>/);
259
259
  });
260
260
  });
261
261
 
@@ -274,7 +274,7 @@ test("ui-generator outlet rejects non-vue target files without changing them", a
274
274
  subcommand: "outlet",
275
275
  args: [targetFile],
276
276
  options: {
277
- target: "vet-view"
277
+ target: "vet-view:sub-pages"
278
278
  }
279
279
  }),
280
280
  /ui-generator outlet target file must be an existing Vue SFC \(\.vue\): src\/pages\/w\/\[workspaceSlug\]\/admin\/practice\/vets\/_components\/VetAddEditFormFields\.js\./
@@ -302,7 +302,7 @@ test("ui-generator outlet validates target format", async () => {
302
302
  target: "customer-view:"
303
303
  }
304
304
  }),
305
- /ui-generator outlet option "target" must be "host" or "host:position"\./
305
+ /ui-generator outlet option "target" must be a target in "host:position" format\./
306
306
  );
307
307
  });
308
308
  });