@mimi-ng/cli 0.1.0

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/THIRD_PARTY_NOTICES.md +84 -0
  4. package/dist/collection.json +22 -0
  5. package/dist/init/css.js +32 -0
  6. package/dist/init/index.js +297 -0
  7. package/dist/init/paths.js +43 -0
  8. package/dist/init/schema.json +24 -0
  9. package/dist/ng-add/index.js +8 -0
  10. package/dist/registry.js +30 -0
  11. package/dist/registry.json +194 -0
  12. package/dist/templates/components/avatar/avatar.ts +129 -0
  13. package/dist/templates/components/avatar/avatar.variants.ts +25 -0
  14. package/dist/templates/components/avatar/index.ts +2 -0
  15. package/dist/templates/components/badge/badge.ts +21 -0
  16. package/dist/templates/components/badge/badge.variants.ts +27 -0
  17. package/dist/templates/components/badge/index.ts +2 -0
  18. package/dist/templates/components/button/button.ts +99 -0
  19. package/dist/templates/components/button/button.variants.ts +60 -0
  20. package/dist/templates/components/button/index.ts +2 -0
  21. package/dist/templates/components/card/card.ts +133 -0
  22. package/dist/templates/components/card/index.ts +1 -0
  23. package/dist/templates/components/checkbox/checkbox.ts +178 -0
  24. package/dist/templates/components/checkbox/index.ts +1 -0
  25. package/dist/templates/components/form-field/error-messages.ts +68 -0
  26. package/dist/templates/components/form-field/form-error.ts +82 -0
  27. package/dist/templates/components/form-field/form-field.ts +114 -0
  28. package/dist/templates/components/form-field/index.ts +3 -0
  29. package/dist/templates/components/input/index.ts +2 -0
  30. package/dist/templates/components/input/input.ts +52 -0
  31. package/dist/templates/components/input/input.variants.ts +38 -0
  32. package/dist/templates/components/separator/index.ts +2 -0
  33. package/dist/templates/components/separator/separator.ts +38 -0
  34. package/dist/templates/components/separator/separator.variants.ts +20 -0
  35. package/dist/templates/components/skeleton/index.ts +1 -0
  36. package/dist/templates/components/skeleton/skeleton.ts +27 -0
  37. package/dist/templates/components/switch/index.ts +1 -0
  38. package/dist/templates/components/switch/switch.ts +138 -0
  39. package/dist/templates/components/textarea/index.ts +2 -0
  40. package/dist/templates/components/textarea/textarea.ts +46 -0
  41. package/dist/templates/components/textarea/textarea.variants.ts +27 -0
  42. package/dist/templates/theme/index.ts +2 -0
  43. package/dist/templates/theme/provider.ts +150 -0
  44. package/dist/templates/theme/theme-base.css +241 -0
  45. package/dist/templates/theme/types.ts +124 -0
  46. package/dist/templates/utils/cn.ts +36 -0
  47. package/dist/templates/utils/control-styles.ts +27 -0
  48. package/dist/templates/utils/field-state.ts +164 -0
  49. package/dist/ui/index.js +172 -0
  50. package/dist/ui/schema.json +75 -0
  51. package/dist/utils/project.js +28 -0
  52. package/dist/utils/write-files.js +167 -0
  53. package/package.json +55 -0
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AVAILABLE = void 0;
4
+ exports.ui = ui;
5
+ const schematics_1 = require("@angular-devkit/schematics");
6
+ const utility_1 = require("@schematics/angular/utility");
7
+ const paths_1 = require("../init/paths");
8
+ const registry_1 = require("../registry");
9
+ const project_1 = require("../utils/project");
10
+ const write_files_1 = require("../utils/write-files");
11
+ /** Componentes que se pueden agregar: los ítems `component` del registro. */
12
+ exports.AVAILABLE = Object.entries(registry_1.registry.items)
13
+ .filter(([, item]) => item.type === 'component')
14
+ .map(([name]) => name)
15
+ .sort();
16
+ /**
17
+ * `ng g mimi button input form-field`: copia los componentes y sus dependencias del registro a la
18
+ * carpeta de mimi.json. Sin nombres, pregunta (x-prompt de selección múltiple).
19
+ *
20
+ * Requiere init (mimi.json). Archivos existentes: igual → nada; distinto → se omite con aviso;
21
+ * --overwrite → se reemplaza. Guarda la copia original en .mimi/base/ y la versión de cada
22
+ * componente en mimi.json. Instala las dependencias que falten con el gestor del proyecto.
23
+ */
24
+ function ui(options) {
25
+ return (tree) => {
26
+ const plan = inspect(tree, options);
27
+ const report = (0, write_files_1.emptyReport)();
28
+ return (0, schematics_1.chain)([
29
+ (0, write_files_1.writeTemplates)({
30
+ files: plan.items.flatMap((item) => registry_1.registry.items[item].files),
31
+ componentsDir: plan.componentsDir,
32
+ overwrite: options.overwrite ?? false,
33
+ version: registry_1.registry.version,
34
+ report,
35
+ }),
36
+ updateMimiConfig(plan, report),
37
+ ...Object.entries(plan.newPackages).map(([name, version]) => (0, utility_1.addDependency)(name, version, {
38
+ type: utility_1.DependencyType.Default,
39
+ existing: utility_1.ExistingBehavior.Skip,
40
+ install: utility_1.InstallBehavior.Auto,
41
+ })),
42
+ summary(plan, report),
43
+ ]);
44
+ };
45
+ }
46
+ // ── Verificaciones (sin escribir) ─────────────────────────────────────────────────────
47
+ function inspect(tree, options) {
48
+ const config = (0, project_1.readJson)(tree, 'mimi.json');
49
+ if (!config) {
50
+ throw new schematics_1.SchematicsException('Mimi no está configurado en este proyecto (falta mimi.json). Ejecuta primero:\n' +
51
+ ' ng add @mimi-ng/cli');
52
+ }
53
+ const requested = [...new Set((options.components ?? []).map((name) => name.trim()))].filter(Boolean);
54
+ if (requested.length === 0) {
55
+ throw new schematics_1.SchematicsException(`Indica qué componentes agregar, por ejemplo: ng g mimi button\nDisponibles: ${exports.AVAILABLE.join(', ')}.`);
56
+ }
57
+ const unknown = requested.filter((name) => !exports.AVAILABLE.includes(name));
58
+ if (unknown.length > 0) {
59
+ const lines = unknown.map((name) => {
60
+ const suggestion = closest(name);
61
+ return `"${name}" no es un componente de Mimi.${suggestion ? ` ¿Quisiste decir ${suggestion}?` : ''}`;
62
+ });
63
+ throw new schematics_1.SchematicsException(`${lines.join('\n')}\nDisponibles: ${exports.AVAILABLE.join(', ')}.`);
64
+ }
65
+ const items = [];
66
+ for (const name of requested) {
67
+ for (const item of (0, registry_1.resolveItems)(name))
68
+ if (!items.includes(item))
69
+ items.push(item);
70
+ }
71
+ const pkg = (0, project_1.readJson)(tree, 'package.json');
72
+ const installed = (0, project_1.allDependencies)(pkg);
73
+ const angularRange = installed['@angular/core'];
74
+ const newPackages = {};
75
+ for (const item of items) {
76
+ for (const [name, version] of Object.entries(registry_1.registry.items[item].dependencies ?? {})) {
77
+ if (!installed[name])
78
+ newPackages[name] = version;
79
+ }
80
+ // peerDependencies: solo si el proyecto no las tiene. Las de Angular, con el rango de
81
+ // @angular/core del proyecto, para que no queden desalineadas.
82
+ for (const [name, version] of Object.entries(registry_1.registry.items[item].peerDependencies ?? {})) {
83
+ if (installed[name])
84
+ continue;
85
+ newPackages[name] = name.startsWith('@angular/') && angularRange ? angularRange : version;
86
+ }
87
+ }
88
+ const componentsDir = (0, paths_1.normalizePath)(config.aliases?.components ?? 'src/app/components/ui');
89
+ return { componentsDir, config, items, newPackages };
90
+ }
91
+ /** El componente más parecido (distancia de edición ≤ 2, o que empiece igual). */
92
+ function closest(name) {
93
+ const lower = name.toLowerCase();
94
+ let best = null;
95
+ for (const candidate of exports.AVAILABLE) {
96
+ const distance = candidate.startsWith(lower) ? 1 : editDistance(lower, candidate);
97
+ if (distance <= 2 && (!best || distance < best.distance))
98
+ best = { name: candidate, distance };
99
+ }
100
+ return best?.name ?? null;
101
+ }
102
+ function editDistance(a, b) {
103
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
104
+ for (let i = 1; i <= a.length; i++) {
105
+ let previous = row[0];
106
+ row[0] = i;
107
+ for (let j = 1; j <= b.length; j++) {
108
+ const current = row[j];
109
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
110
+ previous = current;
111
+ }
112
+ }
113
+ return row[b.length];
114
+ }
115
+ // ── mimi.json ─────────────────────────────────────────────────────────────────────────
116
+ /**
117
+ * Registra la versión de cada componente cuyos archivos quedaron iguales a la plantilla. Si
118
+ * alguno se omitió por estar modificado, se conserva lo que había.
119
+ */
120
+ function updateMimiConfig(plan, report) {
121
+ return (tree) => {
122
+ const skipped = new Set(report.skipped);
123
+ const components = { ...plan.config.components };
124
+ for (const item of plan.items) {
125
+ if (registry_1.registry.items[item].type !== 'component')
126
+ continue;
127
+ if (registry_1.registry.items[item].files.some((file) => skipped.has(file)))
128
+ continue;
129
+ components[item] = { version: registry_1.registry.version };
130
+ }
131
+ const next = { ...plan.config, components };
132
+ const text = `${JSON.stringify(next, null, 2)}\n`;
133
+ if (tree.readText('mimi.json') !== text)
134
+ tree.overwrite('mimi.json', text);
135
+ };
136
+ }
137
+ // ── Mensaje final ─────────────────────────────────────────────────────────────────────
138
+ function summary(plan, report) {
139
+ return (_tree, context) => {
140
+ const log = context.logger;
141
+ const path = (file) => `${plan.componentsDir}/${(0, write_files_1.projectPathOf)(file)}`;
142
+ if (report.created.length > 0) {
143
+ log.info('Agregado:');
144
+ for (const file of report.created)
145
+ log.info(` + ${path(file)}`);
146
+ }
147
+ if (report.overwritten.length > 0) {
148
+ log.info('Reemplazado (--overwrite):');
149
+ for (const file of report.overwritten)
150
+ log.info(` ~ ${path(file)}`);
151
+ }
152
+ const itemOf = (file) => plan.items.find((item) => registry_1.registry.items[item].files.includes(file)) ?? file;
153
+ for (const message of (0, write_files_1.updatedMessages)(report, itemOf, registry_1.registry.version))
154
+ log.info(message);
155
+ for (const file of report.skipped) {
156
+ log.warn(`Omitido: ${path(file)} ya existe con otro contenido (usa --overwrite para reemplazarlo).`);
157
+ }
158
+ if (!(0, write_files_1.hasFileChanges)(report) && report.skipped.length === 0) {
159
+ log.info('Nada que hacer: los archivos ya están al día.');
160
+ }
161
+ const packages = Object.entries(plan.newPackages);
162
+ if (packages.length > 0) {
163
+ log.info('Dependencias que se instalan:');
164
+ for (const [name, version] of packages)
165
+ log.info(` ${name}@${version}`);
166
+ }
167
+ if ((0, write_files_1.hasFileChanges)(report)) {
168
+ log.info(`Guarda la carpeta ${write_files_1.BASE_DIR.split('/')[0]}/ en git: Mimi la usa para actualizar tus ` +
169
+ 'componentes sin perder tus cambios.');
170
+ }
171
+ };
172
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "MimiUi",
4
+ "title": "Mimi ui",
5
+ "type": "object",
6
+ "properties": {
7
+ "components": {
8
+ "type": "array",
9
+ "items": {
10
+ "type": "string"
11
+ },
12
+ "description": "Componentes que se agregan (varios a la vez: button input form-field).",
13
+ "$default": {
14
+ "$source": "argv",
15
+ "index": 0
16
+ },
17
+ "x-prompt": {
18
+ "message": "¿Qué componentes quieres agregar?",
19
+ "type": "list",
20
+ "multiselect": true,
21
+ "items": [
22
+ {
23
+ "value": "avatar",
24
+ "label": "avatar"
25
+ },
26
+ {
27
+ "value": "badge",
28
+ "label": "badge"
29
+ },
30
+ {
31
+ "value": "button",
32
+ "label": "button"
33
+ },
34
+ {
35
+ "value": "card",
36
+ "label": "card"
37
+ },
38
+ {
39
+ "value": "checkbox",
40
+ "label": "checkbox"
41
+ },
42
+ {
43
+ "value": "form-field",
44
+ "label": "form-field"
45
+ },
46
+ {
47
+ "value": "input",
48
+ "label": "input"
49
+ },
50
+ {
51
+ "value": "separator",
52
+ "label": "separator"
53
+ },
54
+ {
55
+ "value": "skeleton",
56
+ "label": "skeleton"
57
+ },
58
+ {
59
+ "value": "switch",
60
+ "label": "switch"
61
+ },
62
+ {
63
+ "value": "textarea",
64
+ "label": "textarea"
65
+ }
66
+ ]
67
+ }
68
+ },
69
+ "overwrite": {
70
+ "type": "boolean",
71
+ "default": false,
72
+ "description": "Reemplaza los archivos que ya existan con otro contenido."
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MIMI_COLLECTION = void 0;
4
+ exports.readJson = readJson;
5
+ exports.allDependencies = allDependencies;
6
+ exports.majorOf = majorOf;
7
+ const schematics_1 = require("@angular-devkit/schematics");
8
+ /** Nombre de esta colección (el paquete). */
9
+ exports.MIMI_COLLECTION = '@mimi-ng/cli';
10
+ function readJson(tree, path) {
11
+ if (!tree.exists(path))
12
+ return null;
13
+ try {
14
+ return JSON.parse(tree.readText(path));
15
+ }
16
+ catch {
17
+ throw new schematics_1.SchematicsException(`${path} no es un JSON válido.`);
18
+ }
19
+ }
20
+ /** Dependencias y devDependencies juntas. */
21
+ function allDependencies(pkg) {
22
+ return { ...pkg?.devDependencies, ...pkg?.dependencies };
23
+ }
24
+ /** `^22.1.0`, `~22.0.0`, `>=22`, `22.1.0` → 22. `latest` → null. */
25
+ function majorOf(range) {
26
+ const match = range ? /(\d+)/.exec(range) : null;
27
+ return match ? Number(match[1]) : null;
28
+ }
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasFileChanges = exports.emptyReport = exports.MANIFEST = exports.BASE_DIR = void 0;
4
+ exports.projectPathOf = projectPathOf;
5
+ exports.basePathOf = basePathOf;
6
+ exports.writeTemplates = writeTemplates;
7
+ exports.compareVersions = compareVersions;
8
+ exports.updatedMessages = updatedMessages;
9
+ const schematics_1 = require("@angular-devkit/schematics");
10
+ /** Carpeta de las copias originales, en la raíz del workspace. Debe quedar en git. */
11
+ exports.BASE_DIR = '.mimi/base';
12
+ /** Versión de Mimi con la que se escribió la base de cada archivo. */
13
+ exports.MANIFEST = '.mimi/manifest.json';
14
+ const emptyReport = () => ({
15
+ created: [],
16
+ overwritten: [],
17
+ updated: [],
18
+ unchanged: [],
19
+ skipped: [],
20
+ });
21
+ exports.emptyReport = emptyReport;
22
+ /** ¿El reporte tiene cambios en archivos del proyecto? */
23
+ const hasFileChanges = (report) => report.created.length + report.overwritten.length + report.updated.length > 0;
24
+ exports.hasFileChanges = hasFileChanges;
25
+ /**
26
+ * Ruta de un archivo de las plantillas dentro de la carpeta de componentes del proyecto. Los
27
+ * componentes pierden `components/` para que `@/components/ui/button` apunte a
28
+ * `<carpeta>/button` (`components/button/button.ts` → `button/button.ts`); utils y tema
29
+ * conservan su carpeta (`utils/cn.ts`, `theme/theme-base.css`).
30
+ */
31
+ function projectPathOf(file) {
32
+ return file.replace(/^components\//, '');
33
+ }
34
+ /** Copia original en .mimi/base, con la misma estructura (`.mimi/base/button/button.ts`). */
35
+ function basePathOf(file) {
36
+ return `${exports.BASE_DIR}/${projectPathOf(file)}`;
37
+ }
38
+ function readManifest(tree) {
39
+ if (!tree.exists(exports.MANIFEST))
40
+ return { files: {} };
41
+ try {
42
+ const parsed = JSON.parse(tree.readText(exports.MANIFEST));
43
+ return { files: { ...parsed.files } };
44
+ }
45
+ catch {
46
+ return { files: {} };
47
+ }
48
+ }
49
+ /**
50
+ * Copia archivos de las plantillas del paquete (`dist/templates`, rutas relativas a esa
51
+ * carpeta) a `componentsDir`, revisando el árbol antes de escribir. Si el archivo ya existe:
52
+ *
53
+ * - igual a la plantilla → al día;
54
+ * - igual a su base en `.mimi/base/` → el usuario no lo tocó. Si la base es de esta versión,
55
+ * la diferencia con la plantilla es solo de formato (la CLI de Angular pasa el Prettier del
56
+ * proyecto por lo que escriben los schematics): al día. Si es de una versión anterior, se
57
+ * actualiza;
58
+ * - distinto de su base, o sin base → modificado por el usuario: se omite (o se reemplaza con
59
+ * `overwrite`).
60
+ *
61
+ * Nunca deja que mergeWith encuentre un archivo existente ("merge conflicted"). Guarda la base
62
+ * y su versión en `.mimi/manifest.json` siempre que el archivo quede igual a la plantilla; si se
63
+ * omitió por estar modificado, su base no cambia.
64
+ */
65
+ function writeTemplates(options) {
66
+ const files = new Set(options.files);
67
+ return (tree) => {
68
+ const manifest = readManifest(tree);
69
+ let manifestChanged = false;
70
+ const writeBase = (file, content) => {
71
+ const base = basePathOf(file);
72
+ if (!tree.exists(base))
73
+ tree.create(base, content);
74
+ else if (tree.readText(base) !== content)
75
+ tree.overwrite(base, content);
76
+ const key = projectPathOf(file);
77
+ if (manifest.files[key] !== options.version) {
78
+ manifest.files[key] = options.version;
79
+ manifestChanged = true;
80
+ }
81
+ };
82
+ const copy = (0, schematics_1.mergeWith)((0, schematics_1.apply)((0, schematics_1.url)('../templates'), [
83
+ (0, schematics_1.filter)((path) => files.has(path.replace(/^\//, ''))),
84
+ (0, schematics_1.forEach)((entry) => {
85
+ const file = entry.path.replace(/^\//, '');
86
+ const target = `${options.componentsDir}/${projectPathOf(file)}`;
87
+ const base = basePathOf(file);
88
+ const content = entry.content.toString('utf8');
89
+ if (!tree.exists(target)) {
90
+ tree.create(target, content);
91
+ options.report.created.push(file);
92
+ writeBase(file, content);
93
+ return null;
94
+ }
95
+ const current = tree.readText(target);
96
+ if (current === content) {
97
+ options.report.unchanged.push(file);
98
+ writeBase(file, content);
99
+ }
100
+ else if (tree.exists(base) && tree.readText(base) === current) {
101
+ const from = manifest.files[projectPathOf(file)] ?? null;
102
+ if (from === options.version) {
103
+ // Mismo contenido que escribió Mimi, solo con otro formato.
104
+ options.report.unchanged.push(file);
105
+ }
106
+ else {
107
+ tree.overwrite(target, content);
108
+ options.report.updated.push({ file, from });
109
+ writeBase(file, content);
110
+ }
111
+ }
112
+ else if (options.overwrite) {
113
+ tree.overwrite(target, content);
114
+ options.report.overwritten.push(file);
115
+ writeBase(file, content);
116
+ }
117
+ else {
118
+ options.report.skipped.push(file);
119
+ }
120
+ // Todo se escribe arriba, en el árbol del proyecto: nada llega a mergeWith.
121
+ return null;
122
+ }),
123
+ ]));
124
+ const saveManifest = (t) => {
125
+ if (!manifestChanged)
126
+ return;
127
+ const sorted = Object.fromEntries(Object.entries(manifest.files).sort());
128
+ const text = `${JSON.stringify({ files: sorted }, null, 2)}\n`;
129
+ if (t.exists(exports.MANIFEST))
130
+ t.overwrite(exports.MANIFEST, text);
131
+ else
132
+ t.create(exports.MANIFEST, text);
133
+ };
134
+ return (0, schematics_1.chain)([copy, saveManifest]);
135
+ };
136
+ }
137
+ /** Compara versiones `x.y.z` numéricamente (`0.10.0` > `0.9.0`). `null` es la más vieja. */
138
+ function compareVersions(a, b) {
139
+ if (a === b)
140
+ return 0;
141
+ if (a === null)
142
+ return -1;
143
+ if (b === null)
144
+ return 1;
145
+ const pa = a.split(/[.-]/).map((n) => Number.parseInt(n, 10) || 0);
146
+ const pb = b.split(/[.-]/).map((n) => Number.parseInt(n, 10) || 0);
147
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
148
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
149
+ if (diff !== 0)
150
+ return diff;
151
+ }
152
+ return 0;
153
+ }
154
+ /**
155
+ * «button actualizado de 0.1.0 a 0.2.0», uno por ítem (con la versión más vieja de sus
156
+ * archivos). `itemOf` dice a qué ítem del registro pertenece cada archivo.
157
+ */
158
+ function updatedMessages(report, itemOf, version) {
159
+ const oldest = new Map();
160
+ for (const { file, from } of report.updated) {
161
+ const item = itemOf(file);
162
+ const previous = oldest.get(item);
163
+ if (!oldest.has(item) || compareVersions(from, previous ?? null) < 0)
164
+ oldest.set(item, from);
165
+ }
166
+ return [...oldest].map(([item, from]) => `${item} actualizado de ${from ?? 'una versión anterior'} a ${version}`);
167
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@mimi-ng/cli",
3
+ "version": "0.1.0",
4
+ "description": "Angular + Tailwind CSS 4 UI components that a CLI copies into your project, so you own the code. Schematics for ng add and ng generate.",
5
+ "keywords": [
6
+ "angular",
7
+ "angular-components",
8
+ "tailwind",
9
+ "tailwindcss",
10
+ "tailwindcss-v4",
11
+ "ui",
12
+ "ui-components",
13
+ "component-library",
14
+ "design-system",
15
+ "schematics",
16
+ "ng-add",
17
+ "shadcn",
18
+ "mimi"
19
+ ],
20
+ "homepage": "https://ng.mimiworks.dev",
21
+ "bugs": {
22
+ "url": "https://github.com/nuki23/mimi-ng/issues"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/nuki23/mimi-ng.git",
27
+ "directory": "packages/cli"
28
+ },
29
+ "license": "MIT",
30
+ "author": "Ariel O (https://github.com/nuki23)",
31
+ "type": "commonjs",
32
+ "engines": {
33
+ "node": "^22.22.3 || ^24.15.0 || >=26.0.0"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "schematics": "./dist/collection.json",
39
+ "ng-add": {
40
+ "save": "devDependencies"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "LICENSE",
45
+ "THIRD_PARTY_NOTICES.md"
46
+ ],
47
+ "dependencies": {
48
+ "@angular-devkit/core": "^22.0.0",
49
+ "@angular-devkit/schematics": "^22.0.0",
50
+ "@schematics/angular": "^22.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "node scripts/build.mjs"
54
+ }
55
+ }