@ngflow/ng-architect 1.2.6 → 1.2.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngflow/ng-architect",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Angular schematics to generate files and maintain clean architecture",
5
5
  "scripts": {
6
6
  "build": "tsc -p tsconfig.json",
@@ -17,7 +17,8 @@
17
17
  "schematics": "./src/collection.json",
18
18
  "dependencies": {
19
19
  "@angular-devkit/core": "^21.0.4",
20
- "@angular-devkit/schematics": "^21.0.4"
20
+ "@angular-devkit/schematics": "^21.0.4",
21
+ "inquirer": "13.1.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@types/jasmine": "5.1.13",
@@ -30,9 +30,10 @@
30
30
  "factory": "./ng-architect/service/index",
31
31
  "schema": "./ng-architect/service/schema.json"
32
32
  },
33
- "my-extend-schematic": {
34
- "description": "A schematic that extends another schematic.",
35
- "extends": "my-full-schematic"
36
- }
33
+ "structure-migrate": {
34
+ "description": "Analyzes and migrates existing feature structures",
35
+ "factory": "./ng-architect/structure-migrate/index",
36
+ "schema": "./ng-architect/structure-migrate/schema.json"
37
+ }
37
38
  }
38
39
  }
@@ -8,92 +8,77 @@ const structure_resolver_1 = require("../utils/structure-resolver");
8
8
  const options_validator_1 = require("../utils/options-validator");
