@dusted/anqst 0.1.1 → 0.1.2

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.
@@ -5,189 +5,272 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DEFAULT_ANQST_GENERATE_TARGETS = void 0;
7
7
  exports.readProjectPackage = readProjectPackage;
8
+ exports.resolveAnQstSettings = resolveAnQstSettings;
8
9
  exports.resolveAnQstSpecPath = resolveAnQstSpecPath;
9
10
  exports.resolveAnQstGenerateTargets = resolveAnQstGenerateTargets;
10
11
  exports.resolveAnQstWidgetCategory = resolveAnQstWidgetCategory;
12
+ exports.resolveAnQstWidgetName = resolveAnQstWidgetName;
11
13
  exports.buildSpecScaffold = buildSpecScaffold;
12
14
  exports.runInstill = runInstill;
13
15
  const node_fs_1 = __importDefault(require("node:fs"));
14
16
  const node_path_1 = __importDefault(require("node:path"));
15
17
  const errors_1 = require("./errors");
16
- exports.DEFAULT_ANQST_GENERATE_TARGETS = ["QWidget", "AngularService", "//DOM", "//node_express_ws"];
17
- const ANQST_DSL_IMPORT_LINE = 'import { AnQst } from "anqst";';
18
+ const layout_1 = require("./layout");
19
+ exports.DEFAULT_ANQST_GENERATE_TARGETS = ["QWidget", "AngularService", "node_express_ws"];
20
+ const ANQST_DSL_IMPORT_LINE = 'import type { AnQst } from "@dusted/anqst";';
21
+ const ANQST_BUILD_HOOK = "npx anqst build";
18
22
  function readJsonFile(filePath) {
19
- const raw = node_fs_1.default.readFileSync(filePath, "utf8");
20
- return JSON.parse(raw);
23
+ try {
24
+ const raw = node_fs_1.default.readFileSync(filePath, "utf8");
25
+ return JSON.parse(raw);
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ throw new errors_1.VerifyError(`Unable to parse JSON file '${(0, layout_1.normalizeSlashes)(filePath)}': ${message}`);
30
+ }
31
+ }
32
+ function ensureObject(value, errorMessage) {
33
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
34
+ throw new errors_1.VerifyError(errorMessage);
35
+ }
36
+ return value;
21
37
  }
