@ngflow/ng-architect 1.2.7 → 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 +3 -2
- package/src/collection.json +5 -4
- package/src/ng-architect/structure/index.js +26 -0
- package/src/ng-architect/structure/index.js.map +1 -0
- package/src/ng-architect/structure/index.ts +29 -0
- package/src/ng-architect/structure/prompt.js +72 -0
- package/src/ng-architect/structure/prompt.js.map +1 -0
- package/src/ng-architect/structure/prompt.ts +60 -0
- package/src/ng-architect/structure/schema.json +17 -0
- package/src/ng-architect/utils/path-resolver.js +75 -0
- package/src/ng-architect/utils/path-resolver.js.map +1 -0
- package/src/ng-architect/utils/path-resolver.ts +94 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngflow/ng-architect",
|
|
3
|
-
"version": "1.2.
|
|
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",
|
package/src/collection.json
CHANGED
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
"factory": "./ng-architect/service/index",
|
|
31
31
|
"schema": "./ng-architect/service/schema.json"
|
|
32
32
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
}
|
|
@@ -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
|
+
}
|