nest-hex 0.2.0 → 0.3.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/README.md +155 -267
- package/dist/src/cli/bin.js +2644 -0
- package/dist/src/cli/commands/index.d.ts +23 -0
- package/dist/src/cli/commands/index.js +2609 -0
- package/dist/src/cli/config/defaults.d.ts +74 -0
- package/dist/src/cli/config/defaults.js +76 -0
- package/dist/src/cli/config/define-config.d.ts +74 -0
- package/dist/src/cli/config/define-config.js +62 -0
- package/dist/src/cli/config/loader.d.ts +74 -0
- package/dist/src/cli/config/loader.js +106 -0
- package/dist/src/cli/config/validator.d.ts +81 -0
- package/dist/src/cli/config/validator.js +108 -0
- package/dist/src/cli/generators/adapter.generator.d.ts +235 -0
- package/dist/src/cli/generators/adapter.generator.js +377 -0
- package/dist/src/cli/generators/base.generator.d.ts +190 -0
- package/dist/src/cli/generators/base.generator.js +312 -0
- package/dist/src/cli/generators/index.d.ts +264 -0
- package/dist/src/cli/generators/index.js +467 -0
- package/dist/src/cli/generators/port.generator.d.ts +211 -0
- package/dist/src/cli/generators/port.generator.js +364 -0
- package/dist/src/cli/generators/service.generator.d.ts +208 -0
- package/dist/src/cli/generators/service.generator.js +340 -0
- package/dist/src/cli/index.d.ts +74 -0
- package/dist/src/cli/index.js +69 -0
- package/dist/src/cli/types/config.types.d.ts +73 -0
- package/dist/src/cli/types/config.types.js +56 -0
- package/dist/src/cli/types/generator.types.d.ts +46 -0
- package/dist/src/cli/types/generator.types.js +56 -0
- package/dist/src/cli/types/index.d.ts +121 -0
- package/dist/src/cli/types/index.js +56 -0
- package/dist/src/cli/types/template.types.d.ts +28 -0
- package/dist/src/cli/types/template.types.js +56 -0
- package/dist/src/cli/ui/components/index.d.ts +97 -0
- package/dist/src/cli/ui/components/index.js +1507 -0
- package/dist/src/cli/utils/file-writer.d.ts +17 -0
- package/dist/src/cli/utils/file-writer.js +100 -0
- package/dist/src/cli/utils/linter-detector.d.ts +12 -0
- package/dist/src/cli/utils/linter-detector.js +128 -0
- package/dist/src/cli/utils/linter-runner.d.ts +17 -0
- package/dist/src/cli/utils/linter-runner.js +101 -0
- package/dist/src/cli/utils/name-transformer.d.ts +18 -0
- package/dist/src/cli/utils/name-transformer.js +90 -0
- package/dist/src/cli/utils/path-resolver.d.ts +5 -0
- package/dist/src/cli/utils/path-resolver.js +78 -0
- package/dist/src/cli/utils/port-scanner.d.ts +93 -0
- package/dist/src/cli/utils/port-scanner.js +104 -0
- package/dist/src/cli/utils/template-renderer.d.ts +30 -0
- package/dist/src/cli/utils/template-renderer.js +95 -0
- package/dist/{index.d.ts → src/index.d.ts} +60 -30
- package/dist/{index.js → src/index.js} +45 -13
- package/package.json +10 -10
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
var import_node_module = require("node:module");
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
20
|
+
var __toCommonJS = (from) => {
|
|
21
|
+
var entry = __moduleCache.get(from), desc;
|
|
22
|
+
if (entry)
|
|
23
|
+
return entry;
|
|
24
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
25
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
26
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
27
|
+
get: () => from[key],
|
|
28
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
29
|
+
}));
|
|
30
|
+
__moduleCache.set(from, entry);
|
|
31
|
+
return entry;
|
|
32
|
+
};
|
|
33
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __export = (target, all) => {
|
|
35
|
+
for (var name in all)
|
|
36
|
+
__defProp(target, name, {
|
|
37
|
+
get: all[name],
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
set: (newValue) => all[name] = () => newValue
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
44
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
45
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
46
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
47
|
+
else
|
|
48
|
+
for (var i = decorators.length - 1;i >= 0; i--)
|
|
49
|
+
if (d = decorators[i])
|
|
50
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
51
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/cli/config/defaults.ts
|
|
55
|
+
var exports_defaults = {};
|
|
56
|
+
__export(exports_defaults, {
|
|
57
|
+
defaultConfig: () => defaultConfig
|
|
58
|
+
});
|
|
59
|
+
module.exports = __toCommonJS(exports_defaults);
|
|
60
|
+
var defaultConfig = {
|
|
61
|
+
output: {
|
|
62
|
+
portsDir: "src/ports",
|
|
63
|
+
adaptersDir: "src/adapters"
|
|
64
|
+
},
|
|
65
|
+
naming: {
|
|
66
|
+
portSuffix: "PORT",
|
|
67
|
+
adapterSuffix: "Adapter",
|
|
68
|
+
fileCase: "kebab"
|
|
69
|
+
},
|
|
70
|
+
style: {
|
|
71
|
+
indent: "tab",
|
|
72
|
+
quotes: "single",
|
|
73
|
+
semicolons: true
|
|
74
|
+
},
|
|
75
|
+
templates: {}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/cli/utils/file-writer.ts
|
|
79
|
+
var exports_file_writer = {};
|
|
80
|
+
__export(exports_file_writer, {
|
|
81
|
+
writeFile: () => writeFile
|
|
82
|
+
});
|
|
83
|
+
module.exports = __toCommonJS(exports_file_writer);
|
|
84
|
+
async function writeFile(filePath, content, options = {}) {
|
|
85
|
+
const { force = false, dryRun = false } = options;
|
|
86
|
+
const file = Bun.file(filePath);
|
|
87
|
+
const existed = await file.exists();
|
|
88
|
+
if (existed && !force && !dryRun) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
path: filePath,
|
|
92
|
+
existed: true,
|
|
93
|
+
written: false,
|
|
94
|
+
message: "File already exists. Use --force to overwrite."
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (dryRun) {
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
path: filePath,
|
|
101
|
+
existed,
|
|
102
|
+
written: false,
|
|
103
|
+
message: "Dry run - file not written"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
await Bun.write(filePath, content);
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
path: filePath,
|
|
111
|
+
existed,
|
|
112
|
+
written: true,
|
|
113
|
+
message: existed ? "File overwritten" : "File created"
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
path: filePath,
|
|
119
|
+
existed,
|
|
120
|
+
written: false,
|
|
121
|
+
message: `Failed to write file: ${error instanceof Error ? error.message : String(error)}`
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/cli/utils/name-transformer.ts
|
|
127
|
+
var exports_name_transformer = {};
|
|
128
|
+
__export(exports_name_transformer, {
|
|
129
|
+
toSnakeCase: () => toSnakeCase,
|
|
130
|
+
toScreamingSnakeCase: () => toScreamingSnakeCase,
|
|
131
|
+
toPascalCase: () => toPascalCase,
|
|
132
|
+
toKebabCase: () => toKebabCase,
|
|
133
|
+
toCamelCase: () => toCamelCase,
|
|
134
|
+
generateNameVariations: () => generateNameVariations
|
|
135
|
+
});
|
|
136
|
+
module.exports = __toCommonJS(exports_name_transformer);
|
|
137
|
+
function toKebabCase(str) {
|
|
138
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
139
|
+
}
|
|
140
|
+
function toCamelCase(str) {
|
|
141
|
+
return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
142
|
+
}
|
|
143
|
+
function toPascalCase(str) {
|
|
144
|
+
const camel = toCamelCase(str);
|
|
145
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
146
|
+
}
|
|
147
|
+
function toSnakeCase(str) {
|
|
148
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
|
|
149
|
+
}
|
|
150
|
+
function toScreamingSnakeCase(str) {
|
|
151
|
+
return toSnakeCase(str).toUpperCase();
|
|
152
|
+
}
|
|
153
|
+
function generateNameVariations(name) {
|
|
154
|
+
return {
|
|
155
|
+
original: name,
|
|
156
|
+
kebab: toKebabCase(name),
|
|
157
|
+
camel: toCamelCase(name),
|
|
158
|
+
pascal: toPascalCase(name),
|
|
159
|
+
snake: toSnakeCase(name),
|
|
160
|
+
screamingSnake: toScreamingSnakeCase(name)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/cli/utils/path-resolver.ts
|
|
165
|
+
var exports_path_resolver = {};
|
|
166
|
+
__export(exports_path_resolver, {
|
|
167
|
+
resolvePath: () => resolvePath,
|
|
168
|
+
joinPaths: () => joinPaths,
|
|
169
|
+
getRelativePath: () => getRelativePath,
|
|
170
|
+
getImportPath: () => getImportPath
|
|
171
|
+
});
|
|
172
|
+
module.exports = __toCommonJS(exports_path_resolver);
|
|
173
|
+
var import_node_path = require("node:path");
|
|
174
|
+
function resolvePath(...segments) {
|
|
175
|
+
return import_node_path.resolve(...segments);
|
|
176
|
+
}
|
|
177
|
+
function joinPaths(...segments) {
|
|
178
|
+
return import_node_path.join(...segments);
|
|
179
|
+
}
|
|
180
|
+
function getRelativePath(from, to) {
|
|
181
|
+
const relativePath = import_node_path.relative(from, to);
|
|
182
|
+
return relativePath.replace(/\\/g, "/");
|
|
183
|
+
}
|
|
184
|
+
function getImportPath(from, to) {
|
|
185
|
+
const relPath = getRelativePath(import_node_path.dirname(from), to);
|
|
186
|
+
const withoutExt = relPath.replace(/\.(ts|js)$/, "");
|
|
187
|
+
return withoutExt.startsWith(".") ? withoutExt : `./${withoutExt}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/cli/utils/template-renderer.ts
|
|
191
|
+
var exports_template_renderer = {};
|
|
192
|
+
__export(exports_template_renderer, {
|
|
193
|
+
renderTemplateString: () => renderTemplateString,
|
|
194
|
+
renderTemplate: () => renderTemplate
|
|
195
|
+
});
|
|
196
|
+
module.exports = __toCommonJS(exports_template_renderer);
|
|
197
|
+
var import_handlebars = __toESM(require("handlebars"));
|
|
198
|
+
async function renderTemplate(templatePath, context) {
|
|
199
|
+
try {
|
|
200
|
+
const file = Bun.file(templatePath);
|
|
201
|
+
if (!await file.exists()) {
|
|
202
|
+
throw new Error(`Template file not found: ${templatePath}
|
|
203
|
+
` + "Please ensure the template exists or report this as a bug.");
|
|
204
|
+
}
|
|
205
|
+
const templateSource = await file.text();
|
|
206
|
+
try {
|
|
207
|
+
const template = import_handlebars.default.compile(templateSource);
|
|
208
|
+
return template(context);
|
|
209
|
+
} catch (compileError) {
|
|
210
|
+
throw new Error(`Failed to compile template ${templatePath}: ` + `${compileError instanceof Error ? compileError.message : String(compileError)}
|
|
211
|
+
` + "This may indicate invalid Handlebars syntax in the template.");
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (error instanceof Error && error.message.includes("Template file not found")) {
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
if (error instanceof Error && error.message.includes("Failed to compile template")) {
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
throw new Error(`Failed to read template ${templatePath}: ` + `${error instanceof Error ? error.message : String(error)}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function renderTemplateString(templateString, context) {
|
|
224
|
+
try {
|
|
225
|
+
const template = import_handlebars.default.compile(templateString);
|
|
226
|
+
return template(context);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
throw new Error("Failed to compile template string: " + `${error instanceof Error ? error.message : String(error)}
|
|
229
|
+
` + "This may indicate invalid Handlebars syntax.");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/cli/generators/base.generator.ts
|
|
234
|
+
var exports_base_generator = {};
|
|
235
|
+
__export(exports_base_generator, {
|
|
236
|
+
BaseGenerator: () => BaseGenerator
|
|
237
|
+
});
|
|
238
|
+
module.exports = __toCommonJS(exports_base_generator);
|
|
239
|
+
var import_node_path2 = require("node:path");
|
|
240
|
+
var __dirname = "/home/runner/work/nest-hex/nest-hex/src/cli/generators";
|
|
241
|
+
|
|
242
|
+
class BaseGenerator {
|
|
243
|
+
config;
|
|
244
|
+
constructor(config) {
|
|
245
|
+
this.config = config;
|
|
246
|
+
}
|
|
247
|
+
async renderTemplate(templatePath, context) {
|
|
248
|
+
return renderTemplate(templatePath, context);
|
|
249
|
+
}
|
|
250
|
+
async writeFile(filePath, content, dryRun = false) {
|
|
251
|
+
return writeFile(filePath, content, {
|
|
252
|
+
dryRun,
|
|
253
|
+
force: false
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
getNameVariations(name) {
|
|
257
|
+
return generateNameVariations(name);
|
|
258
|
+
}
|
|
259
|
+
resolvePath(relativePath) {
|
|
260
|
+
return resolvePath(relativePath);
|
|
261
|
+
}
|
|
262
|
+
getTemplateDir(type) {
|
|
263
|
+
const templatesRoot = import_node_path2.join(__dirname, "..", "templates");
|
|
264
|
+
return import_node_path2.join(templatesRoot, type);
|
|
265
|
+
}
|
|
266
|
+
createTemplateContext(options, additionalContext = {}) {
|
|
267
|
+
const names = this.getNameVariations(options.name);
|
|
268
|
+
return {
|
|
269
|
+
...names,
|
|
270
|
+
nameKebab: names.kebab,
|
|
271
|
+
nameCamel: names.camel,
|
|
272
|
+
namePascal: names.pascal,
|
|
273
|
+
nameSnake: names.snake,
|
|
274
|
+
nameScreamingSnake: names.screamingSnake,
|
|
275
|
+
portSuffix: this.config.naming?.portSuffix ?? defaultConfig.naming.portSuffix,
|
|
276
|
+
adapterSuffix: this.config.naming?.adapterSuffix ?? defaultConfig.naming.adapterSuffix,
|
|
277
|
+
fileCase: this.config.naming?.fileCase ?? defaultConfig.naming.fileCase,
|
|
278
|
+
indent: this.config.style?.indent ?? defaultConfig.style.indent,
|
|
279
|
+
quotes: this.config.style?.quotes ?? defaultConfig.style.quotes,
|
|
280
|
+
semicolons: this.config.style?.semicolons ?? defaultConfig.style.semicolons,
|
|
281
|
+
includeModule: options.includeModule ?? true,
|
|
282
|
+
includeService: options.includeService ?? true,
|
|
283
|
+
registrationType: options.registrationType ?? "sync",
|
|
284
|
+
generateExample: options.generateExample ?? false,
|
|
285
|
+
coreImportPath: "nest-hex",
|
|
286
|
+
...additionalContext
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
async generateFiles(files, dryRun = false) {
|
|
290
|
+
const generatedFiles = [];
|
|
291
|
+
const failures = [];
|
|
292
|
+
for (const file of files) {
|
|
293
|
+
const result = await this.writeFile(file.path, file.content, dryRun);
|
|
294
|
+
if (result.success) {
|
|
295
|
+
generatedFiles.push(file.path);
|
|
296
|
+
} else {
|
|
297
|
+
failures.push({
|
|
298
|
+
path: file.path,
|
|
299
|
+
error: result.message || "Unknown error"
|
|
300
|
+
});
|
|
301
|
+
console.error(`Failed to write ${file.path}: ${result.message}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (failures.length > 0) {
|
|
305
|
+
const errorMsg = `Failed to generate ${failures.length} file(s):
|
|
306
|
+
` + failures.map((f) => ` - ${f.path}: ${f.error}`).join(`
|
|
307
|
+
`);
|
|
308
|
+
throw new Error(errorMsg);
|
|
309
|
+
}
|
|
310
|
+
return generatedFiles;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/cli/generators/adapter.generator.ts
|
|
315
|
+
var exports_adapter_generator = {};
|
|
316
|
+
__export(exports_adapter_generator, {
|
|
317
|
+
AdapterGenerator: () => AdapterGenerator
|
|
318
|
+
});
|
|
319
|
+
module.exports = __toCommonJS(exports_adapter_generator);
|
|
320
|
+
var import_node_path3 = require("node:path");
|
|
321
|
+
class AdapterGenerator extends BaseGenerator {
|
|
322
|
+
async generate(options) {
|
|
323
|
+
const portNameVariations = options.portName ? this.getNameVariations(options.portName) : undefined;
|
|
324
|
+
let portImportPath;
|
|
325
|
+
if (portNameVariations && !options.portPath) {
|
|
326
|
+
const portsDir = this.config.output?.portsDir || "ports";
|
|
327
|
+
const adaptersDir = this.config.output?.adaptersDir || "adapters";
|
|
328
|
+
const portsDirName = import_node_path3.basename(portsDir);
|
|
329
|
+
const _adaptersDirName = import_node_path3.basename(adaptersDir);
|
|
330
|
+
portImportPath = `../../${portsDirName}/${portNameVariations.kebab}`;
|
|
331
|
+
}
|
|
332
|
+
const context = this.createTemplateContext(options, {
|
|
333
|
+
portName: options.portName,
|
|
334
|
+
portPath: options.portPath,
|
|
335
|
+
technology: options.technology,
|
|
336
|
+
...portNameVariations ? {
|
|
337
|
+
portNameKebab: portNameVariations.kebab,
|
|
338
|
+
portNamePascal: portNameVariations.pascal,
|
|
339
|
+
portNameCamel: portNameVariations.camel,
|
|
340
|
+
portNameScreamingSnake: portNameVariations.screamingSnake,
|
|
341
|
+
portTokenName: options.portTokenName || `${portNameVariations.screamingSnake}_${this.config.naming?.portSuffix || "PORT"}`,
|
|
342
|
+
portInterfaceName: `${portNameVariations.pascal}Port`,
|
|
343
|
+
portImportPath: options.portPath || portImportPath
|
|
344
|
+
} : {}
|
|
345
|
+
});
|
|
346
|
+
const templateDir = this.getTemplateDir("adapter");
|
|
347
|
+
const outputDir = options.outputPath || this.resolvePath(this.config.output?.adaptersDir || "src/adapters");
|
|
348
|
+
const adapterDir = import_node_path3.join(outputDir, context.nameKebab);
|
|
349
|
+
const files = [];
|
|
350
|
+
const adapterContent = await this.renderTemplate(import_node_path3.join(templateDir, "adapter.hbs"), context);
|
|
351
|
+
files.push({
|
|
352
|
+
path: import_node_path3.join(adapterDir, `${context.nameKebab}.adapter.ts`),
|
|
353
|
+
content: adapterContent
|
|
354
|
+
});
|
|
355
|
+
const serviceContent = await this.renderTemplate(import_node_path3.join(templateDir, "service.hbs"), context);
|
|
356
|
+
files.push({
|
|
357
|
+
path: import_node_path3.join(adapterDir, `${context.nameKebab}.service.ts`),
|
|
358
|
+
content: serviceContent
|
|
359
|
+
});
|
|
360
|
+
const typesContent = await this.renderTemplate(import_node_path3.join(templateDir, "types.hbs"), context);
|
|
361
|
+
files.push({
|
|
362
|
+
path: import_node_path3.join(adapterDir, `${context.nameKebab}.types.ts`),
|
|
363
|
+
content: typesContent
|
|
364
|
+
});
|
|
365
|
+
const indexContent = await this.renderTemplate(import_node_path3.join(templateDir, "index.hbs"), context);
|
|
366
|
+
files.push({
|
|
367
|
+
path: import_node_path3.join(adapterDir, "index.ts"),
|
|
368
|
+
content: indexContent
|
|
369
|
+
});
|
|
370
|
+
const generatedFiles = await this.generateFiles(files, options.dryRun);
|
|
371
|
+
return {
|
|
372
|
+
success: true,
|
|
373
|
+
files: generatedFiles,
|
|
374
|
+
message: `Successfully generated adapter files for ${context.namePascal}`
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/cli/generators/port.generator.ts
|
|
380
|
+
var exports_port_generator = {};
|
|
381
|
+
__export(exports_port_generator, {
|
|
382
|
+
PortGenerator: () => PortGenerator
|
|
383
|
+
});
|
|
384
|
+
module.exports = __toCommonJS(exports_port_generator);
|
|
385
|
+
var import_node_path4 = require("node:path");
|
|
386
|
+
class PortGenerator extends BaseGenerator {
|
|
387
|
+
async generate(options) {
|
|
388
|
+
const context = this.createTemplateContext(options);
|
|
389
|
+
const templateDir = this.getTemplateDir("port");
|
|
390
|
+
const outputDir = options.outputPath || this.resolvePath(this.config.output?.portsDir || "src/ports");
|
|
391
|
+
const portDir = import_node_path4.join(outputDir, context.nameKebab);
|
|
392
|
+
const files = [];
|
|
393
|
+
const interfaceContent = await this.renderTemplate(import_node_path4.join(templateDir, "interface.hbs"), context);
|
|
394
|
+
files.push({
|
|
395
|
+
path: import_node_path4.join(portDir, `${context.nameKebab}.port.ts`),
|
|
396
|
+
content: interfaceContent
|
|
397
|
+
});
|
|
398
|
+
const tokenContent = await this.renderTemplate(import_node_path4.join(templateDir, "token.hbs"), context);
|
|
399
|
+
files.push({
|
|
400
|
+
path: import_node_path4.join(portDir, `${context.nameKebab}.token.ts`),
|
|
401
|
+
content: tokenContent
|
|
402
|
+
});
|
|
403
|
+
if (context.includeService) {
|
|
404
|
+
const serviceContent = await this.renderTemplate(import_node_path4.join(templateDir, "service.hbs"), context);
|
|
405
|
+
files.push({
|
|
406
|
+
path: import_node_path4.join(portDir, `${context.nameKebab}.service.ts`),
|
|
407
|
+
content: serviceContent
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
if (context.includeModule) {
|
|
411
|
+
const moduleContent = await this.renderTemplate(import_node_path4.join(templateDir, "module.hbs"), context);
|
|
412
|
+
files.push({
|
|
413
|
+
path: import_node_path4.join(portDir, `${context.nameKebab}.module.ts`),
|
|
414
|
+
content: moduleContent
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const indexContent = await this.renderTemplate(import_node_path4.join(templateDir, "index.hbs"), context);
|
|
418
|
+
files.push({
|
|
419
|
+
path: import_node_path4.join(portDir, "index.ts"),
|
|
420
|
+
content: indexContent
|
|
421
|
+
});
|
|
422
|
+
const generatedFiles = await this.generateFiles(files, options.dryRun);
|
|
423
|
+
return {
|
|
424
|
+
success: true,
|
|
425
|
+
files: generatedFiles,
|
|
426
|
+
message: `Successfully generated port files for ${context.namePascal}`
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/cli/generators/service.generator.ts
|
|
432
|
+
var exports_service_generator = {};
|
|
433
|
+
__export(exports_service_generator, {
|
|
434
|
+
ServiceGenerator: () => ServiceGenerator
|
|
435
|
+
});
|
|
436
|
+
module.exports = __toCommonJS(exports_service_generator);
|
|
437
|
+
var import_node_path5 = require("node:path");
|
|
438
|
+
class ServiceGenerator extends BaseGenerator {
|
|
439
|
+
async generate(options) {
|
|
440
|
+
const context = this.createTemplateContext(options);
|
|
441
|
+
const templateDir = this.getTemplateDir("service");
|
|
442
|
+
const outputDir = options.outputPath || this.resolvePath(this.config.output?.portsDir || "src/services");
|
|
443
|
+
const serviceDir = import_node_path5.join(outputDir, context.nameKebab);
|
|
444
|
+
const files = [];
|
|
445
|
+
const serviceContent = await this.renderTemplate(import_node_path5.join(templateDir, "injectable-service.hbs"), context);
|
|
446
|
+
files.push({
|
|
447
|
+
path: import_node_path5.join(serviceDir, `${context.nameKebab}.service.ts`),
|
|
448
|
+
content: serviceContent
|
|
449
|
+
});
|
|
450
|
+
const generatedFiles = await this.generateFiles(files, options.dryRun);
|
|
451
|
+
return {
|
|
452
|
+
success: true,
|
|
453
|
+
files: generatedFiles,
|
|
454
|
+
message: `Successfully generated ${generatedFiles.length} service file${generatedFiles.length !== 1 ? "s" : ""}`
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/cli/generators/index.ts
|
|
460
|
+
var exports_generators = {};
|
|
461
|
+
__export(exports_generators, {
|
|
462
|
+
ServiceGenerator: () => ServiceGenerator,
|
|
463
|
+
PortGenerator: () => PortGenerator,
|
|
464
|
+
BaseGenerator: () => BaseGenerator,
|
|
465
|
+
AdapterGenerator: () => AdapterGenerator
|
|
466
|
+
});
|
|
467
|
+
module.exports = __toCommonJS(exports_generators);
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types for nest-hex CLI
|
|
3
|
+
*/
|
|
4
|
+
interface NestHexConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Output directory configuration
|
|
7
|
+
*/
|
|
8
|
+
output?: {
|
|
9
|
+
/**
|
|
10
|
+
* Directory for generated ports
|
|
11
|
+
* @default 'src/ports'
|
|
12
|
+
*/
|
|
13
|
+
portsDir?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Directory for generated adapters
|
|
16
|
+
* @default 'src/adapters'
|
|
17
|
+
*/
|
|
18
|
+
adaptersDir?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Naming conventions
|
|
22
|
+
*/
|
|
23
|
+
naming?: {
|
|
24
|
+
/**
|
|
25
|
+
* Suffix for port tokens
|
|
26
|
+
* @default 'PORT'
|
|
27
|
+
*/
|
|
28
|
+
portSuffix?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Suffix for adapter classes
|
|
31
|
+
* @default 'Adapter'
|
|
32
|
+
*/
|
|
33
|
+
adapterSuffix?: string;
|
|
34
|
+
/**
|
|
35
|
+
* File naming case
|
|
36
|
+
* @default 'kebab'
|
|
37
|
+
*/
|
|
38
|
+
fileCase?: "kebab" | "camel" | "pascal";
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Code style preferences
|
|
42
|
+
*/
|
|
43
|
+
style?: {
|
|
44
|
+
/**
|
|
45
|
+
* Indentation style
|
|
46
|
+
* @default 'tab'
|
|
47
|
+
*/
|
|
48
|
+
indent?: "tab" | 2 | 4;
|
|
49
|
+
/**
|
|
50
|
+
* Quote style
|
|
51
|
+
* @default 'single'
|
|
52
|
+
*/
|
|
53
|
+
quotes?: "single" | "double";
|
|
54
|
+
/**
|
|
55
|
+
* Use semicolons
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
semicolons?: boolean;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Custom template paths
|
|
62
|
+
*/
|
|
63
|
+
templates?: {
|
|
64
|
+
portModule?: string;
|
|
65
|
+
portToken?: string;
|
|
66
|
+
portInterface?: string;
|
|
67
|
+
portService?: string;
|
|
68
|
+
adapterModule?: string;
|
|
69
|
+
adapterService?: string;
|
|
70
|
+
adapterTypes?: string;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generator types for CLI
|
|
75
|
+
*/
|
|
76
|
+
interface GeneratorOptions {
|
|
77
|
+
name: string;
|
|
78
|
+
outputPath?: string;
|
|
79
|
+
includeModule?: boolean;
|
|
80
|
+
includeService?: boolean;
|
|
81
|
+
registrationType?: "sync" | "async";
|
|
82
|
+
generateExample?: boolean;
|
|
83
|
+
dryRun?: boolean;
|
|
84
|
+
}
|
|
85
|
+
interface GeneratorContext extends Record<string, unknown> {
|
|
86
|
+
original: string;
|
|
87
|
+
kebab: string;
|
|
88
|
+
camel: string;
|
|
89
|
+
pascal: string;
|
|
90
|
+
snake: string;
|
|
91
|
+
screamingSnake: string;
|
|
92
|
+
nameKebab: string;
|
|
93
|
+
nameCamel: string;
|
|
94
|
+
namePascal: string;
|
|
95
|
+
nameSnake: string;
|
|
96
|
+
nameScreamingSnake: string;
|
|
97
|
+
portSuffix: string;
|
|
98
|
+
adapterSuffix: string;
|
|
99
|
+
fileCase: "kebab" | "camel" | "pascal";
|
|
100
|
+
indent: "tab" | number;
|
|
101
|
+
quotes: "single" | "double";
|
|
102
|
+
semicolons: boolean;
|
|
103
|
+
includeModule: boolean;
|
|
104
|
+
includeService: boolean;
|
|
105
|
+
registrationType: "sync" | "async";
|
|
106
|
+
generateExample: boolean;
|
|
107
|
+
coreImportPath: string;
|
|
108
|
+
}
|
|
109
|
+
interface FileToGenerate {
|
|
110
|
+
path: string;
|
|
111
|
+
content: string;
|
|
112
|
+
}
|
|
113
|
+
interface GeneratorResult {
|
|
114
|
+
files: string[];
|
|
115
|
+
success: boolean;
|
|
116
|
+
message?: string;
|
|
117
|
+
}
|
|
118
|
+
interface NameVariations {
|
|
119
|
+
original: string;
|
|
120
|
+
kebab: string;
|
|
121
|
+
camel: string;
|
|
122
|
+
pascal: string;
|
|
123
|
+
snake: string;
|
|
124
|
+
screamingSnake: string;
|
|
125
|
+
}
|
|
126
|
+
declare function generateNameVariations(name: string): NameVariations;
|
|
127
|
+
/**
|
|
128
|
+
* Abstract base class for all generators.
|
|
129
|
+
*
|
|
130
|
+
* Provides common functionality like:
|
|
131
|
+
* - Template rendering
|
|
132
|
+
* - File writing
|
|
133
|
+
* - Name transformations
|
|
134
|
+
* - Path resolution
|
|
135
|
+
*/
|
|
136
|
+
declare abstract class BaseGenerator {
|
|
137
|
+
protected readonly config: NestHexConfig;
|
|
138
|
+
constructor(config: NestHexConfig);
|
|
139
|
+
/**
|
|
140
|
+
* Render a template with the given context.
|
|
141
|
+
*
|
|
142
|
+
* @param templatePath - Path to the Handlebars template file
|
|
143
|
+
* @param context - Template context
|
|
144
|
+
* @returns Rendered template content
|
|
145
|
+
*/
|
|
146
|
+
protected renderTemplate(templatePath: string, context: GeneratorContext): Promise<string>;
|
|
147
|
+
/**
|
|
148
|
+
* Write a file to disk.
|
|
149
|
+
*
|
|
150
|
+
* @param filePath - Destination file path
|
|
151
|
+
* @param content - File content
|
|
152
|
+
* @param dryRun - If true, don't actually write the file
|
|
153
|
+
* @returns WriteResult indicating success and conflict status
|
|
154
|
+
*/
|
|
155
|
+
protected writeFile(filePath: string, content: string, dryRun?: boolean): Promise<import("../utils/file-writer").WriteResult>;
|
|
156
|
+
/**
|
|
157
|
+
* Generate all name variations for a given name.
|
|
158
|
+
*
|
|
159
|
+
* @param name - Original name
|
|
160
|
+
* @returns Object with all name case variations
|
|
161
|
+
*/
|
|
162
|
+
protected getNameVariations(name: string): ReturnType<typeof generateNameVariations>;
|
|
163
|
+
/**
|
|
164
|
+
* Resolve a path relative to the project root.
|
|
165
|
+
*
|
|
166
|
+
* @param relativePath - Relative path
|
|
167
|
+
* @returns Absolute path
|
|
168
|
+
*/
|
|
169
|
+
protected resolvePath(relativePath: string): string;
|
|
170
|
+
/**
|
|
171
|
+
* Get the template directory for a specific generator type.
|
|
172
|
+
*
|
|
173
|
+
* @param type - Generator type (port, adapter, service)
|
|
174
|
+
* @returns Absolute path to template directory
|
|
175
|
+
*/
|
|
176
|
+
protected getTemplateDir(type: "port" | "adapter" | "service" | "examples"): string;
|
|
177
|
+
/**
|
|
178
|
+
* Create a template context from generator options.
|
|
179
|
+
*
|
|
180
|
+
* @param options - Generator options
|
|
181
|
+
* @param additionalContext - Additional context to merge
|
|
182
|
+
* @returns Complete template context
|
|
183
|
+
*/
|
|
184
|
+
protected createTemplateContext(options: GeneratorOptions, additionalContext?: Record<string, unknown>): GeneratorContext;
|
|
185
|
+
/**
|
|
186
|
+
* Generate files from a list of file specifications.
|
|
187
|
+
*
|
|
188
|
+
* @param files - Array of files to generate
|
|
189
|
+
* @param dryRun - If true, don't actually write files
|
|
190
|
+
* @returns Array of successfully generated file paths
|
|
191
|
+
* @throws Error if any files fail to write
|
|
192
|
+
*/
|
|
193
|
+
protected generateFiles(files: FileToGenerate[], dryRun?: boolean): Promise<string[]>;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Generator for creating port files.
|
|
197
|
+
*
|
|
198
|
+
* Generates:
|
|
199
|
+
* - Port interface (domain contract)
|
|
200
|
+
* - Port token (dependency injection token)
|
|
201
|
+
* - Port service (optional - domain service using the port)
|
|
202
|
+
* - Port module (optional - feature module wrapper)
|
|
203
|
+
* - Index file (barrel exports)
|
|
204
|
+
*/
|
|
205
|
+
declare class PortGenerator extends BaseGenerator {
|
|
206
|
+
/**
|
|
207
|
+
* Generate all port files.
|
|
208
|
+
*/
|
|
209
|
+
generate(options: GeneratorOptions): Promise<GeneratorResult>;
|
|
210
|
+
}
|
|
211
|
+
export { PortGenerator };
|