@lytjs/cli 6.4.0 → 6.6.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/dist/create.cjs +96 -11
- package/dist/create.cjs.map +1 -1
- package/dist/create.mjs +96 -11
- package/dist/create.mjs.map +1 -1
- package/dist/index.cjs +968 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +951 -79
- package/dist/index.mjs.map +1 -1
- package/dist/lyt.cjs +968 -76
- package/dist/lyt.cjs.map +1 -1
- package/dist/lyt.mjs +951 -79
- package/dist/lyt.mjs.map +1 -1
- package/lyt-cli.js +1 -0
- package/package.json +55 -55
- package/dist/create.d.mts +0 -2
- package/dist/create.d.ts +0 -2
- package/dist/index.d.mts +0 -188
- package/dist/index.d.ts +0 -188
- package/dist/lyt.d.mts +0 -1
- package/dist/lyt.d.ts +0 -1
package/dist/lyt.cjs
CHANGED
|
@@ -5,6 +5,26 @@ var fs = require('fs');
|
|
|
5
5
|
var path = require('path');
|
|
6
6
|
var child_process = require('child_process');
|
|
7
7
|
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
27
|
+
|
|
8
28
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
29
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
30
|
}) : x)(function(x) {
|
|
@@ -62,8 +82,8 @@ function writeFile(filePath, content) {
|
|
|
62
82
|
function readFile(filePath) {
|
|
63
83
|
return fs.readFileSync(filePath, "utf-8");
|
|
64
84
|
}
|
|
65
|
-
function exists(
|
|
66
|
-
return fs.existsSync(
|
|
85
|
+
function exists(path2) {
|
|
86
|
+
return fs.existsSync(path2);
|
|
67
87
|
}
|
|
68
88
|
function isEmptyDir(dir) {
|
|
69
89
|
if (!fs.existsSync(dir)) return true;
|
|
@@ -170,7 +190,7 @@ function generateProjectFiles(targetDir, projectName, template) {
|
|
|
170
190
|
},
|
|
171
191
|
devDependencies: {
|
|
172
192
|
"@lytjs/plugin-vite": "^6.0.0",
|
|
173
|
-
|
|
193
|
+
vite: "^5.0.0"
|
|
174
194
|
}
|
|
175
195
|
};
|
|
176
196
|
if (!isMinimal) {
|
|
@@ -443,12 +463,19 @@ export const useCounterStore = defineStore('counter', () => {
|
|
|
443
463
|
writeFile(path.join(targetDir, "src", "stores", "counter.ts"), counterStore);
|
|
444
464
|
}
|
|
445
465
|
if (isSsr) {
|
|
446
|
-
const entryServer = `import { createSSRApp } from '@lytjs/core';
|
|
466
|
+
const entryServer = `import { createSSRApp, h } from '@lytjs/core';
|
|
467
|
+
import { renderToString } from '@lytjs/ssr';
|
|
447
468
|
import App from './App.lyt';
|
|
448
469
|
|
|
449
470
|
export async function render(url: string) {
|
|
450
|
-
const app = createSSRApp(
|
|
451
|
-
|
|
471
|
+
const app = createSSRApp({
|
|
472
|
+
render() {
|
|
473
|
+
return h(App);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const html = await renderToString(app);
|
|
478
|
+
return html;
|
|
452
479
|
}
|
|
453
480
|
`;
|
|
454
481
|
writeFile(path.join(targetDir, "src/entry-server.ts"), entryServer);
|
|
@@ -462,36 +489,114 @@ app.mount('#app');
|
|
|
462
489
|
const serverTs = `/**
|
|
463
490
|
* LytJS SSR Server
|
|
464
491
|
*
|
|
465
|
-
*
|
|
492
|
+
* Complete SSR server with Vite dev server and production build support.
|
|
493
|
+
* Supports streaming SSR, route prefetching, and static file serving.
|
|
466
494
|
*/
|
|
467
495
|
|
|
468
496
|
import fs from 'fs';
|
|
469
497
|
import path from 'path';
|
|
470
498
|
import { fileURLToPath } from 'url';
|
|
499
|
+
import http from 'http';
|
|
471
500
|
|
|
472
501
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
473
502
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
503
|
+
const DIST_DIR = path.join(__dirname, 'dist');
|
|
504
|
+
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
505
|
+
|
|
506
|
+
interface RenderOptions {
|
|
507
|
+
url: string;
|
|
508
|
+
template: string;
|
|
509
|
+
manifest?: Record<string, string[]>;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function renderPage({ url, template, manifest }: RenderOptions): Promise<string> {
|
|
513
|
+
let app: any;
|
|
514
|
+
let vite: any;
|
|
515
|
+
|
|
516
|
+
if (!isProduction) {
|
|
517
|
+
const { createServer: createViteServer } = await import('vite');
|
|
518
|
+
vite = await createViteServer({
|
|
519
|
+
server: { middlewareMode: true },
|
|
520
|
+
appType: 'custom',
|
|
521
|
+
});
|
|
522
|
+
app = (await vite.ssrLoadModule(path.join(__dirname, 'src/entry-server.ts'))).default;
|
|
523
|
+
} else {
|
|
524
|
+
app = (await import(path.join(DIST_DIR, 'server/entry-server.js'))).default;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const html = await app.render(url);
|
|
528
|
+
return template.replace('<!--app-html-->', html);
|
|
529
|
+
}
|
|
474
530
|
|
|
475
531
|
async function createServer() {
|
|
476
|
-
let resolve: any;
|
|
477
532
|
let vite: any;
|
|
478
533
|
|
|
534
|
+
// Load index.html template
|
|
535
|
+
let template: string;
|
|
536
|
+
|
|
479
537
|
if (!isProduction) {
|
|
480
538
|
const { createServer: createViteServer } = await import('vite');
|
|
481
539
|
vite = await createViteServer({
|
|
482
540
|
server: { middlewareMode: true },
|
|
483
541
|
appType: 'custom',
|
|
484
542
|
});
|
|
485
|
-
|
|
543
|
+
template = fs.readFileSync(path.join(__dirname, 'index.html'), 'utf-8');
|
|
486
544
|
} else {
|
|
487
|
-
|
|
545
|
+
template = fs.readFileSync(path.join(DIST_DIR, 'client/index.html'), 'utf-8');
|
|
488
546
|
}
|
|
489
547
|
|
|
490
|
-
|
|
491
|
-
|
|
548
|
+
const server = http.createServer(async (req, res) => {
|
|
549
|
+
const url = req.url || '/';
|
|
550
|
+
|
|
551
|
+
try {
|
|
552
|
+
if (!isProduction && url.startsWith('/@')) {
|
|
553
|
+
// Vite dev server requests
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Static assets
|
|
558
|
+
if (url.startsWith('/assets/') || url.endsWith('.js') || url.endsWith('.css')) {
|
|
559
|
+
const filePath = isProduction
|
|
560
|
+
? path.join(DIST_DIR, 'client', url)
|
|
561
|
+
: path.join(__dirname, url);
|
|
562
|
+
|
|
563
|
+
if (fs.existsSync(filePath)) {
|
|
564
|
+
const ext = path.extname(filePath);
|
|
565
|
+
const contentType = ext === '.css' ? 'text/css' : 'application/javascript';
|
|
566
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
567
|
+
res.end(fs.readFileSync(filePath));
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// SSR rendering
|
|
573
|
+
const html = await renderPage({
|
|
574
|
+
url,
|
|
575
|
+
template,
|
|
576
|
+
manifest: isProduction
|
|
577
|
+
? JSON.parse(fs.readFileSync(path.join(DIST_DIR, 'client/ssr-manifest.json'), 'utf-8'))
|
|
578
|
+
: undefined
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
582
|
+
res.end(html);
|
|
583
|
+
} catch (err: any) {
|
|
584
|
+
if (!isProduction && vite) {
|
|
585
|
+
vite.ssrFixStacktrace(err);
|
|
586
|
+
}
|
|
587
|
+
console.error('SSR Error:', err);
|
|
588
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
589
|
+
res.end('Internal Server Error');
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
server.listen(PORT, () => {
|
|
594
|
+
console.log(\`LytJS SSR server running at http://localhost:\${PORT}\`);
|
|
595
|
+
console.log(\`Mode: \${isProduction ? 'Production' : 'Development'}\`);
|
|
596
|
+
});
|
|
492
597
|
}
|
|
493
598
|
|
|
494
|
-
createServer();
|
|
599
|
+
createServer().catch(console.error);
|
|
495
600
|
`;
|
|
496
601
|
writeFile(path.join(targetDir, "server.ts"), serverTs);
|
|
497
602
|
}
|
|
@@ -641,9 +746,10 @@ async function test(options = {}) {
|
|
|
641
746
|
var TEMPLATES2 = {
|
|
642
747
|
component(name, basePath) {
|
|
643
748
|
const filePath = path.join(basePath, `${name}.lyt`);
|
|
644
|
-
return [
|
|
645
|
-
|
|
646
|
-
|
|
749
|
+
return [
|
|
750
|
+
{
|
|
751
|
+
filePath,
|
|
752
|
+
content: `<template>
|
|
647
753
|
<div class="${name}">
|
|
648
754
|
<slot />
|
|
649
755
|
</div>
|
|
@@ -665,15 +771,18 @@ defineEmits<{
|
|
|
665
771
|
}
|
|
666
772
|
</style>
|
|
667
773
|
`
|
|
668
|
-
|
|
774
|
+
}
|
|
775
|
+
];
|
|
669
776
|
},
|
|
670
777
|
page(name, basePath) {
|
|
778
|
+
const pascalName = toPascalCase(name);
|
|
671
779
|
const filePath = path.join(basePath, `${name}.lyt`);
|
|
672
|
-
return [
|
|
673
|
-
|
|
674
|
-
|
|
780
|
+
return [
|
|
781
|
+
{
|
|
782
|
+
filePath,
|
|
783
|
+
content: `<template>
|
|
675
784
|
<div class="page-${name}">
|
|
676
|
-
<h1>${
|
|
785
|
+
<h1>${pascalName}</h1>
|
|
677
786
|
</div>
|
|
678
787
|
</template>
|
|
679
788
|
|
|
@@ -687,13 +796,15 @@ defineEmits<{
|
|
|
687
796
|
}
|
|
688
797
|
</style>
|
|
689
798
|
`
|
|
690
|
-
|
|
799
|
+
}
|
|
800
|
+
];
|
|
691
801
|
},
|
|
692
802
|
store(name, basePath) {
|
|
693
803
|
const filePath = path.join(basePath, `${name}.ts`);
|
|
694
|
-
return [
|
|
695
|
-
|
|
696
|
-
|
|
804
|
+
return [
|
|
805
|
+
{
|
|
806
|
+
filePath,
|
|
807
|
+
content: `import { defineStore } from '@lytjs/store';
|
|
697
808
|
import { signal, computed } from '@lytjs/reactivity';
|
|
698
809
|
|
|
699
810
|
export const use${toPascalCase(name)}Store = defineStore('${name}', () => {
|
|
@@ -725,12 +836,178 @@ export const use${toPascalCase(name)}Store = defineStore('${name}', () => {
|
|
|
725
836
|
};
|
|
726
837
|
});
|
|
727
838
|
`
|
|
728
|
-
|
|
839
|
+
}
|
|
840
|
+
];
|
|
841
|
+
},
|
|
842
|
+
directive(name, basePath) {
|
|
843
|
+
const filePath = path.join(basePath, `${name}.ts`);
|
|
844
|
+
const camelCaseName = toCamelCase(name);
|
|
845
|
+
return [
|
|
846
|
+
{
|
|
847
|
+
filePath,
|
|
848
|
+
content: `import type { Directive } from '@lytjs/core';
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* ${toPascalCase(name)} Directive
|
|
852
|
+
*
|
|
853
|
+
* @example
|
|
854
|
+
* \`\`\`vue
|
|
855
|
+
* <div v-${camelCaseName} />
|
|
856
|
+
* \`\`\`
|
|
857
|
+
*/
|
|
858
|
+
export const v${toPascalCase(name)}: Directive = {
|
|
859
|
+
mounted(el, binding) {
|
|
860
|
+
// Directive mounted
|
|
861
|
+
},
|
|
862
|
+
|
|
863
|
+
updated(el, binding) {
|
|
864
|
+
// Directive updated
|
|
865
|
+
},
|
|
866
|
+
|
|
867
|
+
unmounted(el) {
|
|
868
|
+
// Directive unmounted
|
|
869
|
+
},
|
|
870
|
+
};
|
|
871
|
+
`
|
|
872
|
+
}
|
|
873
|
+
];
|
|
874
|
+
},
|
|
875
|
+
composable(name, basePath) {
|
|
876
|
+
const filePath = path.join(basePath, `use${toPascalCase(name)}.ts`);
|
|
877
|
+
return [
|
|
878
|
+
{
|
|
879
|
+
filePath,
|
|
880
|
+
content: `import { signal, computed } from '@lytjs/reactivity';
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* ${toPascalCase(name)} Composable
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
* \`\`\`typescript
|
|
887
|
+
* const { state, actions } = use${toPascalCase(name)}();
|
|
888
|
+
* \`\`\`
|
|
889
|
+
*/
|
|
890
|
+
export function use${toPascalCase(name)}() {
|
|
891
|
+
// State
|
|
892
|
+
const isLoading = signal(false);
|
|
893
|
+
const error = signal<Error | null>(null);
|
|
894
|
+
const data = signal<any>(null);
|
|
895
|
+
|
|
896
|
+
// Computed
|
|
897
|
+
const hasData = computed(() => data.value !== null);
|
|
898
|
+
|
|
899
|
+
// Actions
|
|
900
|
+
async function fetch() {
|
|
901
|
+
isLoading.value = true;
|
|
902
|
+
error.value = null;
|
|
903
|
+
try {
|
|
904
|
+
// TODO: Fetch logic here
|
|
905
|
+
// data.value = await someApi();
|
|
906
|
+
} catch (e) {
|
|
907
|
+
error.value = e as Error;
|
|
908
|
+
} finally {
|
|
909
|
+
isLoading.value = false;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function reset() {
|
|
914
|
+
isLoading.value = false;
|
|
915
|
+
error.value = null;
|
|
916
|
+
data.value = null;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return {
|
|
920
|
+
isLoading,
|
|
921
|
+
error,
|
|
922
|
+
data,
|
|
923
|
+
hasData,
|
|
924
|
+
fetch,
|
|
925
|
+
reset,
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
`
|
|
929
|
+
}
|
|
930
|
+
];
|
|
931
|
+
},
|
|
932
|
+
hook(name, basePath) {
|
|
933
|
+
const filePath = path.join(basePath, `use${toPascalCase(name)}.ts`);
|
|
934
|
+
return [
|
|
935
|
+
{
|
|
936
|
+
filePath,
|
|
937
|
+
content: `import { signal, onMounted, onUnmounted } from '@lytjs/core';
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* ${toPascalCase(name)} Hook
|
|
941
|
+
*/
|
|
942
|
+
export function use${toPascalCase(name)}() {
|
|
943
|
+
const state = signal(null);
|
|
944
|
+
|
|
945
|
+
onMounted(() => {
|
|
946
|
+
// Setup code on mount
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
onUnmounted(() => {
|
|
950
|
+
// Cleanup on unmount
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
return {
|
|
954
|
+
state,
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
`
|
|
958
|
+
}
|
|
959
|
+
];
|
|
960
|
+
},
|
|
961
|
+
util(name, basePath) {
|
|
962
|
+
const filePath = path.join(basePath, `${name}.ts`);
|
|
963
|
+
return [
|
|
964
|
+
{
|
|
965
|
+
filePath,
|
|
966
|
+
content: `/**
|
|
967
|
+
* ${toPascalCase(name)} Utility Functions
|
|
968
|
+
*/
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* ${toPascalCase(name)} function
|
|
972
|
+
*
|
|
973
|
+
* @param input - The input value
|
|
974
|
+
* @returns The processed result
|
|
975
|
+
*/
|
|
976
|
+
export function ${toCamelCase(name)}(input: any) {
|
|
977
|
+
// TODO: Implement function
|
|
978
|
+
return input;
|
|
979
|
+
}
|
|
980
|
+
`
|
|
981
|
+
}
|
|
982
|
+
];
|
|
983
|
+
},
|
|
984
|
+
middleware(name, basePath) {
|
|
985
|
+
const filePath = path.join(basePath, `${name}.ts`);
|
|
986
|
+
return [
|
|
987
|
+
{
|
|
988
|
+
filePath,
|
|
989
|
+
content: `import type { NavigationGuard } from '@lytjs/router';
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* ${toPascalCase(name)} Middleware
|
|
993
|
+
*/
|
|
994
|
+
export const ${toCamelCase(name)}Middleware: NavigationGuard = (to, from, next) => {
|
|
995
|
+
// Middleware logic
|
|
996
|
+
console.log('Middleware:', to.path);
|
|
997
|
+
next();
|
|
998
|
+
};
|
|
999
|
+
`
|
|
1000
|
+
}
|
|
1001
|
+
];
|
|
729
1002
|
}
|
|
730
1003
|
};
|
|
731
1004
|
function toPascalCase(str) {
|
|
732
1005
|
return str.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
733
1006
|
}
|
|
1007
|
+
function toCamelCase(str) {
|
|
1008
|
+
const pascalCase = toPascalCase(str);
|
|
1009
|
+
return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
|
|
1010
|
+
}
|
|
734
1011
|
function resolveTargetDir(type) {
|
|
735
1012
|
const cwd = process.cwd();
|
|
736
1013
|
switch (type) {
|
|
@@ -740,6 +1017,16 @@ function resolveTargetDir(type) {
|
|
|
740
1017
|
return path.join(cwd, "src", "pages");
|
|
741
1018
|
case "store":
|
|
742
1019
|
return path.join(cwd, "src", "stores");
|
|
1020
|
+
case "directive":
|
|
1021
|
+
return path.join(cwd, "src", "directives");
|
|
1022
|
+
case "composable":
|
|
1023
|
+
return path.join(cwd, "src", "composables");
|
|
1024
|
+
case "util":
|
|
1025
|
+
return path.join(cwd, "src", "utils");
|
|
1026
|
+
case "middleware":
|
|
1027
|
+
return path.join(cwd, "src", "middleware");
|
|
1028
|
+
case "hook":
|
|
1029
|
+
return path.join(cwd, "src", "hooks");
|
|
743
1030
|
}
|
|
744
1031
|
}
|
|
745
1032
|
async function add(type, name, options = {}) {
|
|
@@ -766,6 +1053,546 @@ async function add(type, name, options = {}) {
|
|
|
766
1053
|
logger.success(`Created ${type}: ${file.filePath}`);
|
|
767
1054
|
}
|
|
768
1055
|
}
|
|
1056
|
+
var TEMPLATES3 = {
|
|
1057
|
+
component: (data, withStyles, withTest, template, lang) => {
|
|
1058
|
+
const styleImport = withStyles ? `
|
|
1059
|
+
import './${data.kebabName}.styles.css';` : "";
|
|
1060
|
+
const tsOnly = lang === "ts";
|
|
1061
|
+
if (template === "sfc") {
|
|
1062
|
+
return `<template>
|
|
1063
|
+
<div class="${data.kebabName}">
|
|
1064
|
+
<slot>
|
|
1065
|
+
${data.pascalName} Component
|
|
1066
|
+
</slot>
|
|
1067
|
+
</div>
|
|
1068
|
+
</template>
|
|
1069
|
+
|
|
1070
|
+
<script setup${tsOnly ? ' lang="ts"' : ""}>
|
|
1071
|
+
${tsOnly ? `import { ref } from '@lytjs/reactivity';
|
|
1072
|
+
` : ""}
|
|
1073
|
+
${tsOnly ? `
|
|
1074
|
+
export interface ${data.pascalName}Props {
|
|
1075
|
+
className?: string;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const props = defineProps<${data.pascalName}Props>();
|
|
1079
|
+
` : ""}
|
|
1080
|
+
|
|
1081
|
+
const title = ref('${data.pascalName}');
|
|
1082
|
+
</script>
|
|
1083
|
+
|
|
1084
|
+
<style scoped>
|
|
1085
|
+
.${data.kebabName} {
|
|
1086
|
+
/* Component styles */
|
|
1087
|
+
}
|
|
1088
|
+
</style>
|
|
1089
|
+
`;
|
|
1090
|
+
}
|
|
1091
|
+
const testImport = withTest ? `
|
|
1092
|
+
import { describe, it, expect } from 'vitest';
|
|
1093
|
+
import { ${data.pascalName} } from './${data.kebabName}';
|
|
1094
|
+
|
|
1095
|
+
describe('${data.pascalName}', () => {
|
|
1096
|
+
it('should render', () => {
|
|
1097
|
+
// Add test here
|
|
1098
|
+
expect(true).toBe(true);
|
|
1099
|
+
});
|
|
1100
|
+
});` : "";
|
|
1101
|
+
const propsDecl = tsOnly ? `['className', 'children']` : `[]`;
|
|
1102
|
+
return `/**
|
|
1103
|
+
* ${data.pascalName} \u7EC4\u4EF6
|
|
1104
|
+
*
|
|
1105
|
+
* @description ${data.description}
|
|
1106
|
+
* @created ${data.date}
|
|
1107
|
+
*/
|
|
1108
|
+
|
|
1109
|
+
import { h, defineComponent } from '@lytjs/core';${styleImport}
|
|
1110
|
+
|
|
1111
|
+
${tsOnly ? `export interface ${data.pascalName}Props {
|
|
1112
|
+
className?: string;
|
|
1113
|
+
children?: any;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
` : ""}${template === "functional" ? `export function ${data.pascalName}(${tsOnly ? `props: ${data.pascalName}Props` : "props"}) {
|
|
1117
|
+
const { className = '', children } = props;
|
|
1118
|
+
|
|
1119
|
+
return (
|
|
1120
|
+
<div className={\`${data.kebabName} \${className}\`}>
|
|
1121
|
+
{children || '${data.pascalName} Component'}
|
|
1122
|
+
</div>
|
|
1123
|
+
);
|
|
1124
|
+
}` : `export const ${data.pascalName} = defineComponent({
|
|
1125
|
+
name: '${data.pascalName}',
|
|
1126
|
+
props: ${propsDecl},
|
|
1127
|
+
setup(props) {
|
|
1128
|
+
const { className = '', children } = props;
|
|
1129
|
+
|
|
1130
|
+
return () => (
|
|
1131
|
+
<div className={\`${data.kebabName} \${className}\`}>
|
|
1132
|
+
{children || '${data.pascalName} Component'}
|
|
1133
|
+
</div>
|
|
1134
|
+
);
|
|
1135
|
+
},
|
|
1136
|
+
});`}
|
|
1137
|
+
|
|
1138
|
+
export default ${data.pascalName};${testImport}
|
|
1139
|
+
`;
|
|
1140
|
+
},
|
|
1141
|
+
page: (data, withStyles, withTest, template, lang) => {
|
|
1142
|
+
const styleImport = withStyles ? `
|
|
1143
|
+
import './${data.kebabName}.styles.css';` : "";
|
|
1144
|
+
const tsOnly = lang === "ts";
|
|
1145
|
+
if (template === "sfc") {
|
|
1146
|
+
return `<template>
|
|
1147
|
+
<div class="${data.kebabName}-page">
|
|
1148
|
+
<h1>{title}</h1>
|
|
1149
|
+
<p>Page content for ${data.pascalName}</p>
|
|
1150
|
+
<slot />
|
|
1151
|
+
</div>
|
|
1152
|
+
</template>
|
|
1153
|
+
|
|
1154
|
+
<script setup${tsOnly ? ' lang="ts"' : ""}>
|
|
1155
|
+
${tsOnly ? `import { ref } from '@lytjs/reactivity';
|
|
1156
|
+
` : ""}
|
|
1157
|
+
${tsOnly ? `
|
|
1158
|
+
export interface ${data.pascalName}PageProps {
|
|
1159
|
+
title?: string;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const props = defineProps<${data.pascalName}PageProps>();
|
|
1163
|
+
` : ""}
|
|
1164
|
+
|
|
1165
|
+
const title = ref(props.title || '${data.pascalName}');
|
|
1166
|
+
</script>
|
|
1167
|
+
|
|
1168
|
+
<style scoped>
|
|
1169
|
+
.${data.kebabName}-page {
|
|
1170
|
+
padding: 2rem;
|
|
1171
|
+
}
|
|
1172
|
+
</style>
|
|
1173
|
+
`;
|
|
1174
|
+
}
|
|
1175
|
+
const testImport = withTest ? `
|
|
1176
|
+
import { describe, it, expect } from 'vitest';
|
|
1177
|
+
import { ${data.pascalName}Page } from './${data.kebabName}';
|
|
1178
|
+
|
|
1179
|
+
describe('${data.pascalName}Page', () => {
|
|
1180
|
+
it('should render', () => {
|
|
1181
|
+
// Add test here
|
|
1182
|
+
expect(true).toBe(true);
|
|
1183
|
+
});
|
|
1184
|
+
});` : "";
|
|
1185
|
+
return `/**
|
|
1186
|
+
* ${data.pascalName} \u9875\u9762
|
|
1187
|
+
*
|
|
1188
|
+
* @description ${data.description}
|
|
1189
|
+
* @created ${data.date}
|
|
1190
|
+
*/
|
|
1191
|
+
|
|
1192
|
+
import { h, ${tsOnly ? "signal" : "signal"} } from '@lytjs/core';
|
|
1193
|
+
${styleImport}
|
|
1194
|
+
|
|
1195
|
+
${tsOnly ? `export interface ${data.pascalName}PageProps {
|
|
1196
|
+
title?: string;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
` : ""}export function ${data.pascalName}Page(${tsOnly ? `props: ${data.pascalName}PageProps` : "props"}) {
|
|
1200
|
+
const { title = '${data.pascalName}' } = props;
|
|
1201
|
+
|
|
1202
|
+
return (
|
|
1203
|
+
<div className="${data.kebabName}-page">
|
|
1204
|
+
<h1>{title}</h1>
|
|
1205
|
+
<p>Page content for ${data.pascalName}</p>
|
|
1206
|
+
</div>
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
export default ${data.pascalName}Page;${testImport}
|
|
1211
|
+
`;
|
|
1212
|
+
},
|
|
1213
|
+
service: (data, _withStyles, _withTest, _template, lang) => {
|
|
1214
|
+
const tsOnly = lang === "ts";
|
|
1215
|
+
return `/**
|
|
1216
|
+
* ${data.pascalName} \u670D\u52A1
|
|
1217
|
+
*
|
|
1218
|
+
* @description ${data.description}
|
|
1219
|
+
* @created ${data.date}
|
|
1220
|
+
*/
|
|
1221
|
+
|
|
1222
|
+
${tsOnly ? `export interface ${data.pascalName}ServiceOptions {
|
|
1223
|
+
baseUrl?: string;
|
|
1224
|
+
timeout?: number;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
` : ""}${tsOnly ? `export class ${data.pascalName}Service {
|
|
1228
|
+
private baseUrl: string;
|
|
1229
|
+
private timeout: number;
|
|
1230
|
+
` : `export class ${data.pascalName}Service {
|
|
1231
|
+
`}
|
|
1232
|
+
constructor(${tsOnly ? `options: ${data.pascalName}ServiceOptions = {}` : "options = {}"}) {
|
|
1233
|
+
this.baseUrl = options.baseUrl || '/api';
|
|
1234
|
+
this.timeout = options.timeout || 30000;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
async getAll()${tsOnly ? ": Promise<any[]>" : ""} {
|
|
1238
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s\`, {
|
|
1239
|
+
method: 'GET',
|
|
1240
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1241
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1242
|
+
});
|
|
1243
|
+
return response.json();
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
async getById(id)${tsOnly ? ": Promise<any>" : ""} {
|
|
1247
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s/\${id}\`, {
|
|
1248
|
+
method: 'GET',
|
|
1249
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1250
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1251
|
+
});
|
|
1252
|
+
return response.json();
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
async create(${tsOnly ? "data: any" : "data"})${tsOnly ? ": Promise<any>" : ""} {
|
|
1256
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s\`, {
|
|
1257
|
+
method: 'POST',
|
|
1258
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1259
|
+
body: JSON.stringify(data),
|
|
1260
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1261
|
+
});
|
|
1262
|
+
return response.json();
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
async update(id)${tsOnly ? ": Promise<any>" : ""} {
|
|
1266
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s/\${id}\`, {
|
|
1267
|
+
method: 'PUT',
|
|
1268
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1269
|
+
body: JSON.stringify(data),
|
|
1270
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1271
|
+
});
|
|
1272
|
+
return response.json();
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
async delete(id)${tsOnly ? ": Promise<void>" : ""} {
|
|
1276
|
+
await fetch(\`\${this.baseUrl}/${data.kebabName}s/\${id}\`, {
|
|
1277
|
+
method: 'DELETE',
|
|
1278
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
export default ${data.pascalName}Service;
|
|
1284
|
+
`;
|
|
1285
|
+
},
|
|
1286
|
+
hook: (data, _withStyles, _withTest, _template, lang) => {
|
|
1287
|
+
const tsOnly = lang === "ts";
|
|
1288
|
+
return `/**
|
|
1289
|
+
* ${data.pascalName} Hook
|
|
1290
|
+
*
|
|
1291
|
+
* @description ${data.description}
|
|
1292
|
+
* @created ${data.date}
|
|
1293
|
+
*/
|
|
1294
|
+
|
|
1295
|
+
import { signal, effect } from '@lytjs/reactivity';
|
|
1296
|
+
|
|
1297
|
+
${tsOnly ? `export interface ${data.pascalName}Options {
|
|
1298
|
+
immediate?: boolean;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
export interface ${data.pascalName}Return {
|
|
1302
|
+
data: ReturnType<typeof signal>;
|
|
1303
|
+
loading: ReturnType<typeof signal>;
|
|
1304
|
+
error: ReturnType<typeof signal>;
|
|
1305
|
+
execute: () => Promise<void>;
|
|
1306
|
+
reset: () => void;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
` : ""}export function use${data.pascalName}(${tsOnly ? `options: ${data.pascalName}Options = {}` : "options = {}"})${tsOnly ? `: ${data.pascalName}Return` : ""} {
|
|
1310
|
+
const { immediate = false } = options;
|
|
1311
|
+
|
|
1312
|
+
const data = signal<any>(null);
|
|
1313
|
+
const loading = signal(false);
|
|
1314
|
+
const error = signal<Error | null>(null);
|
|
1315
|
+
|
|
1316
|
+
async function execute() {
|
|
1317
|
+
loading.value = true;
|
|
1318
|
+
error.value = null;
|
|
1319
|
+
|
|
1320
|
+
try {
|
|
1321
|
+
const result = await new Promise(resolve => setTimeout(() => resolve(null), 100));
|
|
1322
|
+
data.value = result;
|
|
1323
|
+
} catch (e) {
|
|
1324
|
+
error.value = e as Error;
|
|
1325
|
+
} finally {
|
|
1326
|
+
loading.value = false;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function reset() {
|
|
1331
|
+
data.value = null;
|
|
1332
|
+
loading.value = false;
|
|
1333
|
+
error.value = null;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (immediate) {
|
|
1337
|
+
execute();
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
return {
|
|
1341
|
+
data,
|
|
1342
|
+
loading,
|
|
1343
|
+
error,
|
|
1344
|
+
execute,
|
|
1345
|
+
reset,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
export default use${data.pascalName};
|
|
1350
|
+
`;
|
|
1351
|
+
},
|
|
1352
|
+
store: (data, _withStyles, _withTest, _template, lang) => {
|
|
1353
|
+
const tsOnly = lang === "ts";
|
|
1354
|
+
return `/**
|
|
1355
|
+
* ${data.pascalName} Store
|
|
1356
|
+
*
|
|
1357
|
+
* @description ${data.description}
|
|
1358
|
+
* @created ${data.date}
|
|
1359
|
+
*/
|
|
1360
|
+
|
|
1361
|
+
import { signal, computed } from '@lytjs/reactivity';
|
|
1362
|
+
|
|
1363
|
+
${tsOnly ? `export interface ${data.pascalName}State {
|
|
1364
|
+
items: any[];
|
|
1365
|
+
selectedId: string | null;
|
|
1366
|
+
loading: boolean;
|
|
1367
|
+
error: Error | null;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
` : ""}export function create${data.pascalName}Store() {
|
|
1371
|
+
const state = signal${tsOnly ? `<${data.pascalName}State>` : ""}({
|
|
1372
|
+
items: [],
|
|
1373
|
+
selectedId: null,
|
|
1374
|
+
loading: false,
|
|
1375
|
+
error: null,
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
const selectedItem = computed(() => {
|
|
1379
|
+
const currentState = state.value;
|
|
1380
|
+
return currentState.items.find(item => item.id === currentState.selectedId);
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
const itemCount = computed(() => state.value.items.length);
|
|
1384
|
+
|
|
1385
|
+
function setItems(items) {
|
|
1386
|
+
state.value = { ...state.value, items };
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
function selectItem(id) {
|
|
1390
|
+
state.value = { ...state.value, selectedId: id };
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function addItem(item) {
|
|
1394
|
+
state.value = {
|
|
1395
|
+
...state.value,
|
|
1396
|
+
items: [...state.value.items, item],
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
function updateItem(id, updates) {
|
|
1401
|
+
state.value = {
|
|
1402
|
+
...state.value,
|
|
1403
|
+
items: state.value.items.map(item =>
|
|
1404
|
+
item.id === id ? { ...item, ...updates } : item
|
|
1405
|
+
),
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
function removeItem(id) {
|
|
1410
|
+
state.value = {
|
|
1411
|
+
...state.value,
|
|
1412
|
+
items: state.value.items.filter(item => item.id !== id),
|
|
1413
|
+
selectedId: state.value.selectedId === id ? null : state.value.selectedId,
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
function setLoading(loading) {
|
|
1418
|
+
state.value = { ...state.value, loading };
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
function setError(error) {
|
|
1422
|
+
state.value = { ...state.value, error };
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
function reset() {
|
|
1426
|
+
state.value = {
|
|
1427
|
+
items: [],
|
|
1428
|
+
selectedId: null,
|
|
1429
|
+
loading: false,
|
|
1430
|
+
error: null,
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
return {
|
|
1435
|
+
state,
|
|
1436
|
+
selectedItem,
|
|
1437
|
+
itemCount,
|
|
1438
|
+
setItems,
|
|
1439
|
+
selectItem,
|
|
1440
|
+
addItem,
|
|
1441
|
+
updateItem,
|
|
1442
|
+
removeItem,
|
|
1443
|
+
setLoading,
|
|
1444
|
+
setError,
|
|
1445
|
+
reset,
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
${tsOnly ? `export type ${data.pascalName}Store = ReturnType<typeof create${data.pascalName}Store>;
|
|
1450
|
+
` : ""}export default create${data.pascalName}Store;
|
|
1451
|
+
`;
|
|
1452
|
+
},
|
|
1453
|
+
layout: (data, withStyles, _withTest, _template, lang) => {
|
|
1454
|
+
const tsOnly = lang === "ts";
|
|
1455
|
+
const styleImport = withStyles ? `
|
|
1456
|
+
import './${data.kebabName}.styles.css';` : "";
|
|
1457
|
+
return `/**
|
|
1458
|
+
* ${data.pascalName} \u5E03\u5C40
|
|
1459
|
+
*
|
|
1460
|
+
* @description ${data.description}
|
|
1461
|
+
* @created ${data.date}
|
|
1462
|
+
*/
|
|
1463
|
+
|
|
1464
|
+
import { h, defineComponent } from '@lytjs/core';${styleImport}
|
|
1465
|
+
|
|
1466
|
+
${tsOnly ? `export interface ${data.pascalName}LayoutProps {
|
|
1467
|
+
children?: any;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
` : ""}export const ${data.pascalName}Layout = defineComponent({
|
|
1471
|
+
name: '${data.pascalName}Layout',
|
|
1472
|
+
setup(props) {
|
|
1473
|
+
return () => (
|
|
1474
|
+
<div className="${data.kebabName}-layout">
|
|
1475
|
+
<header className="${data.kebabName}-header">
|
|
1476
|
+
<slot name="header">
|
|
1477
|
+
<h1>${data.pascalName}</h1>
|
|
1478
|
+
</slot>
|
|
1479
|
+
</header>
|
|
1480
|
+
<main className="${data.kebabName}-main">
|
|
1481
|
+
<slot />
|
|
1482
|
+
</main>
|
|
1483
|
+
<footer className="${data.kebabName}-footer">
|
|
1484
|
+
<slot name="footer" />
|
|
1485
|
+
</footer>
|
|
1486
|
+
</div>
|
|
1487
|
+
);
|
|
1488
|
+
},
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
export default ${data.pascalName}Layout;
|
|
1492
|
+
`;
|
|
1493
|
+
},
|
|
1494
|
+
middleware: (data, _withStyles, _withTest, _template, lang) => {
|
|
1495
|
+
const tsOnly = lang === "ts";
|
|
1496
|
+
return `/**
|
|
1497
|
+
* ${data.pascalName} \u4E2D\u95F4\u4EF6
|
|
1498
|
+
*
|
|
1499
|
+
* @description ${data.description}
|
|
1500
|
+
* @created ${data.date}
|
|
1501
|
+
*/
|
|
1502
|
+
|
|
1503
|
+
${tsOnly ? `import type { Request, Response, NextFunction } from 'express';
|
|
1504
|
+
` : ""}
|
|
1505
|
+
export function ${data.camelName}Middleware(${tsOnly ? `req: Request, res: Response, next: NextFunction` : "req, res, next"}) {
|
|
1506
|
+
try {
|
|
1507
|
+
console.log('[${data.pascalName}] Middleware executed');
|
|
1508
|
+
next();
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
next(error);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
export default ${data.camelName}Middleware;
|
|
1515
|
+
`;
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
function toPascalCase2(name) {
|
|
1519
|
+
return name.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1520
|
+
}
|
|
1521
|
+
function toCamelCase2(name) {
|
|
1522
|
+
const pascal = toPascalCase2(name);
|
|
1523
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
1524
|
+
}
|
|
1525
|
+
function toKebabCase(name) {
|
|
1526
|
+
return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
1527
|
+
}
|
|
1528
|
+
async function generate(options) {
|
|
1529
|
+
const {
|
|
1530
|
+
type,
|
|
1531
|
+
name,
|
|
1532
|
+
path: basePath = "./src",
|
|
1533
|
+
withStyles = false,
|
|
1534
|
+
withTest = false,
|
|
1535
|
+
description = "",
|
|
1536
|
+
template = "default",
|
|
1537
|
+
language = "ts"
|
|
1538
|
+
} = options;
|
|
1539
|
+
const templateData = {
|
|
1540
|
+
name,
|
|
1541
|
+
pascalName: toPascalCase2(name),
|
|
1542
|
+
kebabName: toKebabCase(name),
|
|
1543
|
+
camelName: toCamelCase2(name),
|
|
1544
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
1545
|
+
description: description || `${toPascalCase2(name)} ${type}`
|
|
1546
|
+
};
|
|
1547
|
+
const typeDirs = {
|
|
1548
|
+
component: "components",
|
|
1549
|
+
page: "pages",
|
|
1550
|
+
service: "services",
|
|
1551
|
+
hook: "hooks",
|
|
1552
|
+
store: "stores",
|
|
1553
|
+
layout: "layouts",
|
|
1554
|
+
middleware: "middleware"
|
|
1555
|
+
};
|
|
1556
|
+
const targetDir = path__namespace.join(process.cwd(), basePath, typeDirs[type] || "components");
|
|
1557
|
+
await ensureDir(targetDir);
|
|
1558
|
+
const templateFn = TEMPLATES3[type];
|
|
1559
|
+
if (!templateFn) {
|
|
1560
|
+
logger.error(`Unknown type: ${type}`);
|
|
1561
|
+
logger.info("Available types: component, page, service, hook, store, layout, middleware");
|
|
1562
|
+
process.exit(1);
|
|
1563
|
+
}
|
|
1564
|
+
const extension = template === "sfc" ? "lyt" : language;
|
|
1565
|
+
const filename = `${templateData.kebabName}${type === "page" ? ".page" : ""}.${extension}`;
|
|
1566
|
+
const filePath = path__namespace.join(targetDir, filename);
|
|
1567
|
+
const content = templateFn(templateData, withStyles, withTest, template, language);
|
|
1568
|
+
await writeFile(filePath, content);
|
|
1569
|
+
logger.success(`Generated ${type}: ${filePath}`);
|
|
1570
|
+
if (withStyles && template !== "sfc") {
|
|
1571
|
+
const styleContent = `/**
|
|
1572
|
+
* ${templateData.pascalName} Styles
|
|
1573
|
+
*/
|
|
1574
|
+
|
|
1575
|
+
.${templateData.kebabName} {
|
|
1576
|
+
/* Component styles */
|
|
1577
|
+
}
|
|
1578
|
+
`;
|
|
1579
|
+
const stylePath = path__namespace.join(targetDir, `${templateData.kebabName}.styles.css`);
|
|
1580
|
+
await writeFile(stylePath, styleContent);
|
|
1581
|
+
logger.success(`Generated styles: ${stylePath}`);
|
|
1582
|
+
}
|
|
1583
|
+
if (withTest && template !== "sfc") {
|
|
1584
|
+
logger.info("Test file included in generated component");
|
|
1585
|
+
}
|
|
1586
|
+
logger.info("\nNext steps:");
|
|
1587
|
+
logger.info(` cd ${targetDir}`);
|
|
1588
|
+
logger.info(
|
|
1589
|
+
` Import your ${type}: import { ${template === "sfc" ? "default" : templateData.pascalName} } from './${templateData.kebabName}'`
|
|
1590
|
+
);
|
|
1591
|
+
logger.info("\nAvailable options:");
|
|
1592
|
+
logger.info(" --template=sfc : Single File Component (.lyt)");
|
|
1593
|
+
logger.info(" --template=functional : Functional component");
|
|
1594
|
+
logger.info(" --language=js : JavaScript output");
|
|
1595
|
+
}
|
|
769
1596
|
var PLUGIN_TEMPLATES = {
|
|
770
1597
|
default: "Default plugin template with TypeScript",
|
|
771
1598
|
minimal: "Minimal plugin without extra dependencies",
|
|
@@ -774,45 +1601,53 @@ var PLUGIN_TEMPLATES = {
|
|
|
774
1601
|
function getTemplateContent(template, pluginName) {
|
|
775
1602
|
const packageName = `@lytjs/plugin-${pluginName}`;
|
|
776
1603
|
const files = {};
|
|
777
|
-
files["package.json"] = JSON.stringify(
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1604
|
+
files["package.json"] = JSON.stringify(
|
|
1605
|
+
{
|
|
1606
|
+
name: packageName,
|
|
1607
|
+
version: "0.1.0",
|
|
1608
|
+
description: `LytJS plugin: ${pluginName}`,
|
|
1609
|
+
main: "dist/index.cjs",
|
|
1610
|
+
module: "dist/index.mjs",
|
|
1611
|
+
types: "dist/index.d.ts",
|
|
1612
|
+
exports: {
|
|
1613
|
+
".": {
|
|
1614
|
+
import: "./dist/index.mjs",
|
|
1615
|
+
require: "./dist/index.cjs",
|
|
1616
|
+
types: "./dist/index.d.ts"
|
|
1617
|
+
}
|
|
1618
|
+
},
|
|
1619
|
+
files: ["dist"],
|
|
1620
|
+
scripts: {
|
|
1621
|
+
build: "tsup",
|
|
1622
|
+
dev: "tsup --watch",
|
|
1623
|
+
test: "vitest",
|
|
1624
|
+
lint: "eslint src",
|
|
1625
|
+
prepublishOnly: "npm run build"
|
|
1626
|
+
},
|
|
1627
|
+
keywords: ["lytjs", "plugin"],
|
|
1628
|
+
license: "MIT",
|
|
1629
|
+
peerDependencies: {
|
|
1630
|
+
"@lytjs/core": ">=6.0.0"
|
|
789
1631
|
}
|
|
790
1632
|
},
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1633
|
+
null,
|
|
1634
|
+
2
|
|
1635
|
+
);
|
|
1636
|
+
files["tsconfig.json"] = JSON.stringify(
|
|
1637
|
+
{
|
|
1638
|
+
extends: "@lytjs/core/tsconfig.json",
|
|
1639
|
+
compilerOptions: {
|
|
1640
|
+
outDir: "./dist",
|
|
1641
|
+
rootDir: "./src",
|
|
1642
|
+
declaration: true,
|
|
1643
|
+
declarationMap: true
|
|
1644
|
+
},
|
|
1645
|
+
include: ["src"],
|
|
1646
|
+
exclude: ["node_modules", "dist", "tests"]
|
|
798
1647
|
},
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
"@lytjs/core": ">=6.0.0"
|
|
803
|
-
}
|
|
804
|
-
}, null, 2);
|
|
805
|
-
files["tsconfig.json"] = JSON.stringify({
|
|
806
|
-
extends: "@lytjs/core/tsconfig.json",
|
|
807
|
-
compilerOptions: {
|
|
808
|
-
outDir: "./dist",
|
|
809
|
-
rootDir: "./src",
|
|
810
|
-
declaration: true,
|
|
811
|
-
declarationMap: true
|
|
812
|
-
},
|
|
813
|
-
include: ["src"],
|
|
814
|
-
exclude: ["node_modules", "dist", "tests"]
|
|
815
|
-
}, null, 2);
|
|
1648
|
+
null,
|
|
1649
|
+
2
|
|
1650
|
+
);
|
|
816
1651
|
files["tsup.config.ts"] = `import { defineConfig } from 'tsup';
|
|
817
1652
|
|
|
818
1653
|
export default defineConfig({
|
|
@@ -857,8 +1692,6 @@ const optionsSchema: ConfigSchema<${pluginName.replace(/-/g, "")}Options> = {
|
|
|
857
1692
|
additionalProperties: false,
|
|
858
1693
|
};
|
|
859
1694
|
|
|
860
|
-
${pluginName.replace(/-/g, "")}Options\`;
|
|
861
|
-
|
|
862
1695
|
export interface ${pluginName.replace(/-/g, "")}Options {
|
|
863
1696
|
debug?: boolean;
|
|
864
1697
|
option1?: string;
|
|
@@ -1026,7 +1859,16 @@ async function buildPlugin(options = {}) {
|
|
|
1026
1859
|
logger.info("Building plugin...");
|
|
1027
1860
|
try {
|
|
1028
1861
|
const buildCmd = "tsup";
|
|
1029
|
-
const args = [
|
|
1862
|
+
const args = [
|
|
1863
|
+
"--entry",
|
|
1864
|
+
"src/index.ts",
|
|
1865
|
+
"--outDir",
|
|
1866
|
+
outDir,
|
|
1867
|
+
"--format",
|
|
1868
|
+
"esm,cjs",
|
|
1869
|
+
"--dts",
|
|
1870
|
+
"--sourcemap"
|
|
1871
|
+
];
|
|
1030
1872
|
if (options.minify) {
|
|
1031
1873
|
args.push("--minify");
|
|
1032
1874
|
}
|
|
@@ -1092,8 +1934,8 @@ async function validatePlugin(options = {}) {
|
|
|
1092
1934
|
if (!fs.existsSync(tsupConfigPath)) {
|
|
1093
1935
|
warnings.push("tsup.config.ts not found (recommended for builds)");
|
|
1094
1936
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1937
|
+
const hasErrors = errors.length > 0;
|
|
1938
|
+
const hasWarnings = warnings.length > 0;
|
|
1097
1939
|
if (hasErrors) {
|
|
1098
1940
|
logger.error("Validation failed with errors:");
|
|
1099
1941
|
for (const err of errors) {
|
|
@@ -1122,7 +1964,7 @@ async function validatePlugin(options = {}) {
|
|
|
1122
1964
|
}
|
|
1123
1965
|
function isEmptyDir2(dir) {
|
|
1124
1966
|
if (!fs.existsSync(dir)) return true;
|
|
1125
|
-
const files =
|
|
1967
|
+
const files = readdirSync(dir);
|
|
1126
1968
|
return files.length === 0;
|
|
1127
1969
|
}
|
|
1128
1970
|
function listPluginTemplates() {
|
|
@@ -1141,7 +1983,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
1141
1983
|
return;
|
|
1142
1984
|
}
|
|
1143
1985
|
if (options.version || command === "version" || command === "-v" || command === "--version") {
|
|
1144
|
-
console.
|
|
1986
|
+
console.warn(`LytJS CLI v${VERSION}`);
|
|
1145
1987
|
return;
|
|
1146
1988
|
}
|
|
1147
1989
|
switch (command) {
|
|
@@ -1175,9 +2017,20 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
1175
2017
|
grep: options.grep
|
|
1176
2018
|
});
|
|
1177
2019
|
break;
|
|
1178
|
-
case "add":
|
|
1179
|
-
|
|
1180
|
-
|
|
2020
|
+
case "add": {
|
|
2021
|
+
const addTypes = [
|
|
2022
|
+
"component",
|
|
2023
|
+
"page",
|
|
2024
|
+
"store",
|
|
2025
|
+
"directive",
|
|
2026
|
+
"composable",
|
|
2027
|
+
"util",
|
|
2028
|
+
"middleware",
|
|
2029
|
+
"hook"
|
|
2030
|
+
];
|
|
2031
|
+
if (!args[0] || !addTypes.includes(args[0])) {
|
|
2032
|
+
logger.error("Usage: lyt add <type> <name>");
|
|
2033
|
+
logger.info("Types: component, page, store, directive, composable, util, middleware, hook");
|
|
1181
2034
|
logger.info("Example: lyt add component Button");
|
|
1182
2035
|
process.exit(1);
|
|
1183
2036
|
}
|
|
@@ -1185,7 +2038,27 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
1185
2038
|
force: options.force
|
|
1186
2039
|
});
|
|
1187
2040
|
break;
|
|
1188
|
-
|
|
2041
|
+
}
|
|
2042
|
+
case "generate":
|
|
2043
|
+
case "g": {
|
|
2044
|
+
const genTypes = ["component", "page", "service", "hook", "store"];
|
|
2045
|
+
if (!args[0] || !genTypes.includes(args[0])) {
|
|
2046
|
+
logger.error("Usage: lyt generate <type> <name>");
|
|
2047
|
+
logger.info("Types: component, page, service, hook, store");
|
|
2048
|
+
logger.info("Example: lyt generate component Button");
|
|
2049
|
+
process.exit(1);
|
|
2050
|
+
}
|
|
2051
|
+
await generate({
|
|
2052
|
+
type: args[0],
|
|
2053
|
+
name: args[1] || "Unnamed",
|
|
2054
|
+
path: options.path,
|
|
2055
|
+
withStyles: options.styles,
|
|
2056
|
+
withTest: options.test,
|
|
2057
|
+
withStorybook: options.storybook
|
|
2058
|
+
});
|
|
2059
|
+
break;
|
|
2060
|
+
}
|
|
2061
|
+
case "plugin": {
|
|
1189
2062
|
if (!args[0]) {
|
|
1190
2063
|
logger.error("Usage: lyt plugin <create|build|validate|templates>");
|
|
1191
2064
|
logger.info("Example: lyt plugin create my-plugin");
|
|
@@ -1222,6 +2095,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
1222
2095
|
process.exit(1);
|
|
1223
2096
|
}
|
|
1224
2097
|
break;
|
|
2098
|
+
}
|
|
1225
2099
|
default:
|
|
1226
2100
|
if (command) {
|
|
1227
2101
|
logger.error(`Unknown command: ${command}`);
|
|
@@ -1268,7 +2142,7 @@ function parseArgs(args) {
|
|
|
1268
2142
|
return { command, args: positional, options };
|
|
1269
2143
|
}
|
|
1270
2144
|
function showHelp() {
|
|
1271
|
-
console.
|
|
2145
|
+
console.warn(`
|
|
1272
2146
|
${logger.bold("LytJS CLI")} v${VERSION}
|
|
1273
2147
|
|
|
1274
2148
|
${logger.bold("Usage:")}
|
|
@@ -1280,10 +2154,11 @@ ${logger.bold("Commands:")}
|
|
|
1280
2154
|
dev Start development server
|
|
1281
2155
|
build Build for production
|
|
1282
2156
|
test Run tests
|
|
1283
|
-
add <type> <name> Generate a component, page,
|
|
2157
|
+
add <type> <name> Generate a component, page, store, directive, composable, etc.
|
|
2158
|
+
generate, g Advanced code generation (component, page, service, hook, store)
|
|
1284
2159
|
plugin <subcmd> Plugin development commands
|
|
1285
2160
|
help Show this help message
|
|
1286
|
-
|
|
2161
|
+
|
|
1287
2162
|
${logger.bold("Options:")}
|
|
1288
2163
|
--version, -v Show version number
|
|
1289
2164
|
--help Show help
|
|
@@ -1307,6 +2182,12 @@ ${logger.bold("Test Options:")}
|
|
|
1307
2182
|
--coverage Generate coverage report
|
|
1308
2183
|
--grep <pattern> Filter tests by pattern
|
|
1309
2184
|
|
|
2185
|
+
${logger.bold("Generate Options:")}
|
|
2186
|
+
--path <dir> Output directory (default: ./src)
|
|
2187
|
+
--styles Generate CSS styles file
|
|
2188
|
+
--test Generate test file
|
|
2189
|
+
--storybook Generate Storybook story file
|
|
2190
|
+
|
|
1310
2191
|
${logger.bold("Plugin Options:")}
|
|
1311
2192
|
--template <name> Use a specific plugin template (default, minimal, withConfig)
|
|
1312
2193
|
--force Overwrite existing directory
|
|
@@ -1327,6 +2208,16 @@ ${logger.bold("Examples:")}
|
|
|
1327
2208
|
lyt add component Button
|
|
1328
2209
|
lyt add page About
|
|
1329
2210
|
lyt add store user
|
|
2211
|
+
lyt directive click-outside
|
|
2212
|
+
lyt add composable fetch-data
|
|
2213
|
+
lyt add util format
|
|
2214
|
+
lyt add hook window-size
|
|
2215
|
+
lyt generate component Button --styles --test
|
|
2216
|
+
lyt generate page Dashboard --path ./src/pages
|
|
2217
|
+
lyt generate service User --path ./src/services
|
|
2218
|
+
lyt generate hook useCounter
|
|
2219
|
+
lyt generate store Auth --path ./src/stores
|
|
2220
|
+
lyt g page Login
|
|
1330
2221
|
lyt plugin create my-plugin
|
|
1331
2222
|
lyt plugin create my-plugin --template withConfig
|
|
1332
2223
|
lyt plugin build
|
|
@@ -1348,6 +2239,7 @@ exports.detectPackageManager = detectPackageManager;
|
|
|
1348
2239
|
exports.dev = dev;
|
|
1349
2240
|
exports.ensureDir = ensureDir;
|
|
1350
2241
|
exports.exists = exists;
|
|
2242
|
+
exports.generate = generate;
|
|
1351
2243
|
exports.getAddCommand = getAddCommand;
|
|
1352
2244
|
exports.getInstallCommand = getInstallCommand;
|
|
1353
2245
|
exports.getRunCommand = getRunCommand;
|