@barcidev/ngx-autogen 0.1.70 → 0.1.72

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/README_ES.md ADDED
@@ -0,0 +1,385 @@
1
+ <p align="center">
2
+ <img src="./logo.png" alt="ngx-autogen logo" width="120">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="README.md">🇺🇸 English</a> | <a href="README_ES.md">🇪🇸 Español</a>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@barcidev/ngx-autogen"><img src="https://img.shields.io/npm/v/@barcidev/ngx-autogen?style=for-the-badge&color=007acc&labelColor=010101" alt="npm version"></a>
11
+ <a href="https://github.com/jpalacio09/ngx-autogen/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@barcidev/ngx-autogen?style=for-the-badge&color=green&labelColor=010101" alt="license"></a>
12
+ <img src="https://img.shields.io/badge/Angular-17%2B-DD0031?style=for-the-badge&logo=angular&labelColor=010101" alt="Angular 17+">
13
+ <img src="https://img.shields.io/badge/NgRx%20Signals-17%2B-BA2BD2?style=for-the-badge&logo=ngrx&labelColor=010101" alt="NgRx Signals 17+">
14
+ </p>
15
+
16
+ <p align="center">
17
+ <b>⚡ Schematics de Angular que generan entidades completas de NgRx Signal Store, scopes de i18n para Transloco y componentes standalone en segundos. ⚡</b>
18
+ </p>
19
+
20
+ ---
21
+
22
+ ## 📖 Tabla de Contenidos
23
+
24
+ - [Resumen](#resumen)
25
+ - [Requisitos](#requisitos)
26
+ - [Instalación](#instalación)
27
+ - [Vía ng add (recomendado)](#vía-ng-add-recomendado)
28
+ - [Vía npm](#vía-npm)
29
+ - [Exports de Runtime](#exports-de-runtime)
30
+ - [Referencia de Schematics](#referencia-de-schematics)
31
+ - [ng-add](#ng-add)
32
+ - [app-store](#app-store)
33
+ - [app-i18n](#app-i18n)
34
+ - [app-component](#app-component)
35
+ - [Licencia](#licencia)
36
+
37
+ ---
38
+
39
+ ## 🌟 Resumen
40
+
41
+ `@barcidev/ngx-autogen` es una colección de schematics para Angular diseñada para eliminar el scaffolding repetitivo al construir aplicaciones empresariales. Genera código listo para producción enfocado en tres áreas principales:
42
+
43
+ - **NgRx Signal Store** (`app-store`) - Un store de señales CRUD completo con gestión de entidades, estado de pagación, seguimiento de estado y una capa de servicio y modelo. Basado en `@ngrx/signals` y `@ngrx/signals/entities`.
44
+ - **Transloco i18n** (`app-i18n`) - Archivos de traducción con integración tipada de Transloco vía `@barcidev/typed-transloco`, registro automático de providers y actualización global del mapa de tipos.
45
+ - **Componente Angular** (`app-component`) - Generación de componentes standalone vía `@schematics/angular` con cableado opcional automático para el store e i18n.
46
+
47
+ La librería también incluye utilidades de runtime bajo `@barcidev/ngx-autogen/entity` que proporcionan características reutilizables para NgRx Signal Store: `withEntityStatus`, `withPagination` y ayudantes para modelos tipados.
48
+
49
+ ---
50
+
51
+ ## 📋 Requisitos
52
+
53
+ | Dependencia | Versión Mínima |
54
+ |---|---|
55
+ | `@angular/core` | `>=17.0.0` |
56
+ | `@angular/common` | `>=17.0.0` |
57
+ | `@angular/forms` | `>=17.0.0` |
58
+ | `@ngrx/signals` | `>=17.0.0` |
59
+ | Node.js | `>=16.0.0` |
60
+
61
+ ---
62
+
63
+ ## 🚀 Instalación
64
+
65
+ ### Vía ng add (recomendado)
66
+
67
+ ```bash
68
+ ng add @barcidev/ngx-autogen
69
+ ```
70
+
71
+ Este comando realiza las siguientes acciones automáticamente:
72
+
73
+ 1. **Mueve la librería a `devDependencies`** - Si se instaló como una dependencia de producción, se reubica en `devDependencies` en el `package.json`.
74
+ 2. **Registra la colección de schematics** - Añade `@barcidev/ngx-autogen` a `cli.schematicCollections` en `angular.json`, habilitando alias cortos como `ng g app-store`.
75
+ 3. **Almacena la configuración global** - Guarda el idioma predeterminado seleccionado (`lang`) bajo `schematics["@barcidev/ngx-autogen:all"]` en `angular.json` para que todos los schematics lo hereden.
76
+
77
+ **Prompt:**
78
+
79
+ | Pregunta | Por defecto |
80
+ |---|---|
81
+ | ¿Cuál es el idioma predeterminado para la aplicación (en, es)? | `en` |
82
+
83
+ ### Vía npm
84
+
85
+ ```bash
86
+ npm install @barcidev/ngx-autogen --save-dev
87
+ ```
88
+
89
+ > [!NOTE]
90
+ > Al instalar vía npm, debe registrar manualmente la colección en `angular.json` y usar el nombre completo del schematic (ej: `ng generate @barcidev/ngx-autogen:app-store`).
91
+
92
+ ---
93
+
94
+ ## 🛠️ Exports de Runtime
95
+
96
+ La librería exporta características reutilizables para NgRx Signal Store bajo el punto de entrada `@barcidev/ngx-autogen/entity`:
97
+
98
+ | Export | Descripción |
99
+ |---|---|
100
+ | `withEntityStatus()` | Característica del Signal Store que añade seguimiento de carga, errores y estado de selección para las entidades. |
101
+ | `withPagination()` | Característica del Signal Store que añade estado de paginación (página, tamaño, total). |
102
+ | `RequestConfig<TPayload, TResponse>` | Tipo genérico para configuraciones de métodos del store con callbacks `payload`, `onSuccess` y `onError`. |
103
+ | `FormGroupType<T>` | Tipo de utilidad que mapea una interfaz a sus tipos de control de `FormGroup` de Angular. |
104
+
105
+ ```typescript
106
+ import { withEntityStatus, withPagination, RequestConfig, FormGroupType } from '@barcidev/ngx-autogen/entity';
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 📖 Referencia de Schematics
112
+
113
+ ### `ng-add`
114
+
115
+ **Descripción**: Schematic de ng add para ngx-autogen. Configura el espacio de trabajo para usar todos los schematics de la librería.
116
+
117
+ **Uso**
118
+ ```bash
119
+ ng add @barcidev/ngx-autogen
120
+ ```
121
+
122
+ **Opciones**
123
+
124
+ | Opción | Tipo | Por defecto | Requerido | Descripción |
125
+ |---|---|---|---|---|
126
+ | `lang` | `string` | `en` | No | El idioma predeterminado para la aplicación (`en`, `es`). |
127
+
128
+ ---
129
+
130
+ ### `app-store`
131
+
132
+ > Alias: `ng g app-store` (cuando la colección está registrada vía `ng add`)
133
+
134
+ **Descripción**: Añade NgRx Store a la aplicación Angular con soporte para señales. Genera un signal store CRUD completo, servicio y capa de modelo para una entidad dada.
135
+
136
+ **Uso**
137
+ ```bash
138
+ # Forma corta (requiere ng add):
139
+ ng g app-store --name product
140
+
141
+ # Forma completa:
142
+ ng generate @barcidev/ngx-autogen:app-store --name product
143
+ ```
144
+
145
+ **Opciones**
146
+
147
+ | Opción | Tipo | Por defecto | Requerido | Descripción |
148
+ |---|---|---|---|---|
149
+ | `name` | `string` | - | **Sí** | Nombre de la entidad. Se usa para derivar todos los nombres de clases, archivos y métodos del store. |
150
+ | `path` | `string` | *(cwd)* | No | Ruta de destino. Por defecto es el directorio de trabajo actual relativo a `src/`. |
151
+ | `grouped` | `boolean` | `false` | No | Indica si los archivos deben agruparse en subcarpetas `models/` y `services/`. |
152
+ | `pk` | `string` | `id` | No | El nombre de la clave primaria predeterminada (ej: `id`, `cod`, `uuid`). |
153
+ | `lang` | `string` | *(global)* | No | El idioma predeterminado para la aplicación (`en`, `es`). Heredado de la configuración de `ng add`. |
154
+ | `isProvideInRoot` | `boolean` | `false` | No | Indica si el store y el servicio deben usar `providedIn: 'root'`. |
155
+
156
+ **Qué genera**
157
+
158
+ Para un nombre de entidad dado, este schematic crea:
159
+
160
+ - `<name>.store.ts` - Signal Store de NgRx con métodos CRUD completos (`add`, `load`, `update`, `remove`, `select`), configuración de entidad, selectores computados y hook `onInit`.
161
+ - `<name>.service.ts` - Servicio inyectable con métodos base para `add$`, `get$` (pluralizado), `update$` y `remove$`.
162
+ - `<name>.model.ts` - Interfaces TypeScript: `Add<Name>`, `<Name>Dto`, `Update<Name>`, `Add<Name>Form` y `<Name>Request`.
163
+ - `state/index.ts` - Archivo barrel con re-exports (creado o actualizado).
164
+
165
+ También:
166
+ - Instala `@ngrx/signals` si no está presente (coincidiendo con la versión mayor de Angular).
167
+ - Configura el alias de ruta `@shared-state/*` en `tsconfig.app.json`.
168
+ - Guarda los valores predeterminados de `pk` y `lang` en `angular.json`.
169
+
170
+ **Ejemplos**
171
+
172
+ **Mínimo - Entidad con valores por defecto:**
173
+ ```bash
174
+ ng g app-store --name product
175
+ ```
176
+ ```
177
+ src/app/state/
178
+ index.ts
179
+ product/
180
+ product.model.ts
181
+ product.service.ts
182
+ product.store.ts
183
+ ```
184
+
185
+ **Diseño agrupado con PK personalizada:**
186
+ ```bash
187
+ ng g app-store --name invoice --grouped --pk cod
188
+ ```
189
+ ```
190
+ src/app/state/
191
+ index.ts
192
+ invoice/
193
+ models/
194
+ invoice.model.ts
195
+ services/
196
+ invoice.service.ts
197
+ invoice.store.ts
198
+ ```
199
+
200
+ **Proporcionado en root con pluralización en español:**
201
+ ```bash
202
+ ng g app-store --name usuario --isProvideInRoot --lang es
203
+ ```
204
+ ```
205
+ src/app/state/
206
+ index.ts
207
+ usuario/
208
+ usuario.model.ts # interfaces con pk: id
209
+ usuario.service.ts # @Injectable({ providedIn: 'root' })
210
+ usuario.store.ts # signalStore({ providedIn: 'root' }, ...) - los métodos usan "Usuarios" (plural en español)
211
+ ```
212
+
213
+ ---
214
+
215
+ ### `app-i18n`
216
+
217
+ > Alias: `ng g app-i18n` (cuando la colección está registrada vía `ng add`)
218
+
219
+ **Descripción**: Añade Transloco a la aplicación Angular. Genera un archivo de traducción con scope i18n y lo conecta al componente objetivo.
220
+
221
+ **Uso**
222
+ ```bash
223
+ # Forma corta:
224
+ ng g app-i18n --name dashboard
225
+
226
+ # Forma completa:
227
+ ng generate @barcidev/ngx-autogen:app-i18n --name dashboard
228
+ ```
229
+
230
+ **Opciones**
231
+
232
+ | Opción | Tipo | Por defecto | Requerido | Descripción |
233
+ |---|---|---|---|---|
234
+ | `name` | `string` | *(auto-detect)* | No | Nombre del componente. Se detecta automáticamente del archivo `.component.ts` en el directorio actual si no se proporciona. |
235
+ | `path` | `string` | *(cwd)* | No | Ruta de destino. Por defecto es el directorio de trabajo actual relativo a `src/`. |
236
+ | `lang` | `string` | *(global)* | No | Idioma para la pluralización (`en` para inglés, `es` para español). |
237
+
238
+ **Qué genera**
239
+
240
+ - `<name>.i18n.ts` - Archivo de scope de traducción con objetos `en-US` y `es-CO` registrados vía `TranslocoUtils.createScopeConfig`.
241
+
242
+ También:
243
+ - Ejecuta `@barcidev/typed-transloco:ng-add` si Transloco aún no está configurado en el proyecto.
244
+ - Añade `provideTranslocoScopeWrapper` al array de `providers` del componente.
245
+ - Añade `AppTypedTranslocoDirective` al array de `imports` del componente.
246
+ - Actualiza el mapa de tipos global `app.i18n.ts` con el nuevo scope.
247
+
248
+ **Ejemplos**
249
+
250
+ **Auto-detectar componente en el directorio actual:**
251
+ ```bash
252
+ cd src/app/features/dashboard
253
+ ng g app-i18n
254
+ ```
255
+ ```
256
+ src/app/features/dashboard/
257
+ dashboard.i18n.ts # nuevo
258
+ dashboard.component.ts # actualizado con providers + imports
259
+ ```
260
+
261
+ **Nombre y ruta explícitos:**
262
+ ```bash
263
+ ng g app-i18n --name settings --path src/app/features/settings
264
+ ```
265
+ ```
266
+ src/app/features/settings/
267
+ settings.i18n.ts
268
+ ```
269
+
270
+ **Con pluralización en español:**
271
+ ```bash
272
+ ng g app-i18n --name usuario --lang es
273
+ ```
274
+ ```
275
+ src/app/features/usuario/
276
+ usuario.i18n.ts # clave de scope: "usuario", la pluralización usa reglas del español
277
+ ```
278
+
279
+ ---
280
+
281
+ ### `app-component`
282
+
283
+ > Alias: `ng g app-component` (cuando la colección está registrada vía `ng add`)
284
+
285
+ **Descripción**: Genera un componente de Angular con soporte opcional para i18n y NgRx. Delega en `@schematics/angular:component` para la generación base, luego encadena opcionalmente los schematics `app-store` y `app-i18n`.
286
+
287
+ **Uso**
288
+ ```bash
289
+ # Forma corta:
290
+ ng g app-component --name product-list
291
+
292
+ # Forma completa:
293
+ ng generate @barcidev/ngx-autogen:app-component --name product-list
294
+ ```
295
+
296
+ **Opciones**
297
+
298
+ | Opción | Tipo | Por defecto | Requerido | Descripción |
299
+ |---|---|---|---|---|
300
+ | `name` | `string` | - | **Sí** | Nombre del componente. |
301
+ | `store` | `string` | - | No | Indica si se debe crear un store para el componente. Enum: `Yes`, `No`. |
302
+ | `i18n` | `string` | - | No | Indica si se debe crear un archivo i18n para el componente. Enum: `Yes`, `No`. |
303
+ | `pk` | `string` | `id` | No | El nombre de la clave primaria predeterminada. Solo se usa cuando `store` es `Yes`. |
304
+ | `isProvideInRoot` | `boolean` | `false` | No | Indica si el store y el servicio deben proporcionarse en root. Solo se usa cuando `store` es `Yes`. |
305
+ | `path` | `string` | *(cwd)* | No | Ruta de destino. |
306
+ | `lang` | `string` | *(global)* | No | Idioma para la pluralización (`en`, `es`). |
307
+ | `style` | `string` | `css` | No | La extensión de archivo o preprocesador a usar para los archivos de estilo. |
308
+ | `skipTests` | `boolean` | `false` | No | No crear archivos de prueba `spec.ts`. |
309
+ | `inlineStyle` | `boolean` | `false` | No | Incluir estilos inline en el archivo `.ts` del componente. |
310
+ | `inlineTemplate` | `boolean` | `false` | No | Incluir plantilla inline en el archivo `.ts` del componente. |
311
+
312
+ **Prompts interactivos** (cuando `store` es `Yes`):
313
+
314
+ | Pregunta | Por defecto |
315
+ |---|---|
316
+ | What is the name of the entity store to create? | *(nombre del componente)* |
317
+ | What is the name of the default Primary Key? | `id` |
318
+ | Should the store and service be provided in root? | `No` |
319
+
320
+ **Qué genera**
321
+
322
+ El componente base es generado por el schematic oficial `@schematics/angular:component` con `standalone: true`. Dependiendo de las opciones:
323
+
324
+ - **Con `store: Yes`**: Encadena el schematic `app-store` dentro del directorio del componente, inyecta el store en la clase del componente, añade `JsonPipe` a los imports, conecta la propiedad computada `data$` y configura los `providers` con `provide<Name>Store()` (a menos que `isProvideInRoot` sea true). También actualiza el `.spec.ts` con providers de mock para el store.
325
+ - **Con `i18n: Yes`**: Encadena el schematic `app-i18n` dentro del directorio del componente.
326
+
327
+ **Ejemplos**
328
+
329
+ **Mínimo - Solo componente:**
330
+ ```bash
331
+ ng g app-component --name dashboard --store No --i18n No
332
+ ```
333
+ ```
334
+ src/app/dashboard/
335
+ dashboard.component.ts
336
+ dashboard.component.html
337
+ dashboard.component.css
338
+ dashboard.component.spec.ts
339
+ ```
340
+
341
+ **Componente con store e i18n:**
342
+ ```bash
343
+ ng g app-component --name product-list --store Yes --i18n Yes --style scss
344
+ ```
345
+ ```
346
+ src/app/product-list/
347
+ product-list.component.ts # inyecta ProductListStore, data$, JsonPipe, providers
348
+ product-list.component.html # <div *typedTransloco="let t; prefix:'productList'">
349
+ product-list.component.scss
350
+ product-list.component.spec.ts # TestBed configurado con mock del store
351
+ product-list.i18n.ts
352
+ state/
353
+ index.ts
354
+ product-list/
355
+ product-list.model.ts
356
+ product-list.service.ts
357
+ product-list.store.ts
358
+ ```
359
+
360
+ **Componente con nombre de store personalizado y provider en root:**
361
+ ```bash
362
+ ng g app-component --name user-profile --store Yes --i18n No --pk uuid --isProvideInRoot
363
+ ```
364
+
365
+ Cuando se le pida el nombre del entity store, ingrese `user` en lugar del predeterminado `user-profile`:
366
+
367
+ ```
368
+ src/app/user-profile/
369
+ user-profile.component.ts # inyecta UserStore (no UserProfileStore)
370
+ user-profile.component.html
371
+ user-profile.component.css
372
+ user-profile.component.spec.ts
373
+ state/
374
+ index.ts
375
+ user/
376
+ user.model.ts # campo pk: uuid
377
+ user.service.ts # @Injectable({ providedIn: 'root' })
378
+ user.store.ts # signalStore({ providedIn: 'root' }, ...)
379
+ ```
380
+
381
+ ---
382
+
383
+ ## ⚖️ Licencia
384
+
385
+ [MIT](./LICENSE) - Jorge Palacio Barcinilla
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barcidev/ngx-autogen",
3
- "version": "0.1.70",
3
+ "version": "0.1.72",
4
4
  "description": "A collection of Angular schematics for essential functionalities.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -18,6 +18,7 @@
18
18
  "dist/lib/**/*.js",
19
19
  "dist/lib/**/*.d.ts",
20
20
  "README.md",
21
+ "README_ES.md",
21
22
  "LICENSE"
22
23
  ],
23
24
  "keywords": [
@@ -54,6 +55,7 @@
54
55
  "@angular-devkit/schematics": "^21.2.0",
55
56
  "@barcidev/typed-transloco": "^0.0.15",
56
57
  "@schematics/angular": "^21.2.0",
58
+ "ts-morph": "^28.0.0",
57
59
  "typescript": "~5.9.2"
58
60
  },
59
61
  "devDependencies": {
@@ -13,7 +13,7 @@ exports.component = component;
13
13
  const schematics_1 = require("@angular-devkit/schematics");
14
14
  const workspace_1 = require("@schematics/angular/utility/workspace");
15
15
  const path_1 = require("path");
16
- const pluralize_1 = require("../../common/pluralize");
16
+ const ts_morph_1 = require("ts-morph");
17
17
  const prompts_1 = require("../../common/prompts");
18
18
  function component(options) {
19
19
  return (tree) => __awaiter(this, void 0, void 0, function* () {
@@ -31,7 +31,17 @@ function component(options) {
31
31
  }
32
32
  const context = resolveComponentContext(workspace, options);
33
33
  return (0, schematics_1.chain)([
34
- generateComponentFiles(context),
34
+ (0, schematics_1.externalSchematic)("@schematics/angular", "component", {
35
+ name: context.options.name,
36
+ path: context.movePath,
37
+ project: context.projectName,
38
+ style: options.style,
39
+ skipTests: options.skipTests,
40
+ inlineStyle: options.inlineStyle,
41
+ inlineTemplate: options.inlineTemplate,
42
+ standalone: true, // Forzar standalone true como estándar moderno
43
+ }),
44
+ injectStoreAndI18n(context),
35
45
  options.i18n === "Yes"
36
46
  ? (0, schematics_1.schematic)("app-i18n", {
37
47
  name: context.options.name,
@@ -76,11 +86,129 @@ function resolveComponentContext(workspace, options) {
76
86
  entityName: schematics_1.strings.classify(options.name),
77
87
  };
78
88
  }
79
- function generateComponentFiles(ctx) {
80
- const { options, movePath, nameDash } = ctx;
81
- const targetPath = (0, path_1.join)(movePath, nameDash);
82
- const templateUtils = Object.assign(Object.assign(Object.assign({}, schematics_1.strings), options), { pluralize: (word) => options.lang === "es" ? (0, pluralize_1.pluralizeEs)(word) : (0, pluralize_1.pluralizeEn)(word) });
83
- const createSource = (srcUrl, dest) => (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)(srcUrl), [(0, schematics_1.applyTemplates)(templateUtils), (0, schematics_1.move)(dest)]));
84
- return (0, schematics_1.chain)([createSource("./files", targetPath)]);
89
+ function injectStoreAndI18n(ctx) {
90
+ return (tree) => {
91
+ const { options, movePath, nameDash } = ctx;
92
+ const storeName = options.storeName ? schematics_1.strings.classify(options.storeName) : '';
93
+ const storeVar = options.storeName ? schematics_1.strings.camelize(options.storeName) : '';
94
+ const componentName = schematics_1.strings.classify(options.name);
95
+ const tsPath = (0, path_1.join)(movePath, nameDash, `${nameDash}.component.ts`);
96
+ const project = new ts_morph_1.Project({ manipulationSettings: { quoteKind: ts_morph_1.QuoteKind.Single } });
97
+ if (options.store === 'Yes' && tree.exists(tsPath)) {
98
+ const sourceFile = project.createSourceFile('temp.ts', tree.read(tsPath).toString());
99
+ sourceFile.addImportDeclaration({
100
+ moduleSpecifier: './state',
101
+ namedImports: options.isProvideInRoot ? [`${storeName}Store`] : [`${storeName}Store`, `provide${storeName}Store`]
102
+ });
103
+ const hasJsonPipe = sourceFile.getImportDeclarations().some(imp => imp.getNamedImports().some(n => n.getName() === 'JsonPipe'));
104
+ if (!hasJsonPipe) {
105
+ sourceFile.addImportDeclaration({
106
+ moduleSpecifier: '@angular/common',
107
+ namedImports: ['JsonPipe']
108
+ });
109
+ }
110
+ const hasInject = sourceFile.getImportDeclarations().some(imp => imp.getNamedImports().some(n => n.getName() === 'inject'));
111
+ if (!hasInject) {
112
+ const coreImport = sourceFile.getImportDeclaration('@angular/core');
113
+ if (coreImport) {
114
+ coreImport.addNamedImport('inject');
115
+ }
116
+ else {
117
+ sourceFile.addImportDeclaration({
118
+ moduleSpecifier: '@angular/core',
119
+ namedImports: ['inject']
120
+ });
121
+ }
122
+ }
123
+ const classDecl = sourceFile.getClass(`${componentName}Component`);
124
+ if (classDecl) {
125
+ classDecl.insertProperty(0, {
126
+ name: `_${storeVar}Store`,
127
+ initializer: `inject(${storeName}Store)`,
128
+ scope: ts_morph_1.Scope.Private
129
+ });
130
+ classDecl.insertProperty(1, {
131
+ name: `data$`,
132
+ initializer: `this._${storeVar}Store.entities()`
133
+ });
134
+ const componentDecorator = classDecl.getDecorator('Component');
135
+ if (componentDecorator) {
136
+ const objExpr = componentDecorator.getArguments()[0];
137
+ if (objExpr && objExpr.getKind() === ts_morph_1.SyntaxKind.ObjectLiteralExpression) {
138
+ const obj = objExpr.asKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
139
+ const importsProp = obj.getProperty('imports');
140
+ if (importsProp && importsProp.getKind() === ts_morph_1.SyntaxKind.PropertyAssignment) {
141
+ const arr = importsProp.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment).getInitializerIfKind(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
142
+ if (arr) {
143
+ arr.addElement('JsonPipe');
144
+ }
145
+ }
146
+ else {
147
+ obj.addPropertyAssignment({ name: 'imports', initializer: '[JsonPipe]' });
148
+ }
149
+ if (!options.isProvideInRoot) {
150
+ const providersProp = obj.getProperty('providers');
151
+ if (providersProp && providersProp.getKind() === ts_morph_1.SyntaxKind.PropertyAssignment) {
152
+ const arr = providersProp.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment).getInitializerIfKind(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
153
+ if (arr) {
154
+ arr.addElement(`...provide${storeName}Store()`);
155
+ }
156
+ }
157
+ else {
158
+ obj.addPropertyAssignment({ name: 'providers', initializer: `[...provide${storeName}Store()]` });
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+ tree.overwrite(tsPath, sourceFile.getFullText());
165
+ }
166
+ if (!options.inlineTemplate) {
167
+ const htmlPath = (0, path_1.join)(movePath, nameDash, `${nameDash}.component.html`);
168
+ if (tree.exists(htmlPath)) {
169
+ let htmlContent = `<div>\n <h1>${componentName} Works!</h1>\n`;
170
+ if (options.store === 'Yes') {
171
+ htmlContent += ` <pre>{{ data$() | json }}</pre>\n`;
172
+ }
173
+ htmlContent += `</div>`;
174
+ if (options.i18n === 'Yes') {
175
+ htmlContent = `<div *typedTransloco="let t; prefix:'${schematics_1.strings.camelize(options.name)}'">\n <h1>{{ t('title') }}</h1>\n`;
176
+ if (options.store === 'Yes') {
177
+ htmlContent += ` <pre>{{ data$() | json }}</pre>\n`;
178
+ }
179
+ htmlContent += `</div>`;
180
+ }
181
+ tree.overwrite(htmlPath, htmlContent);
182
+ }
183
+ }
184
+ const specPath = (0, path_1.join)(movePath, nameDash, `${nameDash}.component.spec.ts`);
185
+ if (options.store === 'Yes' && !options.skipTests && tree.exists(specPath)) {
186
+ const specSource = project.createSourceFile('spec.ts', tree.read(specPath).toString());
187
+ specSource.addImportDeclaration({
188
+ moduleSpecifier: './state',
189
+ namedImports: [`${storeName}Store`]
190
+ });
191
+ const callExprs = specSource.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
192
+ const configTestBed = callExprs.find(c => c.getExpression().getText() === 'TestBed.configureTestingModule');
193
+ if (configTestBed) {
194
+ const objArg = configTestBed.getArguments()[0];
195
+ if (objArg && objArg.getKind() === ts_morph_1.SyntaxKind.ObjectLiteralExpression) {
196
+ const obj = objArg.asKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
197
+ const providersProp = obj.getProperty('providers');
198
+ if (providersProp && providersProp.getKind() === ts_morph_1.SyntaxKind.PropertyAssignment) {
199
+ const arr = providersProp.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment).getInitializerIfKind(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
200
+ if (arr) {
201
+ arr.addElement(`{ provide: ${storeName}Store, useValue: { entities: () => [] } }`);
202
+ }
203
+ }
204
+ else {
205
+ obj.addPropertyAssignment({ name: 'providers', initializer: `[{ provide: ${storeName}Store, useValue: { entities: () => [] } }]` });
206
+ }
207
+ }
208
+ }
209
+ tree.overwrite(specPath, specSource.getFullText());
210
+ }
211
+ return tree;
212
+ };
85
213
  }
86
214
  //# sourceMappingURL=index.js.map
@@ -40,6 +40,26 @@
40
40
  "type": "string",
41
41
  "description": "Language for pluralization ('en' for English, 'es' for Spanish).",
42
42
  "enum": ["en", "es"]
43
+ },
44
+ "style": {
45
+ "type": "string",
46
+ "description": "The file extension or preprocessor to use for style files.",
47
+ "default": "css"
48
+ },
49
+ "skipTests": {
50
+ "type": "boolean",
51
+ "description": "Do not create \"spec.ts\" test files for the new class.",
52
+ "default": false
53
+ },
54
+ "inlineStyle": {
55
+ "type": "boolean",
56
+ "description": "Include styles inline in the component.ts file. Only CSS styles can be included inline.",
57
+ "default": false
58
+ },
59
+ "inlineTemplate": {
60
+ "type": "boolean",
61
+ "description": "Include template inline in the component.ts file.",
62
+ "default": false
43
63
  }
44
64
  },
45
65
  "required": ["name"]
@@ -15,4 +15,8 @@ export interface ComponentSchemaOptions {
15
15
  isProvideInRoot?: boolean;
16
16
  path?: string;
17
17
  lang?: "en" | "es";
18
+ style?: string;
19
+ skipTests?: boolean;
20
+ inlineStyle?: boolean;
21
+ inlineTemplate?: boolean;
18
22
  }
@@ -30,7 +30,7 @@ export const <%= classify(name) %>Store = signalStore(
30
30
  withEntities(config),
31
31
  withEntityStatus(),
32
32
  withPagination(),
33
- withComputed(({ entities, entityMap, status: { idSelected } }) => ({
33
+ withComputed(({ entityMap, status: { idSelected } }) => ({
34
34
  <%= camelize(name) %>Seleccionado: computed(() => {
35
35
  const <%= pk %> = idSelected();
36
36
  return <%= pk %> ? entityMap()[<%= pk %>] : null;
@@ -54,8 +54,7 @@ export const <%= classify(name) %>Store = signalStore(
54
54
  onSuccess(new<%= classify(name) %>);
55
55
  }
56
56
  }),
57
- catchError(() => {
58
- const error = new Error('');
57
+ catchError((error) => {
59
58
  patchState(store, (state) => ({
60
59
  status: { ...state.status, addError: error, addLoading: false }
61
60
  }));
@@ -80,9 +79,9 @@ export const <%= classify(name) %>Store = signalStore(
80
79
  status: { ...state.status, error: null, loaded: true, loading: false }
81
80
  }));
82
81
  }),
83
- catchError(() => {
82
+ catchError((error) => {
84
83
  patchState(store, (state) => ({
85
- status: { ...state.status, error: new Error('Error al cargar'), loading: false }
84
+ status: { ...state.status, error, loading: false }
86
85
  }));
87
86
  return EMPTY;
88
87
  })
@@ -121,13 +120,13 @@ export const <%= classify(name) %>Store = signalStore(
121
120
  throw new Error();
122
121
  }
123
122
  }),
124
- catchError(() => {
123
+ catchError((error) => {
125
124
  const idsRemoving = store.status.idsRemoving() || [];
126
125
  patchState(store, (state) => ({
127
126
  status: {
128
127
  ...state.status,
129
128
  _removeLoading: false,
130
- error: new Error(),
129
+ error,
131
130
  idsRemoving: idsRemoving.filter((idRemoving) => idRemoving !== payload)
132
131
  }
133
132
  }));
@@ -183,13 +182,13 @@ export const <%= classify(name) %>Store = signalStore(
183
182
  throw new Error('');
184
183
  }
185
184
  }),
186
- catchError(() => {
185
+ catchError((error) => {
187
186
  const idsUpdating = store.status.idsUpdating() || [];
188
187
  patchState(store, (state) => ({
189
188
  status: {
190
189
  ...state.status,
191
190
  _updateLoading: false,
192
- error: new Error('Error al actualizar'),
191
+ error,
193
192
  idsUpdating: idsUpdating.filter((idUpdating) => idUpdating !== payload.<%= pk %>)
194
193
  }
195
194
  }));