9
9
  function default_1(options) {
10
10
  return (tree, context) => {
11
- // 🔹 Resolver a structure antes de qualquer lógica
11
+ const featureKebab = (0, string_utils_1.toKebabCase)(options.name);
12
+ const featurePascal = (0, string_utils_1.toPascalCase)(options.name);
13
+ // 🔹 Resolve auto-structure
12
14
  const resolvedStructure = (0, structure_resolver_1.resolveStructure)(tree, options);
13
15
  options.structure = resolvedStructure;
16
+ context.logger.info(`🟢 Creating feature: ${featurePascal} (${featureKebab})`);
14
17
  context.logger.info(`📐 Structure resolved to: ${resolvedStructure}`);
15
- // 🔹 Validar flags e opções obrigatórias
18
+ // 🔹 Validate options according to resolved structure
16
19
  (0, options_validator_1.validateOptions)(options, resolvedStructure);
17
- // 🔹 Continuar com a geração real da feature
18
- return featureRule(options)(tree, context);
19
- };
20
- }
21
- ;
22
- // ------------------------------------------------------
23
- // Função interna que gera a feature de forma limpa
24
- function featureRule(options) {
25
- const featureKebab = (0, string_utils_1.toKebabCase)(options.name);
26
- const featurePascal = (0, string_utils_1.toPascalCase)(options.name);
27
- let basePath;
28
- let subFolders = [];
29
- switch (options.structure) {
30
- case 'flat':
31
- basePath = `src/app/${featureKebab}`;
32
- break;
33
- case 'domain-driven':
34
- basePath = `src/app/features/${featureKebab}`;
35
- subFolders = ['pages', 'data', 'models'];
36
- break;
37
- case 'module-driven':
38
- if (!options.module)
39
- throw new Error('module-driven requires --module=<moduleName>');
40
- basePath = `src/app/${options.module}/${featureKebab}`;
41
- subFolders = ['pages', 'data', 'models'];
42
- break;
43
- case 'monorepo':
44
- if (!options.app && !options.lib)
45
- throw new Error('monorepo requires --app=<appName> or --lib=<libName>');
46
- basePath = options.app
47
- ? `apps/${options.app}/src/app/${featureKebab}`
48
- : `libs/${options.lib}/src/lib/${featureKebab}`;
49
- subFolders = ['pages', 'data', 'models'];
50
- break;
51
- }
52
- return (0, schematics_1.chain)([
53
- (_tree, context) => {
54
- context.logger.info(`🟢 Creating feature: ${featurePascal} (${featureKebab})`);
55
- context.logger.info(`Structure: ${options.structure}`);
56
- },
57
- // 🔹 Gerar ficheiros a partir do template
58
- (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
59
- (0, schematics_1.template)({ featureKebab, featurePascal }),
60
- (0, schematics_1.move)(basePath),
61
- ])),
62
- // 🔹 Atualizar routes.ts (standalone + lazy loading quando necessário)
63
- (_tree, context) => {
64
- context.logger.info(`🟢 Updating app.routes.ts with ${featurePascal}Page...`);
65
- (0, routes_utils_1.updateAppRoutes)(_tree, featureKebab, featurePascal, options.structure, options);
66
- },
67
- // 🔹 Criar subpastas e barrels (index.ts)
68
- (_tree) => {
20
+ // 🔹 Determine base path and subfolders
21
+ let basePath;
22
+ let subFolders = [];
23
+ switch (resolvedStructure) {
24
+ case 'flat':
25
+ basePath = `src/app/${featureKebab}`;
26
+ break;
27
+ case 'domain-driven':
28
+ basePath = `src/app/features/${featureKebab}`;
29
+ subFolders = ['pages', 'data', 'models'];
30
+ break;
31
+ case 'module-driven':
32
+ if (!options.module)
33
+ throw new Error('module-driven requires --module=<moduleName>');
34
+ basePath = `src/app/${options.module}/${featureKebab}`;
35
+ subFolders = ['pages', 'data', 'models'];
36
+ break;
37
+ case 'monorepo':
38
+ if (!options.app && !options.lib)
39
+ throw new Error('monorepo requires --app=<appName> or --lib=<libName>');
40
+ basePath = options.app
41
+ ? `apps/${options.app}/src/app/${featureKebab}`
42
+ : `libs/${options.lib}/src/lib/${featureKebab}`;
43
+ subFolders = ['pages', 'data', 'models'];
44
+ break;
45
+ default:
46
+ throw new Error(`Invalid structure: ${resolvedStructure}`);
47
+ }
48
+ // 🔹 Apply templates and move to proper folder
49
+ const templateRule = (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [(0, schematics_1.template)({ featureKebab, featurePascal }), (0, schematics_1.move)(basePath)]));
50
+ // 🔹 Update app routes
51
+ const routesRule = (t, ctx) => {
52
+ ctx.logger.info(`🟢 Updating routes with ${featurePascal}Page...`);
53
+ (0, routes_utils_1.updateAppRoutes)(t, featureKebab, featurePascal, resolvedStructure, options);
54
+ return t;
55
+ };
56
+ // 🔹 Create subfolders and barrels
57
+ const subFoldersRule = (t) => {
69
58
  subFolders.forEach((folder) => {
70
59
  const folderPath = `${basePath}/${folder}`;
71
- if (!_tree.exists(folderPath))
72
- _tree.create(folderPath + '/.gitkeep', '');
73
- // Criar barrel index.ts automaticamente
74
- const files = _tree.getDir(folderPath).subfiles.filter((f) => f.endsWith('.ts'));
60
+ if (!t.exists(folderPath))
61
+ t.create(folderPath + '/.gitkeep', '');
62
+ const files = t.getDir(folderPath).subfiles.filter((f) => f.endsWith('.ts'));
75
63
  const barrelContent = files
76
- .map((f) => {
77
- const name = f.replace('.ts', '');
78
- return `export * from './${name}';`;
79
- })
64
+ .map((f) => `export * from './${f.replace('.ts', '')}';`)
80
65
  .join('\n');
81
- if (!_tree.exists(`${folderPath}/index.ts`)) {
82
- _tree.create(`${folderPath}/index.ts`, barrelContent);
66
+ if (!t.exists(`${folderPath}/index.ts`)) {
67
+ t.create(`${folderPath}/index.ts`, barrelContent);
83
68
  }
84
69
  else {
85
- // Adiciona novos exports sem duplicar
86
- const existing = _tree.read(`${folderPath}/index.ts`).toString('utf-8');
70
+ const existing = t.read(`${folderPath}/index.ts`).toString('utf-8');
87
71
  const newExports = files
88
72
  .map((f) => `export * from './${f.replace('.ts', '')}';`)
89
73
  .filter((line) => !existing.includes(line))
90
74
  .join('\n');
91
75
  if (newExports)
92
- _tree.overwrite(`${folderPath}/index.ts`, existing + '\n' + newExports);
76
+ t.overwrite(`${folderPath}/index.ts`, existing + '\n' + newExports);
93
77
  }
94
78
  });
95
- return _tree;
96
- },
97
- ]);
79
+ return t;
80
+ };
81
+ return (0, schematics_1.chain)([templateRule, routesRule, subFoldersRule]);
82
+ };
98
83
  }
