@okalit/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.
- package/README.md +32 -0
- package/bin/okalit-cli.js +8 -0
- package/lib/cli.js +515 -0
- package/package.json +30 -0
- package/templates/app/@okalit/AppMixin.js +29 -0
- package/templates/app/@okalit/EventBus.js +152 -0
- package/templates/app/@okalit/ModuleMixin.js +7 -0
- package/templates/app/@okalit/Okalit.js +129 -0
- package/templates/app/@okalit/OkalitService.js +145 -0
- package/templates/app/@okalit/defineElement.js +65 -0
- package/templates/app/@okalit/i18n.js +89 -0
- package/templates/app/@okalit/idle.js +40 -0
- package/templates/app/@okalit/index.js +10 -0
- package/templates/app/@okalit/lazy.js +32 -0
- package/templates/app/@okalit/okalit-router.js +309 -0
- package/templates/app/@okalit/service.js +33 -0
- package/templates/app/@okalit/trigger.js +14 -0
- package/templates/app/@okalit/viewport.js +69 -0
- package/templates/app/@okalit/when.js +40 -0
- package/templates/app/babel.config.json +5 -0
- package/templates/app/index.html +15 -0
- package/templates/app/package.json +23 -0
- package/templates/app/public/i18n/en.json +3 -0
- package/templates/app/public/i18n/es.json +3 -0
- package/templates/app/public/lit.svg +1 -0
- package/templates/app/src/app.routes.ts +10 -0
- package/templates/app/src/main-app.js +13 -0
- package/templates/app/src/modules/example/example.module.js +4 -0
- package/templates/app/src/modules/example/example.routes.js +7 -0
- package/templates/app/src/modules/example/pages/example.page.js +43 -0
- package/templates/app/src/modules/example/pages/example.page.scss +76 -0
- package/templates/app/src/styles/global.scss +0 -0
- package/templates/app/src/styles/index.css +4 -0
- package/templates/app/vite.config.js +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Okalit CLI
|
|
2
|
+
|
|
3
|
+
CLI para crear apps Okalit y generar recursos dentro de un proyecto existente.
|
|
4
|
+
|
|
5
|
+
## Uso
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx okalit-cli new app-name
|
|
9
|
+
npx okalit-cli -g -c ./src/components/component-name
|
|
10
|
+
npx okalit-cli -g -s ./src/modules/home/services/example
|
|
11
|
+
npx okalit-cli -g -m ./src/modules/profile
|
|
12
|
+
npx okalit-cli -g --guard ./src/guards/auth
|
|
13
|
+
npx okalit-cli -g --interceptor ./src/interceptors/auth
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Qué genera
|
|
17
|
+
|
|
18
|
+
- `new <app-name>`: crea una app Vite basada en el template de Okalit.
|
|
19
|
+
- `-g -c`: crea carpeta del componente con `js` y `scss` o `css` según el proyecto.
|
|
20
|
+
- `-g -s`: crea un archivo `*.service.js` en la ruta indicada.
|
|
21
|
+
- `-g -m`: crea carpeta del módulo con `*.module.js`, `*.routes.js` y página inicial.
|
|
22
|
+
- `-g --guard`: crea un archivo `*.guard.js`.
|
|
23
|
+
- `-g --interceptor`: crea un archivo `*.interceptor.js`.
|
|
24
|
+
|
|
25
|
+
## Desarrollo local
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd okalit-cli
|
|
29
|
+
npm install
|
|
30
|
+
npm link
|
|
31
|
+
okalit-cli --help
|
|
32
|
+
```
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
const TEMPLATE_DIR = path.resolve(__dirname, '../templates/app');
|
|
8
|
+
const HELP_CONTENT = `
|
|
9
|
+
|
|
10
|
+
▄▄▄▄ ▄▄ ▄▄▄▄ ██
|
|
11
|
+
██▀▀██ ██ ▀▀██ ▀▀ ██
|
|
12
|
+
██ ██ ██ ▄██▀ ▄█████▄ ██ ████ ███████
|
|
13
|
+
██ ██ ██▄██ ▀ ▄▄▄██ ██ ██ ██
|
|
14
|
+
██ ██ ██▀██▄ ▄██▀▀▀██ ██ ██ ██
|
|
15
|
+
██▄▄██ ██ ▀█▄ ██▄▄▄███ ██▄▄▄ ▄▄▄██▄▄▄ ██▄▄▄
|
|
16
|
+
▀▀▀▀ ▀▀ ▀▀▀ ▀▀▀▀ ▀▀ ▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀
|
|
17
|
+
By LIAPF Okalit CLI - v0.0.1
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
okalit new <app-name>
|
|
21
|
+
okalit generate <type> <path>
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
new <name> Create a new app
|
|
25
|
+
|
|
26
|
+
generate, -g
|
|
27
|
+
-c, --component <path> Create a component
|
|
28
|
+
-s, --service <path> Create a service
|
|
29
|
+
-m, --module <path> Create a module
|
|
30
|
+
--guard <path> Create a guard
|
|
31
|
+
--interceptor <path> Create an interceptor
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
okalit new my-app
|
|
35
|
+
okalit -g -c ./src/components/user-card
|
|
36
|
+
okalit -g -s ./src/modules/home/services/user
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
function getHelpText() {
|
|
40
|
+
return `${HELP_CONTENT}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function main(argv) {
|
|
44
|
+
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
|
|
45
|
+
console.log(getHelpText());
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const command = argv[0];
|
|
50
|
+
|
|
51
|
+
if (command === 'new') {
|
|
52
|
+
const appName = argv[1];
|
|
53
|
+
if (!appName) {
|
|
54
|
+
throw new Error('You must provide an app name: okalit new <app-name>.');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await createApp(appName, process.cwd());
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (argv.includes('-g') || argv.includes('--generate')) {
|
|
62
|
+
await handleGenerate(argv, process.cwd());
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
throw new Error('Unknown command. Use --help to see available options.');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function createApp(rawName, cwd) {
|
|
70
|
+
const appName = toKebabCase(rawName);
|
|
71
|
+
const targetDir = path.resolve(cwd, appName);
|
|
72
|
+
|
|
73
|
+
if (fs.existsSync(targetDir)) {
|
|
74
|
+
throw new Error(`La carpeta ya existe: ${targetDir}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
copyDirectory(TEMPLATE_DIR, targetDir);
|
|
78
|
+
|
|
79
|
+
const initialClassName = toPascalCase(appName);
|
|
80
|
+
const mainAppFile = path.join(targetDir, 'src', 'main-app.js');
|
|
81
|
+
const packageJsonFile = path.join(targetDir, 'package.json');
|
|
82
|
+
const indexHtmlFile = path.join(targetDir, 'index.html');
|
|
83
|
+
const routesFile = [
|
|
84
|
+
path.join(targetDir, 'src', 'app.routes.ts'),
|
|
85
|
+
path.join(targetDir, 'src', 'app.routes.js'),
|
|
86
|
+
].find((filePath) => fs.existsSync(filePath));
|
|
87
|
+
|
|
88
|
+
if (!routesFile) {
|
|
89
|
+
throw new Error('Could not find the app routes file.');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const routesExtension = path.extname(routesFile);
|
|
93
|
+
|
|
94
|
+
replaceInFile(packageJsonFile, '"name": "okalit-app"', `"name": "${appName}"`);
|
|
95
|
+
replaceInFile(indexHtmlFile, '<title>Okalit App</title>', `<title>${initialClassName}</title>`);
|
|
96
|
+
replaceManyInFile(mainAppFile, [
|
|
97
|
+
['./app.routes.js', `./app.routes${routesExtension}`],
|
|
98
|
+
['./app.routes.ts', `./app.routes${routesExtension}`],
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
console.log(`Okalit app created at ${targetDir}`);
|
|
102
|
+
console.log(`\nNext steps:`);
|
|
103
|
+
console.log(` cd ${appName}`);
|
|
104
|
+
console.log(' npm install');
|
|
105
|
+
console.log(' npm run dev');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function handleGenerate(argv, cwd) {
|
|
109
|
+
const projectRoot = findProjectRoot(cwd);
|
|
110
|
+
const styleExt = detectStyleExtension(projectRoot);
|
|
111
|
+
const coreAlias = detectCoreAlias(projectRoot);
|
|
112
|
+
const globalStyleSpecifier = detectGlobalStyleSpecifier(projectRoot);
|
|
113
|
+
|
|
114
|
+
const targets = [
|
|
115
|
+
{ kind: 'component', flags: ['-c', '--component'] },
|
|
116
|
+
{ kind: 'service', flags: ['-s', '--service'] },
|
|
117
|
+
{ kind: 'module', flags: ['-m', '--module'] },
|
|
118
|
+
{ kind: 'guard', flags: ['--guard'] },
|
|
119
|
+
{ kind: 'interceptor', flags: ['--interceptor'] },
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
for (const target of targets) {
|
|
123
|
+
const value = readFlagValue(argv, target.flags);
|
|
124
|
+
if (!value) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
switch (target.kind) {
|
|
129
|
+
case 'component':
|
|
130
|
+
generateComponent(projectRoot, value, styleExt, coreAlias, globalStyleSpecifier);
|
|
131
|
+
break;
|
|
132
|
+
case 'service':
|
|
133
|
+
generatePlainFile(projectRoot, value, 'service', buildServiceTemplate(value, coreAlias));
|
|
134
|
+
break;
|
|
135
|
+
case 'module':
|
|
136
|
+
generateModule(projectRoot, value, styleExt, coreAlias, globalStyleSpecifier);
|
|
137
|
+
break;
|
|
138
|
+
case 'guard':
|
|
139
|
+
generatePlainFile(projectRoot, value, 'guard', buildGuardTemplate(value));
|
|
140
|
+
break;
|
|
141
|
+
case 'interceptor':
|
|
142
|
+
generatePlainFile(projectRoot, value, 'interceptor', buildInterceptorTemplate(value));
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
throw new Error('You must specify what to generate. Use -c, -s, -m, --guard or --interceptor.');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function generateComponent(projectRoot, targetPath, styleExt, coreAlias, globalStyleSpecifier) {
|
|
155
|
+
const normalizedTarget = normalizeProjectPath(projectRoot, targetPath);
|
|
156
|
+
const baseName = toKebabCase(path.basename(normalizedTarget));
|
|
157
|
+
const componentDir = normalizedTarget;
|
|
158
|
+
const jsFile = path.join(componentDir, `${baseName}.js`);
|
|
159
|
+
const styleFile = path.join(componentDir, `${baseName}.${styleExt}`);
|
|
160
|
+
const className = toPascalCase(baseName);
|
|
161
|
+
|
|
162
|
+
ensureMissing(componentDir);
|
|
163
|
+
fs.mkdirSync(componentDir, { recursive: true });
|
|
164
|
+
fs.writeFileSync(jsFile, buildComponentTemplate(baseName, className, styleExt, coreAlias, globalStyleSpecifier));
|
|
165
|
+
fs.writeFileSync(styleFile, '', 'utf8');
|
|
166
|
+
|
|
167
|
+
console.log(`Component created: ${path.relative(projectRoot, componentDir)}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function generateModule(projectRoot, targetPath, styleExt, coreAlias, globalStyleSpecifier) {
|
|
171
|
+
const normalizedTarget = normalizeProjectPath(projectRoot, targetPath);
|
|
172
|
+
const moduleName = toKebabCase(path.basename(normalizedTarget));
|
|
173
|
+
const moduleDir = normalizedTarget;
|
|
174
|
+
const pagesDir = path.join(moduleDir, 'pages');
|
|
175
|
+
const className = toPascalCase(moduleName);
|
|
176
|
+
|
|
177
|
+
ensureMissing(moduleDir);
|
|
178
|
+
fs.mkdirSync(pagesDir, { recursive: true });
|
|
179
|
+
|
|
180
|
+
const moduleFile = path.join(moduleDir, `${moduleName}.module.js`);
|
|
181
|
+
const routesFile = path.join(moduleDir, `${moduleName}.routes.js`);
|
|
182
|
+
const pageFile = path.join(pagesDir, `${moduleName}.page.js`);
|
|
183
|
+
const styleFile = path.join(pagesDir, `${moduleName}.page.${styleExt}`);
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync(moduleFile, buildModuleTemplate(moduleName, className, coreAlias));
|
|
186
|
+
fs.writeFileSync(routesFile, buildModuleRoutesTemplate(moduleName), 'utf8');
|
|
187
|
+
fs.writeFileSync(pageFile, buildPageTemplate(moduleName, className, styleExt, coreAlias, globalStyleSpecifier));
|
|
188
|
+
fs.writeFileSync(styleFile, '', 'utf8');
|
|
189
|
+
|
|
190
|
+
attachModuleToAppRoutes(projectRoot, moduleName, className);
|
|
191
|
+
|
|
192
|
+
console.log(`Module created: ${path.relative(projectRoot, moduleDir)}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function generatePlainFile(projectRoot, targetPath, suffix, template) {
|
|
196
|
+
const normalizedTarget = normalizeProjectPath(projectRoot, targetPath);
|
|
197
|
+
const dirname = path.dirname(normalizedTarget);
|
|
198
|
+
const baseName = toKebabCase(path.basename(normalizedTarget));
|
|
199
|
+
const filePath = path.join(dirname, `${baseName}.${suffix}.js`);
|
|
200
|
+
|
|
201
|
+
ensureMissing(filePath);
|
|
202
|
+
fs.mkdirSync(dirname, { recursive: true });
|
|
203
|
+
fs.writeFileSync(filePath, template(baseName), 'utf8');
|
|
204
|
+
|
|
205
|
+
console.log(`File created: ${path.relative(projectRoot, filePath)}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function buildComponentTemplate(tagName, className, styleExt, coreAlias, globalStyleSpecifier) {
|
|
209
|
+
const globalStyleImport = globalStyleSpecifier
|
|
210
|
+
? `\nimport global from "${globalStyleSpecifier}";`
|
|
211
|
+
: '';
|
|
212
|
+
const stylesArray = globalStyleSpecifier ? '[styles, global]' : '[styles]';
|
|
213
|
+
|
|
214
|
+
return `import { html } from "lit";
|
|
215
|
+
import { Okalit, defineElement } from "${coreAlias}";
|
|
216
|
+
|
|
217
|
+
import styles from "./${tagName}.${styleExt}?inline";${globalStyleImport}
|
|
218
|
+
|
|
219
|
+
@defineElement({
|
|
220
|
+
tag: "${tagName}",
|
|
221
|
+
styles: ${stylesArray}
|
|
222
|
+
})
|
|
223
|
+
export class ${className} extends Okalit {
|
|
224
|
+
render() {
|
|
225
|
+
return html\`
|
|
226
|
+
<div class="${tagName}">
|
|
227
|
+
<slot></slot>
|
|
228
|
+
</div>
|
|
229
|
+
\`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function buildModuleTemplate(moduleName, className, coreAlias) {
|
|
236
|
+
return `import { ModuleMixin, Okalit, defineElement } from "${coreAlias}";
|
|
237
|
+
|
|
238
|
+
@defineElement({ tag: "${moduleName}-module" })
|
|
239
|
+
export class ${className}Module extends ModuleMixin(Okalit) {}
|
|
240
|
+
`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function buildModuleRoutesTemplate(moduleName) {
|
|
244
|
+
return `export default [
|
|
245
|
+
{
|
|
246
|
+
path: "",
|
|
247
|
+
component: "${moduleName}-page",
|
|
248
|
+
import: () => import("./pages/${moduleName}.page.js")
|
|
249
|
+
}
|
|
250
|
+
];
|
|
251
|
+
`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function buildPageTemplate(pageName, className, styleExt, coreAlias, globalStyleSpecifier) {
|
|
255
|
+
const globalStyleImport = globalStyleSpecifier
|
|
256
|
+
? `import global from "${globalStyleSpecifier}";`
|
|
257
|
+
: '';
|
|
258
|
+
const stylesArray = globalStyleSpecifier ? '[styles, global]' : '[styles]';
|
|
259
|
+
|
|
260
|
+
return `import { html } from "lit";
|
|
261
|
+
import { Okalit, defineElement } from "${coreAlias}";
|
|
262
|
+
|
|
263
|
+
import styles from "./${pageName}.page.${styleExt}?inline";
|
|
264
|
+
${globalStyleImport}
|
|
265
|
+
|
|
266
|
+
@defineElement({
|
|
267
|
+
tag: "${pageName}-page",
|
|
268
|
+
styles: ${stylesArray}
|
|
269
|
+
})
|
|
270
|
+
export class ${className}Page extends Okalit {
|
|
271
|
+
render() {
|
|
272
|
+
return html\`
|
|
273
|
+
<main>
|
|
274
|
+
<h1>${'${this.t("WELCOME")}'}</h1>
|
|
275
|
+
</main>
|
|
276
|
+
\`;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function buildServiceTemplate(rawTarget, coreAlias) {
|
|
283
|
+
return (baseName) => {
|
|
284
|
+
const className = `${toPascalCase(baseName)}Service`;
|
|
285
|
+
const serviceName = `${toCamelCase(baseName)}Service`;
|
|
286
|
+
const resourcePath = `/${baseName}`;
|
|
287
|
+
|
|
288
|
+
return `import { OkalitService, service } from "${coreAlias}";
|
|
289
|
+
|
|
290
|
+
@service("${serviceName}")
|
|
291
|
+
export class ${className} extends OkalitService {
|
|
292
|
+
constructor() {
|
|
293
|
+
super();
|
|
294
|
+
this.baseUrl = "";
|
|
295
|
+
this.version = "1";
|
|
296
|
+
this.path = "${resourcePath}";
|
|
297
|
+
this.headers = {};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
`;
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function buildGuardTemplate(rawTarget) {
|
|
305
|
+
return (baseName) => {
|
|
306
|
+
const functionName = `${toCamelCase(baseName)}Guard`;
|
|
307
|
+
|
|
308
|
+
return `export async function ${functionName}(path, args) {
|
|
309
|
+
void path;
|
|
310
|
+
void args;
|
|
311
|
+
|
|
312
|
+
return { allow: true };
|
|
313
|
+
}
|
|
314
|
+
`;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function buildInterceptorTemplate(rawTarget) {
|
|
319
|
+
return (baseName) => {
|
|
320
|
+
const functionName = `${toCamelCase(baseName)}Interceptor`;
|
|
321
|
+
|
|
322
|
+
return `export async function ${functionName}(context) {
|
|
323
|
+
return context;
|
|
324
|
+
}
|
|
325
|
+
`;
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function attachModuleToAppRoutes(projectRoot, moduleName, className) {
|
|
330
|
+
const candidateFiles = [
|
|
331
|
+
path.join(projectRoot, 'src', 'app.routes.ts'),
|
|
332
|
+
path.join(projectRoot, 'src', 'app.routes.js'),
|
|
333
|
+
];
|
|
334
|
+
const routesFile = candidateFiles.find((file) => fs.existsSync(file));
|
|
335
|
+
|
|
336
|
+
if (!routesFile) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const importLine = `import ${className}ModuleRoutes from "./modules/${moduleName}/${moduleName}.routes.js";`;
|
|
341
|
+
const routeEntry = ` {\n path: "/${moduleName}",\n component: "${moduleName}-module",\n import: () => import("./modules/${moduleName}/${moduleName}.module.js"),\n children: ${className}ModuleRoutes,\n }`;
|
|
342
|
+
const source = fs.readFileSync(routesFile, 'utf8');
|
|
343
|
+
|
|
344
|
+
if (source.includes(importLine) || source.includes(`"/${moduleName}"`)) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let next = source;
|
|
349
|
+
if (/^export default \[/m.test(next)) {
|
|
350
|
+
next = `${importLine}\n\n${next}`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
next = next.replace(/export default \[(.*?)\];/s, (match, body) => {
|
|
354
|
+
const trimmedBody = body.trim();
|
|
355
|
+
if (!trimmedBody) {
|
|
356
|
+
return `export default [\n${routeEntry}\n];`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return `export default [\n${trimmedBody.endsWith(',') ? trimmedBody : `${trimmedBody},`}\n${routeEntry}\n];`;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
fs.writeFileSync(routesFile, `${next.endsWith('\n') ? next : `${next}\n`}`, 'utf8');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function findProjectRoot(startDir) {
|
|
366
|
+
let currentDir = startDir;
|
|
367
|
+
|
|
368
|
+
while (true) {
|
|
369
|
+
const packageJsonFile = path.join(currentDir, 'package.json');
|
|
370
|
+
if (fs.existsSync(packageJsonFile)) {
|
|
371
|
+
return currentDir;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const parentDir = path.dirname(currentDir);
|
|
375
|
+
if (parentDir === currentDir) {
|
|
376
|
+
throw new Error('No package.json found. Run this command inside an Okalit project.');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
currentDir = parentDir;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function detectStyleExtension(projectRoot) {
|
|
384
|
+
const packageJsonFile = path.join(projectRoot, 'package.json');
|
|
385
|
+
if (!fs.existsSync(packageJsonFile)) {
|
|
386
|
+
return 'scss';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf8'));
|
|
390
|
+
const hasSass = Boolean(packageJson.dependencies?.sass || packageJson.devDependencies?.sass);
|
|
391
|
+
return hasSass ? 'scss' : 'css';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function detectGlobalStyleSpecifier(projectRoot) {
|
|
395
|
+
const candidates = [
|
|
396
|
+
{
|
|
397
|
+
filePath: path.join(projectRoot, 'src', 'styles', 'global.scss'),
|
|
398
|
+
specifier: '@styles/global.scss?inline',
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
filePath: path.join(projectRoot, 'src', 'styles', 'global.css'),
|
|
402
|
+
specifier: '@styles/global.css?inline',
|
|
403
|
+
},
|
|
404
|
+
];
|
|
405
|
+
|
|
406
|
+
return candidates.find((candidate) => fs.existsSync(candidate.filePath))?.specifier ?? null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function detectCoreAlias(projectRoot) {
|
|
410
|
+
const viteConfigFile = path.join(projectRoot, 'vite.config.js');
|
|
411
|
+
if (!fs.existsSync(viteConfigFile)) {
|
|
412
|
+
return '@okalit';
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const viteConfig = fs.readFileSync(viteConfigFile, 'utf8');
|
|
416
|
+
if (viteConfig.includes("'@core'") || viteConfig.includes('"@core"')) {
|
|
417
|
+
return '@core';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (viteConfig.includes("'@okalit'") || viteConfig.includes('"@okalit"')) {
|
|
421
|
+
return '@okalit';
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return '@okalit';
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function normalizeProjectPath(projectRoot, inputPath) {
|
|
428
|
+
const resolved = path.resolve(projectRoot, inputPath);
|
|
429
|
+
return resolved;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function readFlagValue(argv, flags) {
|
|
433
|
+
for (const flag of flags) {
|
|
434
|
+
const index = argv.indexOf(flag);
|
|
435
|
+
if (index !== -1) {
|
|
436
|
+
return argv[index + 1];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function copyDirectory(sourceDir, targetDir) {
|
|
444
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
445
|
+
|
|
446
|
+
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
447
|
+
if (entry.name === '.DS_Store') {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
452
|
+
const targetPath = path.join(targetDir, entry.name);
|
|
453
|
+
|
|
454
|
+
if (entry.isDirectory()) {
|
|
455
|
+
copyDirectory(sourcePath, targetPath);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function replaceInFile(filePath, searchValue, replaceValue) {
|
|
464
|
+
const source = fs.readFileSync(filePath, 'utf8');
|
|
465
|
+
fs.writeFileSync(filePath, source.replace(searchValue, replaceValue), 'utf8');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function replaceManyInFile(filePath, replacements) {
|
|
469
|
+
let source = fs.readFileSync(filePath, 'utf8');
|
|
470
|
+
|
|
471
|
+
for (const [searchValue, replaceValue] of replacements) {
|
|
472
|
+
source = source.replaceAll(searchValue, replaceValue);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
fs.writeFileSync(filePath, source, 'utf8');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function renameIfExists(sourcePath, targetPath) {
|
|
479
|
+
if (fs.existsSync(sourcePath)) {
|
|
480
|
+
fs.renameSync(sourcePath, targetPath);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function ensureMissing(targetPath) {
|
|
485
|
+
if (fs.existsSync(targetPath)) {
|
|
486
|
+
throw new Error(`Ya existe: ${targetPath}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function toKebabCase(value) {
|
|
491
|
+
return value
|
|
492
|
+
.replace(/^[./\\]+/, '')
|
|
493
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
494
|
+
.replace(/[^a-zA-Z0-9/]+/g, '-')
|
|
495
|
+
.replace(/\/+/g, '/')
|
|
496
|
+
.split('/')
|
|
497
|
+
.filter(Boolean)
|
|
498
|
+
.map((segment) => segment.toLowerCase().replace(/^-+|-+$/g, ''))
|
|
499
|
+
.join('/');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function toPascalCase(value) {
|
|
503
|
+
return toKebabCase(value)
|
|
504
|
+
.split('/')
|
|
505
|
+
.at(-1)
|
|
506
|
+
.split('-')
|
|
507
|
+
.filter(Boolean)
|
|
508
|
+
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
509
|
+
.join('');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function toCamelCase(value) {
|
|
513
|
+
const pascal = toPascalCase(value);
|
|
514
|
+
return pascal ? pascal[0].toLowerCase() + pascal.slice(1) : '';
|
|
515
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@okalit/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI to create and generate resources for Okalit projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"okalit": "./bin/okalit-cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"templates",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"okalit",
|
|
17
|
+
"cli",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"vite",
|
|
20
|
+
"lit"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "node ./bin/okalit-cli.js",
|
|
28
|
+
"test:smoke": "node ./bin/okalit-cli.js --help"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import './okalit-router.js';
|
|
3
|
+
import { OkalitI18n } from './i18n.js';
|
|
4
|
+
|
|
5
|
+
export const AppMixin = (Base) => class extends Base {
|
|
6
|
+
async connectedCallback() {
|
|
7
|
+
super.connectedCallback();
|
|
8
|
+
if (this._appConfig.i18n && this._appConfig.i18n !== false) {
|
|
9
|
+
await OkalitI18n.init(this._appConfig.i18n);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get _getOkalitRouter() {
|
|
14
|
+
return html`
|
|
15
|
+
<okalit-router
|
|
16
|
+
.routes=${this._appConfig.routes}
|
|
17
|
+
.guards=${this._appConfig.guards}
|
|
18
|
+
.interceptors=${this._appConfig.interceptors}
|
|
19
|
+
></okalit-router>
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
render() {
|
|
23
|
+
return html`
|
|
24
|
+
${ this._appConfig.layout
|
|
25
|
+
? html`${this._appConfig.layout(this._getOkalitRouter) }`
|
|
26
|
+
: this._getOkalitRouter }
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
}
|