@expressots/cli 4.0.0-preview.2 → 4.0.0-preview.3

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 (63) hide show
  1. package/bin/cicd/cli.d.ts +1 -1
  2. package/bin/cicd/cli.js +3 -1
  3. package/bin/cicd/form.js +5 -4
  4. package/bin/cli.d.ts +1 -5
  5. package/bin/cli.js +56 -6
  6. package/bin/commands/project.commands.js +233 -26
  7. package/bin/containerize/cli.d.ts +1 -1
  8. package/bin/containerize/cli.js +1 -1
  9. package/bin/containerize/form.js +49 -51
  10. package/bin/containerize/generators/ci-generator.js +16 -12
  11. package/bin/containerize/generators/docker-compose-generator.js +3 -2
  12. package/bin/containerize/generators/dockerfile-generator.js +50 -28
  13. package/bin/containerize/generators/kubernetes-generator.js +5 -4
  14. package/bin/costs/cli.d.ts +1 -1
  15. package/bin/costs/cli.js +4 -2
  16. package/bin/dev/cli.d.ts +1 -1
  17. package/bin/dev/cli.js +3 -1
  18. package/bin/generate/cli.d.ts +1 -1
  19. package/bin/generate/templates/nonopinionated/config.tpl +12 -12
  20. package/bin/generate/templates/nonopinionated/event.tpl +10 -10
  21. package/bin/generate/templates/nonopinionated/guard.tpl +18 -18
  22. package/bin/generate/templates/nonopinionated/handler.tpl +12 -12
  23. package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -27
  24. package/bin/generate/templates/opinionated/config.tpl +47 -47
  25. package/bin/generate/templates/opinionated/event.tpl +15 -15
  26. package/bin/generate/templates/opinionated/guard.tpl +41 -41
  27. package/bin/generate/templates/opinionated/handler.tpl +23 -23
  28. package/bin/generate/templates/opinionated/interceptor.tpl +50 -50
  29. package/bin/generate/utils/command-utils.d.ts +13 -2
  30. package/bin/generate/utils/command-utils.js +50 -17
  31. package/bin/generate/utils/opinionated-cmd.js +19 -12
  32. package/bin/help/cli.d.ts +1 -1
  33. package/bin/help/command-help-registry.d.ts +23 -0
  34. package/bin/help/command-help-registry.js +303 -0
  35. package/bin/help/command-help.d.ts +36 -0
  36. package/bin/help/command-help.js +56 -0
  37. package/bin/help/form.js +127 -30
  38. package/bin/help/main-help.d.ts +8 -0
  39. package/bin/help/main-help.js +126 -0
  40. package/bin/help/render.d.ts +32 -0
  41. package/bin/help/render.js +46 -0
  42. package/bin/info/cli.d.ts +1 -1
  43. package/bin/info/form.d.ts +1 -1
  44. package/bin/info/form.js +11 -11
  45. package/bin/migrate/cli.d.ts +1 -1
  46. package/bin/migrate/cli.js +3 -1
  47. package/bin/migrate/form.js +4 -3
  48. package/bin/new/cli.d.ts +5 -1
  49. package/bin/new/cli.js +62 -14
  50. package/bin/new/form.d.ts +3 -1
  51. package/bin/new/form.js +338 -23
  52. package/bin/profile/cli.d.ts +1 -1
  53. package/bin/profile/cli.js +3 -1
  54. package/bin/profile/form.js +5 -4
  55. package/bin/providers/create/form.js +53 -4
  56. package/bin/studio/cli.js +9 -3
  57. package/bin/templates/cli.js +7 -5
  58. package/bin/utils/add-module-to-container.d.ts +14 -3
  59. package/bin/utils/add-module-to-container.js +330 -111
  60. package/bin/utils/cli-ui.d.ts +20 -1
  61. package/bin/utils/cli-ui.js +41 -3
  62. package/bin/utils/update-tsconfig-paths.js +73 -33
  63. package/package.json +22 -13
