@hexaijs/plugin-contracts-generator 0.2.1 → 0.2.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.
- package/dist/ast-utils.d.ts +6 -0
- package/dist/ast-utils.d.ts.map +1 -0
- package/dist/ast-utils.js +73 -0
- package/dist/ast-utils.js.map +1 -0
- package/dist/class-analyzer.d.ts +16 -0
- package/dist/class-analyzer.d.ts.map +1 -0
- package/dist/class-analyzer.js +114 -0
- package/dist/class-analyzer.js.map +1 -0
- package/dist/cli.d.ts +32 -1
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +223 -2471
- package/dist/cli.js.map +1 -1
- package/dist/config-loader.d.ts +24 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +126 -0
- package/dist/config-loader.js.map +1 -0
- package/dist/context-config.d.ts +44 -0
- package/dist/context-config.d.ts.map +1 -0
- package/dist/context-config.js +140 -0
- package/dist/context-config.js.map +1 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +2 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/types.d.ts +182 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +49 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +112 -0
- package/dist/errors.js.map +1 -0
- package/dist/file-copier.d.ts +84 -0
- package/dist/file-copier.d.ts.map +1 -0
- package/dist/file-copier.js +721 -0
- package/dist/file-copier.js.map +1 -0
- package/dist/file-graph-resolver.d.ts +42 -0
- package/dist/file-graph-resolver.d.ts.map +1 -0
- package/dist/file-graph-resolver.js +138 -0
- package/dist/file-graph-resolver.js.map +1 -0
- package/dist/file-system.d.ts +26 -0
- package/dist/file-system.d.ts.map +1 -0
- package/dist/file-system.js +30 -0
- package/dist/file-system.js.map +1 -0
- package/dist/hexai-plugin.d.ts +16 -0
- package/dist/hexai-plugin.d.ts.map +1 -0
- package/dist/hexai-plugin.js +56 -0
- package/dist/hexai-plugin.js.map +1 -0
- package/dist/import-analyzer.d.ts +6 -0
- package/dist/import-analyzer.d.ts.map +1 -0
- package/dist/import-analyzer.js +39 -0
- package/dist/import-analyzer.js.map +1 -0
- package/dist/index.d.ts +39 -459
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -2712
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +21 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +43 -0
- package/dist/logger.js.map +1 -0
- package/dist/parser.d.ts +32 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +202 -0
- package/dist/parser.js.map +1 -0
- package/dist/pipeline.d.ts +57 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +148 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/reexport-generator.d.ts +81 -0
- package/dist/reexport-generator.d.ts.map +1 -0
- package/dist/reexport-generator.js +171 -0
- package/dist/reexport-generator.js.map +1 -0
- package/dist/registry-generator.d.ts +27 -0
- package/dist/registry-generator.d.ts.map +1 -0
- package/dist/registry-generator.js +104 -0
- package/dist/registry-generator.js.map +1 -0
- package/dist/runtime/index.d.ts +2 -23
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +1 -38
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/message-registry.d.ts +23 -0
- package/dist/runtime/message-registry.d.ts.map +1 -0
- package/dist/runtime/message-registry.js +35 -0
- package/dist/runtime/message-registry.js.map +1 -0
- package/dist/scanner.d.ts +21 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +49 -0
- package/dist/scanner.js.map +1 -0
- package/dist/test-utils.d.ts +23 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +188 -0
- package/dist/test-utils.js.map +1 -0
- package/package.json +69 -60
- package/dist/cli-DajurpEQ.d.ts +0 -277
package/dist/cli.js
CHANGED
|
@@ -1,2344 +1,90 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
var ConfigurationError = class extends MessageParserError {
|
|
21
|
-
constructor(message, options) {
|
|
22
|
-
super(message, options);
|
|
23
|
-
this.name = "ConfigurationError";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var ConfigLoadError = class extends ConfigurationError {
|
|
27
|
-
constructor(message, options) {
|
|
28
|
-
super(message, options);
|
|
29
|
-
this.name = "ConfigLoadError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
var FileSystemError = class extends MessageParserError {
|
|
33
|
-
path;
|
|
34
|
-
constructor(message, path5, options) {
|
|
35
|
-
super(message, options);
|
|
36
|
-
this.name = "FileSystemError";
|
|
37
|
-
this.path = path5;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
var FileReadError = class extends FileSystemError {
|
|
41
|
-
constructor(path5, options) {
|
|
42
|
-
super(`Failed to read file: ${path5}`, path5, options);
|
|
43
|
-
this.name = "FileReadError";
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
var FileWriteError = class extends FileSystemError {
|
|
47
|
-
constructor(path5, options) {
|
|
48
|
-
super(`Failed to write file: ${path5}`, path5, options);
|
|
49
|
-
this.name = "FileWriteError";
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
var NodeFileSystem = class {
|
|
53
|
-
async readFile(path5) {
|
|
54
|
-
return readFile(path5, "utf-8");
|
|
55
|
-
}
|
|
56
|
-
async readdir(path5) {
|
|
57
|
-
return readdir(path5);
|
|
58
|
-
}
|
|
59
|
-
async writeFile(path5, content) {
|
|
60
|
-
await writeFile(path5, content);
|
|
61
|
-
}
|
|
62
|
-
async mkdir(path5, options) {
|
|
63
|
-
await mkdir(path5, options);
|
|
64
|
-
}
|
|
65
|
-
async exists(path5) {
|
|
66
|
-
try {
|
|
67
|
-
await access(path5, constants.F_OK);
|
|
68
|
-
return true;
|
|
69
|
-
} catch {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async stat(path5) {
|
|
74
|
-
return stat(path5);
|
|
75
|
-
}
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { resolve, dirname, join, relative } from "node:path";
|
|
4
|
+
import { ConfigLoader, resolveContextEntries } from "./config-loader.js";
|
|
5
|
+
import { ContractsPipeline, ConsoleLogger } from "./index.js";
|
|
6
|
+
import { RegistryGenerator } from "./registry-generator.js";
|
|
7
|
+
import { ReexportGenerator } from "./reexport-generator.js";
|
|
8
|
+
import { nodeFileSystem } from "./file-system.js";
|
|
9
|
+
import { mergeDecoratorNames } from "./domain/index.js";
|
|
10
|
+
const DEFAULT_CONFIG_PATH = "application.config.ts";
|
|
11
|
+
const EXIT_CODE_ERROR = 1;
|
|
12
|
+
const VALID_MESSAGE_TYPES = ["event", "command", "query"];
|
|
13
|
+
const CLI_OPTIONS = {
|
|
14
|
+
config: { short: "-c", long: "--config", requiresValue: true },
|
|
15
|
+
outputDir: { short: "-o", long: "--output-dir", requiresValue: true },
|
|
16
|
+
messageTypes: { short: "-m", long: "--message-types", requiresValue: true },
|
|
17
|
+
generateMessageRegistry: { short: null, long: "--generate-message-registry", requiresValue: false },
|
|
18
|
+
help: { short: "-h", long: "--help", requiresValue: false },
|
|
76
19
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
query: "PublicQuery"
|
|
84
|
-
};
|
|
85
|
-
function mergeDecoratorNames(partial) {
|
|
86
|
-
return {
|
|
87
|
-
...DEFAULT_DECORATOR_NAMES,
|
|
88
|
-
...partial
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
var TYPESCRIPT_EXTENSIONS = [".ts", ".tsx", ".d.ts"];
|
|
92
|
-
var INDEX_FILE = "index.ts";
|
|
93
|
-
var Tsconfig = class _Tsconfig {
|
|
94
|
-
constructor(paths) {
|
|
95
|
-
this.paths = paths;
|
|
96
|
-
}
|
|
97
|
-
static NONE = new _Tsconfig(/* @__PURE__ */ new Map());
|
|
98
|
-
static async load(tsconfigPath, fs) {
|
|
99
|
-
const absolutePath = path__default.resolve(tsconfigPath);
|
|
100
|
-
const configDir = path__default.dirname(absolutePath);
|
|
101
|
-
const content = await fs.readFile(absolutePath);
|
|
102
|
-
const { config, error } = ts8__default.parseConfigFileTextToJson(absolutePath, content);
|
|
103
|
-
if (error) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`Failed to parse tsconfig: ${ts8__default.flattenDiagnosticMessageText(error.messageText, "\n")}`
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
const parsed = ts8__default.parseJsonConfigFileContent(config, ts8__default.sys, configDir);
|
|
109
|
-
const baseUrl = parsed.options.baseUrl ?? configDir;
|
|
110
|
-
const paths = /* @__PURE__ */ new Map();
|
|
111
|
-
if (parsed.options.paths) {
|
|
112
|
-
for (const [alias, targets] of Object.entries(parsed.options.paths)) {
|
|
113
|
-
const resolvedTargets = targets.map(
|
|
114
|
-
(target) => path__default.join(baseUrl, target)
|
|
115
|
-
);
|
|
116
|
-
paths.set(alias, resolvedTargets);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return new _Tsconfig(paths);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Pure string transformation: resolves path alias to potential file paths.
|
|
123
|
-
* Returns null if no alias matches.
|
|
124
|
-
*/
|
|
125
|
-
resolvePath(importPath) {
|
|
126
|
-
for (const [pattern, targets] of this.paths) {
|
|
127
|
-
const wildcardMatch = this.matchPathPattern(importPath, pattern);
|
|
128
|
-
if (wildcardMatch === null) {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
return targets.map((target) => target.replace("*", wildcardMatch));
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
matchPathPattern(moduleSpecifier, pattern) {
|
|
136
|
-
if (pattern.endsWith("*")) {
|
|
137
|
-
const prefix = pattern.slice(0, -1);
|
|
138
|
-
if (moduleSpecifier.startsWith(prefix)) {
|
|
139
|
-
return moduleSpecifier.slice(prefix.length);
|
|
140
|
-
}
|
|
141
|
-
} else if (moduleSpecifier === pattern) {
|
|
142
|
-
return "";
|
|
143
|
-
}
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
var ContextConfig = class _ContextConfig {
|
|
148
|
-
fs;
|
|
149
|
-
tsconfig;
|
|
150
|
-
name;
|
|
151
|
-
sourceDir;
|
|
152
|
-
responseNamingConventions;
|
|
153
|
-
constructor(name, sourceDir, tsconfig, fs, responseNamingConventions) {
|
|
154
|
-
this.name = name;
|
|
155
|
-
this.sourceDir = sourceDir;
|
|
156
|
-
this.tsconfig = tsconfig;
|
|
157
|
-
this.fs = fs;
|
|
158
|
-
this.responseNamingConventions = responseNamingConventions;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Factory method to create ContextConfig with properly loaded tsconfig.
|
|
162
|
-
*/
|
|
163
|
-
static async create(input, configDir, fs = nodeFileSystem) {
|
|
164
|
-
if (!input.name) {
|
|
165
|
-
throw new Error("ContextConfig requires 'name'");
|
|
166
|
-
}
|
|
167
|
-
if (!input.path) {
|
|
168
|
-
throw new Error(`ContextConfig '${input.name}' requires 'path'`);
|
|
169
|
-
}
|
|
170
|
-
const basePath = path__default.resolve(configDir, input.path);
|
|
171
|
-
const sourceDir = path__default.resolve(basePath, input.sourceDir ?? "src");
|
|
172
|
-
const tsconfig = await this.loadTsconfig(basePath, input.tsconfigPath, fs);
|
|
173
|
-
return new _ContextConfig(
|
|
174
|
-
input.name,
|
|
175
|
-
sourceDir,
|
|
176
|
-
tsconfig,
|
|
177
|
-
fs,
|
|
178
|
-
input.responseNamingConventions
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
static async loadTsconfig(basePath, inputPath, fs) {
|
|
182
|
-
const tsconfigPath = path__default.resolve(basePath, inputPath ?? "tsconfig.json");
|
|
183
|
-
if (!await fs.exists(tsconfigPath)) {
|
|
184
|
-
return Tsconfig.NONE;
|
|
185
|
-
}
|
|
186
|
-
return Tsconfig.load(tsconfigPath, fs);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Creates a ContextConfig without async loading (for cases where tsconfig is not needed
|
|
190
|
-
* or already handled externally).
|
|
191
|
-
*/
|
|
192
|
-
static createSync(name, sourceDir, fs = nodeFileSystem, responseNamingConventions) {
|
|
193
|
-
return new _ContextConfig(
|
|
194
|
-
name,
|
|
195
|
-
sourceDir,
|
|
196
|
-
Tsconfig.NONE,
|
|
197
|
-
fs,
|
|
198
|
-
responseNamingConventions
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Resolves a module specifier (path alias) to actual file path.
|
|
203
|
-
* Only handles non-relative imports (path aliases).
|
|
204
|
-
*
|
|
205
|
-
* @param moduleSpecifier - The import path to resolve (e.g., "@/utils/helper")
|
|
206
|
-
* @returns Object with resolvedPath (null if external) and isExternal flag
|
|
207
|
-
*/
|
|
208
|
-
async resolvePath(moduleSpecifier) {
|
|
209
|
-
const resolvedPaths = this.tsconfig.resolvePath(moduleSpecifier);
|
|
210
|
-
if (!resolvedPaths) {
|
|
211
|
-
return { resolvedPath: null, isExternal: true };
|
|
212
|
-
}
|
|
213
|
-
for (const resolvedPath of resolvedPaths) {
|
|
214
|
-
const filePath = await this.tryResolveWithExtensions(resolvedPath);
|
|
215
|
-
if (filePath && filePath.startsWith(this.sourceDir)) {
|
|
216
|
-
return { resolvedPath: filePath, isExternal: false };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return { resolvedPath: null, isExternal: true };
|
|
220
|
-
}
|
|
221
|
-
async tryResolveWithExtensions(basePath) {
|
|
222
|
-
const extensionCandidates = TYPESCRIPT_EXTENSIONS.map((ext) => basePath + ext);
|
|
223
|
-
const indexCandidate = path__default.join(basePath, INDEX_FILE);
|
|
224
|
-
const candidates = [...extensionCandidates, indexCandidate];
|
|
225
|
-
for (const candidate of candidates) {
|
|
226
|
-
if (await this.fs.exists(candidate)) {
|
|
227
|
-
return candidate;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// src/config-loader.ts
|
|
235
|
-
var SUPPORTED_GLOB_PARTS_COUNT = 2;
|
|
236
|
-
async function resolveContextEntries(entries, configDir, fs = nodeFileSystem) {
|
|
237
|
-
const contexts = [];
|
|
238
|
-
for (let i = 0; i < entries.length; i++) {
|
|
239
|
-
const item = entries[i];
|
|
240
|
-
if (typeof item === "string") {
|
|
241
|
-
const resolved = await resolveStringEntry(item, configDir, fs);
|
|
242
|
-
contexts.push(...resolved);
|
|
243
|
-
} else {
|
|
244
|
-
const contextConfig = await createObjectContext(item, i, configDir, fs);
|
|
245
|
-
contexts.push(contextConfig);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return contexts;
|
|
249
|
-
}
|
|
250
|
-
async function resolveStringEntry(contextPath, configDir, fs) {
|
|
251
|
-
if (contextPath.includes("*")) {
|
|
252
|
-
return expandGlobPattern(contextPath, configDir, fs);
|
|
253
|
-
}
|
|
254
|
-
const basePath = resolve(configDir, contextPath);
|
|
255
|
-
const name = basename(basePath);
|
|
256
|
-
return [await ContextConfig.create(
|
|
257
|
-
{ name, path: contextPath },
|
|
258
|
-
configDir,
|
|
259
|
-
fs
|
|
260
|
-
)];
|
|
261
|
-
}
|
|
262
|
-
async function expandGlobPattern(pattern, configDir, fs) {
|
|
263
|
-
const packageDirs = await matchGlobPattern(pattern, configDir, fs);
|
|
264
|
-
return Promise.all(
|
|
265
|
-
packageDirs.map((dir) => {
|
|
266
|
-
const name = basename(dir);
|
|
267
|
-
const relativePath = relative(configDir, dir);
|
|
268
|
-
return ContextConfig.create(
|
|
269
|
-
{ name, path: relativePath },
|
|
270
|
-
configDir,
|
|
271
|
-
fs
|
|
272
|
-
);
|
|
273
|
-
})
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
async function createObjectContext(ctx, index, configDir, fs) {
|
|
277
|
-
if (!ctx.name || typeof ctx.name !== "string") {
|
|
278
|
-
throw new ConfigLoadError(
|
|
279
|
-
`Invalid context at index ${index}: missing 'name'`
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
if (!ctx.path || typeof ctx.path !== "string") {
|
|
283
|
-
throw new ConfigLoadError(
|
|
284
|
-
`Invalid context at index ${index}: missing 'path'`
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
return ContextConfig.create(ctx, configDir, fs);
|
|
288
|
-
}
|
|
289
|
-
async function matchGlobPattern(pattern, configDir, fs) {
|
|
290
|
-
const globParts = pattern.split("*");
|
|
291
|
-
if (globParts.length !== SUPPORTED_GLOB_PARTS_COUNT) {
|
|
292
|
-
throw new ConfigLoadError(
|
|
293
|
-
`Invalid glob pattern: "${pattern}". Only single wildcard patterns like "packages/*" are supported.`
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
const [prefix, suffix] = globParts;
|
|
297
|
-
const baseDir = resolve(configDir, prefix);
|
|
298
|
-
if (!await fs.exists(baseDir)) {
|
|
299
|
-
return [];
|
|
300
|
-
}
|
|
301
|
-
const entries = await fs.readdir(baseDir);
|
|
302
|
-
const matchedDirs = [];
|
|
303
|
-
for (const entry of entries) {
|
|
304
|
-
const fullPath = resolve(baseDir, entry);
|
|
305
|
-
const stats = await fs.stat(fullPath);
|
|
306
|
-
if (!stats.isDirectory()) {
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
if (suffix) {
|
|
310
|
-
const suffixPath = resolve(fullPath, suffix.replace(/^\//, ""));
|
|
311
|
-
if (await fs.exists(suffixPath)) {
|
|
312
|
-
matchedDirs.push(fullPath);
|
|
313
|
-
}
|
|
314
|
-
} else {
|
|
315
|
-
matchedDirs.push(fullPath);
|
|
20
|
+
function parseMessageTypes(value) {
|
|
21
|
+
const types = value.split(",").map((type) => type.trim().toLowerCase());
|
|
22
|
+
const invalidTypes = types.filter((type) => !VALID_MESSAGE_TYPES.includes(type));
|
|
23
|
+
if (invalidTypes.length > 0) {
|
|
24
|
+
throw new Error(`Invalid message type(s): ${invalidTypes.join(", ")}. ` +
|
|
25
|
+
`Valid types are: ${VALID_MESSAGE_TYPES.join(", ")}`);
|
|
316
26
|
}
|
|
317
|
-
|
|
318
|
-
return matchedDirs.sort();
|
|
27
|
+
return types;
|
|
319
28
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return this.extractContractsConfig(config, dirname(absolutePath));
|
|
329
|
-
}
|
|
330
|
-
async loadTypeScriptConfig(absolutePath) {
|
|
331
|
-
const source = await this.fs.readFile(absolutePath);
|
|
332
|
-
const result = ts8__default.transpileModule(source, {
|
|
333
|
-
compilerOptions: {
|
|
334
|
-
module: ts8__default.ModuleKind.CommonJS,
|
|
335
|
-
target: ts8__default.ScriptTarget.ES2020,
|
|
336
|
-
esModuleInterop: true
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
const exports$1 = {};
|
|
340
|
-
const moduleWrapper = new Function("exports", result.outputText);
|
|
341
|
-
moduleWrapper(exports$1);
|
|
342
|
-
return exports$1.default ?? exports$1;
|
|
343
|
-
}
|
|
344
|
-
async extractContractsConfig(config, configDir) {
|
|
345
|
-
const contracts = config.contracts;
|
|
346
|
-
if (!contracts) {
|
|
347
|
-
throw new ConfigLoadError("Missing 'contracts' section in config");
|
|
348
|
-
}
|
|
349
|
-
if (!contracts.contexts || !Array.isArray(contracts.contexts)) {
|
|
350
|
-
throw new ConfigLoadError("Missing 'contracts.contexts' in config");
|
|
29
|
+
function extractOptionValue(args, currentIndex, optionName) {
|
|
30
|
+
const currentArg = args[currentIndex];
|
|
31
|
+
const equalsIndex = currentArg.indexOf("=");
|
|
32
|
+
if (equalsIndex !== -1) {
|
|
33
|
+
return {
|
|
34
|
+
value: currentArg.slice(equalsIndex + 1),
|
|
35
|
+
nextIndex: currentIndex,
|
|
36
|
+
};
|
|
351
37
|
}
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
354
|
-
|
|
38
|
+
const nextValue = args[currentIndex + 1];
|
|
39
|
+
if (!nextValue) {
|
|
40
|
+
throw new Error(`Missing value for ${optionName} option`);
|
|
355
41
|
}
|
|
356
|
-
const decoratorNames = mergeDecoratorNames(contracts.decoratorNames);
|
|
357
42
|
return {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
externalDependencies: contracts.externalDependencies,
|
|
361
|
-
decoratorNames,
|
|
362
|
-
responseNamingConventions: contracts.responseNamingConventions,
|
|
363
|
-
removeDecorators: contracts.removeDecorators ?? true
|
|
43
|
+
value: nextValue,
|
|
44
|
+
nextIndex: currentIndex + 1,
|
|
364
45
|
};
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
368
|
-
"**/node_modules/**",
|
|
369
|
-
"**/dist/**",
|
|
370
|
-
"**/*.d.ts",
|
|
371
|
-
"**/*.test.ts",
|
|
372
|
-
"**/*.spec.ts"
|
|
373
|
-
];
|
|
374
|
-
var Scanner = class {
|
|
375
|
-
exclude;
|
|
376
|
-
fs;
|
|
377
|
-
decoratorPatterns;
|
|
378
|
-
constructor(options = {}) {
|
|
379
|
-
this.exclude = options.exclude ?? DEFAULT_EXCLUDE_PATTERNS;
|
|
380
|
-
this.fs = options.fileSystem ?? nodeFileSystem;
|
|
381
|
-
const names = mergeDecoratorNames(options.decoratorNames);
|
|
382
|
-
const messageTypes = options.messageTypes ?? ["event", "command", "query"];
|
|
383
|
-
this.decoratorPatterns = messageTypes.map((type) => {
|
|
384
|
-
const decoratorName = names[type];
|
|
385
|
-
return `@${decoratorName}(`;
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
async scan(sourceDir) {
|
|
389
|
-
const files = await glob(`${sourceDir}/**/*.ts`, {
|
|
390
|
-
ignore: this.exclude
|
|
391
|
-
});
|
|
392
|
-
const result = [];
|
|
393
|
-
for (const file of files) {
|
|
394
|
-
let content;
|
|
395
|
-
try {
|
|
396
|
-
content = await this.fs.readFile(file);
|
|
397
|
-
} catch (error) {
|
|
398
|
-
throw new FileReadError(file, { cause: error });
|
|
399
|
-
}
|
|
400
|
-
if (this.containsPublicDecorator(content)) {
|
|
401
|
-
result.push(file);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return result;
|
|
405
|
-
}
|
|
406
|
-
containsPublicDecorator(content) {
|
|
407
|
-
return this.decoratorPatterns.some((pattern) => content.includes(pattern));
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
function parseTypeNode(typeNode) {
|
|
411
|
-
if (typeNode.kind === ts8.SyntaxKind.StringKeyword) {
|
|
412
|
-
return { kind: "primitive", name: "string" };
|
|
413
|
-
}
|
|
414
|
-
if (typeNode.kind === ts8.SyntaxKind.NumberKeyword) {
|
|
415
|
-
return { kind: "primitive", name: "number" };
|
|
416
|
-
}
|
|
417
|
-
if (typeNode.kind === ts8.SyntaxKind.BooleanKeyword) {
|
|
418
|
-
return { kind: "primitive", name: "boolean" };
|
|
419
|
-
}
|
|
420
|
-
if (ts8.isArrayTypeNode(typeNode)) {
|
|
421
|
-
const elementType = parseTypeNode(typeNode.elementType);
|
|
422
|
-
return { kind: "array", elementType };
|
|
423
|
-
}
|
|
424
|
-
if (ts8.isTypeReferenceNode(typeNode)) {
|
|
425
|
-
const name = typeNode.typeName.getText();
|
|
426
|
-
const typeArguments = typeNode.typeArguments ? typeNode.typeArguments.map((t) => parseTypeNode(t)) : void 0;
|
|
427
|
-
return { kind: "reference", name, typeArguments };
|
|
428
|
-
}
|
|
429
|
-
if (ts8.isIntersectionTypeNode(typeNode)) {
|
|
430
|
-
const types = typeNode.types.map((t) => parseTypeNode(t));
|
|
431
|
-
return { kind: "intersection", types };
|
|
432
|
-
}
|
|
433
|
-
if (ts8.isUnionTypeNode(typeNode)) {
|
|
434
|
-
const types = typeNode.types.map((t) => parseTypeNode(t));
|
|
435
|
-
return { kind: "union", types };
|
|
436
|
-
}
|
|
437
|
-
if (ts8.isTypeLiteralNode(typeNode)) {
|
|
438
|
-
const fields = extractFieldsFromMembers(typeNode.members);
|
|
439
|
-
return { kind: "object", fields };
|
|
440
|
-
}
|
|
441
|
-
return { kind: "reference", name: typeNode.getText() };
|
|
442
|
-
}
|
|
443
|
-
function extractFieldsFromMembers(members) {
|
|
444
|
-
const fields = [];
|
|
445
|
-
for (const member of members) {
|
|
446
|
-
if (ts8.isPropertySignature(member) && member.name) {
|
|
447
|
-
const fieldName = member.name.getText();
|
|
448
|
-
const fieldType = member.type ? parseTypeNode(member.type) : { kind: "primitive", name: "any" };
|
|
449
|
-
const optional = !!member.questionToken;
|
|
450
|
-
const readonly = member.modifiers?.some(
|
|
451
|
-
(m) => m.kind === ts8.SyntaxKind.ReadonlyKeyword
|
|
452
|
-
) ?? false;
|
|
453
|
-
fields.push({
|
|
454
|
-
name: fieldName,
|
|
455
|
-
type: fieldType,
|
|
456
|
-
optional,
|
|
457
|
-
readonly
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return fields;
|
|
462
|
-
}
|
|
463
|
-
function hasDecorator(node, decoratorName) {
|
|
464
|
-
const decorators = ts8.getDecorators(node);
|
|
465
|
-
if (!decorators) return false;
|
|
466
|
-
return decorators.some((decorator) => {
|
|
467
|
-
if (ts8.isCallExpression(decorator.expression)) {
|
|
468
|
-
const expr = decorator.expression.expression;
|
|
469
|
-
if (ts8.isIdentifier(expr)) {
|
|
470
|
-
return expr.text === decoratorName;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
return false;
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
function getDecoratorOptions(node, decoratorName) {
|
|
477
|
-
const decorators = ts8.getDecorators(node);
|
|
478
|
-
if (!decorators) return void 0;
|
|
479
|
-
for (const decorator of decorators) {
|
|
480
|
-
if (!ts8.isCallExpression(decorator.expression)) continue;
|
|
481
|
-
const expr = decorator.expression.expression;
|
|
482
|
-
if (!ts8.isIdentifier(expr) || expr.text !== decoratorName) continue;
|
|
483
|
-
const args = decorator.expression.arguments;
|
|
484
|
-
if (args.length === 0) return {};
|
|
485
|
-
const firstArg = args[0];
|
|
486
|
-
if (!ts8.isObjectLiteralExpression(firstArg)) return {};
|
|
487
|
-
const options = {};
|
|
488
|
-
for (const prop of firstArg.properties) {
|
|
489
|
-
if (!ts8.isPropertyAssignment(prop)) continue;
|
|
490
|
-
if (!ts8.isIdentifier(prop.name)) continue;
|
|
491
|
-
const propName = prop.name.text;
|
|
492
|
-
if (propName === "response" && ts8.isStringLiteral(prop.initializer)) {
|
|
493
|
-
options.response = prop.initializer.text;
|
|
494
|
-
} else if (propName === "context" && ts8.isStringLiteral(prop.initializer)) {
|
|
495
|
-
options.context = prop.initializer.text;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return options;
|
|
499
|
-
}
|
|
500
|
-
return void 0;
|
|
501
|
-
}
|
|
502
|
-
function hasExportModifier(node) {
|
|
503
|
-
const modifiers = ts8.canHaveModifiers(node) ? ts8.getModifiers(node) : void 0;
|
|
504
|
-
return modifiers?.some((m) => m.kind === ts8.SyntaxKind.ExportKeyword) ?? false;
|
|
505
46
|
}
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
sourceText = sourceText.replace(/^\s*\n/, "");
|
|
511
|
-
return sourceText;
|
|
512
|
-
}
|
|
513
|
-
function getBaseClassName(node) {
|
|
514
|
-
if (!node.heritageClauses) return void 0;
|
|
515
|
-
for (const clause of node.heritageClauses) {
|
|
516
|
-
if (clause.token === ts8.SyntaxKind.ExtendsKeyword) {
|
|
517
|
-
const firstType = clause.types[0];
|
|
518
|
-
if (firstType && ts8.isExpressionWithTypeArguments(firstType)) {
|
|
519
|
-
const expr = firstType.expression;
|
|
520
|
-
if (ts8.isIdentifier(expr)) {
|
|
521
|
-
return expr.text;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return void 0;
|
|
527
|
-
}
|
|
528
|
-
function isExternalModule(moduleSpecifier) {
|
|
529
|
-
return !moduleSpecifier.startsWith(".");
|
|
530
|
-
}
|
|
531
|
-
function extractImports(sourceFile) {
|
|
532
|
-
const imports = [];
|
|
533
|
-
ts8.forEachChild(sourceFile, (node) => {
|
|
534
|
-
if (ts8.isImportDeclaration(node) && node.importClause) {
|
|
535
|
-
const moduleSpecifier = node.moduleSpecifier.text;
|
|
536
|
-
const isExternal = isExternalModule(moduleSpecifier);
|
|
537
|
-
const isTypeOnly = node.importClause.isTypeOnly ?? false;
|
|
538
|
-
const importedNames = extractImportedNames(node.importClause);
|
|
539
|
-
if (importedNames.length > 0) {
|
|
540
|
-
imports.push({
|
|
541
|
-
names: importedNames,
|
|
542
|
-
source: moduleSpecifier,
|
|
543
|
-
isTypeOnly,
|
|
544
|
-
isExternal
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
return imports;
|
|
550
|
-
}
|
|
551
|
-
function extractImportedNames(importClause) {
|
|
552
|
-
const names = [];
|
|
553
|
-
if (importClause.name) {
|
|
554
|
-
names.push(importClause.name.text);
|
|
555
|
-
}
|
|
556
|
-
if (importClause.namedBindings) {
|
|
557
|
-
if (ts8.isNamedImports(importClause.namedBindings)) {
|
|
558
|
-
for (const element of importClause.namedBindings.elements) {
|
|
559
|
-
names.push(element.name.text);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return names;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// src/parser.ts
|
|
567
|
-
var PAYLOAD_TYPE_ARGUMENT_INDEX = 0;
|
|
568
|
-
function buildDecoratorMappings(decoratorNames) {
|
|
569
|
-
return [
|
|
570
|
-
{ decorator: decoratorNames.event, messageType: "event" },
|
|
571
|
-
{ decorator: decoratorNames.command, messageType: "command" },
|
|
572
|
-
{ decorator: decoratorNames.query, messageType: "query" }
|
|
573
|
-
];
|
|
574
|
-
}
|
|
575
|
-
function extractTypeParameterNames(typeParameters) {
|
|
576
|
-
if (!typeParameters) return void 0;
|
|
577
|
-
return typeParameters.map((tp) => tp.name.text);
|
|
47
|
+
function matchesOption(arg, option) {
|
|
48
|
+
const matchesShortOrLong = arg === option.short || arg === option.long;
|
|
49
|
+
const matchesLongWithValue = option.long !== null && arg.startsWith(`${option.long}=`);
|
|
50
|
+
return matchesShortOrLong || matchesLongWithValue;
|
|
578
51
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
messageTypes;
|
|
583
|
-
constructor(options = {}) {
|
|
584
|
-
const names = mergeDecoratorNames(options.decoratorNames);
|
|
585
|
-
this.decoratorMappings = buildDecoratorMappings(names);
|
|
586
|
-
this.responseNamingConventions = options.responseNamingConventions ?? [];
|
|
587
|
-
this.messageTypes = options.messageTypes;
|
|
588
|
-
}
|
|
589
|
-
parse(sourceCode, sourceFileInfo) {
|
|
590
|
-
const tsSourceFile = ts8__default.createSourceFile(
|
|
591
|
-
sourceFileInfo.absolutePath,
|
|
592
|
-
sourceCode,
|
|
593
|
-
ts8__default.ScriptTarget.Latest,
|
|
594
|
-
true
|
|
595
|
-
);
|
|
596
|
-
const events = [];
|
|
597
|
-
const commands = [];
|
|
598
|
-
const queries = [];
|
|
599
|
-
const typeDefinitions = [];
|
|
600
|
-
const messageCollectors = {
|
|
601
|
-
event: (m) => events.push(m),
|
|
602
|
-
command: (m) => commands.push(m),
|
|
603
|
-
query: (m) => queries.push(m)
|
|
604
|
-
};
|
|
605
|
-
const visit = (node) => {
|
|
606
|
-
if (ts8__default.isClassDeclaration(node) && node.name) {
|
|
607
|
-
this.collectMessagesFromClass(node, sourceCode, tsSourceFile, sourceFileInfo, messageCollectors);
|
|
608
|
-
}
|
|
609
|
-
if (ts8__default.isTypeAliasDeclaration(node) && node.name) {
|
|
610
|
-
typeDefinitions.push(this.extractTypeDefinition(node, sourceFileInfo));
|
|
611
|
-
}
|
|
612
|
-
if (ts8__default.isInterfaceDeclaration(node) && node.name) {
|
|
613
|
-
typeDefinitions.push(this.extractInterfaceDefinition(node, sourceFileInfo));
|
|
614
|
-
}
|
|
615
|
-
ts8__default.forEachChild(node, visit);
|
|
616
|
-
};
|
|
617
|
-
visit(tsSourceFile);
|
|
618
|
-
this.applyNamingConventionMatching(commands, typeDefinitions);
|
|
619
|
-
this.applyNamingConventionMatching(queries, typeDefinitions);
|
|
620
|
-
return { events, commands, queries, typeDefinitions };
|
|
621
|
-
}
|
|
622
|
-
collectMessagesFromClass(node, sourceCode, tsSourceFile, sourceFileInfo, collectors) {
|
|
623
|
-
for (const { decorator, messageType } of this.decoratorMappings) {
|
|
624
|
-
if (!hasDecorator(node, decorator)) continue;
|
|
625
|
-
if (this.messageTypes && !this.messageTypes.includes(messageType)) continue;
|
|
626
|
-
const message = this.buildMessage(node, sourceCode, tsSourceFile, sourceFileInfo, messageType, decorator);
|
|
627
|
-
collectors[messageType](message);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
applyNamingConventionMatching(messages, typeDefinitions) {
|
|
631
|
-
for (const message of messages) {
|
|
632
|
-
const hasExplicitResultType = Boolean(message.resultType);
|
|
633
|
-
if (hasExplicitResultType) continue;
|
|
634
|
-
const matchedTypeName = this.findMatchingResponseType(message.name, typeDefinitions);
|
|
635
|
-
if (matchedTypeName) {
|
|
636
|
-
message.resultType = {
|
|
637
|
-
kind: "reference",
|
|
638
|
-
name: matchedTypeName
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
findMatchingResponseType(messageName, typeDefinitions) {
|
|
644
|
-
for (const convention of this.responseNamingConventions) {
|
|
645
|
-
const hasSuffix = messageName.endsWith(convention.messageSuffix);
|
|
646
|
-
if (!hasSuffix) continue;
|
|
647
|
-
const messagePrefix = messageName.slice(0, -convention.messageSuffix.length);
|
|
648
|
-
const expectedResponseName = messagePrefix + convention.responseSuffix;
|
|
649
|
-
const matchingType = typeDefinitions.find((t) => t.name === expectedResponseName);
|
|
650
|
-
if (matchingType) return matchingType.name;
|
|
651
|
-
}
|
|
652
|
-
return void 0;
|
|
653
|
-
}
|
|
654
|
-
buildMessage(node, sourceCode, tsSourceFile, sourceFileInfo, messageType, decoratorName) {
|
|
655
|
-
const extracted = this.extractMessageDetails(node, sourceCode, tsSourceFile);
|
|
656
|
-
const decoratorOptions = getDecoratorOptions(node, decoratorName);
|
|
657
|
-
const baseMessage = {
|
|
658
|
-
name: node.name.text,
|
|
659
|
-
messageType,
|
|
660
|
-
sourceFile: sourceFileInfo,
|
|
661
|
-
fields: extracted.fields,
|
|
662
|
-
payloadType: extracted.payloadType,
|
|
663
|
-
sourceText: extracted.sourceText,
|
|
664
|
-
imports: extracted.imports,
|
|
665
|
-
baseClass: extracted.baseClass
|
|
666
|
-
};
|
|
667
|
-
const explicitResponseName = decoratorOptions?.response;
|
|
668
|
-
const supportsResultType = messageType === "command" || messageType === "query";
|
|
669
|
-
if (!supportsResultType || !explicitResponseName) {
|
|
670
|
-
return baseMessage;
|
|
671
|
-
}
|
|
672
|
-
return {
|
|
673
|
-
...baseMessage,
|
|
674
|
-
resultType: {
|
|
675
|
-
kind: "reference",
|
|
676
|
-
name: explicitResponseName
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
extractMessageDetails(node, sourceCode, tsSourceFile) {
|
|
681
|
-
const { fields, payloadType } = this.extractPayload(node);
|
|
682
|
-
const sourceText = extractClassSourceText(node, sourceCode);
|
|
683
|
-
const imports = extractImports(tsSourceFile);
|
|
684
|
-
const baseClass = getBaseClassName(node);
|
|
685
|
-
return {
|
|
686
|
-
fields,
|
|
687
|
-
payloadType,
|
|
688
|
-
sourceText,
|
|
689
|
-
imports,
|
|
690
|
-
baseClass
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
extractPayload(node) {
|
|
694
|
-
const emptyPayload = { fields: [] };
|
|
695
|
-
if (!node.heritageClauses) return emptyPayload;
|
|
696
|
-
const extendsClause = node.heritageClauses.find(
|
|
697
|
-
(clause) => clause.token === ts8__default.SyntaxKind.ExtendsKeyword
|
|
698
|
-
);
|
|
699
|
-
if (!extendsClause) return emptyPayload;
|
|
700
|
-
for (const type of extendsClause.types) {
|
|
701
|
-
const typeArgs = type.typeArguments;
|
|
702
|
-
if (!typeArgs || typeArgs.length === 0) continue;
|
|
703
|
-
const payloadTypeArg = typeArgs[PAYLOAD_TYPE_ARGUMENT_INDEX];
|
|
704
|
-
const extractedPayload = this.parsePayloadTypeArgument(payloadTypeArg);
|
|
705
|
-
if (extractedPayload) return extractedPayload;
|
|
706
|
-
}
|
|
707
|
-
return emptyPayload;
|
|
708
|
-
}
|
|
709
|
-
parsePayloadTypeArgument(typeArg) {
|
|
710
|
-
if (ts8__default.isTypeReferenceNode(typeArg) && ts8__default.isIdentifier(typeArg.typeName)) {
|
|
711
|
-
return {
|
|
712
|
-
fields: [],
|
|
713
|
-
payloadType: parseTypeNode(typeArg)
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
if (ts8__default.isTypeLiteralNode(typeArg)) {
|
|
717
|
-
return {
|
|
718
|
-
fields: extractFieldsFromMembers(typeArg.members)
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
if (ts8__default.isIntersectionTypeNode(typeArg)) {
|
|
722
|
-
const parsedType = parseTypeNode(typeArg);
|
|
723
|
-
return {
|
|
724
|
-
fields: this.flattenIntersectionToFields(parsedType),
|
|
725
|
-
payloadType: parsedType
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
return void 0;
|
|
729
|
-
}
|
|
730
|
-
flattenIntersectionToFields(intersection) {
|
|
731
|
-
const fields = [];
|
|
732
|
-
for (const type of intersection.types) {
|
|
733
|
-
if (type.kind === "object") {
|
|
734
|
-
fields.push(...type.fields);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
return fields;
|
|
738
|
-
}
|
|
739
|
-
extractTypeDefinition(node, sourceFileInfo) {
|
|
740
|
-
return this.buildTypeDefinition(
|
|
741
|
-
node.name.text,
|
|
742
|
-
"type",
|
|
743
|
-
sourceFileInfo,
|
|
744
|
-
parseTypeNode(node.type),
|
|
745
|
-
node.typeParameters,
|
|
746
|
-
node
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
extractInterfaceDefinition(node, sourceFileInfo) {
|
|
750
|
-
const body = {
|
|
751
|
-
kind: "object",
|
|
752
|
-
fields: extractFieldsFromMembers(node.members)
|
|
753
|
-
};
|
|
754
|
-
return this.buildTypeDefinition(
|
|
755
|
-
node.name.text,
|
|
756
|
-
"interface",
|
|
757
|
-
sourceFileInfo,
|
|
758
|
-
body,
|
|
759
|
-
node.typeParameters,
|
|
760
|
-
node
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
buildTypeDefinition(name, kind, sourceFile, body, typeParameters, node) {
|
|
764
|
-
return {
|
|
765
|
-
name,
|
|
766
|
-
kind,
|
|
767
|
-
sourceFile,
|
|
768
|
-
body,
|
|
769
|
-
typeParameters: extractTypeParameterNames(typeParameters),
|
|
770
|
-
exported: hasExportModifier(node)
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
};
|
|
774
|
-
var TYPESCRIPT_EXTENSIONS2 = [".ts", ".tsx"];
|
|
775
|
-
var INDEX_FILE2 = "index.ts";
|
|
776
|
-
var FileGraphResolver = class _FileGraphResolver {
|
|
777
|
-
contextConfig;
|
|
778
|
-
fs;
|
|
779
|
-
excludeDependencies;
|
|
780
|
-
constructor(contextConfig, fileSystem, excludeDependencies) {
|
|
781
|
-
this.contextConfig = contextConfig;
|
|
782
|
-
this.fs = fileSystem;
|
|
783
|
-
this.excludeDependencies = excludeDependencies;
|
|
784
|
-
}
|
|
785
|
-
static create(options) {
|
|
786
|
-
const fs = options.fileSystem ?? nodeFileSystem;
|
|
787
|
-
const excludeDependencies = options.excludeDependencies ?? [];
|
|
788
|
-
return new _FileGraphResolver(options.contextConfig, fs, excludeDependencies);
|
|
789
|
-
}
|
|
790
|
-
async buildGraph(entryPoints, sourceRoot) {
|
|
791
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
792
|
-
const entryPointSet = new Set(entryPoints);
|
|
793
|
-
const excludedPaths = /* @__PURE__ */ new Set();
|
|
794
|
-
const visited = /* @__PURE__ */ new Set();
|
|
795
|
-
const queue = [...entryPoints];
|
|
796
|
-
while (queue.length > 0) {
|
|
797
|
-
const filePath = queue.shift();
|
|
798
|
-
if (visited.has(filePath)) {
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
visited.add(filePath);
|
|
802
|
-
const imports = await this.extractImports(filePath);
|
|
803
|
-
const node = {
|
|
804
|
-
absolutePath: filePath,
|
|
805
|
-
relativePath: path__default.relative(sourceRoot, filePath),
|
|
806
|
-
imports,
|
|
807
|
-
isEntryPoint: entryPointSet.has(filePath)
|
|
808
|
-
};
|
|
809
|
-
nodes.set(filePath, node);
|
|
810
|
-
this.queueUnvisitedLocalDependencies(imports, visited, queue, excludedPaths);
|
|
811
|
-
}
|
|
812
|
-
return {
|
|
813
|
-
nodes,
|
|
814
|
-
entryPoints: entryPointSet,
|
|
815
|
-
excludedPaths
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
async extractImports(filePath) {
|
|
819
|
-
const sourceCode = await this.readSourceFile(filePath);
|
|
820
|
-
const sourceFile = ts8__default.createSourceFile(
|
|
821
|
-
filePath,
|
|
822
|
-
sourceCode,
|
|
823
|
-
ts8__default.ScriptTarget.Latest,
|
|
824
|
-
true
|
|
825
|
-
);
|
|
826
|
-
const imports = [];
|
|
827
|
-
for (const node of sourceFile.statements) {
|
|
828
|
-
const moduleSpecifier = this.extractModuleSpecifier(node);
|
|
829
|
-
if (!moduleSpecifier) {
|
|
830
|
-
continue;
|
|
831
|
-
}
|
|
832
|
-
const resolution = await this.resolveModule(moduleSpecifier, filePath);
|
|
833
|
-
const importedNames = this.extractImportedNamesFromNode(node);
|
|
834
|
-
imports.push({
|
|
835
|
-
moduleSpecifier,
|
|
836
|
-
resolvedPath: resolution.resolvedPath,
|
|
837
|
-
isExternal: resolution.isExternal,
|
|
838
|
-
importedNames
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
return imports;
|
|
842
|
-
}
|
|
843
|
-
async readSourceFile(filePath) {
|
|
844
|
-
try {
|
|
845
|
-
return await this.fs.readFile(filePath);
|
|
846
|
-
} catch (error) {
|
|
847
|
-
throw new FileReadError(filePath, { cause: error });
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
extractModuleSpecifier(node) {
|
|
851
|
-
if (ts8__default.isImportDeclaration(node)) {
|
|
852
|
-
return node.moduleSpecifier.text;
|
|
853
|
-
}
|
|
854
|
-
if (ts8__default.isExportDeclaration(node) && node.moduleSpecifier) {
|
|
855
|
-
return node.moduleSpecifier.text;
|
|
856
|
-
}
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
859
|
-
extractImportedNamesFromNode(node) {
|
|
860
|
-
if (ts8__default.isImportDeclaration(node) && node.importClause) {
|
|
861
|
-
return extractImportedNames(node.importClause);
|
|
862
|
-
}
|
|
863
|
-
return [];
|
|
864
|
-
}
|
|
865
|
-
async resolveModule(moduleSpecifier, fromFile) {
|
|
866
|
-
if (moduleSpecifier.startsWith(".")) {
|
|
867
|
-
const resolvedPath = await this.resolveRelativeModule(
|
|
868
|
-
moduleSpecifier,
|
|
869
|
-
fromFile
|
|
870
|
-
);
|
|
871
|
-
return { resolvedPath, isExternal: false };
|
|
872
|
-
}
|
|
873
|
-
return this.contextConfig.resolvePath(moduleSpecifier);
|
|
874
|
-
}
|
|
875
|
-
queueUnvisitedLocalDependencies(imports, visited, queue, excludedPaths) {
|
|
876
|
-
const localImports = imports.filter(
|
|
877
|
-
(importInfo) => !importInfo.isExternal && importInfo.resolvedPath !== null
|
|
878
|
-
);
|
|
879
|
-
for (const importInfo of localImports) {
|
|
880
|
-
const resolvedPath = importInfo.resolvedPath;
|
|
881
|
-
if (this.shouldExclude(resolvedPath)) {
|
|
882
|
-
excludedPaths.add(resolvedPath);
|
|
883
|
-
continue;
|
|
884
|
-
}
|
|
885
|
-
if (!visited.has(resolvedPath)) {
|
|
886
|
-
queue.push(resolvedPath);
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
shouldExclude(filePath) {
|
|
891
|
-
if (this.excludeDependencies.length === 0) {
|
|
892
|
-
return false;
|
|
893
|
-
}
|
|
894
|
-
return this.excludeDependencies.some(
|
|
895
|
-
(pattern) => minimatch(filePath, pattern, { matchBase: true })
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
async resolveRelativeModule(moduleSpecifier, fromFile) {
|
|
899
|
-
const dir = path__default.dirname(fromFile);
|
|
900
|
-
const basePath = path__default.resolve(dir, moduleSpecifier);
|
|
901
|
-
return this.tryResolveWithExtensions(basePath);
|
|
902
|
-
}
|
|
903
|
-
async tryResolveWithExtensions(basePath) {
|
|
904
|
-
const extensionCandidates = TYPESCRIPT_EXTENSIONS2.map((ext) => basePath + ext);
|
|
905
|
-
const indexCandidate = path__default.join(basePath, INDEX_FILE2);
|
|
906
|
-
const candidates = [...extensionCandidates, indexCandidate];
|
|
907
|
-
for (const candidate of candidates) {
|
|
908
|
-
if (await this.fs.exists(candidate)) {
|
|
909
|
-
return candidate;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
return null;
|
|
913
|
-
}
|
|
914
|
-
};
|
|
915
|
-
var CONTRACTS_GENERATOR_MODULE = "@hexaijs/plugin-contracts-generator";
|
|
916
|
-
var CONTRACT_DECORATORS = /* @__PURE__ */ new Set([
|
|
917
|
-
"PublicCommand",
|
|
918
|
-
"PublicEvent",
|
|
919
|
-
"PublicQuery"
|
|
920
|
-
]);
|
|
921
|
-
var TS_EXTENSION_PATTERN = /\.ts$/;
|
|
922
|
-
var REQUEST_SUFFIX = "Request";
|
|
923
|
-
var QUERY_SUFFIX = "Query";
|
|
924
|
-
var FileCopier = class {
|
|
925
|
-
fs;
|
|
926
|
-
constructor(options = {}) {
|
|
927
|
-
this.fs = options.fileSystem ?? nodeFileSystem;
|
|
928
|
-
}
|
|
929
|
-
async copyFiles(options) {
|
|
930
|
-
const {
|
|
931
|
-
sourceRoot,
|
|
932
|
-
outputDir,
|
|
933
|
-
fileGraph,
|
|
934
|
-
pathAliasRewrites,
|
|
935
|
-
removeDecorators,
|
|
936
|
-
responseTypesToExport,
|
|
937
|
-
messageTypes,
|
|
938
|
-
decoratorNames
|
|
939
|
-
} = options;
|
|
940
|
-
const copiedFiles = [];
|
|
941
|
-
const rewrittenImports = /* @__PURE__ */ new Map();
|
|
942
|
-
const { entryContents, usedLocalImports } = await this.preprocessEntryFiles(
|
|
943
|
-
fileGraph,
|
|
944
|
-
messageTypes,
|
|
945
|
-
decoratorNames,
|
|
946
|
-
sourceRoot
|
|
947
|
-
);
|
|
948
|
-
this.expandTransitiveDependencies(usedLocalImports, fileGraph);
|
|
949
|
-
for (const node of fileGraph.nodes.values()) {
|
|
950
|
-
const content = await this.resolveNodeContent(
|
|
951
|
-
node,
|
|
952
|
-
entryContents,
|
|
953
|
-
usedLocalImports,
|
|
954
|
-
messageTypes
|
|
955
|
-
);
|
|
956
|
-
if (content === null) {
|
|
957
|
-
continue;
|
|
958
|
-
}
|
|
959
|
-
const transformedContent = this.applyTransformations(
|
|
960
|
-
content,
|
|
961
|
-
node,
|
|
962
|
-
fileGraph,
|
|
963
|
-
sourceRoot,
|
|
964
|
-
removeDecorators,
|
|
965
|
-
responseTypesToExport,
|
|
966
|
-
pathAliasRewrites
|
|
967
|
-
);
|
|
968
|
-
const outputPath = await this.writeOutputFile(
|
|
969
|
-
outputDir,
|
|
970
|
-
node.relativePath,
|
|
971
|
-
transformedContent.content
|
|
972
|
-
);
|
|
973
|
-
copiedFiles.push(outputPath);
|
|
974
|
-
if (transformedContent.rewrites.length > 0) {
|
|
975
|
-
rewrittenImports.set(outputPath, transformedContent.rewrites);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
return { copiedFiles, rewrittenImports };
|
|
979
|
-
}
|
|
980
|
-
async preprocessEntryFiles(fileGraph, messageTypes, decoratorNames, sourceRoot) {
|
|
981
|
-
const entryContents = /* @__PURE__ */ new Map();
|
|
982
|
-
const usedLocalImports = /* @__PURE__ */ new Set();
|
|
983
|
-
if (!messageTypes?.length) {
|
|
984
|
-
return { entryContents, usedLocalImports };
|
|
985
|
-
}
|
|
986
|
-
for (const node of fileGraph.nodes.values()) {
|
|
987
|
-
if (!node.isEntryPoint) {
|
|
988
|
-
continue;
|
|
989
|
-
}
|
|
990
|
-
const rawContent = await this.readFileContent(node.absolutePath);
|
|
991
|
-
const { content: extractedContent, usedModuleSpecifiers } = this.extractSymbolsFromEntry(
|
|
992
|
-
rawContent,
|
|
993
|
-
node.absolutePath,
|
|
994
|
-
messageTypes,
|
|
995
|
-
decoratorNames
|
|
996
|
-
);
|
|
997
|
-
entryContents.set(node.absolutePath, extractedContent);
|
|
998
|
-
for (const specifier of usedModuleSpecifiers) {
|
|
999
|
-
const importInfo = node.imports.find(
|
|
1000
|
-
(i) => i.moduleSpecifier === specifier
|
|
1001
|
-
);
|
|
1002
|
-
if (importInfo?.resolvedPath && !importInfo.isExternal) {
|
|
1003
|
-
usedLocalImports.add(importInfo.resolvedPath);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
return { entryContents, usedLocalImports };
|
|
1008
|
-
}
|
|
1009
|
-
expandTransitiveDependencies(usedLocalImports, fileGraph) {
|
|
1010
|
-
let changed = true;
|
|
1011
|
-
while (changed) {
|
|
1012
|
-
changed = false;
|
|
1013
|
-
for (const importPath of usedLocalImports) {
|
|
1014
|
-
const node = fileGraph.nodes.get(importPath);
|
|
1015
|
-
if (!node) continue;
|
|
1016
|
-
for (const imp of node.imports) {
|
|
1017
|
-
const isUnusedLocalImport = imp.resolvedPath && !imp.isExternal && !usedLocalImports.has(imp.resolvedPath);
|
|
1018
|
-
if (isUnusedLocalImport) {
|
|
1019
|
-
usedLocalImports.add(imp.resolvedPath);
|
|
1020
|
-
changed = true;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
async resolveNodeContent(node, entryContents, usedLocalImports, messageTypes) {
|
|
1027
|
-
if (node.isEntryPoint && messageTypes?.length) {
|
|
1028
|
-
return entryContents.get(node.absolutePath) ?? await this.readFileContent(node.absolutePath);
|
|
1029
|
-
}
|
|
1030
|
-
const isUnusedDependency = messageTypes?.length && !usedLocalImports.has(node.absolutePath);
|
|
1031
|
-
if (isUnusedDependency) {
|
|
1032
|
-
return null;
|
|
1033
|
-
}
|
|
1034
|
-
return await this.readFileContent(node.absolutePath);
|
|
1035
|
-
}
|
|
1036
|
-
applyTransformations(content, node, fileGraph, sourceRoot, removeDecorators, responseTypesToExport, pathAliasRewrites) {
|
|
1037
|
-
const rewrites = [];
|
|
1038
|
-
let transformedContent = content;
|
|
1039
|
-
transformedContent = this.processExcludedImports(
|
|
1040
|
-
transformedContent,
|
|
1041
|
-
node,
|
|
1042
|
-
fileGraph.excludedPaths,
|
|
1043
|
-
rewrites
|
|
1044
|
-
);
|
|
1045
|
-
transformedContent = this.processDecoratorRemoval(
|
|
1046
|
-
transformedContent,
|
|
1047
|
-
node.absolutePath,
|
|
1048
|
-
removeDecorators,
|
|
1049
|
-
rewrites
|
|
1050
|
-
);
|
|
1051
|
-
transformedContent = this.processTypeExports(
|
|
1052
|
-
transformedContent,
|
|
1053
|
-
node.absolutePath,
|
|
1054
|
-
responseTypesToExport?.get(node.absolutePath),
|
|
1055
|
-
rewrites
|
|
1056
|
-
);
|
|
1057
|
-
transformedContent = this.processInternalPathAliases(
|
|
1058
|
-
transformedContent,
|
|
1059
|
-
node,
|
|
1060
|
-
fileGraph,
|
|
1061
|
-
sourceRoot,
|
|
1062
|
-
rewrites
|
|
1063
|
-
);
|
|
1064
|
-
transformedContent = this.processExternalPathAliases(
|
|
1065
|
-
transformedContent,
|
|
1066
|
-
pathAliasRewrites,
|
|
1067
|
-
rewrites
|
|
1068
|
-
);
|
|
1069
|
-
return { content: transformedContent, rewrites };
|
|
1070
|
-
}
|
|
1071
|
-
generateBarrelExport(copiedFiles, outputDir) {
|
|
1072
|
-
const lines = [];
|
|
1073
|
-
for (const filePath of copiedFiles) {
|
|
1074
|
-
const relativePath = path.relative(outputDir, filePath);
|
|
1075
|
-
lines.push(this.createExportStatement(relativePath));
|
|
1076
|
-
}
|
|
1077
|
-
return lines.join("\n");
|
|
1078
|
-
}
|
|
1079
|
-
async readFileContent(absolutePath) {
|
|
1080
|
-
try {
|
|
1081
|
-
return await this.fs.readFile(absolutePath);
|
|
1082
|
-
} catch (error) {
|
|
1083
|
-
throw new FileReadError(absolutePath, { cause: error });
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
async writeOutputFile(outputDir, relativePath, content) {
|
|
1087
|
-
const outputPath = path.join(outputDir, relativePath);
|
|
1088
|
-
const outputDirPath = path.dirname(outputPath);
|
|
1089
|
-
await this.fs.mkdir(outputDirPath, { recursive: true });
|
|
1090
|
-
try {
|
|
1091
|
-
await this.fs.writeFile(outputPath, content);
|
|
1092
|
-
} catch (error) {
|
|
1093
|
-
throw new FileWriteError(outputPath, { cause: error });
|
|
1094
|
-
}
|
|
1095
|
-
return outputPath;
|
|
1096
|
-
}
|
|
1097
|
-
processExcludedImports(content, node, excludedPaths, rewrites) {
|
|
1098
|
-
if (excludedPaths.size === 0) {
|
|
1099
|
-
return content;
|
|
1100
|
-
}
|
|
1101
|
-
const excludedResult = this.removeExcludedImports(
|
|
1102
|
-
content,
|
|
1103
|
-
node,
|
|
1104
|
-
excludedPaths
|
|
1105
|
-
);
|
|
1106
|
-
rewrites.push(...excludedResult.changes);
|
|
1107
|
-
return excludedResult.content;
|
|
1108
|
-
}
|
|
1109
|
-
processDecoratorRemoval(content, filePath, removeDecorators, rewrites) {
|
|
1110
|
-
if (!removeDecorators) {
|
|
1111
|
-
return content;
|
|
1112
|
-
}
|
|
1113
|
-
const decoratorResult = this.removeContractDecorators(
|
|
1114
|
-
content,
|
|
1115
|
-
filePath
|
|
1116
|
-
);
|
|
1117
|
-
rewrites.push(...decoratorResult.changes);
|
|
1118
|
-
return decoratorResult.content;
|
|
1119
|
-
}
|
|
1120
|
-
processTypeExports(content, filePath, typesToExport, rewrites) {
|
|
1121
|
-
if (!typesToExport || typesToExport.length === 0) {
|
|
1122
|
-
return content;
|
|
1123
|
-
}
|
|
1124
|
-
const exportResult = this.addExportToTypes(
|
|
1125
|
-
content,
|
|
1126
|
-
filePath,
|
|
1127
|
-
typesToExport
|
|
1128
|
-
);
|
|
1129
|
-
rewrites.push(...exportResult.changes);
|
|
1130
|
-
return exportResult.content;
|
|
1131
|
-
}
|
|
1132
|
-
processInternalPathAliases(content, node, fileGraph, sourceRoot, rewrites) {
|
|
1133
|
-
const internalResult = this.rewriteInternalPathAliases(
|
|
1134
|
-
content,
|
|
1135
|
-
node,
|
|
1136
|
-
fileGraph,
|
|
1137
|
-
sourceRoot
|
|
1138
|
-
);
|
|
1139
|
-
rewrites.push(...internalResult.rewrites);
|
|
1140
|
-
return internalResult.content;
|
|
1141
|
-
}
|
|
1142
|
-
processExternalPathAliases(content, pathAliasRewrites, rewrites) {
|
|
1143
|
-
if (!pathAliasRewrites) {
|
|
1144
|
-
return content;
|
|
1145
|
-
}
|
|
1146
|
-
const aliasResult = this.applyPathAliasRewrites(
|
|
1147
|
-
content,
|
|
1148
|
-
pathAliasRewrites
|
|
1149
|
-
);
|
|
1150
|
-
rewrites.push(...aliasResult.rewrites);
|
|
1151
|
-
return aliasResult.content;
|
|
1152
|
-
}
|
|
1153
|
-
createExportStatement(relativePath) {
|
|
1154
|
-
const exportPath = "./" + relativePath.replace(TS_EXTENSION_PATTERN, "");
|
|
1155
|
-
return `export * from '${exportPath}'`;
|
|
1156
|
-
}
|
|
1157
|
-
rewriteInternalPathAliases(content, node, fileGraph, sourceRoot) {
|
|
1158
|
-
let transformedContent = content;
|
|
1159
|
-
const appliedRewrites = [];
|
|
1160
|
-
for (const importInfo of node.imports) {
|
|
1161
|
-
if (this.isExternalOrRelativeImport(importInfo)) {
|
|
1162
|
-
continue;
|
|
1163
|
-
}
|
|
1164
|
-
const targetNode = this.resolveInternalImport(
|
|
1165
|
-
importInfo,
|
|
1166
|
-
fileGraph
|
|
1167
|
-
);
|
|
1168
|
-
if (!targetNode) {
|
|
1169
|
-
continue;
|
|
1170
|
-
}
|
|
1171
|
-
const relativePath = this.computeRelativePath(
|
|
1172
|
-
node.relativePath,
|
|
1173
|
-
targetNode.relativePath
|
|
1174
|
-
);
|
|
1175
|
-
const rewriteResult = this.rewriteModuleSpecifier(
|
|
1176
|
-
transformedContent,
|
|
1177
|
-
importInfo.moduleSpecifier,
|
|
1178
|
-
relativePath
|
|
1179
|
-
);
|
|
1180
|
-
if (rewriteResult.wasRewritten) {
|
|
1181
|
-
transformedContent = rewriteResult.content;
|
|
1182
|
-
appliedRewrites.push(
|
|
1183
|
-
`${importInfo.moduleSpecifier} \u2192 ${relativePath}`
|
|
1184
|
-
);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
return { content: transformedContent, rewrites: appliedRewrites };
|
|
1188
|
-
}
|
|
1189
|
-
isExternalOrRelativeImport(importInfo) {
|
|
1190
|
-
return importInfo.isExternal || importInfo.moduleSpecifier.startsWith(".");
|
|
1191
|
-
}
|
|
1192
|
-
resolveInternalImport(importInfo, fileGraph) {
|
|
1193
|
-
if (!importInfo.resolvedPath || !fileGraph.nodes.has(importInfo.resolvedPath)) {
|
|
1194
|
-
return void 0;
|
|
1195
|
-
}
|
|
1196
|
-
return fileGraph.nodes.get(importInfo.resolvedPath);
|
|
1197
|
-
}
|
|
1198
|
-
rewriteModuleSpecifier(content, originalSpecifier, newSpecifier) {
|
|
1199
|
-
const importPattern = new RegExp(
|
|
1200
|
-
`(from\\s+['"])${this.escapeRegex(originalSpecifier)}(['"])`,
|
|
1201
|
-
"g"
|
|
1202
|
-
);
|
|
1203
|
-
if (!importPattern.test(content)) {
|
|
1204
|
-
return { content, wasRewritten: false };
|
|
1205
|
-
}
|
|
1206
|
-
const rewrittenContent = content.replace(
|
|
1207
|
-
importPattern,
|
|
1208
|
-
`$1${newSpecifier}$2`
|
|
1209
|
-
);
|
|
1210
|
-
return { content: rewrittenContent, wasRewritten: true };
|
|
1211
|
-
}
|
|
1212
|
-
computeRelativePath(fromRelative, toRelative) {
|
|
1213
|
-
const fromDir = path.dirname(fromRelative);
|
|
1214
|
-
let relativePath = path.relative(fromDir, toRelative);
|
|
1215
|
-
relativePath = relativePath.replace(TS_EXTENSION_PATTERN, "");
|
|
1216
|
-
if (!relativePath.startsWith(".")) {
|
|
1217
|
-
relativePath = "./" + relativePath;
|
|
1218
|
-
}
|
|
1219
|
-
return relativePath;
|
|
1220
|
-
}
|
|
1221
|
-
applyPathAliasRewrites(content, rewrites) {
|
|
1222
|
-
let transformedContent = content;
|
|
1223
|
-
const appliedRewrites = [];
|
|
1224
|
-
for (const [from, to] of rewrites) {
|
|
1225
|
-
if (transformedContent.includes(from)) {
|
|
1226
|
-
transformedContent = transformedContent.replace(
|
|
1227
|
-
new RegExp(this.escapeRegex(from), "g"),
|
|
1228
|
-
to
|
|
1229
|
-
);
|
|
1230
|
-
appliedRewrites.push(`${from} \u2192 ${to}`);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return { content: transformedContent, rewrites: appliedRewrites };
|
|
1234
|
-
}
|
|
1235
|
-
escapeRegex(str) {
|
|
1236
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1237
|
-
}
|
|
1238
|
-
removeExcludedImports(content, node, excludedPaths) {
|
|
1239
|
-
const excludedModuleSpecifiers = this.findExcludedModuleSpecifiers(
|
|
1240
|
-
node,
|
|
1241
|
-
excludedPaths
|
|
1242
|
-
);
|
|
1243
|
-
if (excludedModuleSpecifiers.size === 0) {
|
|
1244
|
-
return { content, changes: [] };
|
|
1245
|
-
}
|
|
1246
|
-
const changes = Array.from(excludedModuleSpecifiers).map(
|
|
1247
|
-
(specifier) => `removed import: ${specifier}`
|
|
1248
|
-
);
|
|
1249
|
-
const visitorFactory = this.createExcludedImportsVisitor(
|
|
1250
|
-
excludedModuleSpecifiers
|
|
1251
|
-
);
|
|
1252
|
-
const transformedContent = this.transformSourceFile(
|
|
1253
|
-
content,
|
|
1254
|
-
node.absolutePath,
|
|
1255
|
-
visitorFactory
|
|
1256
|
-
);
|
|
1257
|
-
return { content: transformedContent, changes };
|
|
1258
|
-
}
|
|
1259
|
-
findExcludedModuleSpecifiers(node, excludedPaths) {
|
|
1260
|
-
const excludedModuleSpecifiers = /* @__PURE__ */ new Set();
|
|
1261
|
-
for (const importInfo of node.imports) {
|
|
1262
|
-
if (importInfo.resolvedPath && excludedPaths.has(importInfo.resolvedPath)) {
|
|
1263
|
-
excludedModuleSpecifiers.add(importInfo.moduleSpecifier);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
return excludedModuleSpecifiers;
|
|
1267
|
-
}
|
|
1268
|
-
createExcludedImportsVisitor(excludedModuleSpecifiers) {
|
|
1269
|
-
return (context) => {
|
|
1270
|
-
const visit = (node) => {
|
|
1271
|
-
if (this.isImportFromExcludedModule(
|
|
1272
|
-
node,
|
|
1273
|
-
excludedModuleSpecifiers
|
|
1274
|
-
)) {
|
|
1275
|
-
return void 0;
|
|
1276
|
-
}
|
|
1277
|
-
if (this.isExportFromExcludedModule(
|
|
1278
|
-
node,
|
|
1279
|
-
excludedModuleSpecifiers
|
|
1280
|
-
)) {
|
|
1281
|
-
return void 0;
|
|
1282
|
-
}
|
|
1283
|
-
return ts8.visitEachChild(node, visit, context);
|
|
1284
|
-
};
|
|
1285
|
-
return visit;
|
|
1286
|
-
};
|
|
1287
|
-
}
|
|
1288
|
-
isImportFromExcludedModule(node, excludedModules) {
|
|
1289
|
-
if (!ts8.isImportDeclaration(node)) {
|
|
1290
|
-
return false;
|
|
1291
|
-
}
|
|
1292
|
-
const moduleSpecifier = node.moduleSpecifier.text;
|
|
1293
|
-
return excludedModules.has(moduleSpecifier);
|
|
1294
|
-
}
|
|
1295
|
-
isExportFromExcludedModule(node, excludedModules) {
|
|
1296
|
-
if (!ts8.isExportDeclaration(node) || !node.moduleSpecifier) {
|
|
1297
|
-
return false;
|
|
1298
|
-
}
|
|
1299
|
-
const moduleSpecifier = node.moduleSpecifier.text;
|
|
1300
|
-
return excludedModules.has(moduleSpecifier);
|
|
1301
|
-
}
|
|
1302
|
-
removeContractDecorators(content, filePath) {
|
|
1303
|
-
const changes = [];
|
|
1304
|
-
const visitorFactory = this.createDecoratorRemovalVisitor(changes);
|
|
1305
|
-
const transformedContent = this.transformSourceFile(
|
|
1306
|
-
content,
|
|
1307
|
-
filePath,
|
|
1308
|
-
visitorFactory
|
|
1309
|
-
);
|
|
1310
|
-
return { content: transformedContent, changes };
|
|
1311
|
-
}
|
|
1312
|
-
createDecoratorRemovalVisitor(changes) {
|
|
1313
|
-
return (context) => {
|
|
1314
|
-
const visit = (node) => {
|
|
1315
|
-
if (ts8.isImportDeclaration(node)) {
|
|
1316
|
-
return this.processContractGeneratorImport(node, changes);
|
|
1317
|
-
}
|
|
1318
|
-
if (ts8.isClassDeclaration(node) && node.modifiers) {
|
|
1319
|
-
return this.removeContractDecoratorsFromClass(
|
|
1320
|
-
node,
|
|
1321
|
-
changes
|
|
1322
|
-
);
|
|
1323
|
-
}
|
|
1324
|
-
return ts8.visitEachChild(node, visit, context);
|
|
1325
|
-
};
|
|
1326
|
-
return visit;
|
|
1327
|
-
};
|
|
1328
|
-
}
|
|
1329
|
-
processContractGeneratorImport(node, changes) {
|
|
1330
|
-
const moduleSpecifier = node.moduleSpecifier.text;
|
|
1331
|
-
const isContractGeneratorModule = moduleSpecifier === CONTRACTS_GENERATOR_MODULE || moduleSpecifier.startsWith(CONTRACTS_GENERATOR_MODULE + "/");
|
|
1332
|
-
if (!isContractGeneratorModule) {
|
|
1333
|
-
return node;
|
|
1334
|
-
}
|
|
1335
|
-
const namedBindings = node.importClause?.namedBindings;
|
|
1336
|
-
if (!namedBindings || !ts8.isNamedImports(namedBindings)) {
|
|
1337
|
-
return node;
|
|
1338
|
-
}
|
|
1339
|
-
const remainingElements = namedBindings.elements.filter(
|
|
1340
|
-
(el) => !CONTRACT_DECORATORS.has(el.name.text)
|
|
1341
|
-
);
|
|
1342
|
-
if (remainingElements.length === 0) {
|
|
1343
|
-
changes.push(`removed import: ${moduleSpecifier}`);
|
|
1344
|
-
return void 0;
|
|
1345
|
-
}
|
|
1346
|
-
if (remainingElements.length < namedBindings.elements.length) {
|
|
1347
|
-
changes.push(`removed decorators from import: ${moduleSpecifier}`);
|
|
1348
|
-
return this.createImportWithFilteredBindings(
|
|
1349
|
-
node,
|
|
1350
|
-
remainingElements
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
|
-
return node;
|
|
1354
|
-
}
|
|
1355
|
-
createImportWithFilteredBindings(originalImport, remainingElements) {
|
|
1356
|
-
const newNamedImports = ts8.factory.createNamedImports(remainingElements);
|
|
1357
|
-
const newImportClause = ts8.factory.createImportClause(
|
|
1358
|
-
originalImport.importClause.isTypeOnly,
|
|
1359
|
-
originalImport.importClause.name,
|
|
1360
|
-
newNamedImports
|
|
1361
|
-
);
|
|
1362
|
-
return ts8.factory.createImportDeclaration(
|
|
1363
|
-
originalImport.modifiers,
|
|
1364
|
-
newImportClause,
|
|
1365
|
-
originalImport.moduleSpecifier,
|
|
1366
|
-
originalImport.attributes
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
removeContractDecoratorsFromClass(node, changes) {
|
|
1370
|
-
const filteredModifiers = node.modifiers.filter((modifier) => {
|
|
1371
|
-
if (!ts8.isDecorator(modifier)) {
|
|
1372
|
-
return true;
|
|
1373
|
-
}
|
|
1374
|
-
const decoratorName = this.extractDecoratorName(modifier);
|
|
1375
|
-
if (decoratorName && CONTRACT_DECORATORS.has(decoratorName)) {
|
|
1376
|
-
const decoratorSuffix = this.isDecoratorCallExpression(modifier) ? "()" : "";
|
|
1377
|
-
changes.push(
|
|
1378
|
-
`removed decorator: @${decoratorName}${decoratorSuffix}`
|
|
1379
|
-
);
|
|
1380
|
-
return false;
|
|
1381
|
-
}
|
|
1382
|
-
return true;
|
|
1383
|
-
});
|
|
1384
|
-
if (filteredModifiers.length === node.modifiers.length) {
|
|
1385
|
-
return node;
|
|
1386
|
-
}
|
|
1387
|
-
return ts8.factory.createClassDeclaration(
|
|
1388
|
-
filteredModifiers,
|
|
1389
|
-
node.name,
|
|
1390
|
-
node.typeParameters,
|
|
1391
|
-
node.heritageClauses,
|
|
1392
|
-
node.members
|
|
1393
|
-
);
|
|
1394
|
-
}
|
|
1395
|
-
extractDecoratorName(decorator) {
|
|
1396
|
-
const expression = decorator.expression;
|
|
1397
|
-
if (ts8.isCallExpression(expression) && ts8.isIdentifier(expression.expression)) {
|
|
1398
|
-
return expression.expression.text;
|
|
1399
|
-
}
|
|
1400
|
-
if (ts8.isIdentifier(expression)) {
|
|
1401
|
-
return expression.text;
|
|
1402
|
-
}
|
|
1403
|
-
return void 0;
|
|
1404
|
-
}
|
|
1405
|
-
isDecoratorCallExpression(decorator) {
|
|
1406
|
-
return ts8.isCallExpression(decorator.expression);
|
|
1407
|
-
}
|
|
1408
|
-
hasExportModifier(node) {
|
|
1409
|
-
return node.modifiers?.some(
|
|
1410
|
-
(m) => m.kind === ts8.SyntaxKind.ExportKeyword
|
|
1411
|
-
) ?? false;
|
|
1412
|
-
}
|
|
1413
|
-
prependExportModifier(modifiers) {
|
|
1414
|
-
const exportModifier = ts8.factory.createModifier(
|
|
1415
|
-
ts8.SyntaxKind.ExportKeyword
|
|
1416
|
-
);
|
|
1417
|
-
return modifiers ? [exportModifier, ...modifiers] : [exportModifier];
|
|
1418
|
-
}
|
|
1419
|
-
addExportToTypes(content, filePath, typeNames) {
|
|
1420
|
-
const changes = [];
|
|
1421
|
-
const typeNamesSet = new Set(typeNames);
|
|
1422
|
-
const visitorFactory = this.createExportAdditionVisitor(
|
|
1423
|
-
typeNamesSet,
|
|
1424
|
-
changes
|
|
1425
|
-
);
|
|
1426
|
-
const transformedContent = this.transformSourceFile(
|
|
1427
|
-
content,
|
|
1428
|
-
filePath,
|
|
1429
|
-
visitorFactory
|
|
1430
|
-
);
|
|
1431
|
-
return { content: transformedContent, changes };
|
|
1432
|
-
}
|
|
1433
|
-
createExportAdditionVisitor(typeNamesSet, changes) {
|
|
1434
|
-
return (context) => {
|
|
1435
|
-
const visit = (node) => {
|
|
1436
|
-
if (ts8.isTypeAliasDeclaration(node)) {
|
|
1437
|
-
return this.addExportToTypeAlias(
|
|
1438
|
-
node,
|
|
1439
|
-
typeNamesSet,
|
|
1440
|
-
changes
|
|
1441
|
-
);
|
|
1442
|
-
}
|
|
1443
|
-
if (ts8.isInterfaceDeclaration(node)) {
|
|
1444
|
-
return this.addExportToInterface(
|
|
1445
|
-
node,
|
|
1446
|
-
typeNamesSet,
|
|
1447
|
-
changes
|
|
1448
|
-
);
|
|
1449
|
-
}
|
|
1450
|
-
return ts8.visitEachChild(node, visit, context);
|
|
1451
|
-
};
|
|
1452
|
-
return visit;
|
|
1453
|
-
};
|
|
1454
|
-
}
|
|
1455
|
-
addExportToTypeAlias(node, typeNamesSet, changes) {
|
|
1456
|
-
const typeName = node.name.text;
|
|
1457
|
-
if (!typeNamesSet.has(typeName) || this.hasExportModifier(node)) {
|
|
1458
|
-
return node;
|
|
1459
|
-
}
|
|
1460
|
-
changes.push(`added export: type ${typeName}`);
|
|
1461
|
-
return ts8.factory.createTypeAliasDeclaration(
|
|
1462
|
-
this.prependExportModifier(node.modifiers),
|
|
1463
|
-
node.name,
|
|
1464
|
-
node.typeParameters,
|
|
1465
|
-
node.type
|
|
1466
|
-
);
|
|
1467
|
-
}
|
|
1468
|
-
addExportToInterface(node, typeNamesSet, changes) {
|
|
1469
|
-
const typeName = node.name.text;
|
|
1470
|
-
if (!typeNamesSet.has(typeName) || this.hasExportModifier(node)) {
|
|
1471
|
-
return node;
|
|
1472
|
-
}
|
|
1473
|
-
changes.push(`added export: interface ${typeName}`);
|
|
1474
|
-
return ts8.factory.createInterfaceDeclaration(
|
|
1475
|
-
this.prependExportModifier(node.modifiers),
|
|
1476
|
-
node.name,
|
|
1477
|
-
node.typeParameters,
|
|
1478
|
-
node.heritageClauses,
|
|
1479
|
-
node.members
|
|
1480
|
-
);
|
|
1481
|
-
}
|
|
1482
|
-
transformSourceFile(content, filePath, visitorFactory) {
|
|
1483
|
-
const sourceFile = ts8.createSourceFile(
|
|
1484
|
-
filePath,
|
|
1485
|
-
content,
|
|
1486
|
-
ts8.ScriptTarget.Latest,
|
|
1487
|
-
true,
|
|
1488
|
-
ts8.ScriptKind.TS
|
|
1489
|
-
);
|
|
1490
|
-
const transformer = (context) => {
|
|
1491
|
-
return (sf) => {
|
|
1492
|
-
const visitor = visitorFactory(context);
|
|
1493
|
-
return ts8.visitNode(sf, visitor);
|
|
1494
|
-
};
|
|
1495
|
-
};
|
|
1496
|
-
const result = ts8.transform(sourceFile, [transformer]);
|
|
1497
|
-
const transformedSourceFile = result.transformed[0];
|
|
1498
|
-
const printer = ts8.createPrinter({ newLine: ts8.NewLineKind.LineFeed });
|
|
1499
|
-
const transformedContent = printer.printFile(transformedSourceFile);
|
|
1500
|
-
result.dispose();
|
|
1501
|
-
return transformedContent;
|
|
1502
|
-
}
|
|
1503
|
-
extractSymbolsFromEntry(content, filePath, messageTypes, decoratorNames) {
|
|
1504
|
-
const sourceFile = ts8.createSourceFile(
|
|
1505
|
-
filePath,
|
|
1506
|
-
content,
|
|
1507
|
-
ts8.ScriptTarget.Latest,
|
|
1508
|
-
true,
|
|
1509
|
-
ts8.ScriptKind.TS
|
|
1510
|
-
);
|
|
1511
|
-
const decoratorToMessageType = this.buildDecoratorToMessageTypeMap(decoratorNames);
|
|
1512
|
-
const context = {
|
|
1513
|
-
sourceFile,
|
|
1514
|
-
content,
|
|
1515
|
-
messageTypes,
|
|
1516
|
-
decoratorToMessageType
|
|
1517
|
-
};
|
|
1518
|
-
const { targetClassNames, targetClasses } = this.findTargetClasses(context);
|
|
1519
|
-
if (targetClasses.length === 0) {
|
|
1520
|
-
return { content, usedModuleSpecifiers: /* @__PURE__ */ new Set() };
|
|
1521
|
-
}
|
|
1522
|
-
const relatedTypeNames = this.computeRelatedTypeNames(targetClassNames);
|
|
1523
|
-
const usedIdentifiers = this.collectUsedIdentifiers(targetClasses);
|
|
1524
|
-
const localTypeDeclarations = this.collectLocalTypeDeclarations(sourceFile);
|
|
1525
|
-
const includedLocalTypes = this.resolveIncludedLocalTypes(
|
|
1526
|
-
usedIdentifiers,
|
|
1527
|
-
relatedTypeNames,
|
|
1528
|
-
localTypeDeclarations
|
|
1529
|
-
);
|
|
1530
|
-
const extractedSymbols = {
|
|
1531
|
-
targetClassNames,
|
|
1532
|
-
targetClasses,
|
|
1533
|
-
usedIdentifiers,
|
|
1534
|
-
includedLocalTypes,
|
|
1535
|
-
localTypeDeclarations
|
|
1536
|
-
};
|
|
1537
|
-
return this.generateExtractedOutput(context, extractedSymbols);
|
|
1538
|
-
}
|
|
1539
|
-
buildDecoratorToMessageTypeMap(decoratorNames) {
|
|
1540
|
-
const names = { ...DEFAULT_DECORATOR_NAMES, ...decoratorNames };
|
|
1541
|
-
return {
|
|
1542
|
-
[names.event]: "event",
|
|
1543
|
-
[names.command]: "command",
|
|
1544
|
-
[names.query]: "query"
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
findTargetClasses(context) {
|
|
1548
|
-
const targetClassNames = /* @__PURE__ */ new Set();
|
|
1549
|
-
const targetClasses = [];
|
|
1550
|
-
const findClasses = (node) => {
|
|
1551
|
-
if (ts8.isClassDeclaration(node) && node.name) {
|
|
1552
|
-
for (const [decoratorName, messageType] of Object.entries(
|
|
1553
|
-
context.decoratorToMessageType
|
|
1554
|
-
)) {
|
|
1555
|
-
if (hasDecorator(node, decoratorName) && context.messageTypes.includes(messageType)) {
|
|
1556
|
-
targetClassNames.add(node.name.text);
|
|
1557
|
-
targetClasses.push(node);
|
|
1558
|
-
break;
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
ts8.forEachChild(node, findClasses);
|
|
1563
|
-
};
|
|
1564
|
-
findClasses(context.sourceFile);
|
|
1565
|
-
return { targetClassNames, targetClasses };
|
|
1566
|
-
}
|
|
1567
|
-
computeRelatedTypeNames(targetClassNames) {
|
|
1568
|
-
const relatedTypeNames = /* @__PURE__ */ new Set();
|
|
1569
|
-
for (const className of targetClassNames) {
|
|
1570
|
-
if (className.endsWith(REQUEST_SUFFIX)) {
|
|
1571
|
-
const baseName = className.slice(0, -REQUEST_SUFFIX.length);
|
|
1572
|
-
relatedTypeNames.add(baseName + "Response");
|
|
1573
|
-
relatedTypeNames.add(baseName + "Payload");
|
|
1574
|
-
} else if (className.endsWith(QUERY_SUFFIX)) {
|
|
1575
|
-
const baseName = className.slice(0, -QUERY_SUFFIX.length);
|
|
1576
|
-
relatedTypeNames.add(baseName + "QueryResult");
|
|
1577
|
-
relatedTypeNames.add(baseName + "Payload");
|
|
1578
|
-
}
|
|
1579
|
-
relatedTypeNames.add(className + "Response");
|
|
1580
|
-
relatedTypeNames.add(className + "Payload");
|
|
1581
|
-
}
|
|
1582
|
-
return relatedTypeNames;
|
|
1583
|
-
}
|
|
1584
|
-
collectUsedIdentifiers(targetClasses) {
|
|
1585
|
-
const usedIdentifiers = /* @__PURE__ */ new Set();
|
|
1586
|
-
const collectIdentifiers = (node) => {
|
|
1587
|
-
if (ts8.isTypeReferenceNode(node) && ts8.isIdentifier(node.typeName)) {
|
|
1588
|
-
usedIdentifiers.add(node.typeName.text);
|
|
1589
|
-
}
|
|
1590
|
-
if (ts8.isExpressionWithTypeArguments(node) && ts8.isIdentifier(node.expression)) {
|
|
1591
|
-
usedIdentifiers.add(node.expression.text);
|
|
1592
|
-
}
|
|
1593
|
-
if (ts8.isIdentifier(node) && node.parent) {
|
|
1594
|
-
const isHeritageOrTypeRef = ts8.isHeritageClause(node.parent.parent) || ts8.isTypeReferenceNode(node.parent);
|
|
1595
|
-
if (isHeritageOrTypeRef) {
|
|
1596
|
-
usedIdentifiers.add(node.text);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
if (ts8.isDecorator(node)) {
|
|
1600
|
-
this.collectDecoratorIdentifier(node, usedIdentifiers);
|
|
1601
|
-
}
|
|
1602
|
-
if (ts8.isCallExpression(node) && ts8.isIdentifier(node.expression)) {
|
|
1603
|
-
usedIdentifiers.add(node.expression.text);
|
|
1604
|
-
}
|
|
1605
|
-
ts8.forEachChild(node, collectIdentifiers);
|
|
1606
|
-
};
|
|
1607
|
-
for (const cls of targetClasses) {
|
|
1608
|
-
collectIdentifiers(cls);
|
|
1609
|
-
}
|
|
1610
|
-
return usedIdentifiers;
|
|
1611
|
-
}
|
|
1612
|
-
collectDecoratorIdentifier(decorator, usedIdentifiers) {
|
|
1613
|
-
const expr = decorator.expression;
|
|
1614
|
-
if (ts8.isIdentifier(expr)) {
|
|
1615
|
-
usedIdentifiers.add(expr.text);
|
|
1616
|
-
} else if (ts8.isCallExpression(expr) && ts8.isIdentifier(expr.expression)) {
|
|
1617
|
-
usedIdentifiers.add(expr.expression.text);
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
collectLocalTypeDeclarations(sourceFile) {
|
|
1621
|
-
const localTypeDeclarations = /* @__PURE__ */ new Map();
|
|
1622
|
-
const collectLocalTypes = (node) => {
|
|
1623
|
-
if (ts8.isInterfaceDeclaration(node) && node.name) {
|
|
1624
|
-
this.addToDeclarationMap(
|
|
1625
|
-
localTypeDeclarations,
|
|
1626
|
-
node.name.text,
|
|
1627
|
-
node
|
|
1628
|
-
);
|
|
1629
|
-
}
|
|
1630
|
-
if (ts8.isTypeAliasDeclaration(node) && node.name) {
|
|
1631
|
-
this.addToDeclarationMap(
|
|
1632
|
-
localTypeDeclarations,
|
|
1633
|
-
node.name.text,
|
|
1634
|
-
node
|
|
1635
|
-
);
|
|
1636
|
-
}
|
|
1637
|
-
if (ts8.isVariableStatement(node)) {
|
|
1638
|
-
for (const decl of node.declarationList.declarations) {
|
|
1639
|
-
if (ts8.isIdentifier(decl.name)) {
|
|
1640
|
-
this.addToDeclarationMap(
|
|
1641
|
-
localTypeDeclarations,
|
|
1642
|
-
decl.name.text,
|
|
1643
|
-
node
|
|
1644
|
-
);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
ts8.forEachChild(node, collectLocalTypes);
|
|
52
|
+
function parseArgs(args) {
|
|
53
|
+
const options = {
|
|
54
|
+
config: DEFAULT_CONFIG_PATH,
|
|
1649
55
|
};
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
map.set(name, existing);
|
|
1657
|
-
}
|
|
1658
|
-
resolveIncludedLocalTypes(usedIdentifiers, relatedTypeNames, localTypeDeclarations) {
|
|
1659
|
-
const includedLocalTypes = /* @__PURE__ */ new Set();
|
|
1660
|
-
const queue = [...usedIdentifiers, ...relatedTypeNames];
|
|
1661
|
-
while (queue.length > 0) {
|
|
1662
|
-
const identifier = queue.shift();
|
|
1663
|
-
if (includedLocalTypes.has(identifier)) continue;
|
|
1664
|
-
const typeNodes = localTypeDeclarations.get(identifier);
|
|
1665
|
-
if (!typeNodes || typeNodes.length === 0) continue;
|
|
1666
|
-
includedLocalTypes.add(identifier);
|
|
1667
|
-
const typeIdentifiers = this.collectTypeIdentifiersFromNodes(typeNodes);
|
|
1668
|
-
for (const id of typeIdentifiers) {
|
|
1669
|
-
if (!includedLocalTypes.has(id)) {
|
|
1670
|
-
queue.push(id);
|
|
1671
|
-
usedIdentifiers.add(id);
|
|
56
|
+
for (let i = 0; i < args.length; i++) {
|
|
57
|
+
const arg = args[i];
|
|
58
|
+
if (matchesOption(arg, CLI_OPTIONS.config)) {
|
|
59
|
+
const { value, nextIndex } = extractOptionValue(args, i, "--config");
|
|
60
|
+
options.config = value;
|
|
61
|
+
i = nextIndex;
|
|
1672
62
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
collectTypeIdentifiersFromNodes(nodes) {
|
|
1678
|
-
const typeIdentifiers = /* @__PURE__ */ new Set();
|
|
1679
|
-
const collectFromType = (node) => {
|
|
1680
|
-
if (ts8.isTypeReferenceNode(node) && ts8.isIdentifier(node.typeName)) {
|
|
1681
|
-
typeIdentifiers.add(node.typeName.text);
|
|
1682
|
-
}
|
|
1683
|
-
if (ts8.isExpressionWithTypeArguments(node) && ts8.isIdentifier(node.expression)) {
|
|
1684
|
-
typeIdentifiers.add(node.expression.text);
|
|
1685
|
-
}
|
|
1686
|
-
if (ts8.isComputedPropertyName(node)) {
|
|
1687
|
-
this.collectComputedPropertyIdentifier(node, typeIdentifiers);
|
|
1688
|
-
}
|
|
1689
|
-
ts8.forEachChild(node, collectFromType);
|
|
1690
|
-
};
|
|
1691
|
-
for (const typeNode of nodes) {
|
|
1692
|
-
collectFromType(typeNode);
|
|
1693
|
-
}
|
|
1694
|
-
return typeIdentifiers;
|
|
1695
|
-
}
|
|
1696
|
-
collectComputedPropertyIdentifier(node, typeIdentifiers) {
|
|
1697
|
-
const expr = node.expression;
|
|
1698
|
-
if (ts8.isPropertyAccessExpression(expr) && ts8.isIdentifier(expr.expression)) {
|
|
1699
|
-
typeIdentifiers.add(expr.expression.text);
|
|
1700
|
-
}
|
|
1701
|
-
if (ts8.isIdentifier(expr)) {
|
|
1702
|
-
typeIdentifiers.add(expr.text);
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
generateExtractedOutput(context, symbols) {
|
|
1706
|
-
const output = [];
|
|
1707
|
-
const importMap = this.buildImportMap(context.sourceFile);
|
|
1708
|
-
const filteredImports = this.filterImports(
|
|
1709
|
-
symbols.usedIdentifiers,
|
|
1710
|
-
importMap,
|
|
1711
|
-
symbols.includedLocalTypes
|
|
1712
|
-
);
|
|
1713
|
-
this.appendImportStatements(output, filteredImports);
|
|
1714
|
-
this.appendLocalTypeDeclarations(output, context, symbols);
|
|
1715
|
-
this.appendTargetClasses(output, context, symbols.targetClasses);
|
|
1716
|
-
const usedModuleSpecifiers = new Set(filteredImports.keys());
|
|
1717
|
-
return {
|
|
1718
|
-
content: output.join("\n"),
|
|
1719
|
-
usedModuleSpecifiers
|
|
1720
|
-
};
|
|
1721
|
-
}
|
|
1722
|
-
buildImportMap(sourceFile) {
|
|
1723
|
-
const importMap = /* @__PURE__ */ new Map();
|
|
1724
|
-
const collectImports = (node) => {
|
|
1725
|
-
if (ts8.isImportDeclaration(node)) {
|
|
1726
|
-
const moduleSpecifier = node.moduleSpecifier.text;
|
|
1727
|
-
const namedBindings = node.importClause?.namedBindings;
|
|
1728
|
-
const isTypeOnly = node.importClause?.isTypeOnly ?? false;
|
|
1729
|
-
if (namedBindings && ts8.isNamedImports(namedBindings)) {
|
|
1730
|
-
for (const element of namedBindings.elements) {
|
|
1731
|
-
importMap.set(element.name.text, {
|
|
1732
|
-
moduleSpecifier,
|
|
1733
|
-
isTypeOnly
|
|
1734
|
-
});
|
|
1735
|
-
}
|
|
63
|
+
else if (matchesOption(arg, CLI_OPTIONS.outputDir)) {
|
|
64
|
+
const { value, nextIndex } = extractOptionValue(args, i, "--output-dir");
|
|
65
|
+
options.outputDir = value;
|
|
66
|
+
i = nextIndex;
|
|
1736
67
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
return importMap;
|
|
1742
|
-
}
|
|
1743
|
-
filterImports(usedIdentifiers, importMap, includedLocalTypes) {
|
|
1744
|
-
const filteredImports = /* @__PURE__ */ new Map();
|
|
1745
|
-
for (const identifier of usedIdentifiers) {
|
|
1746
|
-
const importInfo = importMap.get(identifier);
|
|
1747
|
-
if (!importInfo || includedLocalTypes.has(identifier)) {
|
|
1748
|
-
continue;
|
|
1749
|
-
}
|
|
1750
|
-
const existing = filteredImports.get(importInfo.moduleSpecifier);
|
|
1751
|
-
if (existing) {
|
|
1752
|
-
existing.identifiers.add(identifier);
|
|
1753
|
-
} else {
|
|
1754
|
-
filteredImports.set(importInfo.moduleSpecifier, {
|
|
1755
|
-
identifiers: /* @__PURE__ */ new Set([identifier]),
|
|
1756
|
-
isTypeOnly: importInfo.isTypeOnly
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
return filteredImports;
|
|
1761
|
-
}
|
|
1762
|
-
appendImportStatements(output, filteredImports) {
|
|
1763
|
-
for (const [moduleSpecifier, info] of filteredImports) {
|
|
1764
|
-
const identifiers = [...info.identifiers].sort().join(", ");
|
|
1765
|
-
const typeOnlyPrefix = info.isTypeOnly ? "type " : "";
|
|
1766
|
-
output.push(
|
|
1767
|
-
`import ${typeOnlyPrefix}{ ${identifiers} } from "${moduleSpecifier}";`
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1770
|
-
if (output.length > 0) {
|
|
1771
|
-
output.push("");
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
appendLocalTypeDeclarations(output, context, symbols) {
|
|
1775
|
-
const outputNodes = /* @__PURE__ */ new Set();
|
|
1776
|
-
for (const typeName of symbols.includedLocalTypes) {
|
|
1777
|
-
const typeNodes = symbols.localTypeDeclarations.get(typeName);
|
|
1778
|
-
if (!typeNodes) continue;
|
|
1779
|
-
for (const typeNode of typeNodes) {
|
|
1780
|
-
if (outputNodes.has(typeNode)) continue;
|
|
1781
|
-
outputNodes.add(typeNode);
|
|
1782
|
-
this.appendNodeWithExport(output, context, typeNode);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
appendTargetClasses(output, context, targetClasses) {
|
|
1787
|
-
for (const cls of targetClasses) {
|
|
1788
|
-
this.appendNodeWithExport(output, context, cls);
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
appendNodeWithExport(output, context, node) {
|
|
1792
|
-
const nodeText = context.content.substring(node.getStart(context.sourceFile), node.end).trim();
|
|
1793
|
-
const hasExport = this.nodeHasExportKeyword(node);
|
|
1794
|
-
if (hasExport) {
|
|
1795
|
-
output.push(nodeText);
|
|
1796
|
-
} else {
|
|
1797
|
-
output.push("export " + nodeText);
|
|
1798
|
-
}
|
|
1799
|
-
output.push("");
|
|
1800
|
-
}
|
|
1801
|
-
nodeHasExportKeyword(node) {
|
|
1802
|
-
const isExportableDeclaration = ts8.isInterfaceDeclaration(node) || ts8.isTypeAliasDeclaration(node) || ts8.isVariableStatement(node) || ts8.isClassDeclaration(node);
|
|
1803
|
-
if (!isExportableDeclaration) {
|
|
1804
|
-
return false;
|
|
1805
|
-
}
|
|
1806
|
-
const declarationNode = node;
|
|
1807
|
-
return declarationNode.modifiers?.some(
|
|
1808
|
-
(m) => m.kind === ts8.SyntaxKind.ExportKeyword
|
|
1809
|
-
) ?? false;
|
|
1810
|
-
}
|
|
1811
|
-
};
|
|
1812
|
-
|
|
1813
|
-
// src/registry-generator.ts
|
|
1814
|
-
var DEFAULT_OPTIONS = {
|
|
1815
|
-
messageRegistryImport: "@hexaijs/plugin-contracts-generator/runtime"
|
|
1816
|
-
};
|
|
1817
|
-
function hasMessages(ctx) {
|
|
1818
|
-
return ctx.events.length > 0 || ctx.commands.length > 0 || (ctx.queries?.length ?? 0) > 0;
|
|
1819
|
-
}
|
|
1820
|
-
function getAllMessages(ctx) {
|
|
1821
|
-
return [...ctx.events, ...ctx.commands, ...ctx.queries ?? []];
|
|
1822
|
-
}
|
|
1823
|
-
var RegistryGenerator = class {
|
|
1824
|
-
options;
|
|
1825
|
-
constructor(options = {}) {
|
|
1826
|
-
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
1827
|
-
}
|
|
1828
|
-
generate(contexts) {
|
|
1829
|
-
const allMessages = contexts.flatMap(
|
|
1830
|
-
(ctx) => getAllMessages(ctx).map((message) => ({
|
|
1831
|
-
message,
|
|
1832
|
-
contextName: ctx.contextName
|
|
1833
|
-
}))
|
|
1834
|
-
);
|
|
1835
|
-
if (allMessages.length === 0) {
|
|
1836
|
-
return this.generateEmptyRegistry();
|
|
1837
|
-
}
|
|
1838
|
-
if (this.options.useNamespace) {
|
|
1839
|
-
return this.generateWithNamespace(contexts, allMessages);
|
|
1840
|
-
}
|
|
1841
|
-
const imports = this.generateImports(contexts);
|
|
1842
|
-
const registrations = this.generateRegistrations(allMessages);
|
|
1843
|
-
return [
|
|
1844
|
-
imports,
|
|
1845
|
-
"",
|
|
1846
|
-
"export const messageRegistry = new MessageRegistry()",
|
|
1847
|
-
registrations
|
|
1848
|
-
].join("\n");
|
|
1849
|
-
}
|
|
1850
|
-
generateEmptyRegistry() {
|
|
1851
|
-
return [
|
|
1852
|
-
`import { MessageRegistry } from "${this.options.messageRegistryImport}";`,
|
|
1853
|
-
"",
|
|
1854
|
-
"export const messageRegistry = new MessageRegistry();",
|
|
1855
|
-
""
|
|
1856
|
-
].join("\n");
|
|
1857
|
-
}
|
|
1858
|
-
generateWithNamespace(contexts, allMessages) {
|
|
1859
|
-
const imports = this.generateNamespaceImports(contexts);
|
|
1860
|
-
const exports$1 = this.generateNamespaceExports(contexts);
|
|
1861
|
-
const registrations = this.generateNamespaceRegistrations(allMessages);
|
|
1862
|
-
return [
|
|
1863
|
-
imports,
|
|
1864
|
-
"",
|
|
1865
|
-
exports$1,
|
|
1866
|
-
"",
|
|
1867
|
-
"export const messageRegistry = new MessageRegistry()",
|
|
1868
|
-
registrations
|
|
1869
|
-
].join("\n");
|
|
1870
|
-
}
|
|
1871
|
-
getNamespaceInfos(contexts) {
|
|
1872
|
-
return contexts.filter(hasMessages).map((ctx) => ({
|
|
1873
|
-
importPath: ctx.importPath ?? `./${ctx.contextName}`,
|
|
1874
|
-
namespace: this.toNamespace(ctx.contextName)
|
|
1875
|
-
}));
|
|
1876
|
-
}
|
|
1877
|
-
generateNamespaceImports(contexts) {
|
|
1878
|
-
return [
|
|
1879
|
-
`import { MessageRegistry } from "${this.options.messageRegistryImport}";`,
|
|
1880
|
-
...this.getNamespaceInfos(contexts).map(
|
|
1881
|
-
({ importPath, namespace }) => `import * as ${namespace} from "${importPath}";`
|
|
1882
|
-
)
|
|
1883
|
-
].join("\n");
|
|
1884
|
-
}
|
|
1885
|
-
generateNamespaceExports(contexts) {
|
|
1886
|
-
return this.getNamespaceInfos(contexts).map(
|
|
1887
|
-
({ importPath, namespace }) => `export * as ${namespace} from "${importPath}";`
|
|
1888
|
-
).join("\n");
|
|
1889
|
-
}
|
|
1890
|
-
generateNamespaceRegistrations(messages) {
|
|
1891
|
-
const lines = messages.map(({ message, contextName }) => {
|
|
1892
|
-
const namespace = this.toNamespace(contextName);
|
|
1893
|
-
return ` .register(${namespace}.${message.name})`;
|
|
1894
|
-
});
|
|
1895
|
-
return lines.join("\n") + ";\n";
|
|
1896
|
-
}
|
|
1897
|
-
toNamespace(contextName) {
|
|
1898
|
-
return contextName.replace(
|
|
1899
|
-
/-([a-z])/g,
|
|
1900
|
-
(_, letter) => letter.toUpperCase()
|
|
1901
|
-
);
|
|
1902
|
-
}
|
|
1903
|
-
generateImports(contexts) {
|
|
1904
|
-
const lines = [
|
|
1905
|
-
`import { MessageRegistry } from "${this.options.messageRegistryImport}";`
|
|
1906
|
-
];
|
|
1907
|
-
for (const ctx of contexts) {
|
|
1908
|
-
const messageNames = getAllMessages(ctx).map((m) => m.name);
|
|
1909
|
-
if (messageNames.length > 0) {
|
|
1910
|
-
const importPath = ctx.importPath ?? `./${ctx.contextName}`;
|
|
1911
|
-
lines.push(
|
|
1912
|
-
`import { ${messageNames.join(", ")} } from "${importPath}";`
|
|
1913
|
-
);
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
return lines.join("\n");
|
|
1917
|
-
}
|
|
1918
|
-
generateRegistrations(messages) {
|
|
1919
|
-
const lines = messages.map(
|
|
1920
|
-
({ message }) => ` .register(${message.name})`
|
|
1921
|
-
);
|
|
1922
|
-
return lines.join("\n") + ";\n";
|
|
1923
|
-
}
|
|
1924
|
-
};
|
|
1925
|
-
var ReexportGenerator = class {
|
|
1926
|
-
fs;
|
|
1927
|
-
constructor(options = {}) {
|
|
1928
|
-
this.fs = options.fileSystem ?? nodeFileSystem;
|
|
1929
|
-
}
|
|
1930
|
-
/**
|
|
1931
|
-
* Analyzes files to find imports that match pathAliasRewrites
|
|
1932
|
-
* and groups them by rewritten path
|
|
1933
|
-
*/
|
|
1934
|
-
async analyze(options) {
|
|
1935
|
-
const { files, pathAliasRewrites } = options;
|
|
1936
|
-
const rewrittenToOriginal = /* @__PURE__ */ new Map();
|
|
1937
|
-
for (const [original, rewritten] of pathAliasRewrites) {
|
|
1938
|
-
rewrittenToOriginal.set(rewritten, original);
|
|
1939
|
-
}
|
|
1940
|
-
const allImports = [];
|
|
1941
|
-
for (const file of files) {
|
|
1942
|
-
const content = await this.fs.readFile(file);
|
|
1943
|
-
const imports = this.extractRewrittenImports(content, rewrittenToOriginal);
|
|
1944
|
-
allImports.push(...imports);
|
|
1945
|
-
}
|
|
1946
|
-
return this.groupImportsByPath(allImports);
|
|
1947
|
-
}
|
|
1948
|
-
/**
|
|
1949
|
-
* Generates re-export files
|
|
1950
|
-
*/
|
|
1951
|
-
async generate(options) {
|
|
1952
|
-
const { outputDir, reexportFiles } = options;
|
|
1953
|
-
const generatedFiles = [];
|
|
1954
|
-
for (const reexport of reexportFiles) {
|
|
1955
|
-
const filePath = path.join(outputDir, reexport.relativePath);
|
|
1956
|
-
const content = this.generateReexportContent(reexport);
|
|
1957
|
-
await this.fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
1958
|
-
await this.fs.writeFile(filePath, content);
|
|
1959
|
-
generatedFiles.push(filePath);
|
|
1960
|
-
}
|
|
1961
|
-
return generatedFiles;
|
|
1962
|
-
}
|
|
1963
|
-
/**
|
|
1964
|
-
* Extracts imports from source content that match rewritten prefixes
|
|
1965
|
-
*/
|
|
1966
|
-
extractRewrittenImports(content, rewrittenToOriginal) {
|
|
1967
|
-
const sourceFile = ts8.createSourceFile(
|
|
1968
|
-
"temp.ts",
|
|
1969
|
-
content,
|
|
1970
|
-
ts8.ScriptTarget.Latest,
|
|
1971
|
-
true
|
|
1972
|
-
);
|
|
1973
|
-
const imports = [];
|
|
1974
|
-
ts8.forEachChild(sourceFile, (node) => {
|
|
1975
|
-
if (!ts8.isImportDeclaration(node) || !node.importClause) {
|
|
1976
|
-
return;
|
|
1977
|
-
}
|
|
1978
|
-
const moduleSpecifier = node.moduleSpecifier.text;
|
|
1979
|
-
for (const [rewrittenPrefix, originalPrefix] of rewrittenToOriginal) {
|
|
1980
|
-
if (moduleSpecifier.startsWith(rewrittenPrefix)) {
|
|
1981
|
-
const symbols = this.extractSymbolNames(node.importClause);
|
|
1982
|
-
if (symbols.length > 0) {
|
|
1983
|
-
const suffix = moduleSpecifier.slice(rewrittenPrefix.length);
|
|
1984
|
-
const originalPath = originalPrefix + suffix;
|
|
1985
|
-
imports.push({
|
|
1986
|
-
rewrittenPath: moduleSpecifier,
|
|
1987
|
-
originalPath,
|
|
1988
|
-
symbols,
|
|
1989
|
-
isTypeOnly: node.importClause.isTypeOnly ?? false
|
|
1990
|
-
});
|
|
1991
|
-
}
|
|
1992
|
-
break;
|
|
68
|
+
else if (matchesOption(arg, CLI_OPTIONS.messageTypes)) {
|
|
69
|
+
const { value, nextIndex } = extractOptionValue(args, i, "--message-types");
|
|
70
|
+
options.messageTypes = parseMessageTypes(value);
|
|
71
|
+
i = nextIndex;
|
|
1993
72
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
return imports;
|
|
1997
|
-
}
|
|
1998
|
-
/**
|
|
1999
|
-
* Extracts symbol names from import clause
|
|
2000
|
-
*/
|
|
2001
|
-
extractSymbolNames(importClause) {
|
|
2002
|
-
const names = [];
|
|
2003
|
-
if (importClause.name) {
|
|
2004
|
-
names.push(importClause.name.text);
|
|
2005
|
-
}
|
|
2006
|
-
if (importClause.namedBindings && ts8.isNamedImports(importClause.namedBindings)) {
|
|
2007
|
-
for (const element of importClause.namedBindings.elements) {
|
|
2008
|
-
const originalName = element.propertyName?.text ?? element.name.text;
|
|
2009
|
-
names.push(originalName);
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
return names;
|
|
2013
|
-
}
|
|
2014
|
-
/**
|
|
2015
|
-
* Groups imports by rewritten path and merges symbols
|
|
2016
|
-
*/
|
|
2017
|
-
groupImportsByPath(imports) {
|
|
2018
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
2019
|
-
for (const imp of imports) {
|
|
2020
|
-
const existing = grouped.get(imp.rewrittenPath);
|
|
2021
|
-
if (existing) {
|
|
2022
|
-
for (const symbol of imp.symbols) {
|
|
2023
|
-
existing.symbols.add(symbol);
|
|
73
|
+
else if (matchesOption(arg, CLI_OPTIONS.generateMessageRegistry)) {
|
|
74
|
+
options.generateMessageRegistry = true;
|
|
2024
75
|
}
|
|
2025
|
-
if (
|
|
2026
|
-
|
|
76
|
+
else if (matchesOption(arg, CLI_OPTIONS.help)) {
|
|
77
|
+
printHelp();
|
|
78
|
+
process.exit(0);
|
|
2027
79
|
}
|
|
2028
|
-
} else {
|
|
2029
|
-
grouped.set(imp.rewrittenPath, {
|
|
2030
|
-
originalPath: imp.originalPath,
|
|
2031
|
-
symbols: new Set(imp.symbols),
|
|
2032
|
-
hasValueImport: !imp.isTypeOnly
|
|
2033
|
-
});
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
const result = [];
|
|
2037
|
-
for (const [rewrittenPath, data] of grouped) {
|
|
2038
|
-
const relativePath = this.rewrittenPathToRelativePath(rewrittenPath);
|
|
2039
|
-
result.push({
|
|
2040
|
-
relativePath,
|
|
2041
|
-
originalModule: data.originalPath,
|
|
2042
|
-
symbols: Array.from(data.symbols).sort(),
|
|
2043
|
-
isTypeOnly: !data.hasValueImport
|
|
2044
|
-
});
|
|
2045
80
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
/**
|
|
2049
|
-
* Converts a rewritten package path to a relative file path
|
|
2050
|
-
* e.g., "@libera/contracts/common/request" with prefix "@libera/contracts"
|
|
2051
|
-
* -> "common/request.ts"
|
|
2052
|
-
*/
|
|
2053
|
-
rewrittenPathToRelativePath(rewrittenPath) {
|
|
2054
|
-
const parts = rewrittenPath.split("/");
|
|
2055
|
-
let subpathStart;
|
|
2056
|
-
if (parts[0].startsWith("@")) {
|
|
2057
|
-
subpathStart = 2;
|
|
2058
|
-
} else {
|
|
2059
|
-
subpathStart = 1;
|
|
81
|
+
if (!options.outputDir) {
|
|
82
|
+
throw new Error("Missing required option: --output-dir");
|
|
2060
83
|
}
|
|
2061
|
-
|
|
2062
|
-
if (!subpath) {
|
|
2063
|
-
return "index.ts";
|
|
2064
|
-
}
|
|
2065
|
-
return subpath + ".ts";
|
|
2066
|
-
}
|
|
2067
|
-
/**
|
|
2068
|
-
* Generates the content for a re-export file
|
|
2069
|
-
*/
|
|
2070
|
-
generateReexportContent(reexport) {
|
|
2071
|
-
const exportKeyword = reexport.isTypeOnly ? "export type" : "export";
|
|
2072
|
-
const symbols = reexport.symbols.join(", ");
|
|
2073
|
-
return `${exportKeyword} { ${symbols} } from "${reexport.originalModule}";
|
|
2074
|
-
`;
|
|
2075
|
-
}
|
|
2076
|
-
};
|
|
2077
|
-
|
|
2078
|
-
// src/logger.ts
|
|
2079
|
-
var LOG_LEVEL_PRIORITY = {
|
|
2080
|
-
debug: 0,
|
|
2081
|
-
info: 1,
|
|
2082
|
-
warn: 2,
|
|
2083
|
-
error: 3
|
|
2084
|
-
};
|
|
2085
|
-
var ConsoleLogger = class {
|
|
2086
|
-
level;
|
|
2087
|
-
constructor(options = {}) {
|
|
2088
|
-
this.level = options.level ?? "info";
|
|
2089
|
-
}
|
|
2090
|
-
debug(message) {
|
|
2091
|
-
if (this.shouldLog("debug")) {
|
|
2092
|
-
console.debug(message);
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
|
-
info(message) {
|
|
2096
|
-
if (this.shouldLog("info")) {
|
|
2097
|
-
console.info(message);
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
warn(message) {
|
|
2101
|
-
if (this.shouldLog("warn")) {
|
|
2102
|
-
console.warn(message);
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
error(message) {
|
|
2106
|
-
if (this.shouldLog("error")) {
|
|
2107
|
-
console.error(message);
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
shouldLog(level) {
|
|
2111
|
-
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.level];
|
|
2112
|
-
}
|
|
2113
|
-
};
|
|
2114
|
-
var NoopLogger = class {
|
|
2115
|
-
debug() {
|
|
2116
|
-
}
|
|
2117
|
-
info() {
|
|
2118
|
-
}
|
|
2119
|
-
warn() {
|
|
2120
|
-
}
|
|
2121
|
-
error() {
|
|
2122
|
-
}
|
|
2123
|
-
};
|
|
2124
|
-
var noopLogger = new NoopLogger();
|
|
2125
|
-
var DEFAULT_EXCLUDE_DEPENDENCIES = [
|
|
2126
|
-
"**/*.test.ts",
|
|
2127
|
-
"**/*.spec.ts",
|
|
2128
|
-
"**/*.eh.ts",
|
|
2129
|
-
"**/db.ts",
|
|
2130
|
-
"**/infra/**"
|
|
2131
|
-
];
|
|
2132
|
-
var ContractsPipeline = class _ContractsPipeline {
|
|
2133
|
-
constructor(deps, messageTypes) {
|
|
2134
|
-
this.deps = deps;
|
|
2135
|
-
this.messageTypes = messageTypes;
|
|
2136
|
-
}
|
|
2137
|
-
messageTypes;
|
|
2138
|
-
static create(options) {
|
|
2139
|
-
const fileSystem = options.fileSystem ?? nodeFileSystem;
|
|
2140
|
-
const logger = options.logger ?? noopLogger;
|
|
2141
|
-
const excludeDependencies = options.excludeDependencies ?? DEFAULT_EXCLUDE_DEPENDENCIES;
|
|
2142
|
-
const scanner = new Scanner({ fileSystem, messageTypes: options.messageTypes });
|
|
2143
|
-
const parser = new Parser({
|
|
2144
|
-
responseNamingConventions: options.responseNamingConventions ?? options.contextConfig.responseNamingConventions,
|
|
2145
|
-
messageTypes: options.messageTypes
|
|
2146
|
-
});
|
|
2147
|
-
const fileGraphResolver = FileGraphResolver.create({
|
|
2148
|
-
contextConfig: options.contextConfig,
|
|
2149
|
-
fileSystem,
|
|
2150
|
-
excludeDependencies
|
|
2151
|
-
});
|
|
2152
|
-
const fileCopier = new FileCopier({ fileSystem });
|
|
2153
|
-
return new _ContractsPipeline(
|
|
2154
|
-
{
|
|
2155
|
-
scanner,
|
|
2156
|
-
parser,
|
|
2157
|
-
fileGraphResolver,
|
|
2158
|
-
fileCopier,
|
|
2159
|
-
fileSystem,
|
|
2160
|
-
logger
|
|
2161
|
-
},
|
|
2162
|
-
options.messageTypes
|
|
2163
|
-
);
|
|
2164
|
-
}
|
|
2165
|
-
static fromDependencies(deps) {
|
|
2166
|
-
return new _ContractsPipeline(deps);
|
|
2167
|
-
}
|
|
2168
|
-
async execute(options) {
|
|
2169
|
-
const { contextName, sourceDir, outputDir, pathAliasRewrites, removeDecorators } = options;
|
|
2170
|
-
const contextOutputDir = join(outputDir, contextName);
|
|
2171
|
-
this.deps.logger.info(`Processing context: ${contextName}`);
|
|
2172
|
-
this.deps.logger.debug(` Source: ${sourceDir}`);
|
|
2173
|
-
this.deps.logger.debug(` Output: ${contextOutputDir}`);
|
|
2174
|
-
const decoratedFiles = await this.scan(sourceDir);
|
|
2175
|
-
const messages = await this.parse(decoratedFiles, sourceDir);
|
|
2176
|
-
const fileGraph = await this.resolve(decoratedFiles, sourceDir);
|
|
2177
|
-
const responseTypesToExport = this.collectResponseTypesToExport(messages);
|
|
2178
|
-
const copiedFiles = await this.copy(fileGraph, sourceDir, contextOutputDir, pathAliasRewrites, responseTypesToExport, removeDecorators, this.messageTypes);
|
|
2179
|
-
await this.exportBarrel(copiedFiles, contextOutputDir);
|
|
2180
|
-
this.deps.logger.info(`Completed context: ${contextName} (${messages.events.length} events, ${messages.commands.length} commands, ${messages.queries.length} queries, ${copiedFiles.length} files)`);
|
|
2181
|
-
return {
|
|
2182
|
-
events: messages.events,
|
|
2183
|
-
commands: messages.commands,
|
|
2184
|
-
queries: messages.queries,
|
|
2185
|
-
copiedFiles
|
|
2186
|
-
};
|
|
2187
|
-
}
|
|
2188
|
-
collectResponseTypesToExport(messages) {
|
|
2189
|
-
const result = /* @__PURE__ */ new Map();
|
|
2190
|
-
const allMessages = [...messages.commands, ...messages.queries];
|
|
2191
|
-
for (const message of allMessages) {
|
|
2192
|
-
if (message.resultType?.kind !== "reference") continue;
|
|
2193
|
-
const typeName = message.resultType.name;
|
|
2194
|
-
const sourceFile = message.sourceFile.absolutePath;
|
|
2195
|
-
const typeDef = messages.typeDefinitions.find(
|
|
2196
|
-
(t) => t.name === typeName && t.sourceFile.absolutePath === sourceFile && !t.exported
|
|
2197
|
-
);
|
|
2198
|
-
if (typeDef) {
|
|
2199
|
-
const existing = result.get(sourceFile) ?? [];
|
|
2200
|
-
if (!existing.includes(typeName)) {
|
|
2201
|
-
existing.push(typeName);
|
|
2202
|
-
result.set(sourceFile, existing);
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
if (result.size > 0) {
|
|
2207
|
-
this.deps.logger.debug(`Found ${result.size} file(s) with unexported response types`);
|
|
2208
|
-
}
|
|
2209
|
-
return result;
|
|
2210
|
-
}
|
|
2211
|
-
async scan(sourceDir) {
|
|
2212
|
-
this.deps.logger.debug(`Scanning for decorated files in ${sourceDir}`);
|
|
2213
|
-
const files = await this.deps.scanner.scan(sourceDir);
|
|
2214
|
-
this.deps.logger.debug(`Found ${files.length} decorated file(s)`);
|
|
2215
|
-
return files;
|
|
2216
|
-
}
|
|
2217
|
-
async parse(files, sourceRoot) {
|
|
2218
|
-
this.deps.logger.debug(`Parsing ${files.length} file(s)`);
|
|
2219
|
-
const events = [];
|
|
2220
|
-
const commands = [];
|
|
2221
|
-
const queries = [];
|
|
2222
|
-
const typeDefinitions = [];
|
|
2223
|
-
for (const file of files) {
|
|
2224
|
-
const content = await this.deps.fileSystem.readFile(file);
|
|
2225
|
-
const sourceFileInfo = {
|
|
2226
|
-
absolutePath: file,
|
|
2227
|
-
relativePath: relative(sourceRoot, file)
|
|
2228
|
-
};
|
|
2229
|
-
const result = this.deps.parser.parse(content, sourceFileInfo);
|
|
2230
|
-
events.push(...result.events);
|
|
2231
|
-
commands.push(...result.commands);
|
|
2232
|
-
queries.push(...result.queries);
|
|
2233
|
-
typeDefinitions.push(...result.typeDefinitions);
|
|
2234
|
-
}
|
|
2235
|
-
this.deps.logger.debug(`Parsed ${events.length} event(s), ${commands.length} command(s), ${queries.length} query(s), ${typeDefinitions.length} type(s)`);
|
|
2236
|
-
return { events, commands, queries, typeDefinitions };
|
|
2237
|
-
}
|
|
2238
|
-
async resolve(entryPoints, sourceRoot) {
|
|
2239
|
-
this.deps.logger.debug(`Resolving dependencies for ${entryPoints.length} entry point(s)`);
|
|
2240
|
-
const graph = await this.deps.fileGraphResolver.buildGraph(entryPoints, sourceRoot);
|
|
2241
|
-
this.deps.logger.debug(`Resolved ${graph.nodes.size} file(s) in dependency graph`);
|
|
2242
|
-
return graph;
|
|
2243
|
-
}
|
|
2244
|
-
async copy(fileGraph, sourceRoot, outputDir, pathAliasRewrites, responseTypesToExport, removeDecorators, messageTypes) {
|
|
2245
|
-
this.deps.logger.debug(`Copying files to ${outputDir}`);
|
|
2246
|
-
await this.deps.fileSystem.mkdir(outputDir, { recursive: true });
|
|
2247
|
-
const result = await this.deps.fileCopier.copyFiles({
|
|
2248
|
-
sourceRoot,
|
|
2249
|
-
outputDir,
|
|
2250
|
-
fileGraph,
|
|
2251
|
-
pathAliasRewrites,
|
|
2252
|
-
responseTypesToExport,
|
|
2253
|
-
removeDecorators,
|
|
2254
|
-
messageTypes
|
|
2255
|
-
});
|
|
2256
|
-
this.deps.logger.debug(`Copied ${result.copiedFiles.length} file(s)`);
|
|
2257
|
-
return result.copiedFiles;
|
|
2258
|
-
}
|
|
2259
|
-
async exportBarrel(copiedFiles, outputDir) {
|
|
2260
|
-
this.deps.logger.debug(`Generating barrel export at ${outputDir}/index.ts`);
|
|
2261
|
-
const indexContent = this.deps.fileCopier.generateBarrelExport(copiedFiles, outputDir);
|
|
2262
|
-
await this.deps.fileSystem.writeFile(join(outputDir, "index.ts"), indexContent);
|
|
2263
|
-
}
|
|
2264
|
-
};
|
|
2265
|
-
|
|
2266
|
-
// src/cli.ts
|
|
2267
|
-
var DEFAULT_CONFIG_PATH = "application.config.ts";
|
|
2268
|
-
var EXIT_CODE_ERROR = 1;
|
|
2269
|
-
var VALID_MESSAGE_TYPES = ["event", "command", "query"];
|
|
2270
|
-
var CLI_OPTIONS = {
|
|
2271
|
-
config: { short: "-c", long: "--config"},
|
|
2272
|
-
outputDir: { short: "-o", long: "--output-dir"},
|
|
2273
|
-
messageTypes: { short: "-m", long: "--message-types"},
|
|
2274
|
-
generateMessageRegistry: { short: null, long: "--generate-message-registry"},
|
|
2275
|
-
help: { short: "-h", long: "--help"}
|
|
2276
|
-
};
|
|
2277
|
-
function parseMessageTypes(value) {
|
|
2278
|
-
const types = value.split(",").map((type) => type.trim().toLowerCase());
|
|
2279
|
-
const invalidTypes = types.filter((type) => !VALID_MESSAGE_TYPES.includes(type));
|
|
2280
|
-
if (invalidTypes.length > 0) {
|
|
2281
|
-
throw new Error(
|
|
2282
|
-
`Invalid message type(s): ${invalidTypes.join(", ")}. Valid types are: ${VALID_MESSAGE_TYPES.join(", ")}`
|
|
2283
|
-
);
|
|
2284
|
-
}
|
|
2285
|
-
return types;
|
|
2286
|
-
}
|
|
2287
|
-
function extractOptionValue(args, currentIndex, optionName) {
|
|
2288
|
-
const currentArg = args[currentIndex];
|
|
2289
|
-
const equalsIndex = currentArg.indexOf("=");
|
|
2290
|
-
if (equalsIndex !== -1) {
|
|
2291
|
-
return {
|
|
2292
|
-
value: currentArg.slice(equalsIndex + 1),
|
|
2293
|
-
nextIndex: currentIndex
|
|
2294
|
-
};
|
|
2295
|
-
}
|
|
2296
|
-
const nextValue = args[currentIndex + 1];
|
|
2297
|
-
if (!nextValue) {
|
|
2298
|
-
throw new Error(`Missing value for ${optionName} option`);
|
|
2299
|
-
}
|
|
2300
|
-
return {
|
|
2301
|
-
value: nextValue,
|
|
2302
|
-
nextIndex: currentIndex + 1
|
|
2303
|
-
};
|
|
2304
|
-
}
|
|
2305
|
-
function matchesOption(arg, option) {
|
|
2306
|
-
const matchesShortOrLong = arg === option.short || arg === option.long;
|
|
2307
|
-
const matchesLongWithValue = option.long !== null && arg.startsWith(`${option.long}=`);
|
|
2308
|
-
return matchesShortOrLong || matchesLongWithValue;
|
|
2309
|
-
}
|
|
2310
|
-
function parseArgs(args) {
|
|
2311
|
-
const options = {
|
|
2312
|
-
config: DEFAULT_CONFIG_PATH
|
|
2313
|
-
};
|
|
2314
|
-
for (let i = 0; i < args.length; i++) {
|
|
2315
|
-
const arg = args[i];
|
|
2316
|
-
if (matchesOption(arg, CLI_OPTIONS.config)) {
|
|
2317
|
-
const { value, nextIndex } = extractOptionValue(args, i, "--config");
|
|
2318
|
-
options.config = value;
|
|
2319
|
-
i = nextIndex;
|
|
2320
|
-
} else if (matchesOption(arg, CLI_OPTIONS.outputDir)) {
|
|
2321
|
-
const { value, nextIndex } = extractOptionValue(args, i, "--output-dir");
|
|
2322
|
-
options.outputDir = value;
|
|
2323
|
-
i = nextIndex;
|
|
2324
|
-
} else if (matchesOption(arg, CLI_OPTIONS.messageTypes)) {
|
|
2325
|
-
const { value, nextIndex } = extractOptionValue(args, i, "--message-types");
|
|
2326
|
-
options.messageTypes = parseMessageTypes(value);
|
|
2327
|
-
i = nextIndex;
|
|
2328
|
-
} else if (matchesOption(arg, CLI_OPTIONS.generateMessageRegistry)) {
|
|
2329
|
-
options.generateMessageRegistry = true;
|
|
2330
|
-
} else if (matchesOption(arg, CLI_OPTIONS.help)) {
|
|
2331
|
-
printHelp();
|
|
2332
|
-
process.exit(0);
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
if (!options.outputDir) {
|
|
2336
|
-
throw new Error("Missing required option: --output-dir");
|
|
2337
|
-
}
|
|
2338
|
-
return options;
|
|
84
|
+
return options;
|
|
2339
85
|
}
|
|
2340
86
|
function printHelp() {
|
|
2341
|
-
|
|
87
|
+
console.log(`
|
|
2342
88
|
contracts-generator - Extract domain events and commands from TypeScript source
|
|
2343
89
|
|
|
2344
90
|
Usage:
|
|
@@ -2392,171 +138,177 @@ Examples:
|
|
|
2392
138
|
`);
|
|
2393
139
|
}
|
|
2394
140
|
function calculateSummaryTotals(results) {
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
}),
|
|
2401
|
-
{ events: 0, commands: 0, files: 0 }
|
|
2402
|
-
);
|
|
141
|
+
return results.reduce((totals, contextResult) => ({
|
|
142
|
+
events: totals.events + contextResult.result.events.length,
|
|
143
|
+
commands: totals.commands + contextResult.result.commands.length,
|
|
144
|
+
files: totals.files + contextResult.result.copiedFiles.length,
|
|
145
|
+
}), { events: 0, commands: 0, files: 0 });
|
|
2403
146
|
}
|
|
2404
147
|
function countTotalMessages(totals) {
|
|
2405
|
-
|
|
148
|
+
return totals.events + totals.commands;
|
|
2406
149
|
}
|
|
2407
150
|
function logSummary(logger, totals) {
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
}
|
|
2413
|
-
async function run(args) {
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
const
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
151
|
+
logger.info("\n--- Summary ---");
|
|
152
|
+
logger.info(`Total events: ${totals.events}`);
|
|
153
|
+
logger.info(`Total commands: ${totals.commands}`);
|
|
154
|
+
logger.info(`Total files copied: ${totals.files}`);
|
|
155
|
+
}
|
|
156
|
+
export async function run(args) {
|
|
157
|
+
const options = parseArgs(args);
|
|
158
|
+
const configPath = resolve(options.config);
|
|
159
|
+
const configDir = dirname(configPath);
|
|
160
|
+
const outputDir = resolve(configDir, options.outputDir);
|
|
161
|
+
const logger = new ConsoleLogger({ level: "info" });
|
|
162
|
+
logger.info(`Loading config from: ${configPath}`);
|
|
163
|
+
const configLoader = new ConfigLoader();
|
|
164
|
+
const config = await configLoader.load(configPath);
|
|
165
|
+
logger.info(`Found ${config.contexts.length} context(s) to process`);
|
|
166
|
+
logger.info(`Output directory: ${outputDir}`);
|
|
167
|
+
if (options.messageTypes) {
|
|
168
|
+
logger.info(`Message types filter: ${options.messageTypes.join(", ")}`);
|
|
169
|
+
}
|
|
170
|
+
const pathAliasRewrites = config.pathAliasRewrites
|
|
171
|
+
? new Map(Object.entries(config.pathAliasRewrites))
|
|
172
|
+
: undefined;
|
|
173
|
+
const results = [];
|
|
174
|
+
for (const contextConfig of config.contexts) {
|
|
175
|
+
const pipeline = ContractsPipeline.create({
|
|
176
|
+
contextConfig,
|
|
177
|
+
messageTypes: options.messageTypes,
|
|
178
|
+
logger,
|
|
179
|
+
});
|
|
180
|
+
const result = await pipeline.execute({
|
|
181
|
+
contextName: contextConfig.name,
|
|
182
|
+
sourceDir: contextConfig.sourceDir,
|
|
183
|
+
outputDir,
|
|
184
|
+
pathAliasRewrites,
|
|
185
|
+
removeDecorators: config.removeDecorators,
|
|
186
|
+
});
|
|
187
|
+
results.push({ name: contextConfig.name, result, outputDir });
|
|
188
|
+
}
|
|
189
|
+
const totals = calculateSummaryTotals(results);
|
|
190
|
+
logSummary(logger, totals);
|
|
191
|
+
if (options.generateMessageRegistry) {
|
|
192
|
+
await generateRegistry(outputDir, results, totals, logger);
|
|
193
|
+
}
|
|
194
|
+
if (config.pathAliasRewrites) {
|
|
195
|
+
await generateReexports(config, outputDir, results, logger);
|
|
196
|
+
}
|
|
2452
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Converts plugin config to internal ContractsConfig format.
|
|
200
|
+
*/
|
|
2453
201
|
async function toContractsConfig(pluginConfig) {
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
decoratorNames: mergeDecoratorNames(pluginConfig.decoratorNames),
|
|
2464
|
-
responseNamingConventions: pluginConfig.responseNamingConventions,
|
|
2465
|
-
removeDecorators: pluginConfig.removeDecorators ?? true
|
|
2466
|
-
};
|
|
202
|
+
const contexts = await resolveContextEntries(pluginConfig.contexts, process.cwd(), nodeFileSystem);
|
|
203
|
+
return {
|
|
204
|
+
contexts,
|
|
205
|
+
pathAliasRewrites: pluginConfig.pathAliasRewrites,
|
|
206
|
+
externalDependencies: pluginConfig.externalDependencies,
|
|
207
|
+
decoratorNames: mergeDecoratorNames(pluginConfig.decoratorNames),
|
|
208
|
+
responseNamingConventions: pluginConfig.responseNamingConventions,
|
|
209
|
+
removeDecorators: pluginConfig.removeDecorators ?? true,
|
|
210
|
+
};
|
|
2467
211
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Run contracts generator with config provided directly.
|
|
214
|
+
* This is used by the hexai CLI integration where config comes from hexai.config.ts.
|
|
215
|
+
*
|
|
216
|
+
* @param options - CLI options (outputDir, messageTypes, generateMessageRegistry)
|
|
217
|
+
* @param pluginConfig - Plugin configuration from hexai.config.ts
|
|
218
|
+
*/
|
|
219
|
+
export async function runWithConfig(options, pluginConfig) {
|
|
220
|
+
const outputDir = resolve(options.outputDir);
|
|
221
|
+
const logger = new ConsoleLogger({ level: "info" });
|
|
222
|
+
const config = await toContractsConfig(pluginConfig);
|
|
223
|
+
logger.info(`Found ${config.contexts.length} context(s) to process`);
|
|
224
|
+
logger.info(`Output directory: ${outputDir}`);
|
|
225
|
+
if (options.messageTypes) {
|
|
226
|
+
logger.info(`Message types filter: ${options.messageTypes.join(", ")}`);
|
|
227
|
+
}
|
|
228
|
+
const pathAliasRewrites = config.pathAliasRewrites
|
|
229
|
+
? new Map(Object.entries(config.pathAliasRewrites))
|
|
230
|
+
: undefined;
|
|
231
|
+
const results = [];
|
|
232
|
+
for (const contextConfig of config.contexts) {
|
|
233
|
+
const pipeline = ContractsPipeline.create({
|
|
234
|
+
contextConfig,
|
|
235
|
+
messageTypes: options.messageTypes,
|
|
236
|
+
logger,
|
|
237
|
+
});
|
|
238
|
+
const result = await pipeline.execute({
|
|
239
|
+
contextName: contextConfig.name,
|
|
240
|
+
sourceDir: contextConfig.sourceDir,
|
|
241
|
+
outputDir,
|
|
242
|
+
pathAliasRewrites,
|
|
243
|
+
removeDecorators: config.removeDecorators,
|
|
244
|
+
});
|
|
245
|
+
results.push({ name: contextConfig.name, result, outputDir });
|
|
246
|
+
}
|
|
247
|
+
const totals = calculateSummaryTotals(results);
|
|
248
|
+
logSummary(logger, totals);
|
|
249
|
+
if (options.generateMessageRegistry) {
|
|
250
|
+
await generateRegistry(outputDir, results, totals, logger);
|
|
251
|
+
}
|
|
252
|
+
if (config.pathAliasRewrites) {
|
|
253
|
+
await generateReexports(config, outputDir, results, logger);
|
|
254
|
+
}
|
|
2502
255
|
}
|
|
2503
256
|
async function generateRegistry(outputDir, results, totals, logger) {
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
257
|
+
const contextMessages = results.map((contextResult) => {
|
|
258
|
+
const contextOutputDir = join(contextResult.outputDir, contextResult.name);
|
|
259
|
+
const importPath = "./" + relative(outputDir, contextOutputDir).replace(/\\/g, "/");
|
|
260
|
+
return {
|
|
261
|
+
contextName: contextResult.name,
|
|
262
|
+
events: contextResult.result.events,
|
|
263
|
+
commands: contextResult.result.commands,
|
|
264
|
+
queries: contextResult.result.queries,
|
|
265
|
+
importPath,
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
const generator = new RegistryGenerator({ useNamespace: true });
|
|
269
|
+
const registryContent = generator.generate(contextMessages);
|
|
270
|
+
await nodeFileSystem.mkdir(outputDir, { recursive: true });
|
|
271
|
+
const indexPath = join(outputDir, "index.ts");
|
|
272
|
+
await nodeFileSystem.writeFile(indexPath, registryContent);
|
|
273
|
+
logger.info(` Generated index.ts with ${countTotalMessages(totals)} message(s)`);
|
|
2521
274
|
}
|
|
2522
275
|
async function generateReexports(config, outputDir, results, logger) {
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
276
|
+
logger.info("\n--- Generating re-exports for pathAliasRewrites ---");
|
|
277
|
+
const pathAliasRewrites = new Map(Object.entries(config.pathAliasRewrites));
|
|
278
|
+
const allCopiedFiles = results.flatMap((contextResult) => contextResult.result.copiedFiles);
|
|
279
|
+
const generator = new ReexportGenerator({ fileSystem: nodeFileSystem });
|
|
280
|
+
const reexportFiles = await generator.analyze({
|
|
281
|
+
files: allCopiedFiles,
|
|
282
|
+
pathAliasRewrites,
|
|
283
|
+
});
|
|
284
|
+
if (reexportFiles.length === 0) {
|
|
285
|
+
logger.info(" No re-export files needed");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const generatedFiles = await generator.generate({
|
|
289
|
+
outputDir,
|
|
290
|
+
reexportFiles,
|
|
291
|
+
});
|
|
292
|
+
logger.info(` Generated ${generatedFiles.length} re-export file(s):`);
|
|
293
|
+
for (const file of generatedFiles) {
|
|
294
|
+
logger.info(` - ${relative(outputDir, file)}`);
|
|
295
|
+
}
|
|
2543
296
|
}
|
|
2544
297
|
async function main() {
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
298
|
+
try {
|
|
299
|
+
await run(process.argv.slice(2));
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (error instanceof Error) {
|
|
303
|
+
console.error(`Error: ${error.message}`);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
console.error("Unknown error occurred");
|
|
307
|
+
}
|
|
308
|
+
process.exit(EXIT_CODE_ERROR);
|
|
2552
309
|
}
|
|
2553
|
-
process.exit(EXIT_CODE_ERROR);
|
|
2554
|
-
}
|
|
2555
310
|
}
|
|
2556
311
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
2557
|
-
|
|
312
|
+
main();
|
|
2558
313
|
}
|
|
2559
|
-
|
|
2560
|
-
export { run, runWithConfig };
|
|
2561
|
-
//# sourceMappingURL=cli.js.map
|
|
2562
314
|
//# sourceMappingURL=cli.js.map
|