@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.
- package/package.descriptor.mjs +19 -16
- package/package.json +3 -2
- package/src/server/buildTemplateContext.js +2 -2
- package/src/server/subcommands/addSubpages.js +4 -5
- package/src/server/subcommands/element.js +1 -2
- package/src/server/subcommands/outlet.js +15 -21
- package/src/server/subcommands/page.js +3 -3
- package/src/server/subcommands/pageSupport.js +57 -114
- package/src/server/subcommands/support.js +5 -31
- package/test/addSubpagesSubcommand.test.js +40 -31
- package/test/buildTemplateContext.test.js +82 -28
- package/test/elementSubcommand.test.js +11 -18
- package/test/outletSubcommand.test.js +18 -18
- package/test/pageSubcommand.test.js +66 -14
- package/README.md +0 -214
|
@@ -38,8 +38,12 @@ async function writeAppFixture(appRoot, { configSource = "" } = {}) {
|
|
|
38
38
|
path.join(appRoot, "src", "components", "ShellLayout.vue"),
|
|
39
39
|
`<template>
|
|
40
40
|
<div>
|
|
41
|
-
<ShellOutlet
|
|
42
|
-
|
|
41
|
+
<ShellOutlet
|
|
42
|
+
target="shell-layout:primary-menu"
|
|
43
|
+
default
|
|
44
|
+
default-link-component-token="local.main.ui.surface-aware-menu-link-item"
|
|
45
|
+
/>
|
|
46
|
+
<ShellOutlet target="shell-layout:top-right" />
|
|
43
47
|
</div>
|
|
44
48
|
</template>
|
|
45
49
|
`,
|
|
@@ -109,6 +113,36 @@ test("ui-generator page subcommand creates a file route and derives label from t
|
|
|
109
113
|
});
|
|
110
114
|
});
|
|
111
115
|
|
|
116
|
+
test("ui-generator page subcommand omits the auth guard for public surface links", async () => {
|
|
117
|
+
await withTempApp(async (appRoot) => {
|
|
118
|
+
await writeAppFixture(appRoot, {
|
|
119
|
+
configSource: `export const config = {
|
|
120
|
+
surfaceAccessPolicies: {
|
|
121
|
+
public: {}
|
|
122
|
+
},
|
|
123
|
+
surfaceDefinitions: {
|
|
124
|
+
home: { id: "home", pagesRoot: "home", enabled: true, accessPolicyId: "public" }
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
`
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const targetFile = "home/reports/index.vue";
|
|
131
|
+
await runGeneratorSubcommand({
|
|
132
|
+
appRoot,
|
|
133
|
+
subcommand: "page",
|
|
134
|
+
args: [targetFile],
|
|
135
|
+
options: {
|
|
136
|
+
name: "Reports"
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
|
|
141
|
+
assert.match(placementSource, /id: "ui-generator\.page\.home\.reports\.link"/);
|
|
142
|
+
assert.doesNotMatch(placementSource, /when: \(\{ auth \}\) => Boolean\(auth\?\.authenticated\)/);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
112
146
|
test("ui-generator page subcommand supports link placement options", async () => {
|
|
113
147
|
await withTempApp(async (appRoot) => {
|
|
114
148
|
await writeAppFixture(appRoot);
|
|
@@ -126,8 +160,7 @@ test("ui-generator page subcommand supports link placement options", async () =>
|
|
|
126
160
|
});
|
|
127
161
|
|
|
128
162
|
const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
|
|
129
|
-
assert.match(placementSource, /
|
|
130
|
-
assert.match(placementSource, /position: "top-right"/);
|
|
163
|
+
assert.match(placementSource, /target: "shell-layout:top-right"/);
|
|
131
164
|
assert.match(placementSource, /componentToken: "local\.main\.ui\.tab-link-item"/);
|
|
132
165
|
assert.match(placementSource, /to: "\.\/notes"/);
|
|
133
166
|
});
|
|
@@ -144,7 +177,7 @@ test("ui-generator page subcommand infers subpage link placement, tab token, and
|
|
|
144
177
|
`<template>
|
|
145
178
|
<SectionContainerShell>
|
|
146
179
|
<template #tabs>
|
|
147
|
-
<ShellOutlet
|
|
180
|
+
<ShellOutlet target="contact-view:sub-pages" />
|
|
148
181
|
</template>
|
|
149
182
|
<RouterView />
|
|
150
183
|
</SectionContainerShell>
|
|
@@ -162,8 +195,7 @@ test("ui-generator page subcommand infers subpage link placement, tab token, and
|
|
|
162
195
|
});
|
|
163
196
|
|
|
164
197
|
const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
|
|
165
|
-
assert.match(placementSource, /
|
|
166
|
-
assert.match(placementSource, /position: "sub-pages"/);
|
|
198
|
+
assert.match(placementSource, /target: "contact-view:sub-pages"/);
|
|
167
199
|
assert.match(placementSource, /componentToken: "local\.main\.ui\.tab-link-item"/);
|
|
168
200
|
assert.match(placementSource, /to: "\.\/notes"/);
|
|
169
201
|
});
|
|
@@ -181,7 +213,7 @@ test("ui-generator page subcommand prefers the nearest index-route parent host",
|
|
|
181
213
|
`<template>
|
|
182
214
|
<SectionContainerShell>
|
|
183
215
|
<template #tabs>
|
|
184
|
-
<ShellOutlet
|
|
216
|
+
<ShellOutlet target="catalog:sub-pages" />
|
|
185
217
|
</template>
|
|
186
218
|
<RouterView />
|
|
187
219
|
</SectionContainerShell>
|
|
@@ -194,7 +226,7 @@ test("ui-generator page subcommand prefers the nearest index-route parent host",
|
|
|
194
226
|
`<template>
|
|
195
227
|
<SectionContainerShell>
|
|
196
228
|
<template #tabs>
|
|
197
|
-
<ShellOutlet
|
|
229
|
+
<ShellOutlet target="catalog-products:sub-pages" />
|
|
198
230
|
</template>
|
|
199
231
|
<RouterView />
|
|
200
232
|
</SectionContainerShell>
|
|
@@ -213,8 +245,7 @@ test("ui-generator page subcommand prefers the nearest index-route parent host",
|
|
|
213
245
|
});
|
|
214
246
|
|
|
215
247
|
const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
|
|
216
|
-
assert.match(placementSource, /
|
|
217
|
-
assert.match(placementSource, /position: "sub-pages"/);
|
|
248
|
+
assert.match(placementSource, /target: "catalog-products:sub-pages"/);
|
|
218
249
|
assert.match(placementSource, /componentToken: "local\.main\.ui\.tab-link-item"/);
|
|
219
250
|
assert.match(placementSource, /to: "\.\/variants"/);
|
|
220
251
|
});
|
|
@@ -266,7 +297,28 @@ test("ui-generator page subcommand rejects unsupported options", async () => {
|
|
|
266
297
|
});
|
|
267
298
|
});
|
|
268
299
|
|
|
269
|
-
test("ui-generator page subcommand
|
|
300
|
+
test("ui-generator page subcommand accepts target files with a src/pages prefix", async () => {
|
|
301
|
+
await withTempApp(async (appRoot) => {
|
|
302
|
+
await writeAppFixture(appRoot);
|
|
303
|
+
|
|
304
|
+
const targetFile = "src/pages/w/[workspaceSlug]/admin/practice/index.vue";
|
|
305
|
+
const result = await runGeneratorSubcommand({
|
|
306
|
+
appRoot,
|
|
307
|
+
subcommand: "page",
|
|
308
|
+
args: [targetFile],
|
|
309
|
+
options: {}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
assert.deepEqual(result.touchedFiles, [targetFile, "src/placement.js"]);
|
|
313
|
+
const pageSource = await readFile(path.join(appRoot, targetFile), "utf8");
|
|
314
|
+
assert.match(pageSource, /Practice/);
|
|
315
|
+
const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
|
|
316
|
+
assert.match(placementSource, /id: "ui-generator\.page\.admin\.practice\.link"/);
|
|
317
|
+
assert.match(placementSource, /workspaceSuffix: "\/practice"/);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("ui-generator page subcommand still rejects non-page src targets", async () => {
|
|
270
322
|
await withTempApp(async (appRoot) => {
|
|
271
323
|
await writeAppFixture(appRoot);
|
|
272
324
|
|
|
@@ -274,10 +326,10 @@ test("ui-generator page subcommand rejects target files with a src/pages prefix"
|
|
|
274
326
|
runGeneratorSubcommand({
|
|
275
327
|
appRoot,
|
|
276
328
|
subcommand: "page",
|
|
277
|
-
args: ["src/
|
|
329
|
+
args: ["src/components/PracticePanel.vue"],
|
|
278
330
|
options: {}
|
|
279
331
|
}),
|
|
280
|
-
/must be relative to src\/pages
|
|
332
|
+
/must be relative to src\/pages\/ or start with src\/pages\/:/
|
|
281
333
|
);
|
|
282
334
|
});
|
|
283
335
|
});
|
package/README.md
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
# @jskit-ai/ui-generator
|
|
2
|
-
|
|
3
|
-
Generate app-local UI pages, page links, placed elements, and routed subpage hosts for JSKIT apps.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
List available placement targets in the current app:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npx jskit list placements
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Create a normal page at an explicit file:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npx jskit generate @jskit-ai/ui-generator page \
|
|
17
|
-
admin/reports-dashboard/index.vue \
|
|
18
|
-
--name "Reports Dashboard"
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Create a file-route page:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npx jskit generate @jskit-ai/ui-generator page \
|
|
25
|
-
admin/contacts/[contactId].vue \
|
|
26
|
-
--name "Contact"
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Upgrade an existing page into a routed subpage host:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npx jskit generate @jskit-ai/ui-generator add-subpages \
|
|
33
|
-
admin/contacts/[contactId].vue \
|
|
34
|
-
--title "Contact" \
|
|
35
|
-
--subtitle "Manage contact modules."
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Create a reusable placed element:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npx jskit generate @jskit-ai/ui-generator placed-element \
|
|
42
|
-
--name "Alerts Widget" \
|
|
43
|
-
--surface admin
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Inject a plain generic outlet into an existing Vue file:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npx jskit generate @jskit-ai/ui-generator outlet \
|
|
50
|
-
src/components/ContactSummaryCard.vue \
|
|
51
|
-
--target contact-view:summary-actions
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Commands
|
|
55
|
-
|
|
56
|
-
- `page <target-file>`: create a route page at that exact file relative to `src/pages/` and add a link placement entry for it.
|
|
57
|
-
- `add-subpages <target-file>`: upgrade an existing page relative to `src/pages/` into the standard `SectionContainerShell + ShellOutlet + RouterView` host shape.
|
|
58
|
-
- `placed-element`: create a reusable component and register a placement for it.
|
|
59
|
-
- `outlet <target-file>`: inject a plain `ShellOutlet` into an existing Vue SFC.
|
|
60
|
-
|
|
61
|
-
For `placed-element`, the default placement target is `shell-layout:top-right`.
|
|
62
|
-
Use `--placement host:position` to override it.
|
|
63
|
-
|
|
64
|
-
## The Mental Model
|
|
65
|
-
|
|
66
|
-
`page` and `add-subpages` operate on explicit page files relative to `src/pages/`. `outlet` still targets an explicit Vue file path relative to the app root.
|
|
67
|
-
|
|
68
|
-
That means:
|
|
69
|
-
|
|
70
|
-
- `catalog/index.vue` is obviously an index-route page
|
|
71
|
-
- `catalog.vue` is obviously a file-route page
|
|
72
|
-
- there is no extra route-shape flag to remember
|
|
73
|
-
- the owning surface is derived from where the file lives
|
|
74
|
-
|
|
75
|
-
This is the reference model for JSKIT page-producing generators.
|
|
76
|
-
|
|
77
|
-
- `@jskit-ai/ui-generator page <target-file>` works from an explicit page file relative to `src/pages/`.
|
|
78
|
-
- `@jskit-ai/crud-ui-generator crud <target-root>` works from an explicit route root relative to `src/pages/`.
|
|
79
|
-
- `@jskit-ai/assistant page <target-file>` and `settings-page <target-file>` work from explicit page files relative to `src/pages/`.
|
|
80
|
-
|
|
81
|
-
## Page Links
|
|
82
|
-
|
|
83
|
-
`page` creates a page file and appends a link placement block for it.
|
|
84
|
-
|
|
85
|
-
These options control that generated page link:
|
|
86
|
-
|
|
87
|
-
- `--link-placement`: where the page link renders
|
|
88
|
-
- `--link-component-token`: how the page link is rendered
|
|
89
|
-
- `--link-to`: explicit `props.to` override for the page link
|
|
90
|
-
|
|
91
|
-
This is intentionally separate from `placed-element`, which still uses `--placement` because it places arbitrary UI, not a page link.
|
|
92
|
-
|
|
93
|
-
## Routed Subpages
|
|
94
|
-
|
|
95
|
-
`add-subpages` is the only routed-subpages command.
|
|
96
|
-
|
|
97
|
-
It upgrades an existing page so the page itself owns:
|
|
98
|
-
|
|
99
|
-
- `SectionContainerShell`
|
|
100
|
-
- `ShellOutlet host="..." position="sub-pages"`
|
|
101
|
-
- `RouterView`
|
|
102
|
-
|
|
103
|
-
`--target` controls that outlet target:
|
|
104
|
-
|
|
105
|
-
- if omitted, the target is derived from the page path
|
|
106
|
-
- `--target contact-view` means `contact-view:sub-pages`
|
|
107
|
-
- `--target contact-view:secondary-tabs` uses an explicit custom position
|
|
108
|
-
|
|
109
|
-
Derived target examples:
|
|
110
|
-
|
|
111
|
-
- `src/pages/admin/catalog/index.vue` -> `catalog:sub-pages`
|
|
112
|
-
- `src/pages/admin/catalog.vue` -> `catalog:sub-pages`
|
|
113
|
-
- `src/pages/admin/contacts/[contactId].vue` -> `contacts-contact-id:sub-pages`
|
|
114
|
-
- `src/pages/admin/catalog/products/index.vue` -> `catalog-products:sub-pages`
|
|
115
|
-
|
|
116
|
-
If the page already contains a `RouterView`, `add-subpages` fails instead of trying to update an existing routed host.
|
|
117
|
-
|
|
118
|
-
It also ensures the shared support scaffold exists:
|
|
119
|
-
|
|
120
|
-
- `src/components/SectionContainerShell.vue`
|
|
121
|
-
- `src/components/TabLinkItem.vue`
|
|
122
|
-
- `packages/main/src/client/providers/MainClientProvider.js` registration for `local.main.ui.tab-link-item`
|
|
123
|
-
|
|
124
|
-
## Child Route Placement Rule
|
|
125
|
-
|
|
126
|
-
Child routes attach differently depending on the parent page file shape.
|
|
127
|
-
|
|
128
|
-
If the parent is a file route:
|
|
129
|
-
|
|
130
|
-
- parent: `src/pages/admin/catalog.vue`
|
|
131
|
-
- child pages go under: `src/pages/admin/catalog/...`
|
|
132
|
-
- example child page: `src/pages/admin/catalog/products/index.vue`
|
|
133
|
-
|
|
134
|
-
If the parent is an index route:
|
|
135
|
-
|
|
136
|
-
- parent: `src/pages/admin/catalog/index.vue`
|
|
137
|
-
- child pages go under: `src/pages/admin/catalog/index/...`
|
|
138
|
-
- example child page: `src/pages/admin/catalog/index/products/index.vue`
|
|
139
|
-
|
|
140
|
-
That `index/...` folder shape is the native file-based routing rule for nesting children under an `index.vue` page.
|
|
141
|
-
|
|
142
|
-
## Example: File Route Parent
|
|
143
|
-
|
|
144
|
-
Create the parent page:
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
npx jskit generate @jskit-ai/ui-generator page \
|
|
148
|
-
admin/contacts/[contactId].vue \
|
|
149
|
-
--name "Contact"
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Upgrade it to host subpages:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
npx jskit generate @jskit-ai/ui-generator add-subpages \
|
|
156
|
-
admin/contacts/[contactId].vue \
|
|
157
|
-
--title "Contact" \
|
|
158
|
-
--subtitle "Manage contact modules."
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Generate a child page link inside that host:
|
|
162
|
-
|
|
163
|
-
```bash
|
|
164
|
-
npx jskit generate @jskit-ai/ui-generator page \
|
|
165
|
-
admin/contacts/[contactId]/notes/index.vue \
|
|
166
|
-
--name "Notes"
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Example: Index Route Parent
|
|
170
|
-
|
|
171
|
-
Create the parent page:
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
npx jskit generate @jskit-ai/ui-generator page \
|
|
175
|
-
admin/catalog/index.vue \
|
|
176
|
-
--name "Catalog"
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
Upgrade it to host subpages:
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
npx jskit generate @jskit-ai/ui-generator add-subpages \
|
|
183
|
-
admin/catalog/index.vue \
|
|
184
|
-
--title "Catalog"
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Because the parent page is `index.vue`, nested child pages live under the matching `index/` folder.
|
|
188
|
-
|
|
189
|
-
Generate a child page link inside that host:
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
npx jskit generate @jskit-ai/ui-generator page \
|
|
193
|
-
admin/catalog/index/products/index.vue \
|
|
194
|
-
--name "Products"
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
When `page` finds the nearest parent page upgraded with `add-subpages`, it reuses that parent’s real outlet target, defaults the link renderer to `local.main.ui.tab-link-item`, and derives `to` from the child route automatically.
|
|
198
|
-
|
|
199
|
-
## Generic Outlet Injection
|
|
200
|
-
|
|
201
|
-
`outlet` is intentionally small.
|
|
202
|
-
|
|
203
|
-
It only adds:
|
|
204
|
-
|
|
205
|
-
- `import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";`
|
|
206
|
-
- `<ShellOutlet host="..." position="..." />`
|
|
207
|
-
|
|
208
|
-
It does not:
|
|
209
|
-
|
|
210
|
-
- add `RouterView`
|
|
211
|
-
- add `SectionContainerShell`
|
|
212
|
-
- add routed subpage scaffolding
|
|
213
|
-
|
|
214
|
-
Use `add-subpages` when the goal is routed child pages inside a page.
|