22
- function prependScript(existing, prefix) {
23
- if (!existing || existing.trim().length === 0)
24
- return prefix;
38
+ function ensureBuildHook(existing) {
39
+ if (!existing || existing.trim().length === 0) {
40
+ return ANQST_BUILD_HOOK;
41
+ }
25
42
  const trimmed = existing.trim();
26
- if (trimmed === prefix || trimmed.startsWith(`${prefix} &&`))
43
+ if (trimmed === ANQST_BUILD_HOOK || trimmed.startsWith(`${ANQST_BUILD_HOOK} &&`) || trimmed.includes(ANQST_BUILD_HOOK)) {
27
44
  return trimmed;
28
- return `${prefix} && ${trimmed}`;
45
+ }
46
+ return `${ANQST_BUILD_HOOK} && ${trimmed}`;
47
+ }
48
+ function normalizeAnQstImport(sourceText) {
49
+ const importPattern = /^\s*import\s+(?:type\s+)?\{\s*AnQst\s*\}\s+from\s+["'][^"']+["'];\s*$/m;
50
+ if (importPattern.test(sourceText)) {
51
+ const nextText = sourceText.replace(importPattern, ANQST_DSL_IMPORT_LINE);
52
+ return { nextText, changed: nextText !== sourceText };
53
+ }
54
+ return {
55
+ nextText: `${ANQST_DSL_IMPORT_LINE}\n\n${sourceText}`,
56
+ changed: true
57
+ };
58
+ }
59
+ function isSubPath(parentPath, childPath) {
60
+ const relative = node_path_1.default.relative(parentPath, childPath);
61
+ if (relative === "") {
62
+ return true;
63
+ }
64
+ return !relative.startsWith("..") && !node_path_1.default.isAbsolute(relative);
65
+ }
66
+ function readAnQstSettingsFromPath(cwd, settingsPath) {
67
+ if (!node_fs_1.default.existsSync(settingsPath)) {
68
+ throw new errors_1.VerifyError(`Missing AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}'. Run 'anqst instill <WidgetName>' first.`);
69
+ }
70
+ const raw = readJsonFile(settingsPath);
71
+ const settingsObject = ensureObject(raw, `Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': expected JSON object.`);
72
+ const layoutVersion = settingsObject.layoutVersion;
73
+ if (layoutVersion !== layout_1.ANQST_LAYOUT_VERSION) {
74
+ throw new errors_1.VerifyError(`Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': expected layoutVersion ${layout_1.ANQST_LAYOUT_VERSION}.`);
75
+ }
76
+ const widgetName = settingsObject.widgetName;
77
+ if (typeof widgetName !== "string" || widgetName.trim().length === 0) {
78
+ throw new errors_1.VerifyError(`Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': expected non-empty string 'widgetName'.`);
79
+ }
80
+ const spec = settingsObject.spec;
81
+ if (typeof spec !== "string" || spec.trim().length === 0) {
82
+ throw new errors_1.VerifyError(`Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': expected non-empty string 'spec'.`);
83
+ }
84
+ const generate = settingsObject.generate;
85
+ if (generate !== undefined && (!Array.isArray(generate) || generate.some((entry) => typeof entry !== "string"))) {
86
+ throw new errors_1.VerifyError(`Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': expected string array 'generate'.`);
87
+ }
88
+ const widgetCategory = settingsObject.widgetCategory;
89
+ if (widgetCategory !== undefined) {
90
+ if (typeof widgetCategory !== "string" || widgetCategory.trim().length === 0) {
91
+ throw new errors_1.VerifyError(`Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': expected non-empty string 'widgetCategory'.`);
92
+ }
93
+ }
94
+ const resolvedSpecPath = node_path_1.default.resolve(cwd, spec.trim());
95
+ const anqstRoot = (0, layout_1.anqstRootDir)(cwd);
96
+ if (!isSubPath(anqstRoot, resolvedSpecPath)) {
97
+ throw new errors_1.VerifyError(`Invalid AnQst settings file '${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, settingsPath))}': 'spec' must resolve inside './AnQst'.`);
98
+ }
99
+ return {
100
+ layoutVersion,
101
+ widgetName: widgetName.trim(),
102
+ spec: spec.trim(),
103
+ generate: Array.isArray(generate) ? [...generate] : undefined,
104
+ widgetCategory: typeof widgetCategory === "string" ? widgetCategory.trim() : undefined
105
+ };
106
+ }
107
+ function resolveSettingsPathFromPackage(cwd, packageJson) {
108
+ const settingsRef = packageJson.AnQst;
109
+ if (settingsRef === undefined) {
110
+ throw new errors_1.VerifyError("Missing package.json key 'AnQst'. Run 'anqst instill <WidgetName>' first.");
111
+ }
112
+ if (typeof settingsRef !== "string" || settingsRef.trim().length === 0) {
113
+ throw new errors_1.VerifyError("Invalid package.json key 'AnQst': expected non-empty string path to settings JSON.");
114
+ }
115
+ return node_path_1.default.resolve(cwd, settingsRef.trim());
116
+ }
117
+ function buildAnQstDirectoryReadme(widgetName) {
118
+ return [
119
+ "# AnQst Project Directory",
120
+ "",
121
+ "This directory is owned by the AnQst CLI for this project.",
122
+ "",
123
+ "## Files",
124
+ "",
125
+ `- \`${(0, layout_1.anqstSpecFileName)(widgetName)}\`: AnQst widget spec source.`,
126
+ `- \`${(0, layout_1.anqstSettingsFileName)(widgetName)}\`: project-local AnQst configuration used by \`anqst build\`.`,
127
+ "- `generated/`: deterministic build output roots managed by `anqst build`.",
128
+ "",
129
+ "## Regeneration",
130
+ "",
131
+ "- `npx anqst build` refreshes generated outputs under `generated/`.",
132
+ "- Build hooks in package.json (`postinstall`, `prebuild`, `prestart`) call `npx anqst build`.",
133
+ "",
134
+ "Do not hand-edit generated files under `generated/`; they are overwritten by design.",
135
+ ""
136
+ ].join("\n");
137
+ }
138
+ function updateTsConfig(cwd, widgetName) {
139
+ const tsConfigPath = node_path_1.default.join(cwd, "tsconfig.json");
140
+ if (!node_fs_1.default.existsSync(tsConfigPath)) {
141
+ return;
142
+ }
143
+ const tsConfigRaw = readJsonFile(tsConfigPath);
144
+ const tsConfig = ensureObject(tsConfigRaw, "Invalid tsconfig.json: expected top-level object.");
145
+ const compilerOptions = tsConfig.compilerOptions === undefined
146
+ ? {}
147
+ : ensureObject(tsConfig.compilerOptions, "Invalid tsconfig.json: expected object at 'compilerOptions'.");
148
+ tsConfig.compilerOptions = compilerOptions;
149
+ if (compilerOptions.baseUrl === undefined) {
150
+ compilerOptions.baseUrl = ".";
151
+ }
152
+ const pathsObject = compilerOptions.paths === undefined
153
+ ? {}
154
+ : ensureObject(compilerOptions.paths, "Invalid tsconfig.json: expected object at 'compilerOptions.paths'.");
155
+ compilerOptions.paths = pathsObject;
156
+ const generatedAliasPath = `AnQst/generated/frontend/${(0, layout_1.generatedFrontendDirName)(widgetName)}/*`;
157
+ const existingAlias = pathsObject["anqst-generated/*"];
158
+ const aliasList = Array.isArray(existingAlias)
159
+ ? existingAlias.filter((entry) => typeof entry === "string")
160
+ : [];
161
+ if (!aliasList.includes(generatedAliasPath)) {
162
+ aliasList.unshift(generatedAliasPath);
163
+ }
164
+ pathsObject["anqst-generated/*"] = [...new Set(aliasList)];
165
+ if (Array.isArray(tsConfig.include)) {
166
+ const includeList = tsConfig.include.filter((entry) => typeof entry === "string");
167
+ const generatedTypesPattern = `AnQst/generated/frontend/${(0, layout_1.generatedFrontendDirName)(widgetName)}/**/*.d.ts`;
168
+ if (!includeList.includes(generatedTypesPattern)) {
169
+ includeList.push(generatedTypesPattern);
170
+ }
171
+ tsConfig.include = includeList;
172
+ }
173
+ node_fs_1.default.writeFileSync(tsConfigPath, `${JSON.stringify(tsConfig, null, 2)}\n`, "utf8");
174
+ }
175
+ function validateWidgetName(widgetName) {
176
+ const trimmed = widgetName.trim();
177
+ if (trimmed.length === 0) {
178
+ throw new errors_1.VerifyError("Usage: anqst instill <WidgetName>");
179
+ }
180
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed)) {
181
+ throw new errors_1.VerifyError("Invalid widget name: expected a TypeScript identifier for namespace generation.");
182
+ }
183
+ return trimmed;
29
184
  }
