@hexaijs/plugin-application-builder 0.2.3 → 0.2.4
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/application-builder-generator.d.ts +15 -0
- package/dist/application-builder-generator.d.ts.map +1 -0
- package/dist/application-builder-generator.js +44 -0
- package/dist/application-builder-generator.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +20 -572
- package/dist/cli.js.map +1 -1
- package/dist/code-generator.d.ts +30 -0
- package/dist/code-generator.d.ts.map +1 -0
- package/dist/code-generator.js +127 -0
- package/dist/code-generator.js.map +1 -0
- package/dist/config-loader.d.ts +3 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +31 -0
- package/dist/config-loader.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -0
- package/dist/decorators/index.d.ts +6 -14
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +28 -14
- package/dist/decorators/index.js.map +1 -1
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -0
- package/dist/hexai-plugin.d.ts +30 -0
- package/dist/hexai-plugin.d.ts.map +1 -0
- package/dist/hexai-plugin.js +45 -0
- package/dist/hexai-plugin.js.map +1 -0
- package/dist/index.d.ts +5 -48
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -596
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +6 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +15 -0
- package/dist/main.js.map +1 -0
- package/dist/metadata-extractor.d.ts +25 -0
- package/dist/metadata-extractor.d.ts.map +1 -0
- package/dist/metadata-extractor.js +210 -0
- package/dist/metadata-extractor.js.map +1 -0
- package/dist/test.d.ts +15 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +67 -0
- package/dist/test.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +7 -6
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BuildPluginConfig } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Orchestrates the application builder generation process
|
|
4
|
+
*/
|
|
5
|
+
export declare class ApplicationBuilderGenerator {
|
|
6
|
+
private contextPath;
|
|
7
|
+
private config;
|
|
8
|
+
private metadataExtractor;
|
|
9
|
+
private codeGenerator;
|
|
10
|
+
constructor(contextPath: string, config: BuildPluginConfig);
|
|
11
|
+
generate(): Promise<void>;
|
|
12
|
+
private scanHandlerFiles;
|
|
13
|
+
private writeToFile;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=application-builder-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-builder-generator.d.ts","sourceRoot":"","sources":["../src/application-builder-generator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIhD;;GAEG;AACH,qBAAa,2BAA2B;IAKhC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM;IALlB,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,aAAa,CAA2B;IAEhD,YACY,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,iBAAiB,EAOpC;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAK9B;YAEa,gBAAgB;IAc9B,OAAO,CAAC,WAAW;CAOtB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
import { HandlerMetadataExtractor } from "./metadata-extractor.js";
|
|
5
|
+
import { ApplicationCodeGenerator } from "./code-generator.js";
|
|
6
|
+
/**
|
|
7
|
+
* Orchestrates the application builder generation process
|
|
8
|
+
*/
|
|
9
|
+
export class ApplicationBuilderGenerator {
|
|
10
|
+
contextPath;
|
|
11
|
+
config;
|
|
12
|
+
metadataExtractor;
|
|
13
|
+
codeGenerator;
|
|
14
|
+
constructor(contextPath, config) {
|
|
15
|
+
this.contextPath = contextPath;
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.metadataExtractor = new HandlerMetadataExtractor(contextPath, config.outputFile);
|
|
18
|
+
this.codeGenerator = new ApplicationCodeGenerator(config);
|
|
19
|
+
}
|
|
20
|
+
async generate() {
|
|
21
|
+
const handlerFiles = await this.scanHandlerFiles();
|
|
22
|
+
const handlers = this.metadataExtractor.extractHandlersMetadata(handlerFiles);
|
|
23
|
+
const code = this.codeGenerator.generateCode(handlers);
|
|
24
|
+
this.writeToFile(code);
|
|
25
|
+
}
|
|
26
|
+
async scanHandlerFiles() {
|
|
27
|
+
const allFiles = [];
|
|
28
|
+
for (const pattern of this.config.handlers) {
|
|
29
|
+
const files = await glob(pattern, {
|
|
30
|
+
cwd: this.contextPath,
|
|
31
|
+
absolute: true,
|
|
32
|
+
});
|
|
33
|
+
allFiles.push(...files);
|
|
34
|
+
}
|
|
35
|
+
return allFiles.sort();
|
|
36
|
+
}
|
|
37
|
+
writeToFile(code) {
|
|
38
|
+
const outputFile = path.join(this.contextPath, this.config.outputFile);
|
|
39
|
+
const outputDir = path.dirname(outputFile);
|
|
40
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
41
|
+
fs.writeFileSync(outputFile, code, "utf-8");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=application-builder-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-builder-generator.js","sourceRoot":"","sources":["../src/application-builder-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D;;GAEG;AACH,MAAM,OAAO,2BAA2B;IAKxB,WAAW;IACX,MAAM;IALV,iBAAiB,CAA2B;IAC5C,aAAa,CAA2B;IAEhD,YACY,WAAmB,EACnB,MAAyB;2BADzB,WAAW;sBACX,MAAM;QAEd,IAAI,CAAC,iBAAiB,GAAG,IAAI,wBAAwB,CACjD,WAAW,EACX,MAAM,CAAC,UAAU,CACpB,CAAC;QACF,IAAI,CAAC,aAAa,GAAG,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,QAAQ;QACV,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE;gBAC9B,GAAG,EAAE,IAAI,CAAC,WAAW;gBACrB,QAAQ,EAAE,IAAI;aACjB,CAAC,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAEO,WAAW,CAAC,IAAY;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAE3C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;CACJ"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
CHANGED
|
@@ -1,578 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as path from
|
|
3
|
-
import
|
|
4
|
-
import { glob } from 'glob';
|
|
5
|
-
import * as ts from 'typescript';
|
|
6
|
-
|
|
7
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
-
}) : x)(function(x) {
|
|
10
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// src/errors.ts
|
|
15
|
-
var DuplicateCommandHandlerError = class extends Error {
|
|
16
|
-
constructor(commandClassName, handlers) {
|
|
17
|
-
super(
|
|
18
|
-
`Duplicate command handlers for "${commandClassName}": ${handlers.join(", ")}`
|
|
19
|
-
);
|
|
20
|
-
this.name = "DuplicateCommandHandlerError";
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
var DuplicateEventHandlerError = class extends Error {
|
|
24
|
-
constructor(eventName, handlers) {
|
|
25
|
-
super(
|
|
26
|
-
`Duplicate event handlers for event "${eventName}": ${handlers.join(", ")}`
|
|
27
|
-
);
|
|
28
|
-
this.name = "DuplicateEventHandlerError";
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
var DuplicateQueryHandlerError = class extends Error {
|
|
32
|
-
constructor(queryClassName, handlers) {
|
|
33
|
-
super(
|
|
34
|
-
`Duplicate query handlers for "${queryClassName}": ${handlers.join(", ")}`
|
|
35
|
-
);
|
|
36
|
-
this.name = "DuplicateQueryHandlerError";
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
var MessageClassNotFoundError = class extends Error {
|
|
40
|
-
constructor(messageClassName, filePath) {
|
|
41
|
-
super(
|
|
42
|
-
`Cannot find "${messageClassName}" - not imported and not defined in "${filePath}"`
|
|
43
|
-
);
|
|
44
|
-
this.name = "MessageClassNotFoundError";
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// src/metadata-extractor.ts
|
|
49
|
-
var HandlerMetadataExtractor = class {
|
|
50
|
-
constructor(contextPath, outputFile, config = {}) {
|
|
51
|
-
this.contextPath = contextPath;
|
|
52
|
-
this.outputFile = outputFile;
|
|
53
|
-
if (config.commandHandlerDecorator) {
|
|
54
|
-
this.config.commandHandlerDecorator = config.commandHandlerDecorator;
|
|
55
|
-
}
|
|
56
|
-
if (config.eventHandlerDecorator) {
|
|
57
|
-
this.config.eventHandlerDecorator = config.eventHandlerDecorator;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
config = {
|
|
61
|
-
commandHandlerDecorator: "CommandHandlerMarker",
|
|
62
|
-
eventHandlerDecorator: "EventHandlerMarker"
|
|
63
|
-
};
|
|
64
|
-
extractHandlersMetadata(files) {
|
|
65
|
-
const handlers = [];
|
|
66
|
-
for (const file of files) {
|
|
67
|
-
const sourceCode = fs.readFileSync(file, "utf-8");
|
|
68
|
-
const sourceFile = ts.createSourceFile(
|
|
69
|
-
file,
|
|
70
|
-
sourceCode,
|
|
71
|
-
ts.ScriptTarget.Latest,
|
|
72
|
-
true
|
|
73
|
-
);
|
|
74
|
-
ts.forEachChild(sourceFile, (node) => {
|
|
75
|
-
if (ts.isClassDeclaration(node)) {
|
|
76
|
-
const metadata = this.extractFromClass(
|
|
77
|
-
node,
|
|
78
|
-
file,
|
|
79
|
-
sourceFile
|
|
80
|
-
);
|
|
81
|
-
if (metadata) {
|
|
82
|
-
handlers.push(metadata);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return handlers;
|
|
88
|
-
}
|
|
89
|
-
extractFromClass(classNode, filePath, sourceFile) {
|
|
90
|
-
const className = classNode.name?.text;
|
|
91
|
-
if (!className) return null;
|
|
92
|
-
const decorators = ts.canHaveDecorators(classNode) ? ts.getDecorators(classNode) : void 0;
|
|
93
|
-
if (!decorators) return null;
|
|
94
|
-
for (const decorator of decorators) {
|
|
95
|
-
const expression = decorator.expression;
|
|
96
|
-
if (!ts.isCallExpression(expression)) continue;
|
|
97
|
-
const decoratorName = expression.expression.getText();
|
|
98
|
-
if (decoratorName === "CommandHandlerMarker") {
|
|
99
|
-
return this.extractCommandHandlerMetadata(
|
|
100
|
-
className,
|
|
101
|
-
expression,
|
|
102
|
-
filePath,
|
|
103
|
-
sourceFile
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
if (decoratorName === "EventHandlerMarker") {
|
|
107
|
-
return this.extractEventHandlerMetadata(
|
|
108
|
-
className,
|
|
109
|
-
expression,
|
|
110
|
-
filePath
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
if (decoratorName === "QueryHandlerMarker") {
|
|
114
|
-
return this.extractQueryHandlerMetadata(
|
|
115
|
-
className,
|
|
116
|
-
expression,
|
|
117
|
-
filePath,
|
|
118
|
-
sourceFile
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
extractMessageHandlerMetadata(className, expression, filePath, sourceFile) {
|
|
125
|
-
const messageClassArg = expression.arguments[0];
|
|
126
|
-
const messageClassName = messageClassArg.getText();
|
|
127
|
-
const messageImport = this.findImportForSymbol(
|
|
128
|
-
messageClassName,
|
|
129
|
-
sourceFile
|
|
130
|
-
);
|
|
131
|
-
let messagePath = "";
|
|
132
|
-
let resolvedMessageClassName = messageClassName;
|
|
133
|
-
if (messageImport) {
|
|
134
|
-
const handlerDir = path.dirname(filePath);
|
|
135
|
-
const resolvedImportPath = this.resolvePathAlias(messageImport.path);
|
|
136
|
-
const messageAbsolutePath = path.resolve(
|
|
137
|
-
handlerDir,
|
|
138
|
-
resolvedImportPath + ".ts"
|
|
139
|
-
);
|
|
140
|
-
messagePath = this.toRelativeImport(messageAbsolutePath);
|
|
141
|
-
resolvedMessageClassName = messageImport.symbol;
|
|
142
|
-
} else if (this.isClassDefinedInFile(messageClassName, sourceFile)) {
|
|
143
|
-
messagePath = this.toRelativeImport(filePath);
|
|
144
|
-
} else {
|
|
145
|
-
throw new MessageClassNotFoundError(messageClassName, filePath);
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
messagePath,
|
|
149
|
-
messageClassName: resolvedMessageClassName
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
isClassDefinedInFile(className, sourceFile) {
|
|
153
|
-
for (const statement of sourceFile.statements) {
|
|
154
|
-
if (ts.isClassDeclaration(statement) && statement.name?.text === className) {
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
extractCommandHandlerMetadata(className, expression, filePath, sourceFile) {
|
|
161
|
-
const { messagePath, messageClassName } = this.extractMessageHandlerMetadata(
|
|
162
|
-
className,
|
|
163
|
-
expression,
|
|
164
|
-
filePath,
|
|
165
|
-
sourceFile
|
|
166
|
-
);
|
|
167
|
-
return {
|
|
168
|
-
type: "command",
|
|
169
|
-
handlerPath: this.toRelativeImport(filePath),
|
|
170
|
-
handlerClassName: className,
|
|
171
|
-
commandPath: messagePath,
|
|
172
|
-
commandClassName: messageClassName
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
extractEventHandlerMetadata(className, expression, filePath) {
|
|
176
|
-
let options = {};
|
|
177
|
-
if (expression.arguments.length > 0) {
|
|
178
|
-
const optionsArg = expression.arguments[0];
|
|
179
|
-
options = this.parseObjectLiteral(optionsArg);
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
type: "event",
|
|
183
|
-
handlerPath: this.toRelativeImport(filePath),
|
|
184
|
-
handlerClassName: className,
|
|
185
|
-
eventHandlerOptions: options
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
extractQueryHandlerMetadata(className, expression, filePath, sourceFile) {
|
|
189
|
-
const { messagePath, messageClassName } = this.extractMessageHandlerMetadata(
|
|
190
|
-
className,
|
|
191
|
-
expression,
|
|
192
|
-
filePath,
|
|
193
|
-
sourceFile
|
|
194
|
-
);
|
|
195
|
-
return {
|
|
196
|
-
type: "query",
|
|
197
|
-
handlerPath: this.toRelativeImport(filePath),
|
|
198
|
-
handlerClassName: className,
|
|
199
|
-
queryPath: messagePath,
|
|
200
|
-
queryClassName: messageClassName
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
findImportForSymbol(symbol, sourceFile) {
|
|
204
|
-
for (const statement of sourceFile.statements) {
|
|
205
|
-
if (ts.isImportDeclaration(statement)) {
|
|
206
|
-
const importClause = statement.importClause;
|
|
207
|
-
const moduleSpecifier = statement.moduleSpecifier.text;
|
|
208
|
-
if (importClause?.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
|
|
209
|
-
for (const element of importClause.namedBindings.elements) {
|
|
210
|
-
if (element.name.text === symbol) {
|
|
211
|
-
return {
|
|
212
|
-
path: moduleSpecifier,
|
|
213
|
-
symbol: element.name.text
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
toRelativeImport(absolutePath) {
|
|
223
|
-
const outputFileAbsolutePath = path.join(
|
|
224
|
-
this.contextPath,
|
|
225
|
-
this.outputFile
|
|
226
|
-
);
|
|
227
|
-
const outputDir = path.dirname(outputFileAbsolutePath);
|
|
228
|
-
const relative2 = path.relative(outputDir, absolutePath);
|
|
229
|
-
const withoutExtension = relative2.replace(/\.ts$/, "");
|
|
230
|
-
return withoutExtension.startsWith(".") ? withoutExtension : "./" + withoutExtension;
|
|
231
|
-
}
|
|
232
|
-
parseObjectLiteral(node) {
|
|
233
|
-
if (!ts.isObjectLiteralExpression(node)) {
|
|
234
|
-
return {};
|
|
235
|
-
}
|
|
236
|
-
const result = {};
|
|
237
|
-
for (const property of node.properties) {
|
|
238
|
-
if (ts.isPropertyAssignment(property)) {
|
|
239
|
-
const name = property.name.getText();
|
|
240
|
-
const value = property.initializer;
|
|
241
|
-
if (ts.isStringLiteral(value)) {
|
|
242
|
-
result[name] = value.text;
|
|
243
|
-
} else if (ts.isNumericLiteral(value)) {
|
|
244
|
-
result[name] = Number(value.text);
|
|
245
|
-
} else if (value.kind === ts.SyntaxKind.TrueKeyword) {
|
|
246
|
-
result[name] = true;
|
|
247
|
-
} else if (value.kind === ts.SyntaxKind.FalseKeyword) {
|
|
248
|
-
result[name] = false;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return result;
|
|
253
|
-
}
|
|
254
|
-
resolvePathAlias(importPath) {
|
|
255
|
-
const tsconfigPath = path.join(this.contextPath, "tsconfig.json");
|
|
256
|
-
if (!fs.existsSync(tsconfigPath)) return importPath;
|
|
257
|
-
const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, "utf-8"));
|
|
258
|
-
const paths = tsconfig.compilerOptions?.paths;
|
|
259
|
-
const baseUrl = tsconfig.compilerOptions?.baseUrl || ".";
|
|
260
|
-
if (!paths) return importPath;
|
|
261
|
-
for (const [alias, targets] of Object.entries(paths)) {
|
|
262
|
-
const aliasPrefix = alias.replace("/*", "");
|
|
263
|
-
if (importPath.startsWith(aliasPrefix)) {
|
|
264
|
-
const targetBase = targets[0].replace("/*", "");
|
|
265
|
-
const resolvedBase = path.join(this.contextPath, baseUrl, targetBase);
|
|
266
|
-
return importPath.replace(aliasPrefix, resolvedBase);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return importPath;
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
// src/code-generator.ts
|
|
274
|
-
var ApplicationCodeGenerator = class {
|
|
275
|
-
constructor(config) {
|
|
276
|
-
this.config = config;
|
|
277
|
-
}
|
|
278
|
-
generateCode(handlers) {
|
|
279
|
-
this.validateNoDuplicates(handlers);
|
|
280
|
-
const imports = this.collectImports(handlers);
|
|
281
|
-
const registrations = this.generateRegistrations(handlers);
|
|
282
|
-
return this.assembleGeneratedCode(imports, registrations);
|
|
283
|
-
}
|
|
284
|
-
validateNoDuplicates(handlers) {
|
|
285
|
-
this.validateNoDuplicateCommandHandlers(handlers);
|
|
286
|
-
this.validateNoDuplicateEventHandlers(handlers);
|
|
287
|
-
this.validateNoDuplicateQueryHandlers(handlers);
|
|
288
|
-
}
|
|
289
|
-
validateNoDuplicateHandlers(handlers, typeFilter, keyExtractor, errorFactory) {
|
|
290
|
-
const filteredHandlers = handlers.filter(typeFilter);
|
|
291
|
-
const keyToHandlers = /* @__PURE__ */ new Map();
|
|
292
|
-
for (const handler of filteredHandlers) {
|
|
293
|
-
const key = keyExtractor(handler);
|
|
294
|
-
const existing = keyToHandlers.get(key) ?? [];
|
|
295
|
-
existing.push(handler.handlerClassName);
|
|
296
|
-
keyToHandlers.set(key, existing);
|
|
297
|
-
}
|
|
298
|
-
for (const [key, handlerList] of keyToHandlers) {
|
|
299
|
-
if (handlerList.length > 1) {
|
|
300
|
-
throw errorFactory(key, handlerList);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
validateNoDuplicateCommandHandlers(handlers) {
|
|
305
|
-
this.validateNoDuplicateHandlers(
|
|
306
|
-
handlers,
|
|
307
|
-
(h) => h.type === "command",
|
|
308
|
-
(h) => h.commandClassName,
|
|
309
|
-
(key, handlers2) => new DuplicateCommandHandlerError(key, handlers2)
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
validateNoDuplicateEventHandlers(handlers) {
|
|
313
|
-
const eventHandlers = handlers.filter(
|
|
314
|
-
(h) => h.type === "event"
|
|
315
|
-
);
|
|
316
|
-
const namedEventHandlers = eventHandlers.filter(
|
|
317
|
-
(h) => h.eventHandlerOptions.name !== void 0
|
|
318
|
-
);
|
|
319
|
-
this.validateNoDuplicateHandlers(
|
|
320
|
-
namedEventHandlers,
|
|
321
|
-
(h) => true,
|
|
322
|
-
(h) => h.eventHandlerOptions.name,
|
|
323
|
-
(key, handlers2) => new DuplicateEventHandlerError(key, handlers2)
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
validateNoDuplicateQueryHandlers(handlers) {
|
|
327
|
-
this.validateNoDuplicateHandlers(
|
|
328
|
-
handlers,
|
|
329
|
-
(h) => h.type === "query",
|
|
330
|
-
(h) => h.queryClassName,
|
|
331
|
-
(key, handlers2) => new DuplicateQueryHandlerError(key, handlers2)
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
createImportStatement(symbolName, importPath) {
|
|
335
|
-
return `import { ${symbolName} } from '${importPath}';`;
|
|
336
|
-
}
|
|
337
|
-
createCommandHandlerRegistration(commandClassName, handlerClassName) {
|
|
338
|
-
return ` .withCommandHandler(${commandClassName}, () => new ${handlerClassName}())`;
|
|
339
|
-
}
|
|
340
|
-
createEventHandlerRegistration(handlerClassName, eventName) {
|
|
341
|
-
const nameArg = eventName ? `, '${eventName}'` : "";
|
|
342
|
-
return ` .withEventHandler(() => new ${handlerClassName}()${nameArg})`;
|
|
343
|
-
}
|
|
344
|
-
createQueryHandlerRegistration(queryClassName, handlerClassName) {
|
|
345
|
-
return ` .withQueryHandler(${queryClassName}, () => new ${handlerClassName}())`;
|
|
346
|
-
}
|
|
347
|
-
isCommandHandler(handler) {
|
|
348
|
-
return handler.type === "command";
|
|
349
|
-
}
|
|
350
|
-
isEventHandler(handler) {
|
|
351
|
-
return handler.type === "event";
|
|
352
|
-
}
|
|
353
|
-
isQueryHandler(handler) {
|
|
354
|
-
return handler.type === "query";
|
|
355
|
-
}
|
|
356
|
-
collectImports(handlers) {
|
|
357
|
-
const imports = /* @__PURE__ */ new Set();
|
|
358
|
-
imports.add(
|
|
359
|
-
this.createImportStatement(
|
|
360
|
-
"ApplicationBuilder",
|
|
361
|
-
this.config.applicationBuilderImportPath
|
|
362
|
-
)
|
|
363
|
-
);
|
|
364
|
-
for (const handler of handlers) {
|
|
365
|
-
imports.add(
|
|
366
|
-
this.createImportStatement(
|
|
367
|
-
handler.handlerClassName,
|
|
368
|
-
handler.handlerPath
|
|
369
|
-
)
|
|
370
|
-
);
|
|
371
|
-
if (this.isCommandHandler(handler) && handler.commandPath) {
|
|
372
|
-
imports.add(
|
|
373
|
-
this.createImportStatement(
|
|
374
|
-
handler.commandClassName,
|
|
375
|
-
handler.commandPath
|
|
376
|
-
)
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
if (this.isQueryHandler(handler) && handler.queryPath) {
|
|
380
|
-
imports.add(
|
|
381
|
-
this.createImportStatement(
|
|
382
|
-
handler.queryClassName,
|
|
383
|
-
handler.queryPath
|
|
384
|
-
)
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
return imports;
|
|
389
|
-
}
|
|
390
|
-
generateRegistrations(handlers) {
|
|
391
|
-
const commandRegistrations = this.generateCommandRegistrations(handlers);
|
|
392
|
-
const queryRegistrations = this.generateQueryRegistrations(handlers);
|
|
393
|
-
const eventRegistrations = this.generateEventRegistrations(handlers);
|
|
394
|
-
return [...commandRegistrations, ...queryRegistrations, ...eventRegistrations];
|
|
395
|
-
}
|
|
396
|
-
generateCommandRegistrations(handlers) {
|
|
397
|
-
return handlers.filter(
|
|
398
|
-
(h) => this.isCommandHandler(h)
|
|
399
|
-
).map(
|
|
400
|
-
(h) => this.createCommandHandlerRegistration(
|
|
401
|
-
h.commandClassName,
|
|
402
|
-
h.handlerClassName
|
|
403
|
-
)
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
generateQueryRegistrations(handlers) {
|
|
407
|
-
return handlers.filter(
|
|
408
|
-
(h) => this.isQueryHandler(h)
|
|
409
|
-
).map(
|
|
410
|
-
(h) => this.createQueryHandlerRegistration(
|
|
411
|
-
h.queryClassName,
|
|
412
|
-
h.handlerClassName
|
|
413
|
-
)
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
generateEventRegistrations(handlers) {
|
|
417
|
-
return handlers.filter((h) => this.isEventHandler(h)).map((handler) => {
|
|
418
|
-
this.validateEventHandlerOptions(handler);
|
|
419
|
-
const eventName = handler.eventHandlerOptions.name;
|
|
420
|
-
return this.createEventHandlerRegistration(
|
|
421
|
-
handler.handlerClassName,
|
|
422
|
-
eventName
|
|
423
|
-
);
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
validateEventHandlerOptions(handler) {
|
|
427
|
-
const options = handler.eventHandlerOptions;
|
|
428
|
-
const invalidKeys = Object.keys(options).filter(
|
|
429
|
-
(key) => key !== "name"
|
|
430
|
-
);
|
|
431
|
-
if (invalidKeys.length > 0) {
|
|
432
|
-
throw new Error(
|
|
433
|
-
`EventHandler for ${handler.handlerClassName} has invalid options: ${invalidKeys.join(", ")}.
|
|
434
|
-
Only 'name' option is supported. Use @EventHandler({ name: 'event-name' })`
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
assembleGeneratedCode(imports, registrations) {
|
|
439
|
-
const registrationCode = registrations.length > 0 ? "\n" + registrations.join("\n") : "";
|
|
440
|
-
return `
|
|
441
|
-
${Array.from(imports).join("\n")}
|
|
442
|
-
|
|
443
|
-
export function createApplicationBuilder(): ApplicationBuilder {
|
|
444
|
-
return new ApplicationBuilder()${registrationCode};
|
|
445
|
-
}
|
|
446
|
-
`;
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
// src/application-builder-generator.ts
|
|
451
|
-
var ApplicationBuilderGenerator = class {
|
|
452
|
-
constructor(contextPath, config) {
|
|
453
|
-
this.contextPath = contextPath;
|
|
454
|
-
this.config = config;
|
|
455
|
-
this.metadataExtractor = new HandlerMetadataExtractor(
|
|
456
|
-
contextPath,
|
|
457
|
-
config.outputFile
|
|
458
|
-
);
|
|
459
|
-
this.codeGenerator = new ApplicationCodeGenerator(config);
|
|
460
|
-
}
|
|
461
|
-
metadataExtractor;
|
|
462
|
-
codeGenerator;
|
|
463
|
-
async generate() {
|
|
464
|
-
const handlerFiles = await this.scanHandlerFiles();
|
|
465
|
-
const handlers = this.metadataExtractor.extractHandlersMetadata(handlerFiles);
|
|
466
|
-
const code = this.codeGenerator.generateCode(handlers);
|
|
467
|
-
this.writeToFile(code);
|
|
468
|
-
}
|
|
469
|
-
async scanHandlerFiles() {
|
|
470
|
-
const allFiles = [];
|
|
471
|
-
for (const pattern of this.config.handlers) {
|
|
472
|
-
const files = await glob(pattern, {
|
|
473
|
-
cwd: this.contextPath,
|
|
474
|
-
absolute: true
|
|
475
|
-
});
|
|
476
|
-
allFiles.push(...files);
|
|
477
|
-
}
|
|
478
|
-
return allFiles.sort();
|
|
479
|
-
}
|
|
480
|
-
writeToFile(code) {
|
|
481
|
-
const outputFile = path.join(this.contextPath, this.config.outputFile);
|
|
482
|
-
const outputDir = path.dirname(outputFile);
|
|
483
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
484
|
-
fs.writeFileSync(outputFile, code, "utf-8");
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
// src/config.ts
|
|
489
|
-
var BuildPluginConfig = class _BuildPluginConfig {
|
|
490
|
-
handlers;
|
|
491
|
-
outputFile;
|
|
492
|
-
applicationBuilderImportPath;
|
|
493
|
-
constructor(config) {
|
|
494
|
-
this.handlers = config.handlers;
|
|
495
|
-
this.outputFile = config.outputFile;
|
|
496
|
-
this.applicationBuilderImportPath = config.applicationBuilderImportPath;
|
|
497
|
-
}
|
|
498
|
-
static fromRawConfig(raw) {
|
|
499
|
-
if (!raw.applicationBuilderImportPath) {
|
|
500
|
-
throw new Error(
|
|
501
|
-
"applicationBuilderImportPath is required in hexai.config.ts\nExample: { applicationBuilderImportPath: '@/application', ... }"
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
if (!raw.handlers || raw.handlers.length === 0) {
|
|
505
|
-
throw new Error(
|
|
506
|
-
"handlers array is required and must not be empty in hexai.config.ts"
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
return new _BuildPluginConfig({
|
|
510
|
-
handlers: raw.handlers,
|
|
511
|
-
outputFile: raw.outputFile ?? "src/.generated/application-builder.ts",
|
|
512
|
-
applicationBuilderImportPath: raw.applicationBuilderImportPath
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
// src/config-loader.ts
|
|
518
|
-
async function loadConfig(configPath) {
|
|
519
|
-
const configSource = fs.readFileSync(configPath, "utf-8");
|
|
520
|
-
const transpiledCode = transpileConfigFile(configSource);
|
|
521
|
-
const moduleExports = evaluateConfigModule(transpiledCode);
|
|
522
|
-
const rawConfig = extractConfigFromModule(moduleExports);
|
|
523
|
-
return BuildPluginConfig.fromRawConfig(rawConfig);
|
|
524
|
-
}
|
|
525
|
-
function transpileConfigFile(source) {
|
|
526
|
-
const result = ts.transpileModule(source, {
|
|
527
|
-
compilerOptions: {
|
|
528
|
-
module: ts.ModuleKind.CommonJS,
|
|
529
|
-
target: ts.ScriptTarget.ES2020
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
return result.outputText;
|
|
533
|
-
}
|
|
534
|
-
function evaluateConfigModule(code) {
|
|
535
|
-
const tempModule = { exports: {} };
|
|
536
|
-
const fn = new Function("module", "exports", "require", code);
|
|
537
|
-
fn(tempModule, tempModule.exports, __require);
|
|
538
|
-
return tempModule.exports;
|
|
539
|
-
}
|
|
540
|
-
function extractConfigFromModule(moduleExports) {
|
|
541
|
-
return moduleExports.default || moduleExports;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// src/main.ts
|
|
545
|
-
async function generateApplicationBuilder(contextPath, options = {}) {
|
|
546
|
-
let configFile = "hexai.config.ts";
|
|
547
|
-
if (options.configFile) {
|
|
548
|
-
configFile = options.configFile;
|
|
549
|
-
}
|
|
550
|
-
const configPath = path.join(contextPath, configFile);
|
|
551
|
-
const config = await loadConfig(configPath);
|
|
552
|
-
const generator = new ApplicationBuilderGenerator(contextPath, config);
|
|
553
|
-
await generator.generate();
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// src/cli.ts
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { generateApplicationBuilder } from "./index.js";
|
|
557
4
|
async function main() {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
// Support --context-path or default to cwd
|
|
7
|
+
let contextPath = process.cwd();
|
|
8
|
+
const contextPathIndex = args.indexOf("--context-path");
|
|
9
|
+
if (contextPathIndex !== -1 && args[contextPathIndex + 1]) {
|
|
10
|
+
contextPath = path.resolve(args[contextPathIndex + 1]);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
console.log(`Generating application builder for: ${contextPath}`);
|
|
14
|
+
await generateApplicationBuilder(contextPath);
|
|
15
|
+
console.log("✓ Application builder generated successfully");
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.error("Error generating application builder:", error);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
572
21
|
}
|
|
573
22
|
main().catch((error) => {
|
|
574
|
-
|
|
575
|
-
|
|
23
|
+
console.error("Unexpected error:", error);
|
|
24
|
+
process.exit(1);
|
|
576
25
|
});
|
|
577
|
-
//# sourceMappingURL=cli.js.map
|
|
578
26
|
//# sourceMappingURL=cli.js.map
|