@mimi-ng/cli 0.3.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 +42 -1
- package/dist/init/css.js +1 -1
- package/dist/init/index.js +33 -0
- package/dist/init/paths.js +20 -0
- package/dist/registry.js +4 -1
- package/dist/registry.json +198 -1
- package/dist/templates/blocks/login-01/index.ts +1 -0
- package/dist/templates/blocks/login-01/login-01.html +174 -0
- package/dist/templates/blocks/login-01/login-01.ts +114 -0
- package/dist/templates/blocks/login-02/index.ts +1 -0
- package/dist/templates/blocks/login-02/login-02.html +186 -0
- package/dist/templates/blocks/login-02/login-02.ts +101 -0
- package/dist/templates/blocks/otp-01/index.ts +1 -0
- package/dist/templates/blocks/otp-01/otp-01.html +166 -0
- package/dist/templates/blocks/otp-01/otp-01.ts +221 -0
- package/dist/templates/blocks/pricing-01/index.ts +1 -0
- package/dist/templates/blocks/pricing-01/pricing-01.html +173 -0
- package/dist/templates/blocks/pricing-01/pricing-01.ts +178 -0
- package/dist/templates/blocks/settings-01/index.ts +1 -0
- package/dist/templates/blocks/settings-01/settings-01.html +424 -0
- package/dist/templates/blocks/settings-01/settings-01.ts +392 -0
- package/dist/templates/blocks/signup-01/index.ts +1 -0
- package/dist/templates/blocks/signup-01/signup-01.html +332 -0
- package/dist/templates/blocks/signup-01/signup-01.ts +186 -0
- 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/tabs/tabs.ts +88 -23
- 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 +323 -13
- package/dist/ui/schema.json +50 -2
- package/dist/utils/pages.js +138 -0
- package/dist/utils/project.js +59 -0
- package/dist/utils/tailwind.js +84 -0
- package/dist/utils/write-files.js +63 -8
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Blocks de tipo página (B.11): `ng g mimi login-01` no copia el Block tal cual (`Login01`,
|
|
4
|
+
* `mimi-login-01`), sino que genera una página del usuario en `aliases.pages`
|
|
5
|
+
* (`src/app/pages/login/login.ts`, clase `LoginPage`) con su archivo de rutas.
|
|
6
|
+
*
|
|
7
|
+
* Solo funciones de texto, sin schematics: lo usa también el sitio para mostrar el código como
|
|
8
|
+
* lo genera la CLI.
|
|
9
|
+
*
|
|
10
|
+
* **Seguimiento por el nombre del registro (B.11-f):** los archivos de una página se identifican
|
|
11
|
+
* con claves `pages/<ítem>/…` (`pages/login-01/login-01.ts`), que son también las de sus bases
|
|
12
|
+
* (`.mimi/base/pages/login-01/`) y las del manifiesto. La carpeta y el nombre que eligió el
|
|
13
|
+
* usuario solo deciden dónde se escriben y cómo se llaman.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.isBlockLikeName = exports.pageItemOf = exports.pageKeys = exports.pageKeyOf = exports.routesKey = exports.pageSourceFiles = exports.routesConstName = exports.pageClassName = exports.defaultPageName = exports.PAGE_NAME = exports.isPageFile = exports.PAGES_PREFIX = void 0;
|
|
17
|
+
exports.pageSelector = pageSelector;
|
|
18
|
+
exports.pageTargetPath = pageTargetPath;
|
|
19
|
+
exports.renderPageFile = renderPageFile;
|
|
20
|
+
exports.routesFile = routesFile;
|
|
21
|
+
exports.routeLine = routeLine;
|
|
22
|
+
/** Prefijo de las claves de los archivos de una página. */
|
|
23
|
+
exports.PAGES_PREFIX = 'pages/';
|
|
24
|
+
/** ¿Es la clave de un archivo de una página? */
|
|
25
|
+
const isPageFile = (file) => file.startsWith(exports.PAGES_PREFIX);
|
|
26
|
+
exports.isPageFile = isPageFile;
|
|
27
|
+
/** Nombre válido para `--name`: kebab-case, empieza con una letra (`login`, `admin-login`). */
|
|
28
|
+
exports.PAGE_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
29
|
+
/** Nombre por defecto: el del registro sin el número (`login-01` → `login`). */
|
|
30
|
+
const defaultPageName = (item) => item.replace(/-\d+$/, '');
|
|
31
|
+
exports.defaultPageName = defaultPageName;
|
|
32
|
+
const pascal = (kebab) => kebab
|
|
33
|
+
.split('-')
|
|
34
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
35
|
+
.join('');
|
|
36
|
+
const endsWithPage = (name) => name === 'page' || name.endsWith('-page');
|
|
37
|
+
/** `login` → `LoginPage`; `login-page` → `LoginPage` (sin repetir «Page»). */
|
|
38
|
+
const pageClassName = (name) => endsWithPage(name) ? pascal(name) : `${pascal(name)}Page`;
|
|
39
|
+
exports.pageClassName = pageClassName;
|
|
40
|
+
/** `app` y `login` → `app-login-page`; sin prefijo, `login-page`. */
|
|
41
|
+
function pageSelector(prefix, name) {
|
|
42
|
+
const base = endsWithPage(name) ? name : `${name}-page`;
|
|
43
|
+
return prefix ? `${prefix}-${base}` : base;
|
|
44
|
+
}
|
|
45
|
+
/** `login` → `LOGIN_ROUTES`; `admin-login` → `ADMIN_LOGIN_ROUTES`. */
|
|
46
|
+
const routesConstName = (name) => `${name.replace(/-/g, '_').toUpperCase()}_ROUTES`;
|
|
47
|
+
exports.routesConstName = routesConstName;
|
|
48
|
+
const basename = (file) => file.slice(file.lastIndexOf('/') + 1);
|
|
49
|
+
/** Los archivos del Block que pasan a la página: todos menos su `index.ts`. */
|
|
50
|
+
const pageSourceFiles = (files) => files.filter((file) => basename(file) !== 'index.ts');
|
|
51
|
+
exports.pageSourceFiles = pageSourceFiles;
|
|
52
|
+
/** Clave del archivo de rutas (`pages/login-01/login-01.routes.ts`). */
|
|
53
|
+
const routesKey = (item) => `${exports.PAGES_PREFIX}${item}/${item}.routes.ts`;
|
|
54
|
+
exports.routesKey = routesKey;
|
|
55
|
+
/** Clave de un archivo del Block en la página: `blocks/login-01/login-01.html` → `pages/login-01/login-01.html`. */
|
|
56
|
+
const pageKeyOf = (item, file) => `${exports.PAGES_PREFIX}${item}/${basename(file)}`;
|
|
57
|
+
exports.pageKeyOf = pageKeyOf;
|
|
58
|
+
/** Las claves de los archivos de una página: los del Block (sin `index.ts`) y el de rutas. */
|
|
59
|
+
const pageKeys = (item, files) => [
|
|
60
|
+
...(0, exports.pageSourceFiles)(files).map((file) => (0, exports.pageKeyOf)(item, file)),
|
|
61
|
+
(0, exports.routesKey)(item),
|
|
62
|
+
];
|
|
63
|
+
exports.pageKeys = pageKeys;
|
|
64
|
+
/** El ítem de una clave de página (`pages/login-01/login-01.ts` → `login-01`). */
|
|
65
|
+
const pageItemOf = (key) => key.slice(exports.PAGES_PREFIX.length).split('/')[0];
|
|
66
|
+
exports.pageItemOf = pageItemOf;
|
|
67
|
+
/**
|
|
68
|
+
* Ruta en el workspace de un archivo de la página: `pages/login-01/login-01.ts` con
|
|
69
|
+
* `{ dir: 'src/app/pages/login', name: 'login' }` → `src/app/pages/login/login.ts`.
|
|
70
|
+
*/
|
|
71
|
+
function pageTargetPath(key, target) {
|
|
72
|
+
const file = basename(key);
|
|
73
|
+
const rest = file.startsWith(target.item) ? file.slice(target.item.length) : `.${file}`;
|
|
74
|
+
return `${target.dir}/${target.name}${rest}`;
|
|
75
|
+
}
|
|
76
|
+
const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
77
|
+
/** `settings-01` → `SETTINGS_01`; `Login-Page` → `LOGIN_PAGE`. */
|
|
78
|
+
const constantCase = (kebab) => kebab.replace(/-/g, '_').toUpperCase();
|
|
79
|
+
/**
|
|
80
|
+
* ¿El nombre de página contiene el del Block (`login-01`, `login-01-x`)? La conversión no
|
|
81
|
+
* distinguiría lo viejo de lo nuevo: `ng g mimi` lo rechaza con su propio mensaje.
|
|
82
|
+
*/
|
|
83
|
+
const isBlockLikeName = (item, name) => name === item || name.startsWith(`${item}-`);
|
|
84
|
+
exports.isBlockLikeName = isBlockLikeName;
|
|
85
|
+
/**
|
|
86
|
+
* El código de un archivo del Block convertido en el de la página: el selector `mimi-login-01`
|
|
87
|
+
* (también en los `id`) pasa a `app-login-page`; la clase `Login01` y sus tipos (`Login01Value`)
|
|
88
|
+
* a `LoginPage` (`LoginPageValue`); las constantes (`SETTINGS_01_PLAN`) a `SETTINGS_PAGE_PLAN`;
|
|
89
|
+
* los archivos propios (`./login-01.html`) a `./login.html`. Lanza un error si queda algo del
|
|
90
|
+
* nombre del Block (un Block nuevo con otra forma necesita que se revise esta función; lo prueba
|
|
91
|
+
* `pages.spec.ts` con los Blocks reales). El nombre de la página no puede contener el del Block
|
|
92
|
+
* (`--name login-01`): lo rechaza antes `isBlockLikeName`.
|
|
93
|
+
*/
|
|
94
|
+
function renderPageFile(source, target) {
|
|
95
|
+
const { item, name } = target;
|
|
96
|
+
const fromClass = pascal(item);
|
|
97
|
+
const fromConstant = constantCase(item);
|
|
98
|
+
const result = source
|
|
99
|
+
.split(`mimi-${item}`)
|
|
100
|
+
.join(pageSelector(target.prefix, name))
|
|
101
|
+
.replace(new RegExp(`(['"\`])\\./${escape(item)}\\.`, 'g'), `$1./${name}.`)
|
|
102
|
+
.replace(new RegExp(`\\b${escape(fromClass)}`, 'g'), (0, exports.pageClassName)(name))
|
|
103
|
+
.replace(new RegExp(`\\b${escape(fromConstant)}(?=_|\\b)`, 'g'), constantCase((0, exports.pageClassName)(name).replace(/([a-z0-9])([A-Z])/g, '$1-$2')));
|
|
104
|
+
const left = [`mimi-${item}`, `./${item}.`].filter((text) => result.includes(text));
|
|
105
|
+
if (new RegExp(`\\b${escape(fromClass)}`).test(result))
|
|
106
|
+
left.push(fromClass);
|
|
107
|
+
if (new RegExp(`\\b${escape(fromConstant)}(?=_|\\b)`).test(result))
|
|
108
|
+
left.push(fromConstant);
|
|
109
|
+
if (left.length > 0) {
|
|
110
|
+
throw new Error(`No se pudo convertir ${item} en una página: quedan ${left.join(', ')}. Es un error de Mimi; ` +
|
|
111
|
+
'avísanos en https://github.com/nuki23/mimi-ng/issues.');
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
/** `login.routes.ts`: la página en carga diferida, lista para `loadChildren`. */
|
|
116
|
+
function routesFile(target) {
|
|
117
|
+
const className = (0, exports.pageClassName)(target.name);
|
|
118
|
+
return [
|
|
119
|
+
"import type { Routes } from '@angular/router';",
|
|
120
|
+
'',
|
|
121
|
+
'/**',
|
|
122
|
+
` * Rutas de la página ${target.name} (${target.item} de Mimi). La página se carga al entrar en`,
|
|
123
|
+
' * ella. En app.routes.ts, con loadChildren (ng g mimi te dio la línea exacta).',
|
|
124
|
+
' */',
|
|
125
|
+
`export const ${(0, exports.routesConstName)(target.name)}: Routes = [`,
|
|
126
|
+
` { path: '', loadComponent: () => import('./${target.name}').then((m) => m.${className}) },`,
|
|
127
|
+
'];',
|
|
128
|
+
'',
|
|
129
|
+
].join('\n');
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* La línea para `app.routes.ts` (que está en `appDir`): `{ path: 'login', loadChildren: () =>
|
|
133
|
+
* import('./pages/login/login.routes').then((m) => m.LOGIN_ROUTES) },`.
|
|
134
|
+
*/
|
|
135
|
+
function routeLine(target, routesImport) {
|
|
136
|
+
return (`{ path: '${target.name}', loadChildren: () => import('${routesImport}')` +
|
|
137
|
+
`.then((m) => m.${(0, exports.routesConstName)(target.name)}) },`);
|
|
138
|
+
}
|
package/dist/utils/project.js
CHANGED
|
@@ -1,12 +1,71 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MIMI_COLLECTION = void 0;
|
|
4
|
+
exports.defaultBlocksDir = defaultBlocksDir;
|
|
5
|
+
exports.defaultPagesDir = defaultPagesDir;
|
|
6
|
+
exports.assertAliases = assertAliases;
|
|
4
7
|
exports.readJson = readJson;
|
|
5
8
|
exports.allDependencies = allDependencies;
|
|
6
9
|
exports.majorOf = majorOf;
|
|
7
10
|
const schematics_1 = require("@angular-devkit/schematics");
|
|
11
|
+
const paths_1 = require("../init/paths");
|
|
8
12
|
/** Nombre de esta colección (el paquete). */
|
|
9
13
|
exports.MIMI_COLLECTION = '@mimi-ng/cli';
|
|
14
|
+
/**
|
|
15
|
+
* Carpeta de los Blocks si mimi.json no la dice: la hermana de `components/` cuando la de
|
|
16
|
+
* componentes es la de siempre (`src/app/components/ui` → `src/app/blocks`), o si no, la hermana
|
|
17
|
+
* de la carpeta de componentes (`src/app/ui` → `src/app/blocks`).
|
|
18
|
+
*/
|
|
19
|
+
function defaultBlocksDir(componentsDir) {
|
|
20
|
+
return siblingOfComponents(componentsDir, 'blocks');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Carpeta de las páginas si mimi.json no la dice (B.11-b), con la misma regla que la de los
|
|
24
|
+
* Blocks: `src/app/components/ui` → `src/app/pages`.
|
|
25
|
+
*/
|
|
26
|
+
function defaultPagesDir(componentsDir) {
|
|
27
|
+
return siblingOfComponents(componentsDir, 'pages');
|
|
28
|
+
}
|
|
29
|
+
function siblingOfComponents(componentsDir, folder) {
|
|
30
|
+
const parts = componentsDir.split('/').filter(Boolean);
|
|
31
|
+
if (parts.length >= 2 && parts.at(-1) === 'ui' && parts.at(-2) === 'components') {
|
|
32
|
+
parts.splice(-2, 2);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
parts.pop();
|
|
36
|
+
}
|
|
37
|
+
return [...parts, folder].join('/');
|
|
38
|
+
}
|
|
39
|
+
const ALIAS_EXAMPLES = {
|
|
40
|
+
components: 'src/app/components/ui',
|
|
41
|
+
blocks: 'src/app/blocks',
|
|
42
|
+
pages: 'src/app/pages',
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* M2: falla con un error claro (sin escribir nada) si `aliases.components`, `aliases.blocks` o
|
|
46
|
+
* `aliases.pages` de mimi.json, o la carpeta de una página (`blocks.<ítem>.path`, B.11), es
|
|
47
|
+
* absoluto, tiene `..` o sale del proyecto.
|
|
48
|
+
*/
|
|
49
|
+
function assertAliases(config) {
|
|
50
|
+
for (const name of ['components', 'blocks', 'pages']) {
|
|
51
|
+
const value = config.aliases?.[name];
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
assertRelative(`aliases.${name}`, value, ALIAS_EXAMPLES[name]);
|
|
55
|
+
}
|
|
56
|
+
for (const [item, entry] of Object.entries(config.blocks ?? {})) {
|
|
57
|
+
if (entry?.path === undefined)
|
|
58
|
+
continue;
|
|
59
|
+
assertRelative(`blocks.${item}.path`, entry.path, `${ALIAS_EXAMPLES.pages}/login`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function assertRelative(key, value, example) {
|
|
63
|
+
const reason = (0, paths_1.invalidAliasReason)(value);
|
|
64
|
+
if (!reason)
|
|
65
|
+
return;
|
|
66
|
+
throw new schematics_1.SchematicsException(`mimi.json → ${key} ${reason}: ${JSON.stringify(value)}. Usa una ruta relativa a ` +
|
|
67
|
+
`la raíz del workspace y dentro de ella (por ejemplo "${example}"). No se cambió nada.`);
|
|
68
|
+
}
|
|
10
69
|
function readJson(tree, path) {
|
|
11
70
|
if (!tree.exists(path))
|
|
12
71
|
return null;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tailwindSupportsSourceNot = tailwindSupportsSourceNot;
|
|
4
|
+
exports.withFolderSource = withFolderSource;
|
|
5
|
+
exports.mimiSourceNotLine = mimiSourceNotLine;
|
|
6
|
+
exports.mimiSourceNotPath = mimiSourceNotPath;
|
|
7
|
+
exports.missingMimiExclusion = missingMimiExclusion;
|
|
8
|
+
const css_1 = require("../init/css");
|
|
9
|
+
const paths_1 = require("../init/paths");
|
|
10
|
+
/** La carpeta de las copias originales (`.mimi/base/`), en la raíz del workspace. */
|
|
11
|
+
const MIMI_DIR = '.mimi';
|
|
12
|
+
/**
|
|
13
|
+
* ¿El Tailwind del proyecto entiende `@source not` (4.1 o superior)? Manda la versión instalada
|
|
14
|
+
* (`node_modules`, la que corre de verdad); si no está instalado, el rango de package.json: solo
|
|
15
|
+
* un rango que no puede pasar de la 4.0 (`4.0.5`, `~4.0.17`) cuenta como 4.0. `^4.0.17` instala
|
|
16
|
+
* la última 4.x, así que sí lo entiende.
|
|
17
|
+
*/
|
|
18
|
+
function tailwindSupportsSourceNot(tree, declared) {
|
|
19
|
+
const file = tree.read('node_modules/tailwindcss/package.json');
|
|
20
|
+
const installed = file
|
|
21
|
+
? JSON.parse(file.toString('utf-8')).version
|
|
22
|
+
: undefined;
|
|
23
|
+
if (installed)
|
|
24
|
+
return atLeast41(installed);
|
|
25
|
+
if (!declared)
|
|
26
|
+
return true;
|
|
27
|
+
// Solo un rango exacto o con `~` fija el minor; `^4.0.17`, `>=4`, `^4`, `latest`: la última 4.x.
|
|
28
|
+
const match = /^\s*(?:~|=|v)?\s*(\d+)\.(\d+)/.exec(declared);
|
|
29
|
+
if (!match)
|
|
30
|
+
return true;
|
|
31
|
+
const [major, minor] = [Number(match[1]), Number(match[2])];
|
|
32
|
+
return major > 4 || (major === 4 && minor >= 1);
|
|
33
|
+
}
|
|
34
|
+
function atLeast41(version) {
|
|
35
|
+
const [major, minor] = version.split('.').map((n) => Number.parseInt(n, 10) || 0);
|
|
36
|
+
return major > 4 || (major === 4 && minor >= 1);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Con `source(none)`, Tailwind solo escanea lo que dice un `@source`: la carpeta de los Blocks
|
|
40
|
+
* (G1) y la de las páginas (B.11) necesitan el suyo, como la de componentes. Devuelve el CSS con
|
|
41
|
+
* la línea agregada (después del último `@source` de `afterDirs` que esté, o del tema), o `null`
|
|
42
|
+
* si no hace falta: sin `source(none)`, o si ya está (comillas simples o dobles).
|
|
43
|
+
*/
|
|
44
|
+
function withFolderSource(css, cssPath, afterDirs, dir) {
|
|
45
|
+
const tailwind = (0, css_1.findTailwindImport)(css);
|
|
46
|
+
if (!tailwind?.sourceNone)
|
|
47
|
+
return null;
|
|
48
|
+
const cssDir = (0, paths_1.dirnameOf)(cssPath);
|
|
49
|
+
const path = (0, paths_1.relativePath)(cssDir, dir);
|
|
50
|
+
if ((0, css_1.hasDirective)(css, '@source', path))
|
|
51
|
+
return null;
|
|
52
|
+
const line = `@source "${path}";`;
|
|
53
|
+
let end = -1;
|
|
54
|
+
for (const after of afterDirs) {
|
|
55
|
+
const match = new RegExp(String.raw `^[ \t]*@source\s+(["'])` +
|
|
56
|
+
(0, paths_1.relativePath)(cssDir, after).replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
|
|
57
|
+
String.raw `\1[^;\n]*;[^\n]*$`, 'm').exec(css);
|
|
58
|
+
if (match)
|
|
59
|
+
end = Math.max(end, match.index + match[0].length);
|
|
60
|
+
}
|
|
61
|
+
if (end >= 0) {
|
|
62
|
+
const eol = /\r\n/.test(css) ? '\r\n' : '\n';
|
|
63
|
+
return { css: css.slice(0, end) + eol + line + css.slice(end), line };
|
|
64
|
+
}
|
|
65
|
+
const updated = (0, css_1.insertAfterTheme)(css, [line]);
|
|
66
|
+
return updated === null ? null : { css: updated, line };
|
|
67
|
+
}
|
|
68
|
+
/** La línea que excluye `.mimi/` del escaneo de Tailwind, relativa al CSS global. */
|
|
69
|
+
function mimiSourceNotLine(cssPath) {
|
|
70
|
+
return `@source not "${mimiSourceNotPath(cssPath)}";`;
|
|
71
|
+
}
|
|
72
|
+
function mimiSourceNotPath(cssPath) {
|
|
73
|
+
return (0, paths_1.relativePath)((0, paths_1.dirnameOf)(cssPath), MIMI_DIR);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* ¿Falta la exclusión de `.mimi/` en este CSS? No hace falta con `source(none)` (Tailwind no
|
|
77
|
+
* escanea nada por su cuenta) ni si ya está, con comillas simples o dobles.
|
|
78
|
+
*/
|
|
79
|
+
function missingMimiExclusion(css, cssPath) {
|
|
80
|
+
const tailwind = (0, css_1.findTailwindImport)(css);
|
|
81
|
+
if (!tailwind || tailwind.sourceNone)
|
|
82
|
+
return false;
|
|
83
|
+
return !(0, css_1.hasDirective)(css, '@source not', mimiSourceNotPath(cssPath));
|
|
84
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasFileChanges = exports.emptyReport = exports.MANIFEST = exports.BASE_DIR = void 0;
|
|
3
|
+
exports.isBlockFile = exports.hasFileChanges = exports.emptyReport = exports.MANIFEST = exports.BASE_DIR = exports.itemFiles = void 0;
|
|
4
4
|
exports.projectPathOf = projectPathOf;
|
|
5
|
+
exports.targetPathOf = targetPathOf;
|
|
5
6
|
exports.basePathOf = basePathOf;
|
|
6
7
|
exports.writeTemplates = writeTemplates;
|
|
7
8
|
exports.blockedMessages = blockedMessages;
|
|
@@ -9,6 +10,14 @@ exports.compareVersions = compareVersions;
|
|
|
9
10
|
exports.updatedMessages = updatedMessages;
|
|
10
11
|
const schematics_1 = require("@angular-devkit/schematics");
|
|
11
12
|
const registry_1 = require("../registry");
|
|
13
|
+
const pages_1 = require("./pages");
|
|
14
|
+
const project_1 = require("./project");
|
|
15
|
+
/**
|
|
16
|
+
* Los archivos de un ítem: los del registro o, si se genera como página, sus claves
|
|
17
|
+
* `pages/<ítem>/…` (B.11-f).
|
|
18
|
+
*/
|
|
19
|
+
const itemFiles = (item, pages = {}) => pages[item] ? (0, pages_1.pageKeys)(item, registry_1.registry.items[item].files) : registry_1.registry.items[item].files;
|
|
20
|
+
exports.itemFiles = itemFiles;
|
|
12
21
|
/** Carpeta de las copias originales, en la raíz del workspace. Debe quedar en git. */
|
|
13
22
|
exports.BASE_DIR = '.mimi/base';
|
|
14
23
|
/** Versión de Mimi con la que se escribió la base de cada archivo. */
|
|
@@ -34,7 +43,33 @@ exports.hasFileChanges = hasFileChanges;
|
|
|
34
43
|
function projectPathOf(file) {
|
|
35
44
|
return file.replace(/^components\//, '');
|
|
36
45
|
}
|
|
37
|
-
/**
|
|
46
|
+
/** Prefijo de los archivos de un Block en las plantillas (`blocks/login-01/login-01.ts`). */
|
|
47
|
+
const BLOCKS_PREFIX = 'blocks/';
|
|
48
|
+
/** ¿Es un archivo de un Block? */
|
|
49
|
+
const isBlockFile = (file) => file.startsWith(BLOCKS_PREFIX);
|
|
50
|
+
exports.isBlockFile = isBlockFile;
|
|
51
|
+
/**
|
|
52
|
+
* Ruta en el proyecto de un archivo de las plantillas: los Blocks van a su carpeta, sin
|
|
53
|
+
* `blocks/` (`blocks/login-01/login-01.ts` → `<blocksDir>/login-01/login-01.ts`); el resto, a la
|
|
54
|
+
* de componentes (ver projectPathOf).
|
|
55
|
+
*/
|
|
56
|
+
function targetPathOf(file, componentsDir, blocksDir, pages = {}) {
|
|
57
|
+
if ((0, pages_1.isPageFile)(file)) {
|
|
58
|
+
const target = pages[(0, pages_1.pageItemOf)(file)];
|
|
59
|
+
if (!target)
|
|
60
|
+
throw new Error(`Falta la carpeta de la página de ${file}.`);
|
|
61
|
+
return (0, pages_1.pageTargetPath)(file, target);
|
|
62
|
+
}
|
|
63
|
+
return (0, exports.isBlockFile)(file)
|
|
64
|
+
? `${blocksDir}/${file.slice(BLOCKS_PREFIX.length)}`
|
|
65
|
+
: `${componentsDir}/${projectPathOf(file)}`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Copia original en .mimi/base, con la misma estructura (`.mimi/base/button/button.ts`). Los
|
|
69
|
+
* Blocks conservan `blocks/` (`.mimi/base/blocks/login-01/login-01.ts`), y esa es también su
|
|
70
|
+
* clave en el manifiesto. Las páginas van por su nombre del registro, no por el de su carpeta
|
|
71
|
+
* (`.mimi/base/pages/login-01/login-01.ts`, B.11-f).
|
|
72
|
+
*/
|
|
38
73
|
function basePathOf(file) {
|
|
39
74
|
return `${exports.BASE_DIR}/${projectPathOf(file)}`;
|
|
40
75
|
}
|
|
@@ -74,6 +109,8 @@ function readManifest(tree) {
|
|
|
74
109
|
function writeTemplates(options) {
|
|
75
110
|
const items = [...new Set(options.items)];
|
|
76
111
|
const implicit = new Set(options.implicit ?? []);
|
|
112
|
+
const pages = options.pages ?? {};
|
|
113
|
+
const filesOf = (item) => (0, exports.itemFiles)(item, pages);
|
|
77
114
|
const files = new Set(items.flatMap((item) => registry_1.registry.items[item].files));
|
|
78
115
|
const contents = new Map();
|
|
79
116
|
// 1. Lee las plantillas (sin escribir nada: forEach devuelve null).
|
|
@@ -86,9 +123,18 @@ function writeTemplates(options) {
|
|
|
86
123
|
]));
|
|
87
124
|
// 2. Decide por ítem y escribe.
|
|
88
125
|
const write = (tree) => {
|
|
126
|
+
// Las páginas: su código sale del Block, con el nombre que eligió el usuario (B.11).
|
|
127
|
+
for (const [item, target] of Object.entries(pages)) {
|
|
128
|
+
for (const file of (0, pages_1.pageSourceFiles)(registry_1.registry.items[item].files)) {
|
|
129
|
+
const source = contents.get(file);
|
|
130
|
+
if (source !== undefined)
|
|
131
|
+
contents.set((0, pages_1.pageKeyOf)(item, file), (0, pages_1.renderPageFile)(source, target));
|
|
132
|
+
}
|
|
133
|
+
contents.set((0, pages_1.routesKey)(item), (0, pages_1.routesFile)(target));
|
|
134
|
+
}
|
|
89
135
|
const manifest = readManifest(tree);
|
|
90
136
|
let manifestChanged = false;
|
|
91
|
-
const targetOf = (file) =>
|
|
137
|
+
const targetOf = (file) => targetPathOf(file, options.componentsDir, options.blocksDir ?? (0, project_1.defaultBlocksDir)(options.componentsDir), pages);
|
|
92
138
|
const stateOf = (file) => {
|
|
93
139
|
const target = targetOf(file);
|
|
94
140
|
if (!tree.exists(target))
|
|
@@ -109,7 +155,7 @@ function writeTemplates(options) {
|
|
|
109
155
|
};
|
|
110
156
|
const states = new Map(items.map((item) => [
|
|
111
157
|
item,
|
|
112
|
-
|
|
158
|
+
filesOf(item)
|
|
113
159
|
.filter((file) => contents.has(file))
|
|
114
160
|
.map((file) => ({ file, ...stateOf(file) })),
|
|
115
161
|
]));
|
|
@@ -219,8 +265,8 @@ function writeTemplates(options) {
|
|
|
219
265
|
* Los avisos de los ítems que no se escribieron (`report.blocked`): qué no se actualizó, por qué
|
|
220
266
|
* y qué hacer. Nunca dicen «actualizado».
|
|
221
267
|
*/
|
|
222
|
-
function blockedMessages(report, componentsDir, version) {
|
|
223
|
-
const path = (file) =>
|
|
268
|
+
function blockedMessages(report, componentsDir, version, blocksDir = (0, project_1.defaultBlocksDir)(componentsDir), pages = {}) {
|
|
269
|
+
const path = (file) => targetPathOf(file, componentsDir, blocksDir, pages);
|
|
224
270
|
return report.blocked.map(({ item, files, dependency, installed }) => {
|
|
225
271
|
if (dependency) {
|
|
226
272
|
const needs = `necesita ${dependency} ${version}, que no se actualizó (ver el aviso de ${dependency}).`;
|
|
@@ -228,9 +274,18 @@ function blockedMessages(report, componentsDir, version) {
|
|
|
228
274
|
? `${item} no se actualizó a ${version}: ${needs}`
|
|
229
275
|
: `${item} no se agregó: ${needs}`;
|
|
230
276
|
}
|
|
231
|
-
const
|
|
232
|
-
const label = isTheme ? 'El tema' : item;
|
|
277
|
+
const type = registry_1.registry.items[item]?.type;
|
|
233
278
|
const dir = projectPathOf(files[0]).split('/').slice(0, -1).join('/');
|
|
279
|
+
if (type === 'block') {
|
|
280
|
+
// Lo normal es editar un Block (es una pantalla del usuario): el aviso no alarma (B.1-b).
|
|
281
|
+
return (`${item} no se actualizó a ${version}: es tu pantalla y la modificaste ` +
|
|
282
|
+
`(${files.map(path).join(', ')}), así que no se tocó ninguno de sus archivos. Es lo ` +
|
|
283
|
+
`normal con un Block. La versión de Mimi que modificaste está en ${exports.BASE_DIR}/${dir}/, ` +
|
|
284
|
+
'para comparar. Si quieres la nueva, guarda tus cambios (por ejemplo, en git) y vuelve ' +
|
|
285
|
+
'a ejecutar el comando con --overwrite: reemplaza tu pantalla entera.');
|
|
286
|
+
}
|
|
287
|
+
const isTheme = type === 'theme';
|
|
288
|
+
const label = isTheme ? 'El tema' : item;
|
|
234
289
|
return (`${label} no se actualizó a ${version} porque modificaste ${files.map(path).join(', ')}. ` +
|
|
235
290
|
'No se cambió ninguno de sus archivos, para no mezclar versiones.' +
|
|
236
291
|
(isTheme
|
package/package.json
CHANGED