99
84
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
@@ -11,109 +11,95 @@ import {
11
11
  } from '@angular-devkit/schematics';
12
12
  import { toKebabCase, toPascalCase } from '../utils/string-utils';
13
13
  import { updateAppRoutes } from '../utils/routes-utils';
14
- import { resolveStructure } from '../utils/structure-resolver';
14
+ import { resolveStructure, Structure } from '../utils/structure-resolver';
15
15
  import { validateOptions } from '../utils/options-validator';
16
16
 
17
17
  export default function (options: any): Rule {
18
18
  return (tree: Tree, context: SchematicContext) => {
19
- // 🔹 Resolver a structure antes de qualquer lógica
20
- const resolvedStructure = resolveStructure(tree, options);
19
+ const featureKebab = toKebabCase(options.name);
20
+ const featurePascal = toPascalCase(options.name);
21
+
22
+ // 🔹 Resolve auto-structure
23
+ const resolvedStructure: Structure = resolveStructure(tree, options);
21
24
  options.structure = resolvedStructure;
22
25
 
26
+ context.logger.info(`🟢 Creating feature: ${featurePascal} (${featureKebab})`);
23
27
  context.logger.info(`📐 Structure resolved to: ${resolvedStructure}`);
24
28
 
25
- // 🔹 Validar flags e opções obrigatórias
29
+ // 🔹 Validate options according to resolved structure
26
30
  validateOptions(options, resolvedStructure);
27
31
 
28
- // 🔹 Continuar com a geração real da feature
29
- return featureRule(options)(tree, context);
30
- };
31
- };
32
-
33
- // ------------------------------------------------------
34
- // Função interna que gera a feature de forma limpa
35
- function featureRule(options: any): Rule {
36
- const featureKebab = toKebabCase(options.name);
37
- const featurePascal = toPascalCase(options.name);
38
-
39
- let basePath!: string;
40
- let subFolders: string[] = [];
41
-
42
- switch (options.structure) {
43
- case 'flat':
44
- basePath = `src/app/${featureKebab}`;
45
- break;
46
-
47
- case 'domain-driven':
48
- basePath = `src/app/features/${featureKebab}`;
49
- subFolders = ['pages', 'data', 'models'];
50
- break;
51
-
52
- case 'module-driven':
53
- if (!options.module) throw new Error('module-driven requires --module=<moduleName>');
54
- basePath = `src/app/${options.module}/${featureKebab}`;
55
- subFolders = ['pages', 'data', 'models'];
56
- break;
57
-
58
- case 'monorepo':
59
- if (!options.app && !options.lib)
60
- throw new Error('monorepo requires --app=<appName> or --lib=<libName>');
61
- basePath = options.app
62
- ? `apps/${options.app}/src/app/${featureKebab}`
63
- : `libs/${options.lib}/src/lib/${featureKebab}`;
64
- subFolders = ['pages', 'data', 'models'];
65
- break;
66
- }
67
-
68
- return chain([
69
- (_tree: Tree, context: SchematicContext) => {
70
- context.logger.info(`🟢 Creating feature: ${featurePascal} (${featureKebab})`);
71
- context.logger.info(`Structure: ${options.structure}`);
72
- },
73
-
74
- // 🔹 Gerar ficheiros a partir do template
75
- mergeWith(
76
- apply(url('./files'), [
77
- template({ featureKebab, featurePascal }),
78
- move(basePath),
79
- ])
80
- ),
81
-
82
- // 🔹 Atualizar routes.ts (standalone + lazy loading quando necessário)
83
- (_tree: Tree, context: SchematicContext) => {
84
- context.logger.info(`🟢 Updating app.routes.ts with ${featurePascal}Page...`);
85
- updateAppRoutes(_tree, featureKebab, featurePascal, options.structure, options);
86
- },
87
-
88
- // 🔹 Criar subpastas e barrels (index.ts)
89
- (_tree: Tree) => {
32
+ // 🔹 Determine base path and subfolders
33
+ let basePath: string;
34
+ let subFolders: string[] = [];
35
+
36
+ switch (resolvedStructure) {
37
+ case 'flat':
38
+ basePath = `src/app/${featureKebab}`;
39
+ break;
40
+
41
+ case 'domain-driven':
42
+ basePath = `src/app/features/${featureKebab}`;
43
+ subFolders = ['pages', 'data', 'models'];
44
+ break;
45
+
46
+ case 'module-driven':
47
+ if (!options.module)
48
+ throw new Error('module-driven requires --module=<moduleName>');
49
+ basePath = `src/app/${options.module}/${featureKebab}`;
50
+ subFolders = ['pages', 'data', 'models'];
51
+ break;
52
+
53
+ case 'monorepo':
54
+ if (!options.app && !options.lib)
55
+ throw new Error('monorepo requires --app=<appName> or --lib=<libName>');
56
+ basePath = options.app
57
+ ? `apps/${options.app}/src/app/${featureKebab}`
58
+ : `libs/${options.lib}/src/lib/${featureKebab}`;
59
+ subFolders = ['pages', 'data', 'models'];
60
+ break;
61
+
62
+ default:
63
+ throw new Error(`Invalid structure: ${resolvedStructure}`);
64
+ }
65
+
66
+ // 🔹 Apply templates and move to proper folder
67
+ const templateRule = mergeWith(
68
+ apply(url('./files'), [template({ featureKebab, featurePascal }), move(basePath)])
69
+ );
70
+
71
+ // 🔹 Update app routes
72
+ const routesRule = (t: Tree, ctx: SchematicContext) => {
73
+ ctx.logger.info(`🟢 Updating routes with ${featurePascal}Page...`);
74
+ updateAppRoutes(t, featureKebab, featurePascal, resolvedStructure, options);
75
+ return t;
76
+ };
77
+
78
+ // 🔹 Create subfolders and barrels
79
+ const subFoldersRule = (t: Tree) => {
90
80
  subFolders.forEach((folder) => {
91
81
  const folderPath = `${basePath}/${folder}`;
92
- if (!_tree.exists(folderPath)) _tree.create(folderPath + '/.gitkeep', '');
82
+ if (!t.exists(folderPath)) t.create(folderPath + '/.gitkeep', '');
93
83
 
94
- // Criar barrel index.ts automaticamente
95
- const files = _tree.getDir(folderPath).subfiles.filter((f) => f.endsWith('.ts'));
84
+ const files = t.getDir(folderPath).subfiles.filter((f) => f.endsWith('.ts'));
96
85
  const barrelContent = files
97
- .map((f) => {
98
- const name = f.replace('.ts', '');
99
- return `export * from './${name}';`;
100
- })
86
+ .map((f) => `export * from './${f.replace('.ts', '')}';`)
101
87
  .join('\n');
102
88
 
103
- if (!_tree.exists(`${folderPath}/index.ts`)) {
104
- _tree.create(`${folderPath}/index.ts`, barrelContent);
89
+ if (!t.exists(`${folderPath}/index.ts`)) {
90
+ t.create(`${folderPath}/index.ts`, barrelContent);
105
91
  } else {
106
- // Adiciona novos exports sem duplicar
107
- const existing = _tree.read(`${folderPath}/index.ts`)!.toString('utf-8');
92
+ const existing = t.read(`${folderPath}/index.ts`)!.toString('utf-8');
108
93
  const newExports = files
109
94
  .map((f) => `export * from './${f.replace('.ts', '')}';`)
110
95
  .filter((line) => !existing.includes(line))
111
96
  .join('\n');
112
- if (newExports) _tree.overwrite(`${folderPath}/index.ts`, existing + '\n' + newExports);
97
+ if (newExports) t.overwrite(`${folderPath}/index.ts`, existing + '\n' + newExports);
113
98
  }
114
99
  });
100
+ return t;
101
+ };
115
102
 
116
- return _tree;
117
- },
118
- ]);
103
+ return chain([templateRule, routesRule, subFoldersRule]);
104
+ };
119
105
  }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const structure_resolver_1 = require("../utils/structure-resolver");
