@dusted/anqst 1.5.1 → 1.7.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/README.md CHANGED
@@ -43,7 +43,7 @@ Settings file (`./AnQst/<WidgetName>.settings.json`) owns project-local AnQst co
43
43
  "layoutVersion": 2,
44
44
  "widgetName": "<WidgetName>",
45
45
  "spec": "./AnQst/<WidgetName>.AnQst.d.ts",
46
- "generate": ["QWidget", "AngularService", "node_express_ws"],
46
+ "generate": ["QWidget", "AngularService", "VanillaTS", "VanillaJS", "node_express_ws"],
47
47
  "widgetCategory": "AnQst Widgets"
48
48
  }
49
49
  ```
@@ -63,6 +63,16 @@ Settings file (`./AnQst/<WidgetName>.settings.json`) owns project-local AnQst co
63
63
  - Updates `tsconfig.json` (when present):
64
64
  - `compilerOptions.paths["anqst-generated/*"] = ["AnQst/generated/frontend/<WidgetName>_Angular/*"]`
65
65
 
66
+ Available generate targets:
67
+
68
+ - Browser frontend targets:
69
+ - `AngularService`
70
+ - `VanillaTS`
71
+ - `VanillaJS`
72
+ - Backend targets:
73
+ - `QWidget`
74
+ - `node_express_ws`
75
+
66
76
  - `anqst test`
67
77
  - Loads settings from `package.json.AnQst`.
68
78
  - Verifies the configured spec.
@@ -72,9 +82,11 @@ Settings file (`./AnQst/<WidgetName>.settings.json`) owns project-local AnQst co
72
82
  - Verifies spec and regenerates selected targets.
73
83
  - Writes only under `./AnQst/generated`.
74
84
  - Removes selected target roots before regeneration (no stale generated files).
75
- - If `QWidget` is enabled and `angular.json` exists:
76
- - runs production Angular build
85
+ - If `QWidget` is enabled and a browser build output is present under project `dist/`:
77
86
  - embeds built web assets into generated Qt widget `webapp/`.
87
+ - If `QWidget` is enabled and `angular.json` exists:
88
+ - `anqst build` may invoke a production Angular build before embedding.
89
+ - Browser bundle discovery is frontend-profile-neutral: Angular and Vanilla browser outputs are both expected to produce a dist tree containing `index.html`.
78
90
  - Generated Qt integration CMake consumes the existing `./AnQst/generated` widget tree and fails fast if the required generated files are missing.
79
91
  - Downstream CMake no longer invokes `npm`, `npx`, or `anqst`; run `anqst build` first, then build C++ against the generated tree.
80
92
  - If `--designerplugin` is enabled:
@@ -106,6 +118,8 @@ Settings file (`./AnQst/<WidgetName>.settings.json`) owns project-local AnQst co
106
118
  generated/
107
119
  frontend/
108
120
  <WidgetName>_Angular/
121
+ <WidgetName>_VanillaTS/
122
+ <WidgetName>_VanillaJS/
109
123
  backend/
110
124
  node/
111
125
  express/
@@ -143,10 +157,37 @@ npx @dusted/anqst test
143
157
  npx @dusted/anqst build
144
158
  ```
145
159
 
160
+ ## Vanilla browser usage
161
+
162
+ Minimal browser-global usage for `VanillaJS`:
163
+
164
+ ```html
165
+ <script src="./AnQst/generated/frontend/BurgerConstructor_VanillaJS/index.js"></script>
166
+ <script>
167
+ (async () => {
168
+ const frontend = await window.AnQstGenerated.widgets.BurgerConstructor.createFrontend();
169
+ const ok = await frontend.services.BurgerService.validateDraft({ name: "Classic" });
170
+ console.log(ok);
171
+ })();
172
+ </script>
173
+ ```
174
+
175
+ TypeScript authors use the same runtime shape with typings from `VanillaTS`:
176
+
177
+ ```ts
178
+ /// <reference path="./AnQst/generated/frontend/BurgerConstructor_VanillaTS/index.d.ts" />
179
+
180
+ async function boot() {
181
+ const frontend = await window.AnQstGenerated.widgets.BurgerConstructor.createFrontend();
182
+ const ok = await frontend.services.BurgerService.validateDraft({ name: "Classic" });
183
+ console.log(frontend.diagnostics.state(), ok);
184
+ }
185
+ ```
186
+
146
187
  ## Two-stage workflow