@@ -1,12 +1,12 @@
1
- import { provide, OnEvent, IEventHandler } from "@expressots/core";
2
- import { {{{eventName}}} } from "{{{eventPath}}}";
3
-
4
- @provide({{className}}Handler)
5
- @OnEvent({{eventName}}, { priority: {{priority}} })
6
- export class {{className}}Handler implements IEventHandler<{{eventName}}> {
7
- async handle(event: {{eventName}}): Promise<void> {
8
- // TODO: Implement handler logic
9
- console.log(`Handling ${event.constructor.name}`);
10
- }
11
- }
12
-
1
+ import { provide, OnEvent, IEventHandler } from "@expressots/core";
2
+ import { {{{eventName}}} } from "{{{eventPath}}}";
3
+
4
+ @provide({{className}}Handler)
5
+ @OnEvent({{eventName}}, { priority: {{priority}} })
6
+ export class {{className}}Handler implements IEventHandler<{{eventName}}> {
7
+ async handle(event: {{eventName}}): Promise<void> {
8
+ // TODO: Implement handler logic
9
+ console.log(`Handling ${event.constructor.name}`);
10
+ }
11
+ }
12
+
@@ -1,27 +1,27 @@
1
- import {
2
- IInterceptor,
3
- ExecutionContext,
4
- CallHandler,
5
- Interceptor,
6
- provide,
7
- } from "@expressots/core";
8
-
9
- @Interceptor({ priority: {{priority}} })
10
- @provide({{className}}Interceptor)
11
- export class {{className}}Interceptor implements IInterceptor {
12
- async intercept(context: ExecutionContext, next: CallHandler) {
13
- const request = context.getRequest();
14
-
15
- // Pre-processing
16
- const startTime = Date.now();
17
-
18
- const result = await next.handle();
19
-
20
- // Post-processing
21
- const duration = Date.now() - startTime;
22
- console.log(`[{{className}}] ${request.method} ${request.path} - ${duration}ms`);
23
-
24
- return result;
25
- }
26
- }
27
-
1
+ import {
2
+ IInterceptor,
3
+ ExecutionContext,
4
+ CallHandler,
5
+ Interceptor,
6
+ provide,
7
+ } from "@expressots/core";
8
+
9
+ @Interceptor({ priority: {{priority}} })
10
+ @provide({{className}}Interceptor)
11
+ export class {{className}}Interceptor implements IInterceptor {
12
+ async intercept(context: ExecutionContext, next: CallHandler) {
13
+ const request = context.getRequest();
14
+
15
+ // Pre-processing
16
+ const startTime = Date.now();
17
+
18
+ const result = await next.handle();
19
+
20
+ // Post-processing
21
+ const duration = Date.now() - startTime;
22
+ console.log(`[{{className}}] ${request.method} ${request.path} - ${duration}ms`);
23
+
24
+ return result;
25
+ }
26
+ }
27
+
@@ -1,47 +1,47 @@
1
- import { defineConfig, Env, loadEnvSync } from "@expressots/core";
2
-
3
- /**
4
- * {{className}} Configuration
5
- *
6
- * Type-safe configuration with full TypeScript inference.
7
- * Features:
8
- * - Multi-environment defaults
9
- * - Secret management with auto-redaction
10
- * - Validation with helpful errors
11
- */
12
-
13
- // Load environment files before config resolution
14
- const envFiles = {
15
- development: ".env.local",
16
- production: ".env.prod",
17
- };
18
-
19
- loadEnvSync({ files: envFiles });
20
-
21
- export const {{moduleName}}Config = defineConfig({
22
- // Add your configuration schema here
23
- enabled: Env.boolean("{{envPrefix}}_ENABLED", {
24
- default: true,
25
- description: "Enable/disable {{className}} feature",
26
- }),
27
- // Example settings - customize as needed
28
- setting1: Env.string("{{envPrefix}}_SETTING1", {
29
- default: "default-value",
30
- }),
31
- setting2: Env.number("{{envPrefix}}_SETTING2", {
32
- default: 100,
33
- min: 0,
34
- max: 1000,
35
- }),
36
- bootstrap: {
37
- envFileConfig: {
38
- autoCreateTemplate: true,
39
- files: envFiles,
40
- },
41
- },
42
- });
43
-
44
- // Export typed config values
45
- export const config = {{moduleName}}Config.values;
46
- export type {{className}}Config = typeof config;
47
-
1
+ import { defineConfig, Env, loadEnvSync } from "@expressots/core";
2
+
3
+ /**
4
+ * {{className}} Configuration
5
+ *
6
+ * Type-safe configuration with full TypeScript inference.
7
+ * Features:
8
+ * - Multi-environment defaults
9
+ * - Secret management with auto-redaction
10
+ * - Validation with helpful errors
11
+ */
12
+
13
+ // Load environment files before config resolution
14
+ const envFiles = {
15
+ development: ".env.local",
16
+ production: ".env.prod",
17
+ };
18
+
19
+ loadEnvSync({ files: envFiles });
20
+
21
+ export const {{moduleName}}Config = defineConfig({
22
+ // Add your configuration schema here
23
+ enabled: Env.boolean("{{envPrefix}}_ENABLED", {
24
+ default: true,
25
+ description: "Enable/disable {{className}} feature",
26
+ }),
27
+ // Example settings - customize as needed
28
+ setting1: Env.string("{{envPrefix}}_SETTING1", {
29
+ default: "default-value",
30
+ }),
31
+ setting2: Env.number("{{envPrefix}}_SETTING2", {
32
+ default: 100,
33
+ min: 0,
34
+ max: 1000,
35
+ }),
36
+ bootstrap: {
37
+ envFileConfig: {
38
+ autoCreateTemplate: true,
39
+ files: envFiles,
40
+ },
41
+ },
42
+ });
43
+
44
+ // Export typed config values
45
+ export const config = {{moduleName}}Config.values;
46
+ export type {{className}}Config = typeof config;
47
+
@@ -1,15 +1,15 @@
1
- /**
2
- * {{className}} Event
3
- *
4
- * Type-safe event - no strings!
5
- *
6
- * Usage:
7
- * await this.eventEmitter.emit(new {{className}}Event(data));
8
- */
9
- export class {{className}}Event {
10
- constructor(
11
- public readonly data: Record<string, unknown>,
12
- public readonly timestamp: Date = new Date(),
13
- ) {}
14
- }
15
-
1
+ /**
2
+ * {{className}} Event
3
+ *
4
+ * Type-safe event - no strings!
5
+ *
6
+ * Usage:
7
+ * await this.eventEmitter.emit(new {{className}}Event(data));
8
+ */
9
+ export class {{className}}Event {
10
+ constructor(
11
+ public readonly data: Record<string, unknown>,
12
+ public readonly timestamp: Date = new Date(),
13
+ ) {}
14
+ }
15
+
@@ -1,41 +1,41 @@
1
- import { provide } from "@expressots/core";
2
- import { Request, Response, NextFunction } from "express";
3
-
4
- /**
5
- * {{className}} Guard
6
- *
7
- * Usage:
8
- * @controller("/protected")
9
- * @Use({{className}}Guard)
10
- * export class ProtectedController { }
11
- */
12
- @provide({{className}}Guard)
13
- export class {{className}}Guard {
14
- /**
15
- * Check if the request is authorized
16
- */
17
- canActivate(req: Request, res: Response, next: NextFunction): void {
18
- // TODO: Implement authorization logic
19
- const isAuthorized = this.checkAuthorization(req);
20
-
21
- if (!isAuthorized) {
22
- res.status(401).json({ message: "Unauthorized" });
23
- return;
24
- }
25
-
26
- next();
27
- }
28
-
29
- private checkAuthorization(req: Request): boolean {
30
- // Example: Check for authorization header
31
- const authHeader = req.headers.authorization;
32
-
33
- if (!authHeader) {
34
- return false;
35
- }
36
-
37
- // TODO: Validate token/credentials
38
- return true;
39
- }
40
- }
41
-
1
+ import { provide } from "@expressots/core";
2
+ import { Request, Response, NextFunction } from "express";
3
+
4
+ /**
5
+ * {{className}} Guard
6
+ *
7
+ * Usage:
8
+ * @controller("/protected")
9
+ * @Use({{className}}Guard)
10
+ * export class ProtectedController { }
11
+ */
12
+ @provide({{className}}Guard)
13
+ export class {{className}}Guard {
14
+ /**
15
+ * Check if the request is authorized
16
+ */
17
+ canActivate(req: Request, res: Response, next: NextFunction): void {
18
+ // TODO: Implement authorization logic
19
+ const isAuthorized = this.checkAuthorization(req);
20
+
21
+ if (!isAuthorized) {
22
+ res.status(401).json({ message: "Unauthorized" });
23
+ return;
24
+ }
25
+
26
+ next();
27
+ }
28
+
29
+ private checkAuthorization(req: Request): boolean {
30
+ // Example: Check for authorization header
31
+ const authHeader = req.headers.authorization;
32
+
33
+ if (!authHeader) {
34
+ return false;
35
+ }
36
+
37
+ // TODO: Validate token/credentials
38
+ return true;
39
+ }
40
+ }
41
+
@@ -1,23 +1,23 @@
1
- import { provide, OnEvent, IEventHandler } from "@expressots/core";
2
- import { {{{eventName}}} } from "{{{eventPath}}}";
3
-
4
- /**
5
- * Handler for {{eventName}}
6
- *
7
- * Features:
8
- * - Auto-discovered
9
- * - Priority: {{priority}}
10
- * - Full type safety
11
- */
12
- @provide({{className}}Handler)
13
- @OnEvent({{eventName}}, { priority: {{priority}} })
14
- export class {{className}}Handler implements IEventHandler<{{eventName}}> {
15
- async handle(event: {{eventName}}): Promise<void> {
16
- console.log(`Handling ${event.constructor.name}`, {
17
- timestamp: event.timestamp,
18
- });
19
-
20
- // TODO: Implement handler logic
21
- }
22
- }
23
-
1
+ import { provide, OnEvent, IEventHandler } from "@expressots/core";
2
+ import { {{{eventName}}} } from "{{{eventPath}}}";
3
+
4
+ /**
5
+ * Handler for {{eventName}}
6
+ *
7
+ * Features:
8
+ * - Auto-discovered
9
+ * - Priority: {{priority}}
10
+ * - Full type safety
11
+ */
12
+ @provide({{className}}Handler)
13
+ @OnEvent({{eventName}}, { priority: {{priority}} })
14
+ export class {{className}}Handler implements IEventHandler<{{eventName}}> {
15
+ async handle(event: {{eventName}}): Promise<void> {
16
+ console.log(`Handling ${event.constructor.name}`, {
17
+ timestamp: event.timestamp,
18
+ });
19
+
20
+ // TODO: Implement handler logic
21
+ }
22
+ }
23
+
@@ -1,50 +1,50 @@
1
- import {
2
- IInterceptor,
3
- ExecutionContext,
4
- CallHandler,
5
- Interceptor,
6
- provide,
7
- } from "@expressots/core";
8
-
9
- /**
10
- * {{className}} Interceptor
11
- *
12
- * Priority: {{priority}} (lower = earlier execution)
13
- *
14
- * Usage:
15
- * @UseInterceptors({{className}}Interceptor)
16
- * @controller("/route")
17
- * export class MyController { }
18
- */
19
- @Interceptor({ priority: {{priority}} })
20
- @provide({{className}}Interceptor)
21
- export class {{className}}Interceptor implements IInterceptor {
22
- async intercept(context: ExecutionContext, next: CallHandler) {
23
- const request = context.getRequest();
24
-
25
- // Pre-processing logic
26
- console.log(`[{{className}}] Before handler`, {
27
- method: request.method,
28
- path: request.path,
29
- });
30
-
31
- const startTime = Date.now();
32
-
33
- try {
34
- // Execute next interceptor or handler
35
- const result = await next.handle();
36
-
37
- // Post-processing logic
38
- const duration = Date.now() - startTime;
39
- console.log(`[{{className}}] After handler`, {
40
- duration: `${duration}ms`,
41
- });
42
-
43
- return result;
44
- } catch (error) {
45
- console.error(`[{{className}}] Handler error`, error);
46
- throw error;
47
- }
48
- }
49
- }
50
-
1
+ import {
2
+ IInterceptor,
3
+ ExecutionContext,
4
+ CallHandler,
5
+ Interceptor,
6
+ provide,
7
+ } from "@expressots/core";
8
+
9
+ /**
10
+ * {{className}} Interceptor
11
+ *
12
+ * Priority: {{priority}} (lower = earlier execution)
13
+ *
14
+ * Usage:
15
+ * @UseInterceptors({{className}}Interceptor)
16
+ * @controller("/route")
17
+ * export class MyController { }
18
+ */
19
+ @Interceptor({ priority: {{priority}} })
20
+ @provide({{className}}Interceptor)
21
+ export class {{className}}Interceptor implements IInterceptor {
22
+ async intercept(context: ExecutionContext, next: CallHandler) {
23
+ const request = context.getRequest();
24
+
25
+ // Pre-processing logic
26
+ console.log(`[{{className}}] Before handler`, {
27
+ method: request.method,
28
+ path: request.path,
29
+ });
30
+
31
+ const startTime = Date.now();
32
+
33
+ try {
34
+ // Execute next interceptor or handler
35
+ const result = await next.handle();
36
+
37
+ // Post-processing logic
38
+ const duration = Date.now() - startTime;
39
+ console.log(`[{{className}}] After handler`, {
40
+ duration: `${duration}ms`,
41
+ });
42
+
43
+ return result;
44
+ } catch (error) {
45
+ console.error(`[{{className}}] Handler error`, error);
46
+ throw error;
47
+ }
48
+ }
49
+ }
50
+
@@ -69,9 +69,10 @@ export declare function getFileNameWithoutExtension(filePath: string): string;
69
69
  * @param schematic
