@jskit-ai/ui-generator 0.1.15 → 0.1.16
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/ui-generator",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.16",
|
|
5
5
|
kind: "generator",
|
|
6
6
|
description: "Create non-CRUD pages, reusable UI elements, and subpage hosts.",
|
|
7
7
|
options: {
|
|
@@ -274,7 +274,7 @@ export default Object.freeze({
|
|
|
274
274
|
mutations: {
|
|
275
275
|
dependencies: {
|
|
276
276
|
runtime: {
|
|
277
|
-
"@jskit-ai/users-web": "0.1.
|
|
277
|
+
"@jskit-ai/users-web": "0.1.48"
|
|
278
278
|
},
|
|
279
279
|
dev: {}
|
|
280
280
|
},
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/ui-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@jskit-ai/kernel": "0.1.
|
|
9
|
+
"@jskit-ai/kernel": "0.1.33"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
"./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
|
|
@@ -141,6 +141,11 @@ async function runGeneratorSubcommand({
|
|
|
141
141
|
const targetFilePath = resolvePathWithinApp(appRoot, targetFile, {
|
|
142
142
|
context: "ui-generator outlet"
|
|
143
143
|
});
|
|
144
|
+
if (!targetFilePath.relativePath.toLowerCase().endsWith(".vue")) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`ui-generator outlet target file must be an existing Vue SFC (.vue): ${targetFilePath.relativePath}.`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
144
149
|
|
|
145
150
|
let source = "";
|
|
146
151
|
try {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "./support.js";
|
|
12
12
|
import {
|
|
13
13
|
resolvePageTargetDetails,
|
|
14
|
-
renderPlainPageSource
|
|
14
|
+
renderPlainPageSource
|
|
15
15
|
} from "./pageSupport.js";
|
|
16
16
|
|
|
17
17
|
function renderPageLinkPlacementBlock({
|
|
@@ -86,14 +86,6 @@ async function runGeneratorSubcommand({
|
|
|
86
86
|
);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
if (!pageAlreadyExisted || forceOverwrite) {
|
|
90
|
-
if (dryRun !== true) {
|
|
91
|
-
await mkdir(path.dirname(pageFilePath), { recursive: true });
|
|
92
|
-
await writeFile(pageFilePath, renderPlainPageSource(pageLabel), "utf8");
|
|
93
|
-
}
|
|
94
|
-
touchedFiles.add(pageRelativePath);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
89
|
const placementContext = await buildUiPageTemplateContext({
|
|
98
90
|
appRoot: pageTarget.appRoot,
|
|
99
91
|
targetFile,
|
|
@@ -114,6 +106,15 @@ async function runGeneratorSubcommand({
|
|
|
114
106
|
surface: pageTarget.surfaceId
|
|
115
107
|
})
|
|
116
108
|
);
|
|
109
|
+
|
|
110
|
+
if (!pageAlreadyExisted || forceOverwrite) {
|
|
111
|
+
if (dryRun !== true) {
|
|
112
|
+
await mkdir(path.dirname(pageFilePath), { recursive: true });
|
|
113
|
+
await writeFile(pageFilePath, renderPlainPageSource(pageLabel), "utf8");
|
|
114
|
+
}
|
|
115
|
+
touchedFiles.add(pageRelativePath);
|
|
116
|
+
}
|
|
117
|
+
|
|
117
118
|
if (placementApplied.changed) {
|
|
118
119
|
if (dryRun !== true) {
|
|
119
120
|
await writeFile(placementPath.absolutePath, placementApplied.content, "utf8");
|
|
@@ -122,20 +123,11 @@ async function runGeneratorSubcommand({
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
const touchedFileList = [...touchedFiles].sort((left, right) => left.localeCompare(right));
|
|
125
|
-
const summaryParts = [];
|
|
126
|
-
if (!pageAlreadyExisted) {
|
|
127
|
-
summaryParts.push(`Generated UI page "${pageTarget.routeUrlSuffix}" at ${pageRelativePath}.`);
|
|
128
|
-
} else if (forceOverwrite) {
|
|
129
|
-
summaryParts.push(`Regenerated UI page "${pageTarget.routeUrlSuffix}" at ${pageRelativePath}.`);
|
|
130
|
-
} else if (placementApplied.changed) {
|
|
131
|
-
summaryParts.push(`Updated page link placement for existing UI page "${pageTarget.routeUrlSuffix}".`);
|
|
132
|
-
} else {
|
|
133
|
-
summaryParts.push(`UI page "${pageTarget.routeUrlSuffix}" is already up to date.`);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
126
|
return {
|
|
137
127
|
touchedFiles: touchedFileList,
|
|
138
|
-
summary:
|
|
128
|
+
summary: !pageAlreadyExisted
|
|
129
|
+
? `Generated UI page "${pageTarget.routeUrlSuffix}" at ${pageRelativePath}.`
|
|
130
|
+
: `Regenerated UI page "${pageTarget.routeUrlSuffix}" at ${pageRelativePath}.`
|
|
139
131
|
};
|
|
140
132
|
}
|
|
141
133
|
|
|
@@ -259,6 +259,32 @@ test("ui-generator outlet supports explicit target host:position", async () => {
|
|
|
259
259
|
});
|
|
260
260
|
});
|
|
261
261
|
|
|
262
|
+
test("ui-generator outlet rejects non-vue target files without changing them", async () => {
|
|
263
|
+
await withTempApp(async (appRoot) => {
|
|
264
|
+
const targetFile = "src/pages/w/[workspaceSlug]/admin/practice/vets/_components/VetAddEditFormFields.js";
|
|
265
|
+
const targetPath = path.join(appRoot, targetFile);
|
|
266
|
+
const originalSource = "export const fields = [];\n";
|
|
267
|
+
|
|
268
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
269
|
+
await writeFile(targetPath, originalSource, "utf8");
|
|
270
|
+
|
|
271
|
+
await assert.rejects(
|
|
272
|
+
runGeneratorSubcommand({
|
|
273
|
+
appRoot,
|
|
274
|
+
subcommand: "outlet",
|
|
275
|
+
args: [targetFile],
|
|
276
|
+
options: {
|
|
277
|
+
target: "vet-view"
|
|
278
|
+
}
|
|
279
|
+
}),
|
|
280
|
+
/ui-generator outlet target file must be an existing Vue SFC \(\.vue\): src\/pages\/w\/\[workspaceSlug\]\/admin\/practice\/vets\/_components\/VetAddEditFormFields\.js\./
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const output = await readFile(targetPath, "utf8");
|
|
284
|
+
assert.equal(output, originalSource);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
262
288
|
test("ui-generator outlet validates target format", async () => {
|
|
263
289
|
await withTempApp(async (appRoot) => {
|
|
264
290
|
const targetFile = "src/components/ContactDetailsPanel.vue";
|
|
@@ -73,6 +73,7 @@ test("ui-generator page subcommand creates an index page from an explicit target
|
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
assert.deepEqual(result.touchedFiles, [toPagePath(targetFile), "src/placement.js"]);
|
|
76
|
+
assert.equal(result.summary, 'Generated UI page "/practice" at src/pages/w/[workspaceSlug]/admin/practice/index.vue.');
|
|
76
77
|
|
|
77
78
|
const pageSource = await readFile(path.join(appRoot, toPagePath(targetFile)), "utf8");
|
|
78
79
|
assert.match(pageSource, /<h1 class="text-h5 mb-2">Practice<\/h1>/);
|
|
@@ -350,3 +351,65 @@ test("ui-generator page subcommand overwrites an existing page when --force is p
|
|
|
350
351
|
assert.doesNotMatch(pageSource, /custom practice page/);
|
|
351
352
|
});
|
|
352
353
|
});
|
|
354
|
+
|
|
355
|
+
test("ui-generator page subcommand rejects invalid link placement before creating a new page", async () => {
|
|
356
|
+
await withTempApp(async (appRoot) => {
|
|
357
|
+
await writeAppFixture(appRoot);
|
|
358
|
+
|
|
359
|
+
const targetFile = "w/[workspaceSlug]/admin/practice/index.vue";
|
|
360
|
+
const placementPath = path.join(appRoot, "src", "placement.js");
|
|
361
|
+
const originalPlacementSource = await readFile(placementPath, "utf8");
|
|
362
|
+
|
|
363
|
+
await assert.rejects(
|
|
364
|
+
runGeneratorSubcommand({
|
|
365
|
+
appRoot,
|
|
366
|
+
subcommand: "page",
|
|
367
|
+
args: [targetFile],
|
|
368
|
+
options: {
|
|
369
|
+
"link-placement": "missing:target"
|
|
370
|
+
}
|
|
371
|
+
}),
|
|
372
|
+
/ui-generator page option "placement" target "missing:target" is not declared/
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
await assert.rejects(readFile(path.join(appRoot, toPagePath(targetFile)), "utf8"), /ENOENT/);
|
|
376
|
+
const placementSource = await readFile(placementPath, "utf8");
|
|
377
|
+
assert.equal(placementSource, originalPlacementSource);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("ui-generator page subcommand rejects invalid link placement before overwriting an existing page", async () => {
|
|
382
|
+
await withTempApp(async (appRoot) => {
|
|
383
|
+
await writeAppFixture(appRoot);
|
|
384
|
+
|
|
385
|
+
const targetFile = "w/[workspaceSlug]/admin/practice/index.vue";
|
|
386
|
+
const targetPath = path.join(appRoot, toPagePath(targetFile));
|
|
387
|
+
const originalPageSource = `<template>
|
|
388
|
+
<div>custom practice page</div>
|
|
389
|
+
</template>
|
|
390
|
+
`;
|
|
391
|
+
const placementPath = path.join(appRoot, "src", "placement.js");
|
|
392
|
+
const originalPlacementSource = await readFile(placementPath, "utf8");
|
|
393
|
+
|
|
394
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
395
|
+
await writeFile(targetPath, originalPageSource, "utf8");
|
|
396
|
+
|
|
397
|
+
await assert.rejects(
|
|
398
|
+
runGeneratorSubcommand({
|
|
399
|
+
appRoot,
|
|
400
|
+
subcommand: "page",
|
|
401
|
+
args: [targetFile],
|
|
402
|
+
options: {
|
|
403
|
+
force: "true",
|
|
404
|
+
"link-placement": "missing:target"
|
|
405
|
+
}
|
|
406
|
+
}),
|
|
407
|
+
/ui-generator page option "placement" target "missing:target" is not declared/
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
const pageSource = await readFile(targetPath, "utf8");
|
|
411
|
+
assert.equal(pageSource, originalPageSource);
|
|
412
|
+
const placementSource = await readFile(placementPath, "utf8");
|
|
413
|
+
assert.equal(placementSource, originalPlacementSource);
|
|
414
|
+
});
|
|
415
|
+
});
|