@mimi-ng/cli 0.4.0 → 0.5.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.
- package/README.md +29 -7
- package/dist/init/index.js +8 -0
- package/dist/registry.js +4 -1
- package/dist/registry.json +87 -1
- package/dist/templates/blocks/login-01/login-01.ts +12 -3
- package/dist/templates/blocks/login-02/login-02.ts +12 -3
- package/dist/templates/blocks/otp-01/otp-01.ts +12 -3
- package/dist/templates/blocks/pricing-01/pricing-01.ts +1 -1
- package/dist/templates/blocks/settings-01/settings-01.ts +13 -3
- package/dist/templates/blocks/signup-01/signup-01.ts +12 -2
- package/dist/templates/blocks/table-01/index.ts +1 -0
- package/dist/templates/blocks/table-01/table-01.html +214 -0
- package/dist/templates/blocks/table-01/table-01.ts +266 -0
- package/dist/templates/components/checkbox/checkbox.ts +14 -5
- package/dist/templates/components/command/command-texts.ts +65 -0
- package/dist/templates/components/command/command.ts +553 -0
- package/dist/templates/components/command/index.ts +13 -0
- package/dist/templates/components/data-table/data-table-texts.ts +110 -0
- package/dist/templates/components/data-table/data-table.ts +1153 -0
- package/dist/templates/components/data-table/index.ts +18 -0
- package/dist/templates/components/sortable-list/index.ts +9 -0
- package/dist/templates/components/sortable-list/sortable-list-texts.ts +71 -0
- package/dist/templates/components/sortable-list/sortable-list.ts +510 -0
- package/dist/templates/components/tree/index.ts +9 -0
- package/dist/templates/components/tree/tree-texts.ts +45 -0
- package/dist/templates/components/tree/tree.ts +730 -0
- package/dist/templates/theme/provider.ts +4 -0
- package/dist/templates/theme/types.ts +84 -0
- package/dist/ui/index.js +232 -40
- package/dist/ui/schema.json +30 -6
- package/dist/utils/pages.js +138 -0
- package/dist/utils/project.js +33 -9
- package/dist/utils/tailwind.js +15 -11
- package/dist/utils/write-files.js +32 -7
- package/package.json +1 -1
|
@@ -101,6 +101,10 @@ const COMPONENT_PREFIX: Record<keyof MimiComponentTokens, string> = {
|
|
|
101
101
|
date: 'date',
|
|
102
102
|
fileUpload: 'upload',
|
|
103
103
|
colorPicker: 'color-picker',
|
|
104
|
+
tree: 'tree',
|
|
105
|
+
sortable: 'sortable',
|
|
106
|
+
command: 'command',
|
|
107
|
+
dataTable: 'table',
|
|
104
108
|
};
|
|
105
109
|
|
|
106
110
|
/** Nombres de la 0.1.0 que siguen funcionando hasta la 1.0 (spec 12): ruta → qué usar. */
|
|
@@ -555,6 +555,86 @@ export interface MimiColorPickerTokens {
|
|
|
555
555
|
swatchGap?: string;
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Tree. Fondo, hover y el nodo elegido van en el CSS (`--mimi-tree-bg`, `-hover`, `-selected`,
|
|
560
|
+
* `-selected-fg`).
|
|
561
|
+
*/
|
|
562
|
+
export interface MimiTreeTokens {
|
|
563
|
+
/** Relleno del contenedor (6px). */
|
|
564
|
+
padding?: string;
|
|
565
|
+
/** Alto de cada nodo (32px). */
|
|
566
|
+
rowHeight?: string;
|
|
567
|
+
/** Radio del nodo (el global `radius-sm`). */
|
|
568
|
+
rowRadius?: string;
|
|
569
|
+
/** Sangría por nivel (18px). */
|
|
570
|
+
indent?: string;
|
|
571
|
+
/** Tamaño de texto (13.5px). */
|
|
572
|
+
fontSize?: string;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Sortable List. Fondo, el hover del asa y los colores del hueco van en el CSS
|
|
577
|
+
* (`--mimi-sortable-bg`, `-handle-hover`, `-drop`, `-drop-bg`).
|
|
578
|
+
*/
|
|
579
|
+
export interface MimiSortableTokens {
|
|
580
|
+
/** Alto mínimo de cada elemento (56px). */
|
|
581
|
+
itemHeight?: string;
|
|
582
|
+
/** Separación entre elementos (8px). */
|
|
583
|
+
gap?: string;
|
|
584
|
+
/** Radio del elemento y del hueco (el global `radius`). */
|
|
585
|
+
radius?: string;
|
|
586
|
+
/** Giro y escala del elemento levantado (`rotate(-1.5deg) scale(1.03)`). */
|
|
587
|
+
lift?: string;
|
|
588
|
+
/** Curva del reacomodo (`cubic-bezier(.2, .8, .2, 1)`). */
|
|
589
|
+
easing?: string;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Command. El fondo del comando activo va en el CSS (`--mimi-command-active`).
|
|
594
|
+
*/
|
|
595
|
+
export interface MimiCommandTokens {
|
|
596
|
+
/** Ancho del panel en el diálogo (560px). */
|
|
597
|
+
width?: string;
|
|
598
|
+
/** Distancia del panel al borde superior en el diálogo (36px). */
|
|
599
|
+
top?: string;
|
|
600
|
+
/** Alto de la fila del buscador (52px). */
|
|
601
|
+
inputHeight?: string;
|
|
602
|
+
/** Tamaño del texto del buscador (15px). */
|
|
603
|
+
inputFontSize?: string;
|
|
604
|
+
/** Alto máximo de la lista (332px). */
|
|
605
|
+
listHeight?: string;
|
|
606
|
+
/** Alto de cada comando (40px). */
|
|
607
|
+
itemHeight?: string;
|
|
608
|
+
/** Radio del comando (el global `radius-sm`). */
|
|
609
|
+
itemRadius?: string;
|
|
610
|
+
/** Caja del ícono del comando (26px). */
|
|
611
|
+
iconSize?: string;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Data Table. Los fondos (tabla, encabezado, hover, elegida, detalle) y la sombra de la columna
|
|
616
|
+
* fija van en el CSS (`--mimi-table-bg`, `-head-bg`, `-row-hover`, `-row-selected`,
|
|
617
|
+
* `-expanded-bg`, `-sticky-shadow`).
|
|
618
|
+
*/
|
|
619
|
+
export interface MimiTableTokens {
|
|
620
|
+
/** Alto de fila en la densidad compacta (42px). */
|
|
621
|
+
rowHeight?: string;
|
|
622
|
+
/** Alto de fila en la densidad cómoda (60px). */
|
|
623
|
+
rowHeightComfortable?: string;
|
|
624
|
+
/** Alto del encabezado, compacta (38px). */
|
|
625
|
+
headHeight?: string;
|
|
626
|
+
/** Alto del encabezado, cómoda (44px). */
|
|
627
|
+
headHeightComfortable?: string;
|
|
628
|
+
/** Texto de las celdas, compacta (13px). */
|
|
629
|
+
fontSize?: string;
|
|
630
|
+
/** Texto de las celdas, cómoda (14px). */
|
|
631
|
+
fontSizeComfortable?: string;
|
|
632
|
+
/** Relleno horizontal de la celda (10px). */
|
|
633
|
+
cellPx?: string;
|
|
634
|
+
/** Alto mínimo de la barra (56px). */
|
|
635
|
+
toolbarHeight?: string;
|
|
636
|
+
}
|
|
637
|
+
|
|
558
638
|
/**
|
|
559
639
|
* Tokens por componente (spec 13). Solo los que no dependen del modo: los colores de los
|
|
560
640
|
* componentes (`--mimi-<prefijo>-bg`, `-fg`, `-border`…) se cambian en el CSS (spec 6.2).
|
|
@@ -594,6 +674,10 @@ export interface MimiComponentTokens {
|
|
|
594
674
|
date?: MimiDateTokens;
|
|
595
675
|
fileUpload?: MimiFileUploadTokens;
|
|
596
676
|
colorPicker?: MimiColorPickerTokens;
|
|
677
|
+
tree?: MimiTreeTokens;
|
|
678
|
+
sortable?: MimiSortableTokens;
|
|
679
|
+
command?: MimiCommandTokens;
|
|
680
|
+
dataTable?: MimiTableTokens;
|
|
597
681
|
}
|
|
598
682
|
|
|
599
683
|
export interface MimiThemePreset {
|
package/dist/ui/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const css_1 = require("../init/css");
|
|
|
8
8
|
const items_1 = require("../init/items");
|
|
9
9
|
const paths_1 = require("../init/paths");
|
|
10
10
|
const registry_1 = require("../registry");
|
|
11
|
+
const pages_1 = require("../utils/pages");
|
|
11
12
|
const project_1 = require("../utils/project");
|
|
12
13
|
const tailwind_1 = require("../utils/tailwind");
|
|
13
14
|
const write_files_1 = require("../utils/write-files");
|
|
@@ -40,13 +41,14 @@ function ui(options) {
|
|
|
40
41
|
implicit: plan.implicit,
|
|
41
42
|
componentsDir: plan.componentsDir,
|
|
42
43
|
blocksDir: plan.blocksDir,
|
|
44
|
+
pages: plan.pages,
|
|
43
45
|
overwrite: options.overwrite ?? false,
|
|
44
46
|
version: registry_1.registry.version,
|
|
45
47
|
report,
|
|
46
48
|
}),
|
|
47
49
|
updateMimiConfig(plan, report),
|
|
48
50
|
updateGlobalCss(plan),
|
|
49
|
-
|
|
51
|
+
addFolderSources(plan),
|
|
50
52
|
...Object.entries(plan.newPackages).map(([name, version]) => (0, utility_1.addDependency)(name, version, {
|
|
51
53
|
type: utility_1.DependencyType.Default,
|
|
52
54
|
existing: utility_1.ExistingBehavior.Skip,
|
|
@@ -112,12 +114,17 @@ function inspect(tree, options) {
|
|
|
112
114
|
const implicit = [...new Set(items_1.INIT_ITEMS.flatMap((item) => (0, registry_1.resolveItems)(item)))].filter((item) => !items.includes(item));
|
|
113
115
|
const componentsDir = (0, paths_1.normalizePath)(config.aliases?.components ?? 'src/app/components/ui');
|
|
114
116
|
const blocksDir = (0, paths_1.normalizePath)(config.aliases?.blocks ?? (0, project_1.defaultBlocksDir)(componentsDir));
|
|
117
|
+
const pagesDir = (0, paths_1.normalizePath)(config.aliases?.pages ?? (0, project_1.defaultPagesDir)(componentsDir));
|
|
118
|
+
const { pages, legacyPages } = planPages(tree, config, items, options.name, blocksDir, pagesDir);
|
|
115
119
|
const outdated = outdatedPeers(tree, items, installed, newPackages);
|
|
116
120
|
const updatable = PEERS_WITH_MINIMUM.filter((name) => installed[name] || newPackages[name]);
|
|
117
121
|
const missingExclusion = mimiExclusion(tree, config, installed);
|
|
118
122
|
return {
|
|
119
123
|
componentsDir,
|
|
120
124
|
blocksDir,
|
|
125
|
+
pagesDir,
|
|
126
|
+
pages,
|
|
127
|
+
legacyPages,
|
|
121
128
|
config,
|
|
122
129
|
items,
|
|
123
130
|
implicit,
|
|
@@ -127,6 +134,134 @@ function inspect(tree, options) {
|
|
|
127
134
|
missingExclusion,
|
|
128
135
|
};
|
|
129
136
|
}
|
|
137
|
+
// ── Páginas (B.11) ────────────────────────────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* Dónde va cada Block de tipo `page` de esta ejecución. Todo se comprueba antes de escribir: si
|
|
140
|
+
* algo no cuadra (un `--name` inválido, una carpeta que ya existe y no es esa página, dos páginas
|
|
141
|
+
* en la misma carpeta), falla sin cambiar nada.
|
|
142
|
+
*
|
|
143
|
+
* - Ya generada (`mimi.json → blocks.<ítem>.path`): se actualiza ahí, con su nombre (B.11-f).
|
|
144
|
+
* - Instalada con la 0.4.0 (`<aliases.blocks>/<ítem>/<ítem>.ts`, sin `path`): se queda donde está
|
|
145
|
+
* y se actualiza como antes (B.11-g).
|
|
146
|
+
* - Nueva: `<aliases.pages>/<nombre>/`, con `--name` o el nombre por defecto (B.11-c).
|
|
147
|
+
*/
|
|
148
|
+
function planPages(tree, config, items, name, blocksDir, pagesDir) {
|
|
149
|
+
const pageItems = items.filter((item) => (0, registry_1.isPageBlock)(item));
|
|
150
|
+
if (name !== undefined) {
|
|
151
|
+
if (!pages_1.PAGE_NAME.test(name)) {
|
|
152
|
+
throw new schematics_1.SchematicsException(`--name ${JSON.stringify(name)} no sirve como nombre de página: usa minúsculas, números y ` +
|
|
153
|
+
'guiones, empezando con una letra (por ejemplo, --name admin-login). No se cambió nada.');
|
|
154
|
+
}
|
|
155
|
+
if (pageItems.length !== 1) {
|
|
156
|
+
const pageNames = exports.AVAILABLE.filter((item) => (0, registry_1.isPageBlock)(item)).join(', ');
|
|
157
|
+
throw new schematics_1.SchematicsException((pageItems.length === 0
|
|
158
|
+
? '--name es el nombre de la página que se genera, y no pediste ningún Block de tipo página'
|
|
159
|
+
: `--name sirve para una sola página, y pediste ${pageItems.join(', ')}: agrégalas por separado`) +
|
|
160
|
+
` (son páginas: ${pageNames}). No se cambió nada.`);
|
|
161
|
+
}
|
|
162
|
+
if ((0, pages_1.isBlockLikeName)(pageItems[0], name)) {
|
|
163
|
+
throw new schematics_1.SchematicsException(`--name ${name} lleva el nombre del Block (${pageItems[0]}): elige el de tu página, por ` +
|
|
164
|
+
`ejemplo --name ${(0, pages_1.defaultPageName)(pageItems[0])} o --name admin-` +
|
|
165
|
+
`${(0, pages_1.defaultPageName)(pageItems[0])}. No se cambió nada.`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const prefix = projectPrefix(tree, pagesDir);
|
|
169
|
+
const pages = {};
|
|
170
|
+
const legacyPages = [];
|
|
171
|
+
const taken = new Map();
|
|
172
|
+
for (const item of pageItems) {
|
|
173
|
+
const entry = config.blocks?.[item];
|
|
174
|
+
let target;
|
|
175
|
+
if (entry?.path) {
|
|
176
|
+
const dir = (0, paths_1.normalizePath)(entry.path);
|
|
177
|
+
const trackedName = entry.name && pages_1.PAGE_NAME.test(entry.name) ? entry.name : basename(dir);
|
|
178
|
+
if (name !== undefined && name !== trackedName) {
|
|
179
|
+
throw new schematics_1.SchematicsException(`${item} ya es tu página ${trackedName} (${dir}/): Mimi la sigue por su nombre del ` +
|
|
180
|
+
`registro, así que está en un solo sitio. Para actualizarla, ejecuta ng g mimi ${item} ` +
|
|
181
|
+
'sin --name. Si quieres otra copia con otro nombre, renombra o mueve la tuya y cambia ' +
|
|
182
|
+
`"path" y "name" de blocks.${item} en mimi.json. No se cambió nada.`);
|
|
183
|
+
}
|
|
184
|
+
target = { item, dir, name: trackedName, prefix };
|
|
185
|
+
}
|
|
186
|
+
else if (tree.exists(`${blocksDir}/${item}/${item}.ts`)) {
|
|
187
|
+
if (name !== undefined) {
|
|
188
|
+
throw new schematics_1.SchematicsException(`${item} está instalado como en la 0.4.0, en ${blocksDir}/${item}/, y se sigue ` +
|
|
189
|
+
'actualizando ahí. Para generarlo como página, guarda tus cambios (por ejemplo, en git), ' +
|
|
190
|
+
`borra esa carpeta y vuelve a ejecutar el comando. No se cambió nada.`);
|
|
191
|
+
}
|
|
192
|
+
legacyPages.push(item);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const pageName = name ?? (0, pages_1.defaultPageName)(item);
|
|
197
|
+
const dir = `${pagesDir}/${pageName}`;
|
|
198
|
+
if (hasContent(tree, dir)) {
|
|
199
|
+
const owner = Object.entries(config.blocks ?? {}).find(([, other]) => other?.path && (0, paths_1.normalizePath)(other.path) === dir)?.[0];
|
|
200
|
+
throw new schematics_1.SchematicsException(`La carpeta ${dir}/ ya existe${owner ? ` (es tu página de ${owner})` : ''}, así que ` +
|
|
201
|
+
`${item} no se generó ahí: Mimi no sobrescribe tus archivos. Elige otro nombre con ` +
|
|
202
|
+
`--name, por ejemplo: ng g mimi ${item} --name ${pageName}-2. No se cambió nada.`);
|
|
203
|
+
}
|
|
204
|
+
target = { item, dir, name: pageName, prefix };
|
|
205
|
+
}
|
|
206
|
+
const other = taken.get(target.dir);
|
|
207
|
+
if (other) {
|
|
208
|
+
throw new schematics_1.SchematicsException(`${other} y ${item} irían a la misma carpeta (${target.dir}/). Agrégalos por separado, ` +
|
|
209
|
+
`uno de ellos con --name (por ejemplo: ng g mimi ${item} --name ${target.name}-2). ` +
|
|
210
|
+
'No se cambió nada.');
|
|
211
|
+
}
|
|
212
|
+
taken.set(target.dir, item);
|
|
213
|
+
pages[item] = target;
|
|
214
|
+
}
|
|
215
|
+
return { pages, legacyPages };
|
|
216
|
+
}
|
|
217
|
+
const basename = (path) => path.slice(path.lastIndexOf('/') + 1);
|
|
218
|
+
/** ¿La carpeta existe y tiene algo dentro? (Un árbol de schematics no tiene carpetas vacías.) */
|
|
219
|
+
function hasContent(tree, dir) {
|
|
220
|
+
const entry = tree.getDir(dir);
|
|
221
|
+
return entry.subfiles.length + entry.subdirs.length > 0;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Prefijo de los selectores (`app`, de angular.json → projects → prefix): el del proyecto que
|
|
225
|
+
* contiene la carpeta de páginas o, si no, el de la única aplicación. Si no se sabe, `app`.
|
|
226
|
+
*/
|
|
227
|
+
function projectPrefix(tree, dir) {
|
|
228
|
+
let workspace = null;
|
|
229
|
+
try {
|
|
230
|
+
workspace = (0, project_1.readJson)(tree, 'angular.json');
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return 'app';
|
|
234
|
+
}
|
|
235
|
+
const projects = Object.values(workspace?.projects ?? {});
|
|
236
|
+
const sourceOf = (project) => (0, paths_1.normalizePath)(project.sourceRoot ?? `${project.root ?? ''}/src`);
|
|
237
|
+
const inside = projects
|
|
238
|
+
.filter((project) => `${dir}/`.startsWith(`${sourceOf(project)}/`))
|
|
239
|
+
.sort((a, b) => sourceOf(b).length - sourceOf(a).length);
|
|
240
|
+
const apps = projects.filter((project) => project.projectType === 'application');
|
|
241
|
+
const project = inside[0] ?? (apps.length === 1 ? apps[0] : undefined);
|
|
242
|
+
return typeof project?.prefix === 'string' && /^[a-z][a-z0-9-]*$|^$/.test(project.prefix)
|
|
243
|
+
? project.prefix
|
|
244
|
+
: 'app';
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Cómo importa `app.routes.ts` el archivo de rutas de una página: relativo a la carpeta del
|
|
248
|
+
* `app.routes.ts` que está por encima de la página (`src/app/pages/login` →
|
|
249
|
+
* `./pages/login/login.routes`). Se busca subiendo desde la carpeta de arriba de la página (nunca
|
|
250
|
+
* la suya: una página `app` tiene su propio `app.routes.ts`); si no hay ninguno, la carpeta `app/`
|
|
251
|
+
* más cercana por encima. Si tampoco, la ruta del workspace (y `appDir` null: hay que ajustarla).
|
|
252
|
+
*/
|
|
253
|
+
function routesImportOf(tree, page) {
|
|
254
|
+
const routes = `${page.dir}/${page.name}.routes`;
|
|
255
|
+
const ancestors = [];
|
|
256
|
+
const parts = page.dir.split('/');
|
|
257
|
+
for (let length = parts.length - 1; length > 0; length--) {
|
|
258
|
+
ancestors.push(parts.slice(0, length).join('/'));
|
|
259
|
+
}
|
|
260
|
+
const appDir = ancestors.find((dir) => tree.exists(`${dir}/app.routes.ts`)) ??
|
|
261
|
+
ancestors.find((dir) => basename(dir) === 'app') ??
|
|
262
|
+
null;
|
|
263
|
+
return appDir ? { path: (0, paths_1.relativePath)(appDir, routes), appDir } : { path: routes, appDir: null };
|
|
264
|
+
}
|
|
130
265
|
/**
|
|
131
266
|
* Sin `@source not` hacia `.mimi/`, Tailwind escanea las copias originales (van en git) y genera
|
|
132
267
|
* sus clases aunque el usuario ya las haya cambiado en sus componentes. Proyectos de la 0.3.0 o
|
|
@@ -290,21 +425,36 @@ function updateMimiConfig(plan, report) {
|
|
|
290
425
|
const components = { ...plan.config.components };
|
|
291
426
|
const blocks = { ...plan.config.blocks };
|
|
292
427
|
for (const item of plan.items) {
|
|
293
|
-
const { type
|
|
428
|
+
const { type } = registry_1.registry.items[item];
|
|
294
429
|
if (type !== 'component' && type !== 'block')
|
|
295
430
|
continue;
|
|
296
431
|
if (blocked.has(item))
|
|
297
432
|
continue;
|
|
298
|
-
if (
|
|
433
|
+
if ((0, write_files_1.itemFiles)(item, plan.pages).some((file) => skipped.has(file)))
|
|
299
434
|
continue;
|
|
300
|
-
(type === '
|
|
435
|
+
if (type === 'component') {
|
|
436
|
+
components[item] = { version: registry_1.registry.version };
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
// Una página guarda dónde está y con qué nombre se generó (B.11-f).
|
|
440
|
+
const page = plan.pages[item];
|
|
441
|
+
blocks[item] = page
|
|
442
|
+
? { version: registry_1.registry.version, path: page.dir, name: page.name }
|
|
443
|
+
: { version: registry_1.registry.version };
|
|
301
444
|
}
|
|
302
445
|
const next = { ...plan.config, components };
|
|
303
446
|
if (Object.keys(blocks).length > 0) {
|
|
304
447
|
next.blocks = blocks;
|
|
305
|
-
// Proyectos
|
|
306
|
-
|
|
307
|
-
|
|
448
|
+
// Proyectos anteriores: la carpeta queda escrita la primera vez que se copia un Block
|
|
449
|
+
// (B.1-a) o se genera una página (B.11-b).
|
|
450
|
+
const entries = Object.values(blocks);
|
|
451
|
+
const aliases = { ...plan.config.aliases };
|
|
452
|
+
if (!aliases.blocks && entries.some((entry) => !entry.path))
|
|
453
|
+
aliases.blocks = plan.blocksDir;
|
|
454
|
+
if (!aliases.pages && entries.some((entry) => entry.path))
|
|
455
|
+
aliases.pages = plan.pagesDir;
|
|
456
|
+
if (JSON.stringify(aliases) !== JSON.stringify(plan.config.aliases ?? {})) {
|
|
457
|
+
next.aliases = aliases;
|
|
308
458
|
}
|
|
309
459
|
}
|
|
310
460
|
const text = `${JSON.stringify(next, null, 2)}\n`;
|
|
@@ -345,22 +495,40 @@ function updateGlobalCss(plan) {
|
|
|
345
495
|
};
|
|
346
496
|
}
|
|
347
497
|
/**
|
|
348
|
-
* G1: con `@import "tailwindcss" source(none)`, Tailwind no escanea la carpeta de los Blocks
|
|
349
|
-
* la pantalla queda sin sus estilos. Si se copia un Block
|
|
498
|
+
* G1: con `@import "tailwindcss" source(none)`, Tailwind no escanea la carpeta de los Blocks ni
|
|
499
|
+
* la de las páginas (B.11-i), y la pantalla queda sin sus estilos. Si se copia un Block o se
|
|
500
|
+
* genera una página, se agrega el `@source` de su carpeta (una vez).
|
|
350
501
|
*/
|
|
351
|
-
function
|
|
502
|
+
function addFolderSources(plan) {
|
|
352
503
|
return (tree, context) => {
|
|
353
|
-
|
|
504
|
+
const dirs = [];
|
|
505
|
+
if (plan.items.some((item) => registry_1.registry.items[item].type === 'block' && !plan.pages[item])) {
|
|
506
|
+
dirs.push(plan.blocksDir);
|
|
507
|
+
}
|
|
508
|
+
for (const page of Object.values(plan.pages)) {
|
|
509
|
+
const dir = `${page.dir}/`.startsWith(`${plan.pagesDir}/`) ? plan.pagesDir : page.dir;
|
|
510
|
+
if (!dirs.includes(dir))
|
|
511
|
+
dirs.push(dir);
|
|
512
|
+
}
|
|
513
|
+
if (dirs.length === 0)
|
|
354
514
|
return;
|
|
355
515
|
const cssPath = plan.config.tailwind?.css;
|
|
356
516
|
if (!cssPath || !tree.exists(cssPath))
|
|
357
517
|
return;
|
|
358
|
-
|
|
359
|
-
|
|
518
|
+
let css = tree.readText(cssPath);
|
|
519
|
+
const lines = [];
|
|
520
|
+
for (const dir of dirs) {
|
|
521
|
+
const result = (0, tailwind_1.withFolderSource)(css, cssPath, [plan.componentsDir, plan.blocksDir, plan.pagesDir], dir);
|
|
522
|
+
if (!result)
|
|
523
|
+
continue;
|
|
524
|
+
css = result.css;
|
|
525
|
+
lines.push(result.line);
|
|
526
|
+
}
|
|
527
|
+
if (lines.length === 0)
|
|
360
528
|
return;
|
|
361
|
-
tree.overwrite(cssPath,
|
|
529
|
+
tree.overwrite(cssPath, css);
|
|
362
530
|
context.logger.info(`Agregado a ${cssPath} (tu @import de Tailwind usa source(none), así que Tailwind solo ` +
|
|
363
|
-
`escanea las carpetas que indicas):\n
|
|
531
|
+
`escanea las carpetas que indicas):\n${lines.map((line) => ` ${line}`).join('\n')}`);
|
|
364
532
|
};
|
|
365
533
|
}
|
|
366
534
|
// ── Mensaje final ─────────────────────────────────────────────────────────────────────
|
|
@@ -384,18 +552,12 @@ function blocksUsingUpdated(plan, report, itemOf) {
|
|
|
384
552
|
}
|
|
385
553
|
return messages;
|
|
386
554
|
}
|
|
387
|
-
/**
|
|
388
|
-
|
|
389
|
-
* `./blocks/login-01`. Si la carpeta no está dentro de `app/`, la ruta del workspace.
|
|
390
|
-
*/
|
|
391
|
-
function routesImport(blocksDir, item) {
|
|
392
|
-
const at = `/${blocksDir}`.lastIndexOf('/app/');
|
|
393
|
-
return at >= 0 ? `./${`/${blocksDir}`.slice(at + 5)}/${item}` : `${blocksDir}/${item}`;
|
|
394
|
-
}
|
|
555
|
+
/** `pricing-01` → `Pricing01`: la clase de un Block copiado tal cual. */
|
|
556
|
+
const blockClassName = (item) => item.replace(/(^|-)([a-z0-9])/g, (_, _dash, c) => c.toUpperCase());
|
|
395
557
|
function summary(plan, report) {
|
|
396
|
-
return (
|
|
558
|
+
return (tree, context) => {
|
|
397
559
|
const log = context.logger;
|
|
398
|
-
const path = (file) => (0, write_files_1.targetPathOf)(file, plan.componentsDir, plan.blocksDir);
|
|
560
|
+
const path = (file) => (0, write_files_1.targetPathOf)(file, plan.componentsDir, plan.blocksDir, plan.pages);
|
|
399
561
|
if (report.created.length > 0) {
|
|
400
562
|
log.info('Agregado:');
|
|
401
563
|
for (const file of report.created)
|
|
@@ -406,16 +568,23 @@ function summary(plan, report) {
|
|
|
406
568
|
for (const file of report.overwritten)
|
|
407
569
|
log.info(` ~ ${path(file)}`);
|
|
408
570
|
// Un Block es una pantalla del usuario: --overwrite la reemplaza entera (B.1, trampas).
|
|
409
|
-
const screens =
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
571
|
+
const screens = new Map();
|
|
572
|
+
for (const file of report.overwritten) {
|
|
573
|
+
if ((0, pages_1.isPageFile)(file)) {
|
|
574
|
+
const item = (0, pages_1.pageItemOf)(file);
|
|
575
|
+
screens.set(item, `${plan.pages[item].dir}/`);
|
|
576
|
+
}
|
|
577
|
+
else if ((0, write_files_1.isBlockFile)(file)) {
|
|
578
|
+
const item = file.split('/')[1];
|
|
579
|
+
screens.set(item, `${plan.blocksDir}/${item}/`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
for (const [block, dir] of screens) {
|
|
583
|
+
log.warn(`--overwrite reemplazó tu pantalla ${block} (${dir}) por la de ` +
|
|
414
584
|
`Mimi ${registry_1.registry.version}. Si tenías cambios, recupéralos de git.`);
|
|
415
585
|
}
|
|
416
586
|
}
|
|
417
|
-
const itemOf = (file) => [...plan.items, ...plan.implicit].find((item) =>
|
|
418
|
-
file;
|
|
587
|
+
const itemOf = (file) => [...plan.items, ...plan.implicit].find((item) => (0, write_files_1.itemFiles)(item, plan.pages).includes(file)) ?? file;
|
|
419
588
|
for (const message of (0, write_files_1.updatedMessages)(report, itemOf, registry_1.registry.version))
|
|
420
589
|
log.info(message);
|
|
421
590
|
for (const message of blocksUsingUpdated(plan, report, itemOf))
|
|
@@ -423,19 +592,42 @@ function summary(plan, report) {
|
|
|
423
592
|
for (const file of report.skipped) {
|
|
424
593
|
log.warn(`Omitido: ${path(file)} ya existe con otro contenido (usa --overwrite para reemplazarlo).`);
|
|
425
594
|
}
|
|
426
|
-
for (const message of (0, write_files_1.blockedMessages)(report, plan.componentsDir, registry_1.registry.version, plan.blocksDir)) {
|
|
595
|
+
for (const message of (0, write_files_1.blockedMessages)(report, plan.componentsDir, registry_1.registry.version, plan.blocksDir, plan.pages)) {
|
|
427
596
|
log.warn(message);
|
|
428
597
|
}
|
|
429
|
-
//
|
|
598
|
+
// Una página nueva se enruta con la línea exacta (B.11-e); la CLI no edita app.routes.ts.
|
|
599
|
+
// Una sección nueva va dentro de una página.
|
|
430
600
|
const created = new Set(report.created);
|
|
431
601
|
for (const item of plan.items) {
|
|
432
|
-
|
|
433
|
-
|
|
602
|
+
if (registry_1.registry.items[item].type !== 'block' || plan.legacyPages.includes(item))
|
|
603
|
+
continue;
|
|
604
|
+
const page = plan.pages[item];
|
|
605
|
+
if (page) {
|
|
606
|
+
if (!created.has((0, pages_1.routesKey)(item)))
|
|
607
|
+
continue;
|
|
608
|
+
const { path: routes, appDir } = routesImportOf(tree, page);
|
|
609
|
+
const routesFile = appDir ? `${appDir}/app.routes.ts` : null;
|
|
610
|
+
log.info(`${item} es ahora tu página ${page.name} (${page.dir}/, clase ` +
|
|
611
|
+
`${(0, pages_1.pageClassName)(page.name)}). Para abrirla en /${page.name}, agrega esta línea a ` +
|
|
612
|
+
(routesFile && tree.exists(routesFile)
|
|
613
|
+
? `las rutas de ${routesFile}:`
|
|
614
|
+
: 'tus rutas (la ruta del import es relativa a tu app.routes.ts):'));
|
|
615
|
+
log.info(` ${(0, pages_1.routeLine)(page, routes)}`);
|
|
434
616
|
continue;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
617
|
+
}
|
|
618
|
+
if (!registry_1.registry.items[item].files.some((file) => created.has(file)))
|
|
619
|
+
continue;
|
|
620
|
+
log.info(`${item} es una sección (una parte de una página): ponla en una de tus páginas con ` +
|
|
621
|
+
`<mimi-${item} />, importando ${blockClassName(item)} de ${plan.blocksDir}/${item}.`);
|
|
622
|
+
}
|
|
623
|
+
// Pantallas de la 0.4.0: siguen donde estaban (B.11-g). Una bloqueada ya tiene su aviso.
|
|
624
|
+
const blockedItems = new Set(report.blocked.map((b) => b.item));
|
|
625
|
+
for (const item of plan.legacyPages.filter((legacy) => !blockedItems.has(legacy))) {
|
|
626
|
+
log.info(`${item} sigue en ${plan.blocksDir}/${item}/, donde lo instalaste con la 0.4.0, y se ` +
|
|
627
|
+
`actualiza ahí. Los Blocks de pantalla se generan ahora como páginas ` +
|
|
628
|
+
`(${plan.pagesDir}/${(0, pages_1.defaultPageName)(item)}/, ${(0, pages_1.pageClassName)((0, pages_1.defaultPageName)(item))}): ` +
|
|
629
|
+
'para pasarlo, guarda tus cambios, borra esa carpeta y vuelve a ejecutar ' +
|
|
630
|
+
`ng g mimi ${item}.`);
|
|
439
631
|
}
|
|
440
632
|
if (!(0, write_files_1.hasFileChanges)(report) && report.skipped.length === 0 && report.blocked.length === 0) {
|
|
441
633
|
log.info('Nada que hacer: los archivos ya están al día.');
|
package/dist/ui/schema.json
CHANGED
|
@@ -51,6 +51,10 @@
|
|
|
51
51
|
"value": "combobox",
|
|
52
52
|
"label": "combobox"
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
"value": "command",
|
|
56
|
+
"label": "command"
|
|
57
|
+
},
|
|
54
58
|
{
|
|
55
59
|
"value": "confirm",
|
|
56
60
|
"label": "confirm"
|
|
@@ -59,6 +63,10 @@
|
|
|
59
63
|
"value": "context-menu",
|
|
60
64
|
"label": "context-menu"
|
|
61
65
|
},
|
|
66
|
+
{
|
|
67
|
+
"value": "data-table",
|
|
68
|
+
"label": "data-table"
|
|
69
|
+
},
|
|
62
70
|
{
|
|
63
71
|
"value": "date-picker",
|
|
64
72
|
"label": "date-picker"
|
|
@@ -131,6 +139,10 @@
|
|
|
131
139
|
"value": "slider",
|
|
132
140
|
"label": "slider"
|
|
133
141
|
},
|
|
142
|
+
{
|
|
143
|
+
"value": "sortable-list",
|
|
144
|
+
"label": "sortable-list"
|
|
145
|
+
},
|
|
134
146
|
{
|
|
135
147
|
"value": "switch",
|
|
136
148
|
"label": "switch"
|
|
@@ -163,29 +175,37 @@
|
|
|
163
175
|
"value": "tooltip",
|
|
164
176
|
"label": "tooltip"
|
|
165
177
|
},
|
|
178
|
+
{
|
|
179
|
+
"value": "tree",
|
|
180
|
+
"label": "tree"
|
|
181
|
+
},
|
|
166
182
|
{
|
|
167
183
|
"value": "login-01",
|
|
168
|
-
"label": "login-01 (Block)"
|
|
184
|
+
"label": "login-01 (Block, página)"
|
|
169
185
|
},
|
|
170
186
|
{
|
|
171
187
|
"value": "login-02",
|
|
172
|
-
"label": "login-02 (Block)"
|
|
188
|
+
"label": "login-02 (Block, página)"
|
|
173
189
|
},
|
|
174
190
|
{
|
|
175
191
|
"value": "otp-01",
|
|
176
|
-
"label": "otp-01 (Block)"
|
|
192
|
+
"label": "otp-01 (Block, página)"
|
|
177
193
|
},
|
|
178
194
|
{
|
|
179
195
|
"value": "pricing-01",
|
|
180
|
-
"label": "pricing-01 (Block)"
|
|
196
|
+
"label": "pricing-01 (Block, sección)"
|
|
181
197
|
},
|
|
182
198
|
{
|
|
183
199
|
"value": "settings-01",
|
|
184
|
-
"label": "settings-01 (Block)"
|
|
200
|
+
"label": "settings-01 (Block, página)"
|
|
185
201
|
},
|
|
186
202
|
{
|
|
187
203
|
"value": "signup-01",
|
|
188
|
-
"label": "signup-01 (Block)"
|
|
204
|
+
"label": "signup-01 (Block, página)"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"value": "table-01",
|
|
208
|
+
"label": "table-01 (Block, página)"
|
|
189
209
|
}
|
|
190
210
|
]
|
|
191
211
|
}
|
|
@@ -194,6 +214,10 @@
|
|
|
194
214
|
"type": "boolean",
|
|
195
215
|
"default": false,
|
|
196
216
|
"description": "Reemplaza los archivos que ya existan con otro contenido."
|
|
217
|
+
},
|
|
218
|
+
"name": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"description": "Nombre de la página que genera un Block de tipo página (por defecto, el del Block sin el número: login-01 → login). En kebab-case: --name admin-login."
|
|
197
221
|
}
|
|
198
222
|
}
|
|
199
223
|
}
|