30
185
  function readProjectPackage(cwd) {
31
186
  const packagePath = node_path_1.default.join(cwd, "package.json");
32
187
  if (!node_fs_1.default.existsSync(packagePath)) {
33
- throw new errors_1.VerifyError("No package.json: Can only instill AnQst inside an npm project.");
188
+ throw new errors_1.VerifyError("No package.json: AnQst commands must run inside an npm project.");
34
189
  }
35
190
  return {
36
191
  packagePath,
37
192
  packageJson: readJsonFile(packagePath)
38
193
  };
39
194
  }
40
- function resolveAnQstSpecPath(cwd) {
195
+ function resolveAnQstSettings(cwd) {
41
196
  const { packageJson } = readProjectPackage(cwd);
42
- const spec = packageJson.AnQst?.spec;
43
- if (!spec || spec.trim().length === 0) {
44
- throw new errors_1.VerifyError("Missing package.json key 'AnQst.spec'. Run 'anqst instill <WidgetName>' first.");
45
- }
46
- return node_path_1.default.resolve(cwd, spec);
197
+ const settingsPath = resolveSettingsPathFromPackage(cwd, packageJson);
198
+ return {
199
+ settingsPath,
200
+ settings: readAnQstSettingsFromPath(cwd, settingsPath)
201
+ };
202
+ }
203
+ function resolveAnQstSpecPath(cwd) {
204
+ const { settings } = resolveAnQstSettings(cwd);
205
+ return node_path_1.default.resolve(cwd, settings.spec);
47
206
  }
48
207
  function resolveAnQstGenerateTargets(cwd) {
49
- const { packageJson } = readProjectPackage(cwd);
50
- const configured = packageJson.AnQst?.generate;
51
- if (configured === undefined) {
208
+ const { settings } = resolveAnQstSettings(cwd);
209
+ if (!settings.generate) {
52
210
  return [...exports.DEFAULT_ANQST_GENERATE_TARGETS];
53
211
  }
54
- if (!Array.isArray(configured) || configured.some((value) => typeof value !== "string")) {
55
- throw new errors_1.VerifyError("Invalid package.json key 'AnQst.generate': expected string array.");
56
- }
57
- return [...configured];
212
+ return [...settings.generate];
58
213
  }
59
214
  function resolveAnQstWidgetCategory(cwd) {
60
- const { packageJson } = readProjectPackage(cwd);
61
- const configured = packageJson.AnQst?.widgetCategory;
62
- if (configured === undefined) {
63
- return undefined;
64
- }
65
- if (typeof configured !== "string") {
66
- throw new errors_1.VerifyError("Invalid package.json key 'AnQst.widgetCategory': expected string.");
67
- }
68
- const trimmed = configured.trim();
69
- if (trimmed.length === 0) {
70
- throw new errors_1.VerifyError("Invalid package.json key 'AnQst.widgetCategory': expected non-empty string.");
71
- }
72
- return trimmed;
215
+ const { settings } = resolveAnQstSettings(cwd);
216
+ return settings.widgetCategory;
217
+ }
218
+ function resolveAnQstWidgetName(cwd) {
219
+ const { settings } = resolveAnQstSettings(cwd);
220
+ return settings.widgetName;
73
221
  }
74
222
  function buildSpecScaffold(widgetName) {
75
223
  return `${ANQST_DSL_IMPORT_LINE}
76
224
 
77
- declare namespace ${widgetName} {
225
+ ` +
226
+ `declare namespace ${widgetName} {
78
227
 
79
- }
228
+ ` +
229
+ `}
80
230
  `;
81
231
  }
82
- function normalizeAnQstImport(sourceText) {
83
- const importPattern = /^\s*import\s+\{\s*AnQst\s*\}\s+from\s+["'][^"']+["'];\s*$/m;
84
- if (importPattern.test(sourceText)) {
85
- const nextText = sourceText.replace(importPattern, ANQST_DSL_IMPORT_LINE);
86
- return { nextText, changed: nextText !== sourceText };
87
- }
88
- return {
89
- nextText: `${ANQST_DSL_IMPORT_LINE}\n\n${sourceText}`,
90
- changed: true
91
- };
92
- }
93
- function extractDeclaredNamespace(specText) {
94
- const match = specText.match(/^\s*declare\s+namespace\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*\{/m);
95
- return match ? match[1] : null;
96
- }
97
- function readLineSync() {
98
- const out = [];
99
- const buf = Buffer.alloc(1);
100
- while (true) {
101
- const bytesRead = node_fs_1.default.readSync(0, buf, 0, 1, null);
102
- if (bytesRead <= 0)
103
- break;
104
- const code = buf[0];
105
- if (code === 10)
106
- break; // \n
107
- if (code === 13)
108
- continue; // \r
109
- out.push(code);
110
- }
111
- return Buffer.from(out).toString("utf8").trim();
112
- }
113
- function chooseWidgetNamePreference(argumentName, namespaceName, specFileName) {
114
- const envChoice = process.env.ANQST_INSTILL_WIDGET_NAME_CHOICE;
115
- if (envChoice === "argument" || envChoice === "namespace") {
116
- return envChoice;
117
- }
118
- if (!process.stdin.isTTY) {
119
- console.warn(`[AnQst] Existing template ${specFileName} declares namespace '${namespaceName}', but command used '${argumentName}'. Non-interactive session; defaulting to '${argumentName}'.`);
120
- return "argument";
121
- }
122
- console.log(`[AnQst] Existing template ${specFileName} declares namespace '${namespaceName}'.`);
123
- console.log(`Choose widget name: [1] ${argumentName} (command argument), [2] ${namespaceName} (template namespace)`);
124
- while (true) {
125
- process.stdout.write("Selection [1/2]: ");
126
- const answer = readLineSync().toLowerCase();
127
- if (answer === "1" || answer === argumentName.toLowerCase() || answer === "argument") {
128
- return "argument";
129
- }
130
- if (answer === "2" || answer === namespaceName.toLowerCase() || answer === "namespace") {
131
- return "namespace";
132
- }
133
- console.log("Please type 1 or 2.");
134
- }
135
- }
136
232
  function runInstill(cwd, widgetName) {
137
- if (!widgetName || widgetName.trim().length === 0) {
138
- throw new errors_1.VerifyError("Usage: anqst instill <WidgetName>");
139
- }
140
- const cleanName = widgetName.trim();
233
+ const cleanName = validateWidgetName(widgetName);
141
234
  const { packagePath, packageJson } = readProjectPackage(cwd);
142
- if (packageJson.AnQst) {
235
+ if (packageJson.AnQst !== undefined) {
143
236
  throw new errors_1.VerifyError("AnQst already instilled, did you mean to run 'npx anqst build'?");
144
237
  }
145
- let resolvedWidgetName = cleanName;
146
- const requestedSpecPath = node_path_1.default.join(cwd, `${cleanName}.AnQst.d.ts`);
147
- const requestedSpecFileName = node_path_1.default.basename(requestedSpecPath);
148
- if (node_fs_1.default.existsSync(requestedSpecPath)) {
149
- const existingText = node_fs_1.default.readFileSync(requestedSpecPath, "utf8");
238
+ const anqstRoot = (0, layout_1.anqstRootDir)(cwd);
239
+ node_fs_1.default.mkdirSync(anqstRoot, { recursive: true });
240
+ node_fs_1.default.mkdirSync(node_path_1.default.join(anqstRoot, "generated"), { recursive: true });
241
+ const specPath = node_path_1.default.join(anqstRoot, (0, layout_1.anqstSpecFileName)(cleanName));
242
+ if (node_fs_1.default.existsSync(specPath)) {
243
+ const existingText = node_fs_1.default.readFileSync(specPath, "utf8");
150
244
  const normalizedImport = normalizeAnQstImport(existingText);
151
245
  if (normalizedImport.changed) {
152
- node_fs_1.default.writeFileSync(requestedSpecPath, normalizedImport.nextText, "utf8");
153
- }
154
- const declaredNamespace = extractDeclaredNamespace(normalizedImport.nextText);
155
- if (declaredNamespace && declaredNamespace !== cleanName) {
156
- const choice = chooseWidgetNamePreference(cleanName, declaredNamespace, requestedSpecFileName);
157
- if (choice === "namespace") {
158
- resolvedWidgetName = declaredNamespace;
159
- }
246
+ node_fs_1.default.writeFileSync(specPath, normalizedImport.nextText, "utf8");
160
247
  }
161
248
  }
162
- const next = {
249
+ else {
250
+ node_fs_1.default.writeFileSync(specPath, buildSpecScaffold(cleanName), "utf8");
251
+ }
252
+ const settingsPath = node_path_1.default.join(anqstRoot, (0, layout_1.anqstSettingsFileName)(cleanName));
253
+ const settings = {
254
+ layoutVersion: layout_1.ANQST_LAYOUT_VERSION,
255
+ widgetName: cleanName,
256
+ spec: `./AnQst/${(0, layout_1.anqstSpecFileName)(cleanName)}`,
257
+ generate: [...exports.DEFAULT_ANQST_GENERATE_TARGETS],
258
+ widgetCategory: "AnQst Widgets"
259
+ };
260
+ node_fs_1.default.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
261
+ node_fs_1.default.writeFileSync(node_path_1.default.join(anqstRoot, ".gitignore"), "/generated*\n", "utf8");
262
+ node_fs_1.default.writeFileSync(node_path_1.default.join(anqstRoot, "README.md"), buildAnQstDirectoryReadme(cleanName), "utf8");
263
+ const nextPackage = {
163
264
  ...packageJson,
164
265
  scripts: {
165
- ...packageJson.scripts,
166
- build: prependScript(packageJson.scripts?.build, "npx anqst build"),
167
- test: prependScript(packageJson.scripts?.test, "npx anqst test")
266
+ ...(packageJson.scripts ?? {}),
267
+ postinstall: ensureBuildHook(packageJson.scripts?.postinstall),
268
+ prebuild: ensureBuildHook(packageJson.scripts?.prebuild),
269
+ prestart: ensureBuildHook(packageJson.scripts?.prestart)
168
270
  },
169
- AnQst: {
170
- spec: `${resolvedWidgetName}.AnQst.d.ts`,
171
- generate: [...exports.DEFAULT_ANQST_GENERATE_TARGETS]
172
- }
271
+ AnQst: (0, layout_1.anqstSettingsRelativePath)(cleanName)
173
272
  };
174
- node_fs_1.default.writeFileSync(packagePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
175
- const resolvedSpecPath = node_path_1.default.join(cwd, `${resolvedWidgetName}.AnQst.d.ts`);
176
- let createdScaffold = false;
177
- if (!node_fs_1.default.existsSync(resolvedSpecPath)) {
178
- if (resolvedWidgetName !== cleanName && node_fs_1.default.existsSync(requestedSpecPath)) {
179
- node_fs_1.default.renameSync(requestedSpecPath, resolvedSpecPath);
180
- const movedText = node_fs_1.default.readFileSync(resolvedSpecPath, "utf8");
181
- const normalizedMovedImport = normalizeAnQstImport(movedText);
182
- if (normalizedMovedImport.changed) {
183
- node_fs_1.default.writeFileSync(resolvedSpecPath, normalizedMovedImport.nextText, "utf8");
184
- }
185
- }
186
- else {
187
- node_fs_1.default.writeFileSync(resolvedSpecPath, buildSpecScaffold(resolvedWidgetName), "utf8");
188
- createdScaffold = true;
189
- }
190
- }
191
- const mode = createdScaffold ? "scaffolded" : "using";
192
- return `Instill completed: configured package.json and ${mode} ${resolvedWidgetName}.AnQst.d.ts`;
273
+ node_fs_1.default.writeFileSync(packagePath, `${JSON.stringify(nextPackage, null, 2)}\n`, "utf8");
274
+ updateTsConfig(cwd, cleanName);
275
+ return `Instill completed: configured package.json and scaffolded ${(0, layout_1.normalizeSlashes)(node_path_1.default.relative(cwd, specPath))}`;
193
276
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.verifySpec = verifySpec;
7
7
  const typescript_1 = __importDefault(require("typescript"));
8
8
  const errors_1 = require("./errors");
9
+ const program_1 = require("./program");
9
10
  function parseTypeNodeFromText(typeText) {
10
11
  const source = typescript_1.default.createSourceFile("__inline__.ts", `type __X = ${typeText};`, typescript_1.default.ScriptTarget.Latest, true, typescript_1.default.ScriptKind.TS);
11
12
  const stmt = source.statements.find(typescript_1.default.isTypeAliasDeclaration);
@@ -170,7 +171,7 @@ function collectReachableTypeNames(spec) {
170
171
  }
171
172
  return seen;
172
173
  }
173
- function verifySpec(spec) {
174
+ function verifySpecSemantics(spec) {
174
175
  checkServiceDuplicates(spec);
175
176
  for (const service of spec.services) {
176
177
  for (const member of service.members) {
@@ -198,3 +199,10 @@ function verifySpec(spec) {
198
199
  message: `AnQst spec valid:\n ${stats.namespaceDeclaredTypes} types.\n ${stats.serviceCount} services.`
199
200
  };
200
201
  }
202
+ function verifySpec(spec) {
203
+ const diagnostics = (0, program_1.getProgramDiagnostics)(spec.filePath);
204
+ if (diagnostics.length > 0) {
205
+ throw new errors_1.VerifyError(`TypeScript diagnostics in spec:\n ${diagnostics.join("\n ")}`);
206
+ }
207
+ return verifySpecSemantics(spec);
208
+ }
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { AnQst } from "./spec/AnQst-Spec-DSL";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dusted/anqst",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Opinionated backend generator for webapps.",
5
5
  "keywords": [
6
6
  "nodejs",
@@ -21,14 +21,19 @@
21
21
  "author": "DusteD",
22
22
  "type": "commonjs",
23
23
  "main": "dist/src/app.js",
24
+ "types": "index.d.ts",
24
25
  "exports": {
25
- ".": "./dist/src/app.js"
26
+ ".": {
27
+ "types": "./index.d.ts",
28
+ "default": "./dist/src/app.js"
29
+ }
26
30
  },
27
31
  "bin": {
28
32
  "anqst": "dist/src/bin/anqst.js"
29
33
  },
30
34
  "files": [
31
35
  "dist/src",
36
+ "index.d.ts",
32
37
  "spec/AnQst-Spec-DSL.d.ts",
33
38
  "README.md",
34
39
  "LICENSE"
@@ -10,8 +10,8 @@
10
10
  * - This is not an input file to the generator, it is the description of the language that describes the widget.
11
11
  * Not for use by TypeScript application implementation.
12
12
  * @example
13
- * package.json:
14
- * - "AnQst": { "WidgetSpec": "MyUserMgmtWidget.AnQstSpec.d.ts" }
13
+ * package.json:
14
+ * - "AnQst": "./AnQst/MyUserMgmtWidget.settings.json"
15
15
  */
16
16
 
17
17
  export namespace AnQst {
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateOutputs = void 0;
4
- var emit_1 = require("../../emit");
5
- Object.defineProperty(exports, "generateOutputs", { enumerable: true, get: function () { return emit_1.generateOutputs; } });
@@ -1,13 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.astBackend = void 0;
4
- const emit_1 = require("./emit");
5
- const parser_1 = require("./parser");
6
- const verify_1 = require("./verify");
7
- exports.astBackend = {
8
- id: "ast",
9
- parseSpecFile: parser_1.parseSpecFile,
10
- verifySpec: verify_1.verifySpec,
11
- generateOutputs: emit_1.generateOutputs,
12
- emitsArtifacts: true
13
- };
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseSpecFile = void 0;
4
- var parser_1 = require("../../parser");
5
- Object.defineProperty(exports, "parseSpecFile", { enumerable: true, get: function () { return parser_1.parseSpecFile; } });
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifySpec = void 0;
4
- var verify_1 = require("../../verify");
5
- Object.defineProperty(exports, "verifySpec", { enumerable: true, get: function () { return verify_1.verifySpec; } });
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveBackend = resolveBackend;
4
- exports.isBackendId = isBackendId;
5
- const ast_1 = require("./ast");
6
- const tsc_1 = require("./tsc");
7
- const backends = {
8
- ast: ast_1.astBackend,
9
- tsc: tsc_1.tscBackend
10
- };
11
- function resolveBackend(backendId) {
12
- return backends[backendId];
13
- }
14
- function isBackendId(value) {
15
- return value === "ast" || value === "tsc";
16
- }
@@ -1,13 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.emitCppQWidget = emitCppQWidget;
4
- const emit_1 = require("../ast/emit");
5
- function emitCppQWidget(spec, options) {
6
- if (!options.emitQWidget)
7
- return {};
8
- return (0, emit_1.generateOutputs)(spec, {
9
- emitQWidget: true,
10
- emitAngularService: false,
11
- emitNodeExpressWs: false
12
- });
13
- }
@@ -1,13 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.emitNodeExpressWs = emitNodeExpressWs;
4
- const emit_1 = require("../ast/emit");
5
- function emitNodeExpressWs(spec, options) {
6
- if (!options.emitNodeExpressWs)
7
- return {};
8
- return (0, emit_1.generateOutputs)(spec, {
9
- emitQWidget: false,
10
- emitAngularService: false,
11
- emitNodeExpressWs: true
12
- });
13
- }
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tscBackend = void 0;
4
- const emit_cpp_1 = require("./emit-cpp");
5
- const emit_node_1 = require("./emit-node");
6
- const parser_1 = require("./parser");
7
- const verify_1 = require("./verify");
8
- function mergeGeneratedFiles(...parts) {
9
- const out = {};
10
- for (const part of parts) {
11
- for (const [key, value] of Object.entries(part)) {
12
- out[key] = value;
13
- }
14
- }
15
- return out;
16
- }
17
- function formatTargets(options) {
18
- const enabled = [];
19
- if (options.emitQWidget)
20
- enabled.push("QWidget");
21
- if (options.emitNodeExpressWs)
22
- enabled.push("node_express_ws");
23
- if (enabled.length === 0)
24
- return "none";
25
- return enabled.join(", ");
26
- }
27
- function logBackendInput(spec, options) {
28
- console.log(`[AnQst][backend=tsc] parsed widget=${spec.widgetName}, services=${spec.services.length}, targets=${formatTargets(options)}`);
29
- }
30
- exports.tscBackend = {
31
- id: "tsc",
32
- parseSpecFile: parser_1.parseSpecFile,
33
- verifySpec: verify_1.verifySpec,
34
- generateOutputs(spec, options) {
35
- logBackendInput(spec, options);
36
- const cpp = (0, emit_cpp_1.emitCppQWidget)(spec, options);
37
- const node = (0, emit_node_1.emitNodeExpressWs)(spec, options);
38
- return mergeGeneratedFiles(cpp, node);
39
- },
40
- emitsArtifacts: true
41
- };
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseSpecFile = parseSpecFile;
4
- const parser_1 = require("../ast/parser");
5
- const debug_dump_1 = require("./debug-dump");
6
- const program_1 = require("./program");
7
- const typegraph_1 = require("./typegraph");
8
- function parseSpecFile(specPath) {
9
- (0, program_1.createTscProgramContext)(specPath);
10
- const parsed = (0, parser_1.parseSpecFile)(specPath);
11
- if ((0, debug_dump_1.isDebugEnabled)()) {
12
- (0, debug_dump_1.writeDebugFile)(process.cwd(), "anqstmodel/parsed-before-typegraph.txt", `${(0, debug_dump_1.inspectText)(parsed)}\n`);
13
- }
14
- const normalized = (0, typegraph_1.applyResolvedTypeGraph)(parsed);
15
- if ((0, debug_dump_1.isDebugEnabled)()) {
16
- (0, debug_dump_1.writeDebugFile)(process.cwd(), "anqstmodel/parsed-after-typegraph.txt", `${(0, debug_dump_1.inspectText)(normalized)}\n`);
17
- }
18
- return normalized;
19
- }
@@ -1,13 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifySpec = verifySpec;
4
- const errors_1 = require("../../errors");
5
- const verify_1 = require("../ast/verify");
6
- const program_1 = require("./program");
7
- function verifySpec(spec) {
8
- const diagnostics = (0, program_1.getProgramDiagnostics)(spec.filePath);
9
- if (diagnostics.length > 0) {
10
- throw new errors_1.VerifyError(`TypeScript diagnostics in spec:\n ${diagnostics.join("\n ")}`);
11
- }
12
- return (0, verify_1.verifySpec)(spec);
13
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });