nest-hex 0.2.0 → 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.
Files changed (51) hide show
  1. package/README.md +183 -4
  2. package/dist/src/cli/bin.js +2630 -0
  3. package/dist/src/cli/commands/index.d.ts +23 -0
  4. package/dist/src/cli/commands/index.js +2595 -0
  5. package/dist/src/cli/config/defaults.d.ts +74 -0
  6. package/dist/src/cli/config/defaults.js +76 -0
  7. package/dist/src/cli/config/define-config.d.ts +74 -0
  8. package/dist/src/cli/config/define-config.js +62 -0
  9. package/dist/src/cli/config/loader.d.ts +74 -0
  10. package/dist/src/cli/config/loader.js +106 -0
  11. package/dist/src/cli/config/validator.d.ts +81 -0
  12. package/dist/src/cli/config/validator.js +108 -0
  13. package/dist/src/cli/generators/adapter.generator.d.ts +235 -0
  14. package/dist/src/cli/generators/adapter.generator.js +377 -0
  15. package/dist/src/cli/generators/base.generator.d.ts +190 -0
  16. package/dist/src/cli/generators/base.generator.js +312 -0
  17. package/dist/src/cli/generators/index.d.ts +264 -0
  18. package/dist/src/cli/generators/index.js +467 -0
  19. package/dist/src/cli/generators/port.generator.d.ts +211 -0
  20. package/dist/src/cli/generators/port.generator.js +364 -0
  21. package/dist/src/cli/generators/service.generator.d.ts +208 -0
  22. package/dist/src/cli/generators/service.generator.js +340 -0
  23. package/dist/src/cli/index.d.ts +74 -0
  24. package/dist/src/cli/index.js +69 -0
  25. package/dist/src/cli/types/config.types.d.ts +73 -0
  26. package/dist/src/cli/types/config.types.js +56 -0
  27. package/dist/src/cli/types/generator.types.d.ts +46 -0
  28. package/dist/src/cli/types/generator.types.js +56 -0
  29. package/dist/src/cli/types/index.d.ts +121 -0
  30. package/dist/src/cli/types/index.js +56 -0
  31. package/dist/src/cli/types/template.types.d.ts +28 -0
  32. package/dist/src/cli/types/template.types.js +56 -0
  33. package/dist/src/cli/ui/components/index.d.ts +97 -0
  34. package/dist/src/cli/ui/components/index.js +1493 -0
  35. package/dist/src/cli/utils/file-writer.d.ts +17 -0
  36. package/dist/src/cli/utils/file-writer.js +100 -0
  37. package/dist/src/cli/utils/linter-detector.d.ts +12 -0
  38. package/dist/src/cli/utils/linter-detector.js +128 -0
  39. package/dist/src/cli/utils/linter-runner.d.ts +17 -0
  40. package/dist/src/cli/utils/linter-runner.js +101 -0
  41. package/dist/src/cli/utils/name-transformer.d.ts +18 -0
  42. package/dist/src/cli/utils/name-transformer.js +90 -0
  43. package/dist/src/cli/utils/path-resolver.d.ts +5 -0
  44. package/dist/src/cli/utils/path-resolver.js +78 -0
  45. package/dist/src/cli/utils/port-scanner.d.ts +93 -0
  46. package/dist/src/cli/utils/port-scanner.js +104 -0
  47. package/dist/src/cli/utils/template-renderer.d.ts +30 -0
  48. package/dist/src/cli/utils/template-renderer.js +95 -0
  49. package/dist/{index.js → src/index.js} +14 -0
  50. package/package.json +10 -10
  51. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
@@ -0,0 +1,190 @@
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 NameVariations {
114
+ original: string;
115
+ kebab: string;
116
+ camel: string;
117
+ pascal: string;
118
+ snake: string;
119
+ screamingSnake: string;
120
+ }
121
+ declare function generateNameVariations(name: string): NameVariations;
122
+ /**
123
+ * Abstract base class for all generators.
124
+ *
125
+ * Provides common functionality like:
126
+ * - Template rendering
127
+ * - File writing
128
+ * - Name transformations
129
+ * - Path resolution
130
+ */
131
+ declare abstract class BaseGenerator {
132
+ protected readonly config: NestHexConfig;
133
+ constructor(config: NestHexConfig);
134
+ /**
135
+ * Render a template with the given context.
136
+ *
137
+ * @param templatePath - Path to the Handlebars template file
138
+ * @param context - Template context
139
+ * @returns Rendered template content
140
+ */
141
+ protected renderTemplate(templatePath: string, context: GeneratorContext): Promise<string>;
142
+ /**
143
+ * Write a file to disk.
144
+ *
145
+ * @param filePath - Destination file path
146
+ * @param content - File content
147
+ * @param dryRun - If true, don't actually write the file
148
+ * @returns WriteResult indicating success and conflict status
149
+ */
150
+ protected writeFile(filePath: string, content: string, dryRun?: boolean): Promise<import("../utils/file-writer").WriteResult>;
151
+ /**
152
+ * Generate all name variations for a given name.
153
+ *
154
+ * @param name - Original name
155
+ * @returns Object with all name case variations
156
+ */
157
+ protected getNameVariations(name: string): ReturnType<typeof generateNameVariations>;
158
+ /**
159
+ * Resolve a path relative to the project root.
160
+ *
161
+ * @param relativePath - Relative path
162
+ * @returns Absolute path
163
+ */
164
+ protected resolvePath(relativePath: string): string;
165
+ /**
166
+ * Get the template directory for a specific generator type.
167
+ *
168
+ * @param type - Generator type (port, adapter, service)
169
+ * @returns Absolute path to template directory
170
+ */
171
+ protected getTemplateDir(type: "port" | "adapter" | "service" | "examples"): string;
172
+ /**
173
+ * Create a template context from generator options.
174
+ *
175
+ * @param options - Generator options
176
+ * @param additionalContext - Additional context to merge
177
+ * @returns Complete template context
178
+ */
179
+ protected createTemplateContext(options: GeneratorOptions, additionalContext?: Record<string, unknown>): GeneratorContext;
180
+ /**
181
+ * Generate files from a list of file specifications.
182
+ *
183
+ * @param files - Array of files to generate
184
+ * @param dryRun - If true, don't actually write files
185
+ * @returns Array of successfully generated file paths
186
+ * @throws Error if any files fail to write
187
+ */
188
+ protected generateFiles(files: FileToGenerate[], dryRun?: boolean): Promise<string[]>;
189
+ }
190
+ export { BaseGenerator };
@@ -0,0 +1,312 @@
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 = "C:\\Users\\liorv\\OneDrive\\Desktop\\Projects\\Personal\\nestjs-adapter\\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
+ }