147
188
 
148
189
  ```bash
149
- # Stage 1: Node/Angular/generation environment
190
+ # Stage 1: browser/backend/generation environment
150
191
  npx @dusted/anqst build
151
192
 
152
193
  # Stage 2: pure Qt/CMake environment, consuming the generated tree
package/dist/src/app.js CHANGED
@@ -25,6 +25,14 @@ class CliUsageError extends Error {
25
25
  this.name = "CliUsageError";
26
26
  }
27
27
  }
28
+ function firstBrowserFrontendTarget(targets) {
29
+ for (const target of targets) {
30
+ if (target === "AngularService" || target === "VanillaTS" || target === "VanillaJS") {
31
+ return target;
32
+ }
33
+ }
34
+ return null;
35
+ }
28
36
  const ANQSTGEN_ACTIVE_STAMP_FILE = ".anqstgen-version-active.json";
29
37
  function renderHelp() {
30
38
  const version = readActiveBuildStamp();
@@ -65,7 +73,13 @@ function resetGeneratedTargets(cwd, widgetName, targets) {
65
73
  const layout = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, widgetName);
66
74
  const roots = new Set();
67
75
  if (targets.emitAngularService) {
68
- roots.add(layout.frontendRoot);
76
+ roots.add(layout.angularFrontendRoot);
77
+ }
78
+ if (targets.emitVanillaTS) {
79
+ roots.add(layout.vanillaTsFrontendRoot);
80
+ }
81
+ if (targets.emitVanillaJS) {
82
+ roots.add(layout.vanillaJsFrontendRoot);
69
83
  }
70
84
  if (targets.emitNodeExpressWs) {
71
85
  roots.add(layout.nodeExpressRoot);
@@ -84,15 +98,19 @@ function resetGeneratedTargets(cwd, widgetName, targets) {
84
98
  function buildGenerateSummary(cwd, specPath, widgetName, generationTargets) {
85
99
  const layout = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, widgetName);
86
100
  const relativeSpecFile = (0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, specPath));
87
- const servicePath = (0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "services"));
88
- const typePath = (0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "types"));
89
101
  const widgetRootPath = (0, layout_1.toProjectRelative)(cwd, layout.cppQtWidgetRoot);
90
102
  const nodePath = (0, layout_1.toProjectRelative)(cwd, layout.nodeExpressRoot);
91
103
  const messageLines = [];
92
104
  messageLines.push(`AnQst spec ${relativeSpecFile} built.`);
93
105
  if (generationTargets.emitAngularService) {
94
- messageLines.push(` Services are available from ${servicePath}.`);
95
- messageLines.push(` Generated types are available from ${typePath}.`);
106
+ messageLines.push(` Angular services are available from ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.angularFrontendRoot, "services"))}.`);
107
+ messageLines.push(` Angular generated types are available from ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.angularFrontendRoot, "types"))}.`);
108
+ }
109
+ if (generationTargets.emitVanillaTS) {
110
+ messageLines.push(` VanillaTS browser bundle is available from ${(0, layout_1.toProjectRelative)(cwd, layout.vanillaTsFrontendRoot)}.`);
111
+ }
112
+ if (generationTargets.emitVanillaJS) {
113
+ messageLines.push(` VanillaJS browser bundle is available from ${(0, layout_1.toProjectRelative)(cwd, layout.vanillaJsFrontendRoot)}.`);
96
114
  }
97
115
  if (generationTargets.emitQWidget) {
98
116
  messageLines.push(` Widget library available in ${widgetRootPath}.`);
@@ -100,7 +118,11 @@ function buildGenerateSummary(cwd, specPath, widgetName, generationTargets) {
100
118
  if (generationTargets.emitNodeExpressWs) {
101
119
  messageLines.push(` Node Express WS module available in ${nodePath}.`);
102
120
  }
103
- if (!generationTargets.emitAngularService && !generationTargets.emitQWidget && !generationTargets.emitNodeExpressWs) {
121
+ if (!generationTargets.emitAngularService
122
+ && !generationTargets.emitVanillaTS
123
+ && !generationTargets.emitVanillaJS
124
+ && !generationTargets.emitQWidget
125
+ && !generationTargets.emitNodeExpressWs) {
104
126
  messageLines.push(" No outputs selected by AnQst.generate.");
105
127
  }
106
128
  return `\n${messageLines.join("\n")}\n`;
@@ -205,7 +227,9 @@ function runBuild(cwd, designerPlugin = false) {
205
227
  try {
206
228
  const specPath = (0, project_1.resolveAnQstSpecPath)(cwd);
207
229
  const configuredWidgetName = (0, project_1.resolveAnQstWidgetName)(cwd);
230
+ const configuredTargets = (0, project_1.resolveAnQstGenerateTargets)(cwd);
208
231
  const generationTargets = resolveGenerationTargetsFromCwd(cwd, true);
232
+ const preferredFrontendTarget = firstBrowserFrontendTarget(configuredTargets);
209
233
  const parsed = (0, parser_1.parseSpecFile)(specPath);
210
234
  (0, verify_1.verifySpec)(parsed);
211
235
  if (parsed.widgetName !== configuredWidgetName) {
@@ -217,8 +241,10 @@ function runBuild(cwd, designerPlugin = false) {
217
241
  if (generationTargets.emitQWidget) {
218
242
  (0, emit_1.installQtIntegrationCMake)(cwd, parsed.widgetName);
219
243
  }
220
- const hasAngularProject = generationTargets.emitQWidget && node_fs_1.default.existsSync(node_path_1.default.join(cwd, "angular.json"));
221
- if (hasAngularProject) {
244
+ const shouldRunAngularBuild = generationTargets.emitQWidget
245
+ && preferredFrontendTarget === "AngularService"
246
+ && node_fs_1.default.existsSync(node_path_1.default.join(cwd, "angular.json"));
247
+ if (shouldRunAngularBuild) {
222
248
  const angularBuild = (0, node_child_process_1.spawnSync)("npx", ["ng", "build", "--configuration", "production"], {
223
249
  cwd,
224
250
  stdio: "inherit",
@@ -228,10 +254,27 @@ function runBuild(cwd, designerPlugin = false) {
228
254
  throw new errors_1.VerifyError("Angular build failed while preparing embedded widget assets.");
229
255
  }
230
256
  }
257
+ let embeddedAssetsRefreshed = false;
231
258
  if (generationTargets.emitQWidget) {
259
+ if (preferredFrontendTarget === "VanillaJS") {
260
+ try {
261
+ (0, emit_1.buildVanillaJsBrowserBundle)(cwd, parsed.widgetName);
262
+ }
263
+ catch (error) {
264
+ const message = error instanceof Error ? error.message : String(error);
265
+ throw new errors_1.VerifyError(`VanillaJS browser packaging failed: ${message}`);
266
+ }
267
+ }
232
268
  const embedded = (0, emit_1.installEmbeddedWebBundle)(cwd, parsed.widgetName);
233
- if (hasAngularProject && !embedded) {
234
- throw new errors_1.VerifyError("Unable to embed Angular output. Ensure ng build produced a dist bundle with index.html.");
269
+ embeddedAssetsRefreshed = embedded;
270
+ if (preferredFrontendTarget === "VanillaJS" && !embedded) {
271
+ throw new errors_1.VerifyError("Unable to embed VanillaJS browser output. Ensure src/index.html and src/main.js produced a browser bundle.");
272
+ }
273
+ if (preferredFrontendTarget === "VanillaTS" && !embedded) {
274
+ throw new errors_1.VerifyError("Unable to embed VanillaTS browser output. Ensure the widget frontend build produced dist/browser with index.html.");
275
+ }
276
+ if (shouldRunAngularBuild && !embedded) {
277
+ throw new errors_1.VerifyError("Unable to embed browser output. Ensure the browser build produced a dist bundle with index.html.");
235
278
  }
236
279
  }
237
280
  let designerPluginBuilt = false;
@@ -246,7 +289,11 @@ function runBuild(cwd, designerPlugin = false) {
246
289
  designerPluginBuilt = true;
247
290
  }
248
291
  }
249
- if (!generationTargets.emitAngularService && !generationTargets.emitQWidget && !generationTargets.emitNodeExpressWs) {
292
+ if (!generationTargets.emitAngularService
293
+ && !generationTargets.emitVanillaTS
294
+ && !generationTargets.emitVanillaJS
295
+ && !generationTargets.emitQWidget
296
+ && !generationTargets.emitNodeExpressWs) {
250
297
  return {
251
298
  success: true,
252
299
  message: [
@@ -260,15 +307,27 @@ function runBuild(cwd, designerPlugin = false) {
260
307
  const detailLines = [];
261
308
  if (generationTargets.emitAngularService) {
262
309
  detailLines.push(" Target AngularService:");
263
- detailLines.push(` - Services output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "services"))}`);
264
- detailLines.push(` - Types output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "types"))}`);
310
+ detailLines.push(` - Services output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.angularFrontendRoot, "services"))}`);
311
+ detailLines.push(` - Types output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.angularFrontendRoot, "types"))}`);
312
+ }
313
+ if (generationTargets.emitVanillaTS) {
314
+ detailLines.push(" Target VanillaTS:");
315
+ detailLines.push(` - Browser bundle root: ${(0, layout_1.toProjectRelative)(cwd, layout.vanillaTsFrontendRoot)}`);
316
+ detailLines.push(` - Browser global: window.AnQstGenerated.${parsed.widgetName}`);
317
+ }
318
+ if (generationTargets.emitVanillaJS) {
319
+ detailLines.push(" Target VanillaJS:");
320
+ detailLines.push(` - Browser bundle root: ${(0, layout_1.toProjectRelative)(cwd, layout.vanillaJsFrontendRoot)}`);
321
+ detailLines.push(` - Browser global: window.AnQstGenerated.${parsed.widgetName}`);
265
322
  }
266
323
  if (generationTargets.emitQWidget) {
267
324
  detailLines.push(" Target QWidget:");
268
325
  detailLines.push(` - Qt integration CMake: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.cppCmakeRoot, "CMakeLists.txt"))}`);
269
326
  detailLines.push(` - Widget output root: ${(0, layout_1.toProjectRelative)(cwd, layout.cppQtWidgetRoot)}`);
270
327
  detailLines.push(" - C++ handoff: downstream CMake consumes this generated tree directly");
271
- detailLines.push(" - Embedded web assets refreshed from Angular build");
328
+ if (embeddedAssetsRefreshed) {
329
+ detailLines.push(" - Embedded web assets refreshed from detected browser dist output");
330
+ }
272
331
  }
273
332
  if (generationTargets.emitNodeExpressWs) {
274
333
  detailLines.push(" Target node_express_ws:");
@@ -436,6 +495,8 @@ function toGenerationTargets(targets) {
436
495
  return {
437
496
  emitQWidget: targets.includes("QWidget"),
438
497
  emitAngularService: targets.includes("AngularService"),
498
+ emitVanillaTS: targets.includes("VanillaTS"),
499
+ emitVanillaJS: targets.includes("VanillaJS"),
439
500
  emitNodeExpressWs: targets.includes("node_express_ws")
440
501
  };
441
502
  }
@@ -452,7 +513,9 @@ function runClean(pathArg, force) {
452
513
  const context = (0, project_1.resolveAnQstSettings)(targetRoot);
453
514
  const layout = (0, layout_1.resolveGeneratedLayoutPaths)(targetRoot, context.settings.widgetName);
454
515
  const widgetDirs = [
455
- (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.frontendRoot)),
516
+ (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.angularFrontendRoot)),
517
+ (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.vanillaTsFrontendRoot)),
518
+ (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.vanillaJsFrontendRoot)),
456
519
  (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.nodeExpressRoot)),
457
520
  (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.cppQtWidgetRoot)),
458
521
  (0, layout_1.normalizeSlashes)(node_path_1.default.relative(targetRoot, layout.cppCmakeRoot)),
@@ -128,8 +128,10 @@ class BoundaryTransportAnalyzer {
128
128
  const out = new Map();
129
129
  for (const decl of this.spec.namespaceTypeDecls)
130
130
  out.set(decl.name, decl);
131
- for (const decl of this.spec.importedTypeDecls.values())
132
- out.set(decl.name, decl);
131
+ for (const decl of this.spec.importedTypeDecls.values()) {
132
+ if (!out.has(decl.name))
133
+ out.set(decl.name, decl);
134
+ }
133
135
  return [...out.values()];
134
136
  }
135
137
  nodeMeta(typeText, path, identityParts) {