70
70
  * @returns the split target
71
71
  */
72
- export declare const splitTarget: ({ target, schematic, }: {
72
+ export declare const splitTarget: ({ target, schematic, opinionated, }: {
73
73
  target: string;
74
74
  schematic: string;
75
+ opinionated?: boolean;
75
76
  }) => Promise<{
76
77
  path: string;
77
78
  file: string;
@@ -120,7 +121,17 @@ export declare const getNameWithScaffoldPattern: (name: string) => Promise<strin
120
121
  */
121
122
  export declare function extractFirstWord(file: string): Promise<string>;
122
123
  /**
123
- * Check if the path is a nested path, a single path or a sugar path
124
+ * Determine the path style for a generate target.
125
+ *
126
+ * - `Nested`: contains an explicit separator (`billing/invoice`) → grouped
127
+ * under the parent folder.
128
+ * - `Sugar`: a single segment that normalizes to more than one word
129
+ * (`userCreate`, `user-create`, `user_create`, `UserCreate`) → grouped under
130
+ * its first word as a shared module (e.g. `UserModule`). camelCase and
131
+ * kebab-case forms of the same name therefore produce identical output.
132
+ * - `Single`: a true single-word target (`user`) → self-contained module in its
133
+ * own folder.
134
+ *
124
135
  * @param path
125
136
  * @returns the path style
126
137
  */
@@ -36,6 +36,7 @@ const verify_file_exists_1 = require("../../utils/verify-file-exists");
36
36
  const compiler_1 = __importDefault(require("../../utils/compiler"));
37
37
  const update_tsconfig_paths_1 = require("../../utils/update-tsconfig-paths");
38
38
  const input_validation_1 = require("../../utils/input-validation");
39
+ const shared_1 = require("@expressots/shared");
39
40
  /**
40
41
  * Reject generate targets that would resolve outside the project's
41
42
  * source root. We only inspect the user-supplied `rawTarget` for
@@ -79,6 +80,7 @@ async function validateAndPrepareFile(fp) {
79
80
  const { path, file, className, moduleName, modulePath } = await (0, exports.splitTarget)({
80
81
  target: fp.target,
81
82
  schematic: fp.schematic,
83
+ opinionated: true,
82
84
  });
83
85
  ensureWithinSourceRoot(folderToScaffold, `${path}/${file}`, fp.target);
84
86
  const outputPath = `${folderToScaffold}/${path}/${file}`;
@@ -142,7 +144,7 @@ exports.getFileNameWithoutExtension = getFileNameWithoutExtension;
142
144
  * @param schematic
143
145
  * @returns the split target
144
146
  */
145
- const splitTarget = async ({ target, schematic, }) => {
147
+ const splitTarget = async ({ target, schematic, opinionated = false, }) => {
146
148
  const pathContent = target
147
149
  .split("/")
148
150
  .filter((item) => item !== "");
@@ -213,6 +215,30 @@ const splitTarget = async ({ target, schematic, }) => {
213
215
  const firstWord = name
214
216
  .split(isCamelCase ? /(?=[A-Z])/ : kebabCaseRegex)[0]
215
217
  .toLowerCase();
218
+ // Opinionated "syntactic sugar": decompose a compound name into a
219
+ // nested feature/use-case layout so every use-case of a feature is
220
+ // grouped under one module at the feature root. For example
221
+ // `userLogin` -> `user/login/login.{controller,usecase,dto}.ts` with
222
+ // the shared module at `user/user.module.ts`. A later `userLogout`
223
+ // adds `user/logout/...` and joins the same `UserModule`.
224
+ //
225
+ // Only applies to grouped schematics in opinionated mode; standalone
226
+ // schematics and non-opinionated mode keep the flat kebab folder so
227
+ // the developer retains full control over structure.
228
+ if (opinionated && shouldCreateFolder) {
229
+ const words = folderName.split("-").filter(Boolean);
230
+ if (words.length > 1) {
231
+ const feature = words[0];
232
+ const useCase = words.slice(1).join("-");
233
+ return {
234
+ path: `${feature}/${useCase}`,
235
+ file: `${await (0, exports.getNameWithScaffoldPattern)(useCase)}.${schematic}.ts`,
236
+ className: (0, string_utils_1.anyCaseToPascalCase)(useCase),
237
+ moduleName: feature,
238
+ modulePath: feature,
239
+ };
240
+ }
241
+ }
216
242
  // For standalone schematics (entity, provider, middleware, etc.),
217
243
  // only create folder if explicit path is provided
218
244
  const computedPath = shouldCreateFolder
@@ -316,13 +342,13 @@ exports.schematicFolder = schematicFolder;
316
342
  const getNameWithScaffoldPattern = async (name) => {
317
343
  const configObject = await compiler_1.default.loadConfig();
318
344
  switch (configObject.scaffoldPattern) {
319
- case "lowercase" /* Pattern.LOWER_CASE */:
345
+ case shared_1.Pattern.LOWER_CASE:
320
346
  return (0, string_utils_1.anyCaseToLowerCase)(name);
321
- case "kebab-case" /* Pattern.KEBAB_CASE */:
347
+ case shared_1.Pattern.KEBAB_CASE:
322
348
  return (0, string_utils_1.anyCaseToKebabCase)(name);
323
- case "PascalCase" /* Pattern.PASCAL_CASE */:
349
+ case shared_1.Pattern.PASCAL_CASE:
324
350
  return (0, string_utils_1.anyCaseToPascalCase)(name);
325
- case "camelCase" /* Pattern.CAMEL_CASE */:
351
+ case shared_1.Pattern.CAMEL_CASE:
326
352
  return (0, string_utils_1.anyCaseToCamelCase)(name);
327
353
  }
328
354
  };
@@ -346,33 +372,40 @@ async function extractFirstWord(file) {
346
372
  const firstWord = f.split(regex)[0];
347
373
  const config = await compiler_1.default.loadConfig();
348
374
  switch (config.scaffoldPattern) {
349
- case "lowercase" /* Pattern.LOWER_CASE */:
375
+ case shared_1.Pattern.LOWER_CASE:
350
376
  return (0, string_utils_1.anyCaseToLowerCase)(firstWord);
351
- case "kebab-case" /* Pattern.KEBAB_CASE */:
377
+ case shared_1.Pattern.KEBAB_CASE:
352
378
  return (0, string_utils_1.anyCaseToKebabCase)(firstWord);
353
- case "PascalCase" /* Pattern.PASCAL_CASE */:
379
+ case shared_1.Pattern.PASCAL_CASE:
354
380
  return (0, string_utils_1.anyCaseToPascalCase)(firstWord);
355
- case "camelCase" /* Pattern.CAMEL_CASE */:
381
+ case shared_1.Pattern.CAMEL_CASE:
356
382
  return (0, string_utils_1.anyCaseToCamelCase)(firstWord);
357
383
  }
358
384
  }
359
385
  exports.extractFirstWord = extractFirstWord;
360
386
  /**
361
- * Check if the path is a nested path, a single path or a sugar path
387
+ * Determine the path style for a generate target.
388
+ *
389
+ * - `Nested`: contains an explicit separator (`billing/invoice`) → grouped
390
+ * under the parent folder.
391
+ * - `Sugar`: a single segment that normalizes to more than one word
392
+ * (`userCreate`, `user-create`, `user_create`, `UserCreate`) → grouped under
393
+ * its first word as a shared module (e.g. `UserModule`). camelCase and
394
+ * kebab-case forms of the same name therefore produce identical output.
395
+ * - `Single`: a true single-word target (`user`) → self-contained module in its
396
+ * own folder.
397
+ *
362
398
  * @param path
363
399
  * @returns the path style
364
400
  */
365
401
  const checkPathStyle = (path) => {
366
- const singleOrNestedPathRegex = /\/|\\/;
367
- const sugarPathRegex = /^\w+-\w+$/;
368
- if (singleOrNestedPathRegex.test(path)) {
402
+ const nestedPathRegex = /\/|\\/;
403
+ if (nestedPathRegex.test(path)) {
369
404
  return "nested" /* PathStyle.Nested */;
370
405
  }
371
- else if (sugarPathRegex.test(path)) {
406
+ if ((0, string_utils_1.anyCaseToKebabCase)(path).includes("-")) {
372
407
  return "sugar" /* PathStyle.Sugar */;
373
408
  }
374
- else {
375
- return "single" /* PathStyle.Single */;
376
- }
409
+ return "single" /* PathStyle.Single */;
377
410
  };
378
411
  exports.checkPathStyle = checkPathStyle;