@ngflow/ng-architect 1.0.3 → 1.0.4

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 (23) hide show
  1. package/package.json +1 -1
  2. package/src/collection.json +15 -5
  3. package/src/ng-architect/component/files/__componentKebab__.component.scss +0 -0
  4. package/src/ng-architect/component/files/__componentKebab__.component.ts +0 -0
  5. package/src/ng-architect/component/index.ts +27 -0
  6. package/src/ng-architect/component/schema.json +19 -0
  7. package/src/ng-architect/feature/files/src/app/features/__featureKebab__/pages/__featureKebab__.page.scss +0 -0
  8. package/src/ng-architect/feature/index.ts +47 -0
  9. package/src/ng-architect/{schema.json → feature/schema.json} +1 -1
  10. package/src/ng-architect/service/files/__serviceKebab__/__serviceKebab__.service.ts +27 -0
  11. package/src/ng-architect/service/index.ts +0 -0
  12. package/src/ng-architect/service/schema.json +20 -0
  13. package/src/ng-architect/utils/file-utils.ts +58 -0
  14. package/src/ng-architect/utils/string-utils.ts +23 -0
  15. package/tsconfig.json +13 -3
  16. package/src/ng-architect/index.ts +0 -76
  17. /package/src/ng-architect/{files/src/app/features/__featureKebab__/pages/__featureKebab__.page.scss → component/files/__componentKebab__.component.html} +0 -0
  18. /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/__featureKebab__.routes.ts +0 -0
  19. /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/data/__featureKebab__.service.ts +0 -0
  20. /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/data/__featureKebab__.store.ts +0 -0
  21. /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/models/__featureKebab__.model.ts +0 -0
  22. /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/pages/__featureKebab__.page.html +0 -0
  23. /package/src/ng-architect/{files → feature/files}/src/app/features/__featureKebab__/pages/__featureKebab__.page.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngflow/ng-architect",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Angular schematics to generate files and maintain clean architecture",
5
5
  "scripts": {
6
6
  "build": "tsc -p tsconfig.json",
@@ -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
- "ng-architect": {
19
- "description": "Gera uma feature Angular completa com rotas e arquivos",
20
- "factory": "./ng-architect/index#default",
21
- "schema": "./ng-architect/schema.json"
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"
@@ -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
+ }
@@ -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": "MyFullSchematicsSchema",
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
+ }
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,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,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
- "baseUrl": "tsconfig",
3
+ "baseUrl": "src",
4
4
  "lib": ["es2018", "dom"],
5
5
  "module": "commonjs",
6
6
  "moduleResolution": "node",
@@ -16,8 +16,18 @@
16
16
  "sourceMap": true,
17
17
  "strictNullChecks": true,
18
18
  "target": "es6",
19
- "types": ["jasmine", "node"]
19
+ "types": ["jasmine", "node"],
20
+
20
21
  },
21
22
  "include": ["src/**/*.ts"],
22
- "exclude": ["src/*/files/**/*"]
23
+ "exclude": [
24
+ "node_modules",
25
+ "dist",
26
+ "src/**/*.spec.ts",
27
+ "src/feature/files/**/*",
28
+ "src/component/files/**/*",
29
+ "src/service/files/**/*"
30
+ ]
31
+
32
+
23
33
  }
@@ -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
- }