@ngflow/ng-architect 1.0.3 → 1.0.5
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 +9 -4
- package/src/collection.json +15 -5
- package/src/ng-architect/component/files/__componentKebab__.component.scss +0 -0
- package/src/ng-architect/component/files/__componentKebab__.component.ts +0 -0
- package/src/ng-architect/component/index.js +18 -0
- package/src/ng-architect/component/index.js.map +1 -0
- package/src/ng-architect/component/index.ts +27 -0
- package/src/ng-architect/component/schema.json +19 -0
- package/src/ng-architect/feature/files/src/app/features/__featureKebab__/pages/__featureKebab__.page.scss +0 -0
- package/src/ng-architect/feature/index.js +33 -0
- package/src/ng-architect/feature/index.js.map +1 -0
- package/src/ng-architect/feature/index.ts +47 -0
- package/src/ng-architect/{schema.json → feature/schema.json} +1 -1
- package/src/ng-architect/service/files/__serviceKebab__/__serviceKebab__.service.ts +27 -0
- package/src/ng-architect/service/index.js +1 -0
- package/src/ng-architect/service/index.js.map +1 -0
- package/src/ng-architect/service/index.ts +0 -0
- package/src/ng-architect/service/schema.json +20 -0
- package/src/ng-architect/utils/file-utils.js +46 -0
- package/src/ng-architect/utils/file-utils.js.map +1 -0
- package/src/ng-architect/utils/file-utils.ts +58 -0
- package/src/ng-architect/utils/string-utils.js +26 -0
- package/src/ng-architect/utils/string-utils.js.map +1 -0
- package/src/ng-architect/utils/string-utils.ts +23 -0
- package/tsconfig.json +11 -2
- package/src/ng-architect/index.ts +0 -76
- /package/src/ng-architect/{files/src/app/features/__featureKebab__/pages/__featureKebab__.page.scss → component/files/__componentKebab__.component.html} +0 -0
- /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/__featureKebab__.routes.ts +0 -0
- /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/data/__featureKebab__.service.ts +0 -0
- /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/data/__featureKebab__.store.ts +0 -0
- /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/models/__featureKebab__.model.ts +0 -0
- /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/pages/__featureKebab__.page.html +0 -0
- /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/pages/__featureKebab__.page.ts +0 -0
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngflow/ng-architect",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Angular schematics to generate files and maintain clean architecture",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc -p tsconfig.json",
|
|
7
7
|
"test": "npm run build && jasmine src/**/*_spec.js"
|
|
8
8
|
},
|
|
9
|
-
"keywords": [
|
|
9
|
+
"keywords": [
|
|
10
|
+
"schematics",
|
|
11
|
+
"angular",
|
|
12
|
+
"automation",
|
|
13
|
+
"architecture"
|
|
14
|
+
],
|
|
10
15
|
"author": "João Fernandes",
|
|
11
16
|
"license": "MIT",
|
|
12
17
|
"schematics": "./src/collection.json",
|
|
@@ -15,8 +20,8 @@
|
|
|
15
20
|
"@angular-devkit/schematics": "^21.0.4"
|
|
16
21
|
},
|
|
17
22
|
"devDependencies": {
|
|
18
|
-
"@types/
|
|
19
|
-
"@types/
|
|
23
|
+
"@types/jasmine": "5.1.13",
|
|
24
|
+
"@types/node": "20.19.27",
|
|
20
25
|
"jasmine": "~5.12.0",
|
|
21
26
|
"typescript": "~5.9.2"
|
|
22
27
|
}
|
package/src/collection.json
CHANGED
|
@@ -15,11 +15,21 @@
|
|
|
15
15
|
// The factory is required, except when using the extends field. Then the factory can
|
|
16
16
|
// overwrite the extended schematic factory.
|
|
17
17
|
"schematics": {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
"ng-architect": {
|
|
19
|
+
"description": "Gera uma feature Angular completa com rotas e arquivos",
|
|
20
|
+
"factory": "./ng-architect/feature/index#default",
|
|
21
|
+
"schema": "./ng-architect/feature/schema.json"
|
|
22
|
+
},
|
|
23
|
+
"component": {
|
|
24
|
+
"description": "Generate a reusable Angular component",
|
|
25
|
+
"factory": "./ng-architect/component/index",
|
|
26
|
+
"schema": "./ng-architect/component/schema.json"
|
|
27
|
+
},
|
|
28
|
+
"service": {
|
|
29
|
+
"description": "Generate a service with optional store and tests",
|
|
30
|
+
"factory": "./ng-architect/service/index",
|
|
31
|
+
"schema": "./ng-architect/service/schema.json"
|
|
32
|
+
},
|
|
23
33
|
"my-extend-schematic": {
|
|
24
34
|
"description": "A schematic that extends another schematic.",
|
|
25
35
|
"extends": "my-full-schematic"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
|
+
const string_utils_1 = require("../utils/string-utils");
|
|
6
|
+
function default_1(options) {
|
|
7
|
+
const componentKebab = (0, string_utils_1.toKebabCase)(options.name);
|
|
8
|
+
const componentPascal = (0, string_utils_1.toPascalCase)(options.name);
|
|
9
|
+
return (0, schematics_1.chain)([
|
|
10
|
+
(_tree, context) => {
|
|
11
|
+
context.logger.info(`✅ Creating component: ${componentPascal} (${componentKebab})`);
|
|
12
|
+
},
|
|
13
|
+
(0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
|
|
14
|
+
(0, schematics_1.template)({ componentKebab, componentPascal, module: options.module })
|
|
15
|
+
]))
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAYA,4BAcC;AA1BD,2DASoC;AACpC,wDAAkE;AAElE,mBAAwB,OAAY;IAClC,MAAM,cAAc,GAAG,IAAA,0BAAW,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,eAAe,GAAG,IAAA,2BAAY,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD,OAAO,IAAA,kBAAK,EAAC;QACX,CAAC,KAAW,EAAE,OAAyB,EAAE,EAAE;YACzC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,eAAe,KAAK,cAAc,GAAG,CAAC,CAAC;QACtF,CAAC;QACD,IAAA,sBAAS,EACP,IAAA,kBAAK,EAAC,IAAA,gBAAG,EAAC,SAAS,CAAC,EAAE;YACpB,IAAA,qBAAQ,EAAC,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;SACtE,CAAC,CACH;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Rule,
|
|
3
|
+
SchematicContext,
|
|
4
|
+
Tree,
|
|
5
|
+
apply,
|
|
6
|
+
chain,
|
|
7
|
+
mergeWith,
|
|
8
|
+
template,
|
|
9
|
+
url
|
|
10
|
+
} from '@angular-devkit/schematics';
|
|
11
|
+
import { toKebabCase, toPascalCase } from '../utils/string-utils';
|
|
12
|
+
|
|
13
|
+
export default function(options: any): Rule {
|
|
14
|
+
const componentKebab = toKebabCase(options.name);
|
|
15
|
+
const componentPascal = toPascalCase(options.name);
|
|
16
|
+
|
|
17
|
+
return chain([
|
|
18
|
+
(_tree: Tree, context: SchematicContext) => {
|
|
19
|
+
context.logger.info(`✅ Creating component: ${componentPascal} (${componentKebab})`);
|
|
20
|
+
},
|
|
21
|
+
mergeWith(
|
|
22
|
+
apply(url('./files'), [
|
|
23
|
+
template({ componentKebab, componentPascal, module: options.module })
|
|
24
|
+
])
|
|
25
|
+
)
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "ComponentSchema",
|
|
4
|
+
"title": "Angular Component Generator",
|
|
5
|
+
"description": "Generates a reusable Angular component",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The name of the component (e.g., navbar, footer)",
|
|
11
|
+
"minLength": 1
|
|
12
|
+
},
|
|
13
|
+
"module": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Optional: The Angular module to declare this component in"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["name"]
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
|
+
const string_utils_1 = require("../utils/string-utils");
|
|
6
|
+
const file_utils_1 = require("../utils/file-utils");
|
|
7
|
+
function default_1(options) {
|
|
8
|
+
const featureKebab = (0, string_utils_1.toKebabCase)(options.name);
|
|
9
|
+
const featurePascal = (0, string_utils_1.toPascalCase)(options.name);
|
|
10
|
+
return (0, schematics_1.chain)([
|
|
11
|
+
// Log inicial
|
|
12
|
+
(_tree, context) => {
|
|
13
|
+
context.logger.info(`✅ Creating feature: ${featurePascal} (${featureKebab})`);
|
|
14
|
+
},
|
|
15
|
+
// Gera os arquivos a partir do template
|
|
16
|
+
(0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('./files'), [
|
|
17
|
+
(0, schematics_1.filter)(path => !path.endsWith('.swp')),
|
|
18
|
+
(0, schematics_1.template)({ featureKebab, featurePascal })
|
|
19
|
+
])),
|
|
20
|
+
// Atualiza app.routes.ts adicionando a rota
|
|
21
|
+
(tree, context) => {
|
|
22
|
+
(0, file_utils_1.addRoute)(tree, context, featureKebab);
|
|
23
|
+
// Cria barrels automáticos em data, models e pages
|
|
24
|
+
const folders = ['data', 'models', 'pages'];
|
|
25
|
+
folders.forEach(folder => {
|
|
26
|
+
const path = `src/app/features/${featureKebab}/${folder}`;
|
|
27
|
+
(0, file_utils_1.createOrUpdateBarrel)(tree, context, path);
|
|
28
|
+
});
|
|
29
|
+
return tree;
|
|
30
|
+
}
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAcA,4BAgCC;AA9CD,2DAUoC;AACpC,wDAAkE;AAClE,oDAAqE;AAErE,mBAAyB,OAAyB;IAChD,MAAM,YAAY,GAAG,IAAA,0BAAW,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,IAAA,2BAAY,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,OAAO,IAAA,kBAAK,EAAC;QACX,cAAc;QACd,CAAC,KAAW,EAAE,OAAyB,EAAE,EAAE;YACzC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,aAAa,KAAK,YAAY,GAAG,CAAC,CAAC;QAChF,CAAC;QAED,wCAAwC;QACxC,IAAA,sBAAS,EACP,IAAA,kBAAK,EAAC,IAAA,gBAAG,EAAC,SAAS,CAAC,EAAE;YACpB,IAAA,mBAAM,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtC,IAAA,qBAAQ,EAAC,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;SAC1C,CAAC,CACH;QAED,4CAA4C;QAC5C,CAAC,IAAU,EAAE,OAAyB,EAAE,EAAE;YACxC,IAAA,qBAAQ,EAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;YAEtC,mDAAmD;YACnD,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5C,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;gBACvB,MAAM,IAAI,GAAG,oBAAoB,YAAY,IAAI,MAAM,EAAE,CAAC;gBAC1D,IAAA,iCAAoB,EAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Rule,
|
|
3
|
+
SchematicContext,
|
|
4
|
+
Tree,
|
|
5
|
+
apply,
|
|
6
|
+
chain,
|
|
7
|
+
filter,
|
|
8
|
+
mergeWith,
|
|
9
|
+
template,
|
|
10
|
+
url
|
|
11
|
+
} from '@angular-devkit/schematics';
|
|
12
|
+
import { toKebabCase, toPascalCase } from '../utils/string-utils';
|
|
13
|
+
import { addRoute, createOrUpdateBarrel } from '../utils/file-utils';
|
|
14
|
+
|
|
15
|
+
export default function (options: { name: string }): Rule {
|
|
16
|
+
const featureKebab = toKebabCase(options.name);
|
|
17
|
+
const featurePascal = toPascalCase(options.name);
|
|
18
|
+
|
|
19
|
+
return chain([
|
|
20
|
+
// Log inicial
|
|
21
|
+
(_tree: Tree, context: SchematicContext) => {
|
|
22
|
+
context.logger.info(`✅ Creating feature: ${featurePascal} (${featureKebab})`);
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
// Gera os arquivos a partir do template
|
|
26
|
+
mergeWith(
|
|
27
|
+
apply(url('./files'), [
|
|
28
|
+
filter(path => !path.endsWith('.swp')),
|
|
29
|
+
template({ featureKebab, featurePascal })
|
|
30
|
+
])
|
|
31
|
+
),
|
|
32
|
+
|
|
33
|
+
// Atualiza app.routes.ts adicionando a rota
|
|
34
|
+
(tree: Tree, context: SchematicContext) => {
|
|
35
|
+
addRoute(tree, context, featureKebab);
|
|
36
|
+
|
|
37
|
+
// Cria barrels automáticos em data, models e pages
|
|
38
|
+
const folders = ['data', 'models', 'pages'];
|
|
39
|
+
folders.forEach(folder => {
|
|
40
|
+
const path = `src/app/features/${featureKebab}/${folder}`;
|
|
41
|
+
createOrUpdateBarrel(tree, context, path);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return tree;
|
|
45
|
+
}
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
-
"$id": "
|
|
3
|
+
"$id": "ngArchitectSchema",
|
|
4
4
|
"title": "Angular Feature Generator",
|
|
5
5
|
"description": "Generates a complete Angular feature with pages, services, store, models and routes",
|
|
6
6
|
"type": "object",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Rule,
|
|
3
|
+
SchematicContext,
|
|
4
|
+
Tree,
|
|
5
|
+
apply,
|
|
6
|
+
chain,
|
|
7
|
+
mergeWith,
|
|
8
|
+
template,
|
|
9
|
+
url
|
|
10
|
+
} from '@angular-devkit/schematics';
|
|
11
|
+
import { toKebabCase, toPascalCase } from '../../../utils/string-utils';
|
|
12
|
+
|
|
13
|
+
export default function(options: any): Rule {
|
|
14
|
+
const serviceKebab = toKebabCase(options.name);
|
|
15
|
+
const servicePascal = toPascalCase(options.name);
|
|
16
|
+
|
|
17
|
+
return chain([
|
|
18
|
+
(tree: Tree, context: SchematicContext) => {
|
|
19
|
+
context.logger.info(`✅ Creating service: ${servicePascal} (${serviceKebab})`);
|
|
20
|
+
},
|
|
21
|
+
mergeWith(
|
|
22
|
+
apply(url('./files'), [
|
|
23
|
+
template({ serviceKebab, servicePascal, store: options.store })
|
|
24
|
+
])
|
|
25
|
+
)
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":""}
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "ServiceSchema",
|
|
4
|
+
"title": "Angular Service Generator",
|
|
5
|
+
"description": "Generates an Angular service with optional store and tests",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The name of the service (e.g., user, auth)",
|
|
11
|
+
"minLength": 1
|
|
12
|
+
},
|
|
13
|
+
"store": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"description": "Whether to generate a store alongside the service",
|
|
16
|
+
"default": false
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"required": ["name"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addRoute = addRoute;
|
|
4
|
+
exports.createOrUpdateBarrel = createOrUpdateBarrel;
|
|
5
|
+
const string_utils_1 = require("./string-utils");
|
|
6
|
+
/**
|
|
7
|
+
* Atualiza o app.routes.ts adicionando uma nova rota
|
|
8
|
+
*/
|
|
9
|
+
function addRoute(tree, context, featureName) {
|
|
10
|
+
var _a;
|
|
11
|
+
const appRoutesPath = 'src/app/app.routes.ts';
|
|
12
|
+
const featureKebab = (0, string_utils_1.toKebabCase)(featureName);
|
|
13
|
+
const featurePascal = (0, string_utils_1.toPascalCase)(featureName);
|
|
14
|
+
if (!tree.exists(appRoutesPath)) {
|
|
15
|
+
context.logger.warn(`⚠️ ${appRoutesPath} não encontrado, rota não registrada`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let content = ((_a = tree.read(appRoutesPath)) === null || _a === void 0 ? void 0 : _a.toString('utf-8')) || '';
|
|
19
|
+
const routePattern = new RegExp(`path:\\s*['"]${featureKebab}['"]`);
|
|
20
|
+
if (routePattern.test(content)) {
|
|
21
|
+
context.logger.warn(`ℹ️ Rota "${featureKebab}" já existe em app.routes.ts`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const routeBlock = ` {
|
|
25
|
+
path: '${featureKebab}',
|
|
26
|
+
loadChildren: () =>
|
|
27
|
+
import('./features/${featureKebab}/${featureKebab}.routes')
|
|
28
|
+
.then(m => m.${featurePascal}Routes),
|
|
29
|
+
},`;
|
|
30
|
+
content = content.replace(/\n\];\s*$/, `\n${routeBlock}\n];`);
|
|
31
|
+
tree.overwrite(appRoutesPath, content);
|
|
32
|
+
context.logger.info(`➕ Rota "${featureKebab}" registrada em app.routes.ts`);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Cria ou atualiza um arquivo barrel (index.ts) em uma pasta
|
|
36
|
+
*/
|
|
37
|
+
function createOrUpdateBarrel(tree, context, folderPath) {
|
|
38
|
+
if (!tree.exists(folderPath))
|
|
39
|
+
return;
|
|
40
|
+
const files = tree.getDir(folderPath).subfiles.filter(f => f.endsWith('.ts'));
|
|
41
|
+
const exports = files.map(f => `export * from './${f.replace('.ts', '')}';`).join('\n');
|
|
42
|
+
const barrelPath = `${folderPath}/index.ts`;
|
|
43
|
+
tree.overwrite(barrelPath, exports);
|
|
44
|
+
context.logger.info(`📦 Barrel atualizado em ${folderPath}`);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=file-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-utils.js","sourceRoot":"","sources":["file-utils.ts"],"names":[],"mappings":";;AAOA,4BA+BC;AAKD,oDAcC;AAvDD,iDAA2D;AAE3D;;GAEG;AACH,SAAgB,QAAQ,CACtB,IAAU,EACV,OAAyB,EACzB,WAAmB;;IAEnB,MAAM,aAAa,GAAG,uBAAuB,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAA,0BAAW,EAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,IAAA,2BAAY,EAAC,WAAW,CAAC,CAAC;IAEhD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,aAAa,sCAAsC,CAAC,CAAC;QAC/E,OAAO;IACT,CAAC;IAED,IAAI,OAAO,GAAG,CAAA,MAAA,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,0CAAE,QAAQ,CAAC,OAAO,CAAC,KAAI,EAAE,CAAC;IAChE,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,gBAAgB,YAAY,MAAM,CAAC,CAAC;IACpE,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,YAAY,8BAA8B,CAAC,CAAC;QAC5E,OAAO;IACT,CAAC;IAED,MAAM,UAAU,GAAG;aACR,YAAY;;2BAEE,YAAY,IAAI,YAAY;uBAChC,aAAa;KAC/B,CAAC;IAEJ,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,UAAU,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACvC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,YAAY,+BAA+B,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAClC,IAAU,EACV,OAAyB,EACzB,UAAkB;IAElB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAAE,OAAO;IAErC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEvF,MAAM,UAAU,GAAG,GAAG,UAAU,WAAW,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEpC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// utils/file-utils.ts
|
|
2
|
+
import { Tree, SchematicContext } from '@angular-devkit/schematics';
|
|
3
|
+
import { toPascalCase, toKebabCase } from './string-utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Atualiza o app.routes.ts adicionando uma nova rota
|
|
7
|
+
*/
|
|
8
|
+
export function addRoute(
|
|
9
|
+
tree: Tree,
|
|
10
|
+
context: SchematicContext,
|
|
11
|
+
featureName: string
|
|
12
|
+
) {
|
|
13
|
+
const appRoutesPath = 'src/app/app.routes.ts';
|
|
14
|
+
const featureKebab = toKebabCase(featureName);
|
|
15
|
+
const featurePascal = toPascalCase(featureName);
|
|
16
|
+
|
|
17
|
+
if (!tree.exists(appRoutesPath)) {
|
|
18
|
+
context.logger.warn(`⚠️ ${appRoutesPath} não encontrado, rota não registrada`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let content = tree.read(appRoutesPath)?.toString('utf-8') || '';
|
|
23
|
+
const routePattern = new RegExp(`path:\\s*['"]${featureKebab}['"]`);
|
|
24
|
+
if (routePattern.test(content)) {
|
|
25
|
+
context.logger.warn(`ℹ️ Rota "${featureKebab}" já existe em app.routes.ts`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const routeBlock = ` {
|
|
30
|
+
path: '${featureKebab}',
|
|
31
|
+
loadChildren: () =>
|
|
32
|
+
import('./features/${featureKebab}/${featureKebab}.routes')
|
|
33
|
+
.then(m => m.${featurePascal}Routes),
|
|
34
|
+
},`;
|
|
35
|
+
|
|
36
|
+
content = content.replace(/\n\];\s*$/, `\n${routeBlock}\n];`);
|
|
37
|
+
tree.overwrite(appRoutesPath, content);
|
|
38
|
+
context.logger.info(`➕ Rota "${featureKebab}" registrada em app.routes.ts`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cria ou atualiza um arquivo barrel (index.ts) em uma pasta
|
|
43
|
+
*/
|
|
44
|
+
export function createOrUpdateBarrel(
|
|
45
|
+
tree: Tree,
|
|
46
|
+
context: SchematicContext,
|
|
47
|
+
folderPath: string
|
|
48
|
+
) {
|
|
49
|
+
if (!tree.exists(folderPath)) return;
|
|
50
|
+
|
|
51
|
+
const files = tree.getDir(folderPath).subfiles.filter(f => f.endsWith('.ts'));
|
|
52
|
+
const exports = files.map(f => `export * from './${f.replace('.ts','')}';`).join('\n');
|
|
53
|
+
|
|
54
|
+
const barrelPath = `${folderPath}/index.ts`;
|
|
55
|
+
tree.overwrite(barrelPath, exports);
|
|
56
|
+
|
|
57
|
+
context.logger.info(`📦 Barrel atualizado em ${folderPath}`);
|
|
58
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// utils/string-utils.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.toKebabCase = toKebabCase;
|
|
5
|
+
exports.toPascalCase = toPascalCase;
|
|
6
|
+
/**
|
|
7
|
+
* Converte uma string para kebab-case
|
|
8
|
+
* Ex: "UserProfile" -> "user-profile"
|
|
9
|
+
*/
|
|
10
|
+
function toKebabCase(value) {
|
|
11
|
+
return value
|
|
12
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2') // separa camelCase
|
|
13
|
+
.replace(/[\s_]+/g, '-') // substitui espaços ou underscores por "-"
|
|
14
|
+
.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Converte uma string para PascalCase
|
|
18
|
+
* Ex: "user-profile" -> "UserProfile"
|
|
19
|
+
*/
|
|
20
|
+
function toPascalCase(value) {
|
|
21
|
+
return value
|
|
22
|
+
.split(/[-_\s]/)
|
|
23
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
24
|
+
.join('');
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=string-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-utils.js","sourceRoot":"","sources":["string-utils.ts"],"names":[],"mappings":";AAAA,wBAAwB;;AAMxB,kCAKC;AAMD,oCAKC;AApBD;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK;SACT,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,mBAAmB;SACvD,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAa,2CAA2C;SAC/E,WAAW,EAAE,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,KAAa;IACxC,OAAO,KAAK;SACT,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACzD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// utils/string-utils.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converte uma string para kebab-case
|
|
5
|
+
* Ex: "UserProfile" -> "user-profile"
|
|
6
|
+
*/
|
|
7
|
+
export function toKebabCase(value: string): string {
|
|
8
|
+
return value
|
|
9
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2') // separa camelCase
|
|
10
|
+
.replace(/[\s_]+/g, '-') // substitui espaços ou underscores por "-"
|
|
11
|
+
.toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Converte uma string para PascalCase
|
|
16
|
+
* Ex: "user-profile" -> "UserProfile"
|
|
17
|
+
*/
|
|
18
|
+
export function toPascalCase(value: string): string {
|
|
19
|
+
return value
|
|
20
|
+
.split(/[-_\s]/)
|
|
21
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
22
|
+
.join('');
|
|
23
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
|
|
3
|
+
"baseUrl": ".",
|
|
4
4
|
"lib": ["es2018", "dom"],
|
|
5
5
|
"module": "commonjs",
|
|
6
6
|
"moduleResolution": "node",
|
|
@@ -19,5 +19,14 @@
|
|
|
19
19
|
"types": ["jasmine", "node"]
|
|
20
20
|
},
|
|
21
21
|
"include": ["src/**/*.ts"],
|
|
22
|
-
|
|
22
|
+
"exclude": [
|
|
23
|
+
"node_modules",
|
|
24
|
+
"dist",
|
|
25
|
+
"src/**/*.spec.ts",
|
|
26
|
+
"src/ng-architect/feature/files/**/*",
|
|
27
|
+
"src/ng-architect/component/files/**/*",
|
|
28
|
+
"src/ng-architect/service/files/**/*"
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
23
32
|
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Rule,
|
|
3
|
-
SchematicContext,
|
|
4
|
-
Tree,
|
|
5
|
-
apply,
|
|
6
|
-
chain,
|
|
7
|
-
filter,
|
|
8
|
-
mergeWith,
|
|
9
|
-
template,
|
|
10
|
-
url,
|
|
11
|
-
} from '@angular-devkit/schematics';
|
|
12
|
-
|
|
13
|
-
// Helper functions
|
|
14
|
-
const toKebabCase = (value: string): string =>
|
|
15
|
-
value
|
|
16
|
-
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
17
|
-
.replace(/_/g, '-')
|
|
18
|
-
.toLowerCase();
|
|
19
|
-
|
|
20
|
-
const toPascalCase = (value: string): string =>
|
|
21
|
-
value
|
|
22
|
-
.split(/[-_]/)
|
|
23
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
24
|
-
.join('');
|
|
25
|
-
|
|
26
|
-
export default function (options: any): Rule {
|
|
27
|
-
const featureKebab = toKebabCase(options.name);
|
|
28
|
-
const featurePascal = toPascalCase(options.name);
|
|
29
|
-
|
|
30
|
-
return chain([
|
|
31
|
-
(_tree: Tree, context: SchematicContext) => {
|
|
32
|
-
context.logger.info(`✅ Criando feature: ${featurePascal} (${featureKebab})`);
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
// Generate files from templates
|
|
36
|
-
mergeWith(
|
|
37
|
-
apply(url('./files'), [
|
|
38
|
-
filter((path) => !path.endsWith('.swp')),
|
|
39
|
-
template({
|
|
40
|
-
featureKebab,
|
|
41
|
-
featurePascal,
|
|
42
|
-
}),
|
|
43
|
-
])
|
|
44
|
-
),
|
|
45
|
-
|
|
46
|
-
// Update app.routes.ts if it exists
|
|
47
|
-
(tree: Tree, context: SchematicContext) => {
|
|
48
|
-
const appRoutesPath = 'src/app/app.routes.ts';
|
|
49
|
-
|
|
50
|
-
if (tree.exists(appRoutesPath)) {
|
|
51
|
-
let content = tree.read(appRoutesPath)?.toString('utf-8') || '';
|
|
52
|
-
|
|
53
|
-
const routePattern = new RegExp(`path:\\s*['"]${featureKebab}['"]`);
|
|
54
|
-
if (!routePattern.test(content)) {
|
|
55
|
-
const routeBlock = ` {
|
|
56
|
-
path: '${featureKebab}',
|
|
57
|
-
loadChildren: () =>
|
|
58
|
-
import('./features/${featureKebab}/${featureKebab}.routes')
|
|
59
|
-
.then(m => m.${featurePascal}Routes),
|
|
60
|
-
},`;
|
|
61
|
-
|
|
62
|
-
// Insert before the closing bracket
|
|
63
|
-
content = content.replace(/\n\];\s*$/, `\n${routeBlock}\n];`);
|
|
64
|
-
tree.overwrite(appRoutesPath, content);
|
|
65
|
-
context.logger.info(`➕ Rota "${featureKebab}" registada em app.routes.ts`);
|
|
66
|
-
} else {
|
|
67
|
-
context.logger.warn(`ℹ️ Rota "${featureKebab}" já existe em app.routes.ts`);
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
context.logger.warn('⚠️ app.routes.ts não encontrado, rota não registada automaticamente');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return tree;
|
|
74
|
-
},
|
|
75
|
-
]);
|
|
76
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|