5
+ const path_resolver_1 = require("../utils/path-resolver");
6
+ const options_validator_1 = require("../utils/options-validator");
7
+ const string_utils_1 = require("../utils/string-utils");
8
+ function default_1(options) {
9
+ return (tree, context) => {
10
+ if (!options.name)
11
+ throw new Error('Feature name is required');
12
+ const featureKebab = (0, string_utils_1.toKebabCase)(options.name);
13
+ // Detectar estrutura atual
14
+ const currentStructure = (0, structure_resolver_1.resolveStructure)(tree, options);
15
+ context.logger.info(`📌 Current structure detected: ${currentStructure}`);
16
+ // Validação de flags
17
+ (0, options_validator_1.validateOptions)(options, currentStructure);
18
+ if (!options.to)
19
+ throw new Error('Target structure is required (--to=<structure>)');
20
+ context.logger.info(`🚀 Migrating feature "${featureKebab}" from "${currentStructure}" to "${options.to}"...`);
21
+ (0, path_resolver_1.moveFeatureTree)(tree, featureKebab, currentStructure, options.to, options);
22
+ context.logger.info(`✅ Migration complete! Feature is now in "${options.to}" structure.`);
23
+ return tree;
24
+ };
25
+ }
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAMA,4BAsBC;AA3BD,oEAA+D;AAC/D,0DAAyD;AACzD,kEAA6D;AAC7D,wDAAoD;AAEpD,mBAAyB,OAAY;IACnC,OAAO,CAAC,IAAU,EAAE,OAAyB,EAAE,EAAE;QAC/C,IAAI,CAAC,OAAO,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC/D,MAAM,YAAY,GAAG,IAAA,0BAAW,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE/C,2BAA2B;QAC3B,MAAM,gBAAgB,GAAG,IAAA,qCAAgB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,kCAAkC,gBAAgB,EAAE,CAAC,CAAC;QAE1E,qBAAqB;QACrB,IAAA,mCAAe,EAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAE3C,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QAEpF,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,YAAY,WAAW,gBAAgB,SAAS,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;QAE/G,IAAA,+BAAe,EAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAE3E,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,4CAA4C,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC;QAE1F,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ import { resolveStructure } from '../utils/structure-resolver';
3
+ import { moveFeatureTree } from '../utils/path-resolver';
4
+ import { validateOptions } from '../utils/options-validator';
5
+ import { toKebabCase } from '../utils/string-utils';
6
+
7
+ export default function (options: any): Rule {
8
+ return (tree: Tree, context: SchematicContext) => {
9
+ if (!options.name) throw new Error('Feature name is required');
10
+ const featureKebab = toKebabCase(options.name);
11
+
12
+ // Detectar estrutura atual
13
+ const currentStructure = resolveStructure(tree, options);
14
+ context.logger.info(`📌 Current structure detected: ${currentStructure}`);
15
+
16
+ // Validação de flags
17
+ validateOptions(options, currentStructure);
18
+
19
+ if (!options.to) throw new Error('Target structure is required (--to=<structure>)');
20
+
21
+ context.logger.info(`🚀 Migrating feature "${featureKebab}" from "${currentStructure}" to "${options.to}"...`);
22
+
23
+ moveFeatureTree(tree, featureKebab, currentStructure, options.to, options);
24
+
25
+ context.logger.info(`✅ Migration complete! Feature is now in "${options.to}" structure.`);
26
+
27
+ return tree;
28
+ };
29
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.promptConfirm = promptConfirm;
13
+ exports.promptSelect = promptSelect;
14
+ exports.promptInput = promptInput;
15
+ const inquirer_1 = require("inquirer");
16
+ /**
17
+ * Pergunta de confirmação (sim/não) ao usuário
18
+ * @param message Mensagem a ser exibida
19
+ * @returns true se usuário confirmar, false se negar
20
+ */
21
+ function promptConfirm(message) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const answers = yield inquirer_1.default.prompt([
24
+ {
25
+ type: 'confirm',
26
+ name: 'ok',
27
+ message,
28
+ default: true,
29
+ },
30
+ ]);
31
+ return answers.ok;
32
+ });
33
+ }
34
+ /**
35
+ * Pergunta para o usuário escolher entre várias opções
36
+ * @param message Mensagem da pergunta
37
+ * @param choices Lista de opções
38
+ * @param defaultChoice Opção default
39
+ */
40
+ function promptSelect(message, choices, defaultChoice) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ const answers = yield inquirer_1.default.prompt([
43
+ {
44
+ type: 'list',
45
+ name: 'selected',
46
+ message,
47
+ choices,
48
+ default: defaultChoice,
49
+ },
50
+ ]);
51
+ return answers.selected;
52
+ });
53
+ }
54
+ /**
55
+ * Pergunta de input de texto
56
+ * @param message Mensagem da pergunta
57
+ * @param defaultValue Valor default
58
+ */
59
+ function promptInput(message, defaultValue) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ const answers = yield inquirer_1.default.prompt([
62
+ {
63
+ type: 'input',
64
+ name: 'value',
65
+ message,
66
+ default: defaultValue,
67
+ },
68
+ ]);
69
+ return answers.value;
70
+ });
71
+ }
72
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["prompt.ts"],"names":[],"mappings":";;;;;;;;;;;AAMA,sCAWC;AAQD,oCAgBC;AAOD,kCAWC;AA3DD,uCAAgC;AAChC;;;;GAIG;AACH,SAAsB,aAAa,CAAC,OAAe;;QACjD,MAAM,OAAO,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;YACpC;gBACE,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,IAAI;gBACV,OAAO;gBACP,OAAO,EAAE,IAAI;aACd;SACF,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,EAAE,CAAC;IACpB,CAAC;CAAA;AAED;;;;;GAKG;AACH,SAAsB,YAAY,CAChC,OAAe,EACf,OAAiB,EACjB,aAAsB;;QAEtB,MAAM,OAAO,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;YACpC;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,UAAU;gBAChB,OAAO;gBACP,OAAO;gBACP,OAAO,EAAE,aAAa;aACvB;SACF,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,QAAQ,CAAC;IAC1B,CAAC;CAAA;AAED;;;;GAIG;AACH,SAAsB,WAAW,CAAC,OAAe,EAAE,YAAqB;;QACtE,MAAM,OAAO,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC;YACpC;gBACE,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,OAAO;gBACb,OAAO;gBACP,OAAO,EAAE,YAAY;aACtB;SACF,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,KAAK,CAAC;IACvB,CAAC;CAAA"}
@@ -0,0 +1,60 @@
1
+ import inquirer from 'inquirer';
2
+ /**
3
+ * Pergunta de confirmação (sim/não) ao usuário
4
+ * @param message Mensagem a ser exibida
5
+ * @returns true se usuário confirmar, false se negar
6
+ */
7
+ export async function promptConfirm(message: string): Promise<boolean> {
8
+ const answers = await inquirer.prompt([
9
+ {
10
+ type: 'confirm',
11
+ name: 'ok',
12
+ message,
13
+ default: true,
14
+ },
15
+ ]);
16
+
17
+ return answers.ok;
18
+ }
19
+
20
+ /**
21
+ * Pergunta para o usuário escolher entre várias opções
22
+ * @param message Mensagem da pergunta
23
+ * @param choices Lista de opções
24
+ * @param defaultChoice Opção default
25
+ */
26
+ export async function promptSelect(
27
+ message: string,
28
+ choices: string[],
29
+ defaultChoice?: string
30
+ ): Promise<string> {
31
+ const answers = await inquirer.prompt([
32
+ {
33
+ type: 'list',
34
+ name: 'selected',
35
+ message,
36
+ choices,
37
+ default: defaultChoice,
38
+ },
39
+ ]);
40
+
41
+ return answers.selected;
42
+ }
43
+
44
+ /**
45
+ * Pergunta de input de texto
46
+ * @param message Mensagem da pergunta
47
+ * @param defaultValue Valor default
48
+ */
49
+ export async function promptInput(message: string, defaultValue?: string): Promise<string> {
50
+ const answers = await inquirer.prompt([
51
+ {
52
+ type: 'input',
53
+ name: 'value',
54
+ message,
55
+ default: defaultValue,
56
+ },
57
+ ]);
58
+
59
+ return answers.value;
60
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "title": "Structure Migration",
4
+ "type": "object",
5
+ "properties": {
6
+ "name": { "type": "string", "description": "Feature name" },
7
+ "to": {
8
+ "type": "string",
9
+ "enum": ["flat", "domain-driven", "module-driven", "monorepo"],
10
+ "description": "Target structure"
11
+ },
12
+ "module": { "type": "string" },
13
+ "app": { "type": "string" },
14
+ "lib": { "type": "string" }
15
+ },
16
+ "required": ["name", "to"]
17
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFeatureBasePath = getFeatureBasePath;
4
+ exports.moveFeatureTree = moveFeatureTree;
5
+ const path_1 = require("path");
6
+ const string_utils_1 = require("./string-utils");
7
+ const routes_utils_1 = require("./routes-utils");
8
+ /**
9
+ * Calcula o path base de uma feature de acordo com a estrutura
10
+ */
11
+ function getFeatureBasePath(feature, structure, options) {
12
+ switch (structure) {
13
+ case 'flat':
14
+ return `src/app/${feature}`;
15
+ case 'domain-driven':
16
+ return `src/app/features/${feature}`;
17
+ case 'module-driven':
18
+ if (!options.module)
19
+ throw new Error('module-driven requires --module option');
20
+ return `src/app/${options.module}/${feature}`;
21
+ case 'monorepo':
22
+ if (options.app)
23
+ return `apps/${options.app}/src/app/${feature}`;
24
+ if (options.lib)
25
+ return `libs/${options.lib}/src/lib/${feature}`;
26
+ throw new Error('monorepo requires --app or --lib option');
27
+ default:
28
+ throw new Error(`Invalid structure: ${structure}`);
29
+ }
30
+ }
31
+ /**
32
+ * Move arquivos de uma feature de uma estrutura para outra, atualizando barrels e rotas
33
+ */
34
+ function moveFeatureTree(tree, feature, fromStructure, toStructure, options) {
35
+ const sourceBase = getFeatureBasePath(feature, fromStructure, options);
36
+ const targetBase = getFeatureBasePath(feature, toStructure, options);
37
+ if (!tree.exists(sourceBase)) {
38
+ throw new Error(`Feature folder does not exist: ${sourceBase}`);
39
+ }
40
+ // Criar pasta de destino se não existir
41
+ if (!tree.exists(targetBase)) {
42
+ tree.create(targetBase + '/.gitkeep', '');
43
+ }
44
+ const sourceDir = tree.getDir(sourceBase);
45
+ // Mover arquivos
46
+ sourceDir.subfiles.forEach(file => {
47
+ const srcPath = (0, path_1.join)(sourceBase, file);
48
+ const destPath = (0, path_1.join)(targetBase, file);
49
+ const content = tree.read(srcPath);
50
+ tree.create(destPath, content);
51
+ tree.delete(srcPath);
52
+ });
53
+ // Recursivamente mover subpastas
54
+ sourceDir.subdirs.forEach(subdir => {
55
+ moveFeatureTree(tree, `${feature}/${subdir}`, fromStructure, toStructure, options);
56
+ });
57
+ // Atualizar barrels (index.ts)
58
+ const targetDir = tree.getDir(targetBase);
59
+ const tsFiles = targetDir.subfiles.filter(f => f.endsWith('.ts') && f !== 'index.ts');
60
+ if (tsFiles.length) {
61
+ const barrelContent = tsFiles.map(f => `export * from './${f.replace('.ts', '')}';`).join('\n');
62
+ tree.overwrite((0, path_1.join)(targetBase, 'index.ts'), barrelContent);
63
+ }
64
+ // Atualizar app.routes.ts (lazy-load ou standalone)
65
+ const featureKebab = (0, string_utils_1.toKebabCase)(feature);
66
+ const featurePascal = feature
67
+ .split('/')
68
+ .map(s => s[0].toUpperCase() + s.slice(1))
69
+ .join('');
70
+ (0, routes_utils_1.updateAppRoutes)(tree, featureKebab, featurePascal, toStructure, options);
71
+ // Deletar diretório antigo
72
+ if (tree.exists(sourceBase))
73
+ tree.delete(sourceBase);
74
+ }
75
+ //# sourceMappingURL=path-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-resolver.js","sourceRoot":"","sources":["path-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import { Tree } from '@angular-devkit/schematics';
2
+ import { join } from 'path';
3
+ import { toKebabCase } from './string-utils';
4
+ import { updateAppRoutes } from './routes-utils';
5
+
6
+ export interface MoveFeatureOptions {
7
+ structure: string;
8
+ module?: string;
9
+ app?: string;
10
+ lib?: string;
11
+ }
12
+
13
+ /**
14
+ * Calcula o path base de uma feature de acordo com a estrutura
15
+ */
16
+ export function getFeatureBasePath(
17
+ feature: string,
18
+ structure: string,
19
+ options: MoveFeatureOptions
20
+ ): string {
21
+ switch (structure) {
22
+ case 'flat':
23
+ return `src/app/${feature}`;
24
+ case 'domain-driven':
25
+ return `src/app/features/${feature}`;
26
+ case 'module-driven':
27
+ if (!options.module) throw new Error('module-driven requires --module option');
28
+ return `src/app/${options.module}/${feature}`;
29
+ case 'monorepo':
30
+ if (options.app) return `apps/${options.app}/src/app/${feature}`;
31
+ if (options.lib) return `libs/${options.lib}/src/lib/${feature}`;
32
+ throw new Error('monorepo requires --app or --lib option');
33
+ default:
34
+ throw new Error(`Invalid structure: ${structure}`);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Move arquivos de uma feature de uma estrutura para outra, atualizando barrels e rotas
40
+ */
41
+ export function moveFeatureTree(
42
+ tree: Tree,
43
+ feature: string,
44
+ fromStructure: string,
45
+ toStructure: string,
46
+ options: MoveFeatureOptions
47
+ ) {
48
+ const sourceBase = getFeatureBasePath(feature, fromStructure, options);
49
+ const targetBase = getFeatureBasePath(feature, toStructure, options);
50
+
51
+ if (!tree.exists(sourceBase)) {
52
+ throw new Error(`Feature folder does not exist: ${sourceBase}`);
53
+ }
54
+
55
+ // Criar pasta de destino se não existir
56
+ if (!tree.exists(targetBase)) {
57
+ tree.create(targetBase + '/.gitkeep', '');
58
+ }
59
+
60
+ const sourceDir = tree.getDir(sourceBase);
61
+
62
+ // Mover arquivos
63
+ sourceDir.subfiles.forEach(file => {
64
+ const srcPath = join(sourceBase, file);
65
+ const destPath = join(targetBase, file);
66
+ const content = tree.read(srcPath)!;
67
+ tree.create(destPath, content);
68
+ tree.delete(srcPath);
69
+ });
70
+
71
+ // Recursivamente mover subpastas
72
+ sourceDir.subdirs.forEach(subdir => {
73
+ moveFeatureTree(tree, `${feature}/${subdir}`, fromStructure, toStructure, options);
74
+ });
75
+
76
+ // Atualizar barrels (index.ts)
77
+ const targetDir = tree.getDir(targetBase);
78
+ const tsFiles = targetDir.subfiles.filter(f => f.endsWith('.ts') && f !== 'index.ts');
79
+ if (tsFiles.length) {
80
+ const barrelContent = tsFiles.map(f => `export * from './${f.replace('.ts', '')}';`).join('\n');
81
+ tree.overwrite(join(targetBase, 'index.ts'), barrelContent);
82
+ }
83
+
84
+ // Atualizar app.routes.ts (lazy-load ou standalone)
85
+ const featureKebab = toKebabCase(feature);
86
+ const featurePascal = feature
87
+ .split('/')
88
+ .map(s => s[0].toUpperCase() + s.slice(1))
89
+ .join('');
90
+ updateAppRoutes(tree, featureKebab, featurePascal, toStructure, options);
91
+
92
+ // Deletar diretório antigo
93
+ if (tree.exists(sourceBase)) tree.delete(sourceBase);
94
+ }