@lytjs/cli 5.0.6 → 6.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/dist/create.cjs +607 -0
- package/dist/create.cjs.map +1 -0
- package/dist/create.d.mts +2 -0
- package/dist/create.d.ts +2 -0
- package/dist/create.mjs +605 -0
- package/dist/create.mjs.map +1 -0
- package/dist/index.cjs +2212 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +208 -0
- package/dist/index.d.ts +208 -0
- package/dist/index.mjs +1999 -886
- package/dist/index.mjs.map +1 -0
- package/dist/lyt.cjs +2212 -0
- package/dist/lyt.cjs.map +1 -0
- package/dist/lyt.d.mts +1 -0
- package/dist/lyt.d.ts +1 -0
- package/dist/lyt.mjs +2171 -0
- package/dist/lyt.mjs.map +1 -0
- package/lyt-cli.js +3 -0
- package/package.json +34 -31
- package/README.md +0 -227
- package/dist/bin/cli.cjs +0 -1058
- package/dist/bin/cli.js +0 -2
- package/dist/bin/cli.mjs +0 -1058
- package/dist/index.js +0 -1058
- package/dist/types/bin/cli.d.ts +0 -8
- package/dist/types/bin/cli.d.ts.map +0 -1
- package/dist/types/build.d.ts +0 -30
- package/dist/types/build.d.ts.map +0 -1
- package/dist/types/create.d.ts +0 -19
- package/dist/types/create.d.ts.map +0 -1
- package/dist/types/dev.d.ts +0 -24
- package/dist/types/dev.d.ts.map +0 -1
- package/dist/types/generate.d.ts +0 -14
- package/dist/types/generate.d.ts.map +0 -1
- package/dist/types/hmr.d.ts +0 -55
- package/dist/types/hmr.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -20
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/scaffold.d.ts +0 -67
- package/dist/types/scaffold.d.ts.map +0 -1
- package/dist/types/utils.d.ts +0 -92
- package/dist/types/utils.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,1058 +1,2171 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
</body>
|
|
14
|
-
</html>
|
|
15
|
-
`}function ue(){return`import { createApp } from '@lytjs/lytjs';
|
|
16
|
-
import App from './App';
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { join, resolve, dirname } from 'path';
|
|
5
|
+
import { execSync, spawn } from 'child_process';
|
|
6
|
+
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
17
13
|
|
|
18
|
-
//
|
|
19
|
-
|
|
14
|
+
// src/utils/logger.ts
|
|
15
|
+
var colors = {
|
|
16
|
+
reset: "\x1B[0m",
|
|
17
|
+
bright: "\x1B[1m",
|
|
18
|
+
dim: "\x1B[2m",
|
|
19
|
+
red: "\x1B[31m",
|
|
20
|
+
green: "\x1B[32m",
|
|
21
|
+
yellow: "\x1B[33m",
|
|
22
|
+
blue: "\x1B[34m",
|
|
23
|
+
cyan: "\x1B[36m"
|
|
24
|
+
};
|
|
25
|
+
function colorize(text, color) {
|
|
26
|
+
if (!isColorSupported()) return text;
|
|
27
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
28
|
+
}
|
|
29
|
+
var logger = {
|
|
30
|
+
info(message) {
|
|
31
|
+
console.log(colorize("\u2139 ", "blue") + message);
|
|
32
|
+
},
|
|
33
|
+
success(message) {
|
|
34
|
+
console.log(colorize("\u2714 ", "green") + message);
|
|
35
|
+
},
|
|
36
|
+
warning(message) {
|
|
37
|
+
console.log(colorize("\u26A0 ", "yellow") + message);
|
|
38
|
+
},
|
|
39
|
+
error(message) {
|
|
40
|
+
console.error(colorize("\u2716 ", "red") + message);
|
|
41
|
+
},
|
|
42
|
+
dim(message) {
|
|
43
|
+
console.log(colorize(message, "dim"));
|
|
44
|
+
},
|
|
45
|
+
bold(message) {
|
|
46
|
+
return colorize(message, "bright");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function isColorSupported() {
|
|
50
|
+
return process.stdout.isTTY && process.env.NO_COLOR !== "1";
|
|
51
|
+
}
|
|
52
|
+
function ensureDir(dir) {
|
|
53
|
+
if (!existsSync(dir)) {
|
|
54
|
+
mkdirSync(dir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function writeFile(filePath, content) {
|
|
58
|
+
ensureDir(dirname(filePath));
|
|
59
|
+
writeFileSync(filePath, content, "utf-8");
|
|
60
|
+
}
|
|
61
|
+
function readFile(filePath) {
|
|
62
|
+
return readFileSync(filePath, "utf-8");
|
|
63
|
+
}
|
|
64
|
+
function exists(path2) {
|
|
65
|
+
return existsSync(path2);
|
|
66
|
+
}
|
|
67
|
+
function isEmptyDir(dir) {
|
|
68
|
+
if (!existsSync(dir)) return true;
|
|
69
|
+
const files = readdirSync(dir);
|
|
70
|
+
return files.length === 0;
|
|
71
|
+
}
|
|
72
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
73
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
74
|
+
if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
|
|
75
|
+
if (existsSync(join(cwd, "package-lock.json"))) return "npm";
|
|
76
|
+
try {
|
|
77
|
+
execSync("pnpm --version", { stdio: "ignore" });
|
|
78
|
+
return "pnpm";
|
|
79
|
+
} catch {
|
|
80
|
+
return "npm";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function getInstallCommand(pm) {
|
|
84
|
+
switch (pm) {
|
|
85
|
+
case "pnpm":
|
|
86
|
+
return "pnpm install";
|
|
87
|
+
case "yarn":
|
|
88
|
+
return "yarn";
|
|
89
|
+
case "npm":
|
|
90
|
+
return "npm install";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getRunCommand(pm, script) {
|
|
94
|
+
switch (pm) {
|
|
95
|
+
case "pnpm":
|
|
96
|
+
return `pnpm run ${script}`;
|
|
97
|
+
case "yarn":
|
|
98
|
+
return `yarn ${script}`;
|
|
99
|
+
case "npm":
|
|
100
|
+
return `npm run ${script}`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getAddCommand(pm, dep, dev2 = false) {
|
|
104
|
+
const devFlag = dev2 ? " -D" : "";
|
|
105
|
+
switch (pm) {
|
|
106
|
+
case "pnpm":
|
|
107
|
+
return `pnpm add${devFlag} ${dep}`;
|
|
108
|
+
case "yarn":
|
|
109
|
+
return `yarn add${devFlag} ${dep}`;
|
|
110
|
+
case "npm":
|
|
111
|
+
return `npm install${devFlag} ${dep}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
var TEMPLATES = {
|
|
115
|
+
default: "Default template with TypeScript and Vite",
|
|
116
|
+
minimal: "Minimal template without extra dependencies",
|
|
117
|
+
ssr: "SSR-enabled template",
|
|
118
|
+
router: "Template with Router integration",
|
|
119
|
+
store: "Template with Store integration",
|
|
120
|
+
full: "Full-featured template with Router, Store, and UI components"
|
|
121
|
+
};
|
|
122
|
+
async function create(projectName, options = {}) {
|
|
123
|
+
if (!projectName) {
|
|
124
|
+
logger.error("Please provide a project name.");
|
|
125
|
+
logger.info("Usage: lyt create <project-name>");
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
const targetDir = resolve(process.cwd(), projectName);
|
|
129
|
+
if (exists(targetDir) && !isEmptyDir(targetDir) && !options.force) {
|
|
130
|
+
logger.error(`Directory "${projectName}" already exists and is not empty.`);
|
|
131
|
+
logger.info("Use --force to overwrite.");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
logger.info(`Creating a new LytJS project in ${targetDir}...`);
|
|
135
|
+
ensureDir(targetDir);
|
|
136
|
+
generateProjectFiles(targetDir, projectName, options.template || "default");
|
|
137
|
+
logger.info("Installing dependencies...");
|
|
138
|
+
const pm = detectPackageManager();
|
|
139
|
+
try {
|
|
140
|
+
execSync(getInstallCommand(pm), { cwd: targetDir, stdio: "inherit" });
|
|
141
|
+
logger.success("Dependencies installed successfully!");
|
|
142
|
+
} catch (_error) {
|
|
143
|
+
logger.warning("Failed to install dependencies automatically.");
|
|
144
|
+
logger.info(`Please run "${getInstallCommand(pm)}" manually.`);
|
|
145
|
+
}
|
|
146
|
+
logger.success(`Project "${projectName}" created successfully!`);
|
|
147
|
+
logger.info("");
|
|
148
|
+
logger.bold("Next steps:");
|
|
149
|
+
logger.info(` cd ${projectName}`);
|
|
150
|
+
logger.info(` ${pm === "npm" ? "npm run" : pm} dev`);
|
|
151
|
+
}
|
|
152
|
+
function generateProjectFiles(targetDir, projectName, template) {
|
|
153
|
+
const isMinimal = template === "minimal";
|
|
154
|
+
const isSsr = template === "ssr";
|
|
155
|
+
const isRouter = template === "router" || template === "full";
|
|
156
|
+
const isStore = template === "store" || template === "full";
|
|
157
|
+
const isFull = template === "full";
|
|
158
|
+
const packageJson = {
|
|
159
|
+
name: projectName,
|
|
160
|
+
version: "0.0.0",
|
|
161
|
+
type: "module",
|
|
162
|
+
scripts: {
|
|
163
|
+
dev: "vite",
|
|
164
|
+
build: "vite build",
|
|
165
|
+
preview: "vite preview"
|
|
166
|
+
},
|
|
167
|
+
dependencies: {
|
|
168
|
+
"@lytjs/core": "^6.0.0"
|
|
169
|
+
},
|
|
170
|
+
devDependencies: {
|
|
171
|
+
"@lytjs/plugin-vite": "^6.0.0",
|
|
172
|
+
"vite": "^5.0.0"
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
if (!isMinimal) {
|
|
176
|
+
packageJson.scripts.test = "vitest";
|
|
177
|
+
packageJson.devDependencies.vitest = "^1.0.0";
|
|
178
|
+
}
|
|
179
|
+
if (isSsr) {
|
|
180
|
+
packageJson.dependencies["@lytjs/server"] = "^6.0.0";
|
|
181
|
+
packageJson.scripts["build:client"] = "vite build --ssrManifest";
|
|
182
|
+
packageJson.scripts["build:server"] = "vite build --ssr src/entry-server.ts";
|
|
183
|
+
packageJson.scripts["build"] = "npm run build:client && npm run build:server";
|
|
184
|
+
packageJson.scripts["preview"] = "node server";
|
|
185
|
+
}
|
|
186
|
+
if (isRouter) {
|
|
187
|
+
packageJson.dependencies["@lytjs/router"] = "^1.0.0";
|
|
188
|
+
}
|
|
189
|
+
if (isStore) {
|
|
190
|
+
packageJson.dependencies["@lytjs/store"] = "^1.0.0";
|
|
191
|
+
}
|
|
192
|
+
if (isFull) {
|
|
193
|
+
packageJson.dependencies["@lytjs/ui"] = "^0.4.0";
|
|
194
|
+
}
|
|
195
|
+
writeFile(join(targetDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
196
|
+
let viteConfig;
|
|
197
|
+
if (isSsr) {
|
|
198
|
+
viteConfig = `import { defineConfig } from 'vite';
|
|
199
|
+
import lytjs from '@lytjs/plugin-vite';
|
|
200
|
+
|
|
201
|
+
export default defineConfig({
|
|
202
|
+
plugins: [lytjs()],
|
|
203
|
+
build: {
|
|
204
|
+
ssrManifest: true,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
`;
|
|
208
|
+
} else {
|
|
209
|
+
viteConfig = `import { defineConfig } from 'vite';
|
|
210
|
+
import lytjs from '@lytjs/plugin-vite';
|
|
20
211
|
|
|
21
|
-
|
|
212
|
+
export default defineConfig({
|
|
213
|
+
plugins: [lytjs()],
|
|
214
|
+
});
|
|
215
|
+
`;
|
|
216
|
+
}
|
|
217
|
+
writeFile(join(targetDir, "vite.config.ts"), viteConfig);
|
|
218
|
+
const indexHtml = `<!DOCTYPE html>
|
|
219
|
+
<html lang="en">
|
|
220
|
+
<head>
|
|
221
|
+
<meta charset="UTF-8" />
|
|
222
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
223
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
224
|
+
<title>${projectName}</title>
|
|
225
|
+
</head>
|
|
226
|
+
<body>
|
|
227
|
+
<div id="app"></div>
|
|
228
|
+
<script type="module" src="/src/main.ts"></script>
|
|
229
|
+
</body>
|
|
230
|
+
</html>
|
|
231
|
+
`;
|
|
232
|
+
writeFile(join(targetDir, "index.html"), indexHtml);
|
|
233
|
+
let mainTs;
|
|
234
|
+
if (isSsr) {
|
|
235
|
+
mainTs = `import { createApp } from '@lytjs/core';
|
|
236
|
+
import App from './App.lyt';
|
|
237
|
+
import { createSSRApp } from '@lytjs/server';
|
|
238
|
+
${isRouter ? "import { createRouter, createWebHistory } from '@lytjs/router';" : ""}
|
|
239
|
+
${isStore ? "import { createPinia } from '@lytjs/store';" : ""}
|
|
240
|
+
|
|
241
|
+
const app = createSSRApp(App);
|
|
242
|
+
${isStore ? "app.use(createPinia());" : ""}
|
|
243
|
+
${isRouter ? `
|
|
244
|
+
const router = createRouter({
|
|
245
|
+
history: createWebHistory(),
|
|
246
|
+
routes: [
|
|
247
|
+
{ path: '/', component: () => import('./pages/Home.lyt') },
|
|
248
|
+
{ path: '/about', component: () => import('./pages/About.lyt') },
|
|
249
|
+
],
|
|
250
|
+
});
|
|
251
|
+
app.use(router);
|
|
252
|
+
` : ""}
|
|
22
253
|
app.mount('#app');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// \u7EC4\u4EF6\u6A21\u677F
|
|
30
|
-
template: \`
|
|
31
|
-
<div class="app">
|
|
32
|
-
<header class="app-header">
|
|
33
|
-
<div class="logo">
|
|
34
|
-
<span class="logo-text">Lyt</span>
|
|
35
|
-
</div>
|
|
36
|
-
</header>
|
|
37
|
-
|
|
38
|
-
<main class="app-main">
|
|
39
|
-
<div class="welcome-section">
|
|
40
|
-
<h1>\u6B22\u8FCE\u4F7F\u7528 Lyt.js!</h1>
|
|
41
|
-
<p>\u8F7B\u5199\u8F7B\u8DD1\uFF0C\u6240\u89C1\u5373\u4EE3\u7801</p>
|
|
42
|
-
|
|
43
|
-
<div class="counter-section">
|
|
44
|
-
<h2>\u8BA1\u6570\u5668\u793A\u4F8B</h2>
|
|
45
|
-
<p class="count-display">
|
|
46
|
-
\u5F53\u524D\u8BA1\u6570: <strong>{{ count }}</strong>
|
|
47
|
-
</p>
|
|
48
|
-
<p class="double-display">
|
|
49
|
-
\u53CC\u500D: <strong>{{ double }}</strong>
|
|
50
|
-
</p>
|
|
51
|
-
|
|
52
|
-
<div class="button-group">
|
|
53
|
-
<button class="btn btn-primary" on-click="increment">+1</button>
|
|
54
|
-
<button class="btn btn-secondary" on-click="decrement">-1</button>
|
|
55
|
-
<button class="btn btn-success" on-click="reset">\u91CD\u7F6E</button>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
<div class="features-section">
|
|
60
|
-
<h2>\u6838\u5FC3\u529F\u80FD</h2>
|
|
61
|
-
<ul>
|
|
62
|
-
<li v-for="feature in features" :key="feature">
|
|
63
|
-
{{ feature }}
|
|
64
|
-
</li>
|
|
65
|
-
</ul>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
</main>
|
|
69
|
-
|
|
70
|
-
<footer class="app-footer">
|
|
71
|
-
<p>
|
|
72
|
-
\u7528 \u2764\uFE0F \u6253\u9020
|
|
73
|
-
</p>
|
|
74
|
-
</footer>
|
|
75
|
-
</div>
|
|
76
|
-
\`,
|
|
77
|
-
|
|
78
|
-
// \u54CD\u5E94\u5F0F\u6570\u636E
|
|
79
|
-
setup() {
|
|
80
|
-
const count = ref(0);
|
|
81
|
-
const double = computed(() => count.value * 2);
|
|
82
|
-
|
|
83
|
-
const features = [
|
|
84
|
-
'\u54CD\u5E94\u5F0F\u7CFB\u7EDF\uFF08Proxy + Signal\uFF09',
|
|
85
|
-
'\u865A\u62DF DOM + Patch Flag \u4F18\u5316',
|
|
86
|
-
'\u7EC4\u4EF6\u7CFB\u7EDF\uFF08Options + Composition API\uFF09',
|
|
87
|
-
'\u5185\u7F6E\u8DEF\u7531',
|
|
88
|
-
'\u72B6\u6001\u7BA1\u7406',
|
|
89
|
-
'CLI \u5DE5\u5177\u94FE',
|
|
90
|
-
'\u6D4F\u89C8\u5668 DevTools',
|
|
91
|
-
'28+ UI \u7EC4\u4EF6',
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
const increment = () => {
|
|
95
|
-
count.value++;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const decrement = () => {
|
|
99
|
-
count.value--;
|
|
100
|
-
};
|
|
254
|
+
`;
|
|
255
|
+
} else if (isRouter || isStore) {
|
|
256
|
+
mainTs = `import { createApp } from '@lytjs/core';
|
|
257
|
+
import App from './App.lyt';
|
|
258
|
+
${isRouter ? "import { createRouter, createWebHistory } from '@lytjs/router';" : ""}
|
|
259
|
+
${isStore ? "import { createPinia } from '@lytjs/store';" : ""}
|
|
101
260
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
decrement,
|
|
112
|
-
reset,
|
|
113
|
-
};
|
|
114
|
-
},
|
|
261
|
+
const app = createApp(App);
|
|
262
|
+
${isStore ? "app.use(createPinia());" : ""}
|
|
263
|
+
${isRouter ? `
|
|
264
|
+
const router = createRouter({
|
|
265
|
+
history: createWebHistory(),
|
|
266
|
+
routes: [
|
|
267
|
+
{ path: '/', component: () => import('./pages/Home.lyt') },
|
|
268
|
+
{ path: '/about', component: () => import('./pages/About.lyt') },
|
|
269
|
+
],
|
|
115
270
|
});
|
|
271
|
+
app.use(router);
|
|
272
|
+
` : ""}
|
|
273
|
+
app.mount('#app');
|
|
274
|
+
`;
|
|
275
|
+
} else {
|
|
276
|
+
mainTs = `import { createApp } from '@lytjs/core';
|
|
277
|
+
import App from './App.lyt';
|
|
116
278
|
|
|
117
|
-
|
|
118
|
-
|
|
279
|
+
createApp(App).mount('#app');
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
282
|
+
writeFile(join(targetDir, "src/main.ts"), mainTs);
|
|
283
|
+
let appLyt;
|
|
284
|
+
if (isMinimal) {
|
|
285
|
+
appLyt = `<template>
|
|
286
|
+
<div class="app">
|
|
287
|
+
<h1>{{ title }}</h1>
|
|
288
|
+
</div>
|
|
289
|
+
</template>
|
|
119
290
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
--secondary-color: #747b88;
|
|
124
|
-
--success-color: #10b981;
|
|
125
|
-
--danger-color: #ef4444;
|
|
126
|
-
--text-color: #333;
|
|
127
|
-
--bg-color: #f9fafb;
|
|
128
|
-
--card-bg: #ffffff;
|
|
129
|
-
--border-color: #e5e7eb;
|
|
130
|
-
}
|
|
291
|
+
<script setup>
|
|
292
|
+
const title = 'Hello LytJS!';
|
|
293
|
+
</script>
|
|
131
294
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
box-sizing: border-box;
|
|
295
|
+
<style scoped>
|
|
296
|
+
.app {
|
|
297
|
+
text-align: center;
|
|
136
298
|
}
|
|
299
|
+
</style>
|
|
300
|
+
`;
|
|
301
|
+
} else if (isRouter) {
|
|
302
|
+
appLyt = `<template>
|
|
303
|
+
<div class="app">
|
|
304
|
+
<nav class="nav">
|
|
305
|
+
<router-link to="/">Home</router-link>
|
|
306
|
+
<router-link to="/about">About</router-link>
|
|
307
|
+
</nav>
|
|
308
|
+
<router-view />
|
|
309
|
+
</div>
|
|
310
|
+
</template>
|
|
137
311
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
background-color: var(--bg-color);
|
|
142
|
-
line-height: 1.6;
|
|
143
|
-
}
|
|
312
|
+
<script setup lang="ts">
|
|
313
|
+
import { RouterLink, RouterView } from '@lytjs/router';
|
|
314
|
+
</script>
|
|
144
315
|
|
|
316
|
+
<style scoped>
|
|
145
317
|
.app {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
flex-direction: column;
|
|
318
|
+
text-align: center;
|
|
319
|
+
padding: 2rem;
|
|
149
320
|
}
|
|
150
321
|
|
|
151
|
-
.
|
|
152
|
-
|
|
153
|
-
padding: 1.5rem 2rem;
|
|
154
|
-
color: white;
|
|
155
|
-
box-shadow: 0 2px 10px rgba(66, 184, 131, 0.2);
|
|
322
|
+
.nav {
|
|
323
|
+
margin-bottom: 2rem;
|
|
156
324
|
}
|
|
157
325
|
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
326
|
+
.nav a {
|
|
327
|
+
margin: 0 1rem;
|
|
328
|
+
color: #42b883;
|
|
329
|
+
text-decoration: none;
|
|
161
330
|
}
|
|
162
331
|
|
|
163
|
-
.
|
|
164
|
-
|
|
332
|
+
.nav a:hover {
|
|
333
|
+
text-decoration: underline;
|
|
165
334
|
}
|
|
335
|
+
</style>
|
|
336
|
+
`;
|
|
337
|
+
} else {
|
|
338
|
+
appLyt = `<template>
|
|
339
|
+
<div class="app">
|
|
340
|
+
<h1>{{ title }}</h1>
|
|
341
|
+
<p>Welcome to your LytJS app!</p>
|
|
342
|
+
</div>
|
|
343
|
+
</template>
|
|
166
344
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
max-width: 900px;
|
|
171
|
-
margin: 0 auto;
|
|
172
|
-
width: 100%;
|
|
173
|
-
}
|
|
345
|
+
<script setup>
|
|
346
|
+
const title = 'Hello LytJS!';
|
|
347
|
+
</script>
|
|
174
348
|
|
|
175
|
-
|
|
349
|
+
<style scoped>
|
|
350
|
+
.app {
|
|
176
351
|
text-align: center;
|
|
352
|
+
padding: 2rem;
|
|
177
353
|
}
|
|
178
354
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
margin-bottom: 0.5rem;
|
|
182
|
-
color: var(--primary-color);
|
|
355
|
+
h1 {
|
|
356
|
+
color: #42b883;
|
|
183
357
|
}
|
|
358
|
+
</style>
|
|
359
|
+
`;
|
|
360
|
+
}
|
|
361
|
+
writeFile(join(targetDir, "src/App.lyt"), appLyt);
|
|
362
|
+
if (isRouter) {
|
|
363
|
+
const homePage = `<template>
|
|
364
|
+
<div class="home">
|
|
365
|
+
<h1>Home</h1>
|
|
366
|
+
${isStore ? `
|
|
367
|
+
<p>Count: {{ count }}</p>
|
|
368
|
+
<button @click="increment">Increment</button>
|
|
369
|
+
<button @click="decrement">Decrement</button>
|
|
370
|
+
` : ""}
|
|
371
|
+
<p>Welcome to the Home page!</p>
|
|
372
|
+
</div>
|
|
373
|
+
</template>
|
|
184
374
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
375
|
+
<script setup lang="ts">
|
|
376
|
+
${isStore ? `import { useCounterStore } from '../stores/counter';
|
|
377
|
+
const counterStore = useCounterStore();
|
|
378
|
+
const { count, increment, decrement } = counterStore;
|
|
379
|
+
` : ""}
|
|
380
|
+
</script>
|
|
190
381
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
padding: 2rem;
|
|
195
|
-
margin: 2rem 0;
|
|
196
|
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
197
|
-
border: 1px solid var(--border-color);
|
|
382
|
+
<style scoped>
|
|
383
|
+
.home {
|
|
384
|
+
padding: 1rem;
|
|
198
385
|
}
|
|
386
|
+
</style>
|
|
387
|
+
`;
|
|
388
|
+
ensureDir(join(targetDir, "src", "pages"));
|
|
389
|
+
writeFile(join(targetDir, "src", "pages", "Home.lyt"), homePage);
|
|
390
|
+
const aboutPage = `<template>
|
|
391
|
+
<div class="about">
|
|
392
|
+
<h1>About</h1>
|
|
393
|
+
<p>This is the About page!</p>
|
|
394
|
+
</div>
|
|
395
|
+
</template>
|
|
199
396
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
color: var(--text-color);
|
|
203
|
-
}
|
|
397
|
+
<script setup lang="ts">
|
|
398
|
+
</script>
|
|
204
399
|
|
|
205
|
-
|
|
206
|
-
.
|
|
207
|
-
|
|
208
|
-
margin: 0.75rem 0;
|
|
400
|
+
<style scoped>
|
|
401
|
+
.about {
|
|
402
|
+
padding: 1rem;
|
|
209
403
|
}
|
|
404
|
+
</style>
|
|
405
|
+
`;
|
|
406
|
+
writeFile(join(targetDir, "src", "pages", "About.lyt"), aboutPage);
|
|
407
|
+
}
|
|
408
|
+
if (isStore) {
|
|
409
|
+
const counterStore = `import { defineStore } from '@lytjs/store';
|
|
410
|
+
import { signal, computed } from '@lytjs/reactivity';
|
|
210
411
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
gap: 1rem;
|
|
215
|
-
margin-top: 1.5rem;
|
|
216
|
-
flex-wrap: wrap;
|
|
217
|
-
}
|
|
412
|
+
export const useCounterStore = defineStore('counter', () => {
|
|
413
|
+
// State
|
|
414
|
+
const count = signal(0);
|
|
218
415
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
border: none;
|
|
222
|
-
border-radius: 8px;
|
|
223
|
-
font-size: 1rem;
|
|
224
|
-
font-weight: 500;
|
|
225
|
-
cursor: pointer;
|
|
226
|
-
transition: all 0.2s ease;
|
|
227
|
-
color: white;
|
|
228
|
-
}
|
|
416
|
+
// Getters
|
|
417
|
+
const doubleCount = computed(() => count.value * 2);
|
|
229
418
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
419
|
+
// Actions
|
|
420
|
+
function increment() {
|
|
421
|
+
count.value++;
|
|
422
|
+
}
|
|
234
423
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
424
|
+
function decrement() {
|
|
425
|
+
count.value--;
|
|
426
|
+
}
|
|
238
427
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
428
|
+
function reset() {
|
|
429
|
+
count.value = 0;
|
|
430
|
+
}
|
|
242
431
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
432
|
+
return {
|
|
433
|
+
count,
|
|
434
|
+
doubleCount,
|
|
435
|
+
increment,
|
|
436
|
+
decrement,
|
|
437
|
+
reset,
|
|
438
|
+
};
|
|
439
|
+
});
|
|
440
|
+
`;
|
|
441
|
+
ensureDir(join(targetDir, "src", "stores"));
|
|
442
|
+
writeFile(join(targetDir, "src", "stores", "counter.ts"), counterStore);
|
|
443
|
+
}
|
|
444
|
+
if (isSsr) {
|
|
445
|
+
const entryServer = `import { createSSRApp, h } from '@lytjs/core';
|
|
446
|
+
import { renderToString } from '@lytjs/ssr';
|
|
447
|
+
import App from './App.lyt';
|
|
448
|
+
|
|
449
|
+
export async function render(url: string) {
|
|
450
|
+
const app = createSSRApp({
|
|
451
|
+
render() {
|
|
452
|
+
return h(App);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
246
455
|
|
|
247
|
-
|
|
248
|
-
|
|
456
|
+
const html = await renderToString(app);
|
|
457
|
+
return html;
|
|
249
458
|
}
|
|
459
|
+
`;
|
|
460
|
+
writeFile(join(targetDir, "src/entry-server.ts"), entryServer);
|
|
461
|
+
const entryClient = `import { createApp } from '@lytjs/core';
|
|
462
|
+
import App from './App.lyt';
|
|
250
463
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
464
|
+
const app = createApp(App);
|
|
465
|
+
app.mount('#app');
|
|
466
|
+
`;
|
|
467
|
+
writeFile(join(targetDir, "src/entry-client.ts"), entryClient);
|
|
468
|
+
const serverTs = `/**
|
|
469
|
+
* LytJS SSR Server
|
|
470
|
+
*
|
|
471
|
+
* Complete SSR server with Vite dev server and production build support.
|
|
472
|
+
* Supports streaming SSR, route prefetching, and static file serving.
|
|
473
|
+
*/
|
|
254
474
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
475
|
+
import fs from 'fs';
|
|
476
|
+
import path from 'path';
|
|
477
|
+
import { fileURLToPath } from 'url';
|
|
478
|
+
import http from 'http';
|
|
258
479
|
|
|
259
|
-
.
|
|
260
|
-
|
|
261
|
-
|
|
480
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
481
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
482
|
+
const DIST_DIR = path.join(__dirname, 'dist');
|
|
483
|
+
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
262
484
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
485
|
+
interface RenderOptions {
|
|
486
|
+
url: string;
|
|
487
|
+
template: string;
|
|
488
|
+
manifest?: Record<string, string[]>;
|
|
266
489
|
}
|
|
267
490
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
color: var(--text-color);
|
|
272
|
-
}
|
|
491
|
+
async function renderPage({ url, template, manifest }: RenderOptions): Promise<string> {
|
|
492
|
+
let app: any;
|
|
493
|
+
let vite: any;
|
|
273
494
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
495
|
+
if (!isProduction) {
|
|
496
|
+
const { createServer: createViteServer } = await import('vite');
|
|
497
|
+
vite = await createViteServer({
|
|
498
|
+
server: { middlewareMode: true },
|
|
499
|
+
appType: 'custom',
|
|
500
|
+
});
|
|
501
|
+
app = (await vite.ssrLoadModule(path.join(__dirname, 'src/entry-server.ts'))).default;
|
|
502
|
+
} else {
|
|
503
|
+
app = (await import(path.join(DIST_DIR, 'server/entry-server.js'))).default;
|
|
504
|
+
}
|
|
280
505
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
margin: 0.5rem 0;
|
|
284
|
-
background: var(--card-bg);
|
|
285
|
-
border-radius: 8px;
|
|
286
|
-
border-left: 4px solid var(--primary-color);
|
|
287
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
506
|
+
const html = await app.render(url);
|
|
507
|
+
return template.replace('<!--app-html-->', html);
|
|
288
508
|
}
|
|
289
509
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
text-align: center;
|
|
293
|
-
color: var(--secondary-color);
|
|
294
|
-
border-top: 1px solid var(--border-color);
|
|
295
|
-
background: var(--card-bg);
|
|
296
|
-
}
|
|
510
|
+
async function createServer() {
|
|
511
|
+
let vite: any;
|
|
297
512
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
font-size: 2rem;
|
|
301
|
-
}
|
|
513
|
+
// Load index.html template
|
|
514
|
+
let template: string;
|
|
302
515
|
|
|
303
|
-
|
|
304
|
-
|
|
516
|
+
if (!isProduction) {
|
|
517
|
+
const { createServer: createViteServer } = await import('vite');
|
|
518
|
+
vite = await createViteServer({
|
|
519
|
+
server: { middlewareMode: true },
|
|
520
|
+
appType: 'custom',
|
|
521
|
+
});
|
|
522
|
+
template = fs.readFileSync(path.join(__dirname, 'index.html'), 'utf-8');
|
|
523
|
+
} else {
|
|
524
|
+
template = fs.readFileSync(path.join(DIST_DIR, 'client/index.html'), 'utf-8');
|
|
305
525
|
}
|
|
526
|
+
|
|
527
|
+
const server = http.createServer(async (req, res) => {
|
|
528
|
+
const url = req.url || '/';
|
|
529
|
+
|
|
530
|
+
try {
|
|
531
|
+
if (!isProduction && url.startsWith('/@')) {
|
|
532
|
+
// Vite dev server requests
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Static assets
|
|
537
|
+
if (url.startsWith('/assets/') || url.endsWith('.js') || url.endsWith('.css')) {
|
|
538
|
+
const filePath = isProduction
|
|
539
|
+
? path.join(DIST_DIR, 'client', url)
|
|
540
|
+
: path.join(__dirname, url);
|
|
541
|
+
|
|
542
|
+
if (fs.existsSync(filePath)) {
|
|
543
|
+
const ext = path.extname(filePath);
|
|
544
|
+
const contentType = ext === '.css' ? 'text/css' : 'application/javascript';
|
|
545
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
546
|
+
res.end(fs.readFileSync(filePath));
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// SSR rendering
|
|
552
|
+
const html = await renderPage({
|
|
553
|
+
url,
|
|
554
|
+
template,
|
|
555
|
+
manifest: isProduction
|
|
556
|
+
? JSON.parse(fs.readFileSync(path.join(DIST_DIR, 'client/ssr-manifest.json'), 'utf-8'))
|
|
557
|
+
: undefined
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
561
|
+
res.end(html);
|
|
562
|
+
} catch (err: any) {
|
|
563
|
+
if (!isProduction && vite) {
|
|
564
|
+
vite.ssrFixStacktrace(err);
|
|
565
|
+
}
|
|
566
|
+
console.error('SSR Error:', err);
|
|
567
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
568
|
+
res.end('Internal Server Error');
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
server.listen(PORT, () => {
|
|
573
|
+
console.log(\`LytJS SSR server running at http://localhost:\${PORT}\`);
|
|
574
|
+
console.log(\`Mode: \${isProduction ? 'Production' : 'Development'}\`);
|
|
575
|
+
});
|
|
306
576
|
}
|
|
307
|
-
`}function fe(e){return JSON.stringify({name:e,version:"0.1.0",type:"module",private:!0,scripts:{dev:"lytx dev",build:"lytx build",preview:"lytx preview"},dependencies:{"@lytjs/lytjs":"latest"},devDependencies:{"@lytjs/cli":"latest"}},null,2)+`
|
|
308
|
-
`}function he(){return JSON.stringify({compilerOptions:{target:"ES2020",module:"ESNext",moduleResolution:"bundler",strict:!0,jsx:"preserve",resolveJsonModule:!0,isolatedModules:!0,esModuleInterop:!0,lib:["ES2020","DOM","DOM.Iterable"],skipLibCheck:!0,noEmit:!0,paths:{"@/*":["./src/*"]}},include:["src/**/*.ts","src/**/*.tsx"],exclude:["node_modules","dist"]},null,2)+`
|
|
309
|
-
`}function ye(){return`# Dependencies
|
|
310
|
-
node_modules/
|
|
311
|
-
.pnp
|
|
312
|
-
.pnp.js
|
|
313
577
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
578
|
+
createServer().catch(console.error);
|
|
579
|
+
`;
|
|
580
|
+
writeFile(join(targetDir, "server.ts"), serverTs);
|
|
581
|
+
}
|
|
582
|
+
const tsConfig = {
|
|
583
|
+
compilerOptions: {
|
|
584
|
+
target: "ES2020",
|
|
585
|
+
useDefineForClassFields: true,
|
|
586
|
+
module: "ESNext",
|
|
587
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
588
|
+
skipLibCheck: true,
|
|
589
|
+
moduleResolution: "bundler",
|
|
590
|
+
allowImportingTsExtensions: true,
|
|
591
|
+
resolveJsonModule: true,
|
|
592
|
+
isolatedModules: true,
|
|
593
|
+
noEmit: true,
|
|
594
|
+
strict: true,
|
|
595
|
+
noUnusedLocals: true,
|
|
596
|
+
noUnusedParameters: true,
|
|
597
|
+
noFallthroughCasesInSwitch: true
|
|
598
|
+
},
|
|
599
|
+
include: ["src/**/*.ts", "src/**/*.lyt"],
|
|
600
|
+
references: [{ path: "./tsconfig.node.json" }]
|
|
601
|
+
};
|
|
602
|
+
writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2));
|
|
603
|
+
const tsConfigNode = {
|
|
604
|
+
compilerOptions: {
|
|
605
|
+
composite: true,
|
|
606
|
+
skipLibCheck: true,
|
|
607
|
+
module: "ESNext",
|
|
608
|
+
moduleResolution: "bundler",
|
|
609
|
+
allowSyntheticDefaultImports: true
|
|
610
|
+
},
|
|
611
|
+
include: ["vite.config.ts"]
|
|
612
|
+
};
|
|
613
|
+
writeFile(join(targetDir, "tsconfig.node.json"), JSON.stringify(tsConfigNode, null, 2));
|
|
614
|
+
const gitignore = `# Logs
|
|
615
|
+
logs
|
|
317
616
|
*.log
|
|
318
617
|
npm-debug.log*
|
|
618
|
+
yarn-debug.log*
|
|
619
|
+
yarn-error.log*
|
|
620
|
+
pnpm-debug.log*
|
|
621
|
+
lerna-debug.log*
|
|
622
|
+
|
|
623
|
+
node_modules
|
|
624
|
+
dist
|
|
625
|
+
dist-ssr
|
|
626
|
+
*.local
|
|
627
|
+
|
|
628
|
+
# Editor directories and files
|
|
629
|
+
.vscode/*
|
|
630
|
+
!.vscode/extensions.json
|
|
631
|
+
.idea
|
|
632
|
+
.DS_Store
|
|
633
|
+
*.suo
|
|
634
|
+
*.ntvs*
|
|
635
|
+
*.njsproj
|
|
636
|
+
*.sln
|
|
637
|
+
*.sw?
|
|
638
|
+
`;
|
|
639
|
+
writeFile(join(targetDir, ".gitignore"), gitignore);
|
|
640
|
+
}
|
|
641
|
+
function listTemplates() {
|
|
642
|
+
logger.bold("Available templates:");
|
|
643
|
+
for (const [name, description] of Object.entries(TEMPLATES)) {
|
|
644
|
+
logger.info(` ${name.padEnd(10)} - ${description}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
async function dev(options = {}) {
|
|
648
|
+
if (!exists(join(process.cwd(), "package.json"))) {
|
|
649
|
+
logger.error("No package.json found. Are you in a LytJS project directory?");
|
|
650
|
+
process.exit(1);
|
|
651
|
+
}
|
|
652
|
+
const pm = detectPackageManager();
|
|
653
|
+
const runCmd = getRunCommand(pm, "dev");
|
|
654
|
+
logger.info(`Starting development server with ${pm}...`);
|
|
655
|
+
const devArgs = [];
|
|
656
|
+
if (options.port) devArgs.push("--port", String(options.port));
|
|
657
|
+
if (options.host) devArgs.push("--host", options.host);
|
|
658
|
+
if (options.open) devArgs.push("--open");
|
|
659
|
+
const cmdParts = runCmd.split(" ");
|
|
660
|
+
const cmd = cmdParts[0] ?? "pnpm";
|
|
661
|
+
const cmdArgs = [...cmdParts.slice(1), ...devArgs];
|
|
662
|
+
const child = spawn(cmd, cmdArgs, {
|
|
663
|
+
stdio: "inherit",
|
|
664
|
+
shell: true
|
|
665
|
+
});
|
|
666
|
+
child.on("error", (error) => {
|
|
667
|
+
logger.error(`Failed to start dev server: ${error.message}`);
|
|
668
|
+
process.exit(1);
|
|
669
|
+
});
|
|
670
|
+
child.on("exit", (code) => {
|
|
671
|
+
process.exit(code || 0);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
async function build(options = {}) {
|
|
675
|
+
if (!exists(join(process.cwd(), "package.json"))) {
|
|
676
|
+
logger.error("No package.json found. Are you in a LytJS project directory?");
|
|
677
|
+
process.exit(1);
|
|
678
|
+
}
|
|
679
|
+
const pm = detectPackageManager();
|
|
680
|
+
logger.info("Building for production...");
|
|
681
|
+
const args = ["vite", "build"];
|
|
682
|
+
if (options.outDir) args.push("--outDir", options.outDir);
|
|
683
|
+
if (options.ssr) args.push("--ssr");
|
|
684
|
+
if (options.minify === false) args.push("--minify", "false");
|
|
685
|
+
const child = spawn(pm === "npm" ? "npx" : pm, args, {
|
|
686
|
+
stdio: "inherit",
|
|
687
|
+
shell: true
|
|
688
|
+
});
|
|
689
|
+
child.on("error", (error) => {
|
|
690
|
+
logger.error(`Build failed: ${error.message}`);
|
|
691
|
+
process.exit(1);
|
|
692
|
+
});
|
|
693
|
+
child.on("exit", (code) => {
|
|
694
|
+
if (code === 0) {
|
|
695
|
+
logger.success("Build completed successfully!");
|
|
696
|
+
} else {
|
|
697
|
+
logger.error(`Build failed with exit code ${code}`);
|
|
698
|
+
}
|
|
699
|
+
process.exit(code || 0);
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
async function test(options = {}) {
|
|
703
|
+
if (!exists(join(process.cwd(), "package.json"))) {
|
|
704
|
+
logger.error("No package.json found. Are you in a LytJS project directory?");
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
const pm = detectPackageManager();
|
|
708
|
+
logger.info("Running tests...");
|
|
709
|
+
const args = ["vitest"];
|
|
710
|
+
if (options.watch === false) args.push("run");
|
|
711
|
+
if (options.coverage) args.push("--coverage");
|
|
712
|
+
if (options.grep) args.push("--grep", options.grep);
|
|
713
|
+
const child = spawn(pm === "npm" ? "npx" : pm, args, {
|
|
714
|
+
stdio: "inherit",
|
|
715
|
+
shell: true
|
|
716
|
+
});
|
|
717
|
+
child.on("error", (error) => {
|
|
718
|
+
logger.error(`Tests failed: ${error.message}`);
|
|
719
|
+
process.exit(1);
|
|
720
|
+
});
|
|
721
|
+
child.on("exit", (code) => {
|
|
722
|
+
process.exit(code || 0);
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
var TEMPLATES2 = {
|
|
726
|
+
component(name, basePath) {
|
|
727
|
+
const filePath = join(basePath, `${name}.lyt`);
|
|
728
|
+
return [{
|
|
729
|
+
filePath,
|
|
730
|
+
content: `<template>
|
|
731
|
+
<div class="${name}">
|
|
732
|
+
<slot />
|
|
733
|
+
</div>
|
|
734
|
+
</template>
|
|
319
735
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
*.swo
|
|
736
|
+
<script setup lang="ts">
|
|
737
|
+
defineProps<{
|
|
738
|
+
/** Component props */
|
|
739
|
+
}>();
|
|
325
740
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
741
|
+
defineEmits<{
|
|
742
|
+
/** Component events */
|
|
743
|
+
}>();
|
|
744
|
+
</script>
|
|
329
745
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
746
|
+
<style scoped>
|
|
747
|
+
.${name} {
|
|
748
|
+
/* styles */
|
|
749
|
+
}
|
|
750
|
+
</style>
|
|
751
|
+
`
|
|
752
|
+
}];
|
|
753
|
+
},
|
|
754
|
+
page(name, basePath) {
|
|
755
|
+
const pascalName = toPascalCase(name);
|
|
756
|
+
const filePath = join(basePath, `${name}.lyt`);
|
|
757
|
+
return [{
|
|
758
|
+
filePath,
|
|
759
|
+
content: `<template>
|
|
760
|
+
<div class="page-${name}">
|
|
761
|
+
<h1>${pascalName}</h1>
|
|
762
|
+
</div>
|
|
763
|
+
</template>
|
|
333
764
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
`}function be(e){return`# ${e}
|
|
765
|
+
<script setup lang="ts">
|
|
766
|
+
// Page logic here
|
|
767
|
+
</script>
|
|
338
768
|
|
|
339
|
-
|
|
769
|
+
<style scoped>
|
|
770
|
+
.page-${name} {
|
|
771
|
+
padding: 1rem;
|
|
772
|
+
}
|
|
773
|
+
</style>
|
|
774
|
+
`
|
|
775
|
+
}];
|
|
776
|
+
},
|
|
777
|
+
store(name, basePath) {
|
|
778
|
+
const filePath = join(basePath, `${name}.ts`);
|
|
779
|
+
return [{
|
|
780
|
+
filePath,
|
|
781
|
+
content: `import { defineStore } from '@lytjs/store';
|
|
782
|
+
import { signal, computed } from '@lytjs/reactivity';
|
|
783
|
+
|
|
784
|
+
export const use${toPascalCase(name)}Store = defineStore('${name}', () => {
|
|
785
|
+
// State
|
|
786
|
+
const count = signal(0);
|
|
787
|
+
|
|
788
|
+
// Getters
|
|
789
|
+
const doubleCount = computed(() => count.value * 2);
|
|
790
|
+
|
|
791
|
+
// Actions
|
|
792
|
+
function increment() {
|
|
793
|
+
count.value++;
|
|
794
|
+
}
|
|
340
795
|
|
|
341
|
-
|
|
796
|
+
function decrement() {
|
|
797
|
+
count.value--;
|
|
798
|
+
}
|
|
342
799
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
- \u{1F527} **\u5F00\u7BB1\u5373\u7528** - \u5185\u7F6E\u8DEF\u7531\u3001\u72B6\u6001\u7BA1\u7406\u3001\u7EC4\u4EF6\u5E93
|
|
800
|
+
function reset() {
|
|
801
|
+
count.value = 0;
|
|
802
|
+
}
|
|
347
803
|
|
|
348
|
-
|
|
804
|
+
return {
|
|
805
|
+
count,
|
|
806
|
+
doubleCount,
|
|
807
|
+
increment,
|
|
808
|
+
decrement,
|
|
809
|
+
reset,
|
|
810
|
+
};
|
|
811
|
+
});
|
|
812
|
+
`
|
|
813
|
+
}];
|
|
814
|
+
},
|
|
815
|
+
directive(name, basePath) {
|
|
816
|
+
const filePath = join(basePath, `${name}.ts`);
|
|
817
|
+
const camelCaseName = toCamelCase(name);
|
|
818
|
+
return [{
|
|
819
|
+
filePath,
|
|
820
|
+
content: `import type { Directive } from '@lytjs/core';
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* ${toPascalCase(name)} Directive
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* \`\`\`vue
|
|
827
|
+
* <div v-${camelCaseName} />
|
|
828
|
+
* \`\`\`
|
|
829
|
+
*/
|
|
830
|
+
export const v${toPascalCase(name)}: Directive = {
|
|
831
|
+
mounted(el, binding) {
|
|
832
|
+
// Directive mounted
|
|
833
|
+
},
|
|
349
834
|
|
|
350
|
-
|
|
835
|
+
updated(el, binding) {
|
|
836
|
+
// Directive updated
|
|
837
|
+
},
|
|
351
838
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
839
|
+
unmounted(el) {
|
|
840
|
+
// Directive unmounted
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
`
|
|
844
|
+
}];
|
|
845
|
+
},
|
|
846
|
+
composable(name, basePath) {
|
|
847
|
+
const filePath = join(basePath, `use${toPascalCase(name)}.ts`);
|
|
848
|
+
return [{
|
|
849
|
+
filePath,
|
|
850
|
+
content: `import { signal, computed } from '@lytjs/reactivity';
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* ${toPascalCase(name)} Composable
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* \`\`\`typescript
|
|
857
|
+
* const { state, actions } = use${toPascalCase(name)}();
|
|
858
|
+
* \`\`\`
|
|
859
|
+
*/
|
|
860
|
+
export function use${toPascalCase(name)}() {
|
|
861
|
+
// State
|
|
862
|
+
const isLoading = signal(false);
|
|
863
|
+
const error = signal<Error | null>(null);
|
|
864
|
+
const data = signal<any>(null);
|
|
865
|
+
|
|
866
|
+
// Computed
|
|
867
|
+
const hasData = computed(() => data.value !== null);
|
|
868
|
+
|
|
869
|
+
// Actions
|
|
870
|
+
async function fetch() {
|
|
871
|
+
isLoading.value = true;
|
|
872
|
+
error.value = null;
|
|
873
|
+
try {
|
|
874
|
+
// TODO: Fetch logic here
|
|
875
|
+
// data.value = await someApi();
|
|
876
|
+
} catch (e) {
|
|
877
|
+
error.value = e as Error;
|
|
878
|
+
} finally {
|
|
879
|
+
isLoading.value = false;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
355
882
|
|
|
356
|
-
|
|
883
|
+
function reset() {
|
|
884
|
+
isLoading.value = false;
|
|
885
|
+
error.value = null;
|
|
886
|
+
data.value = null;
|
|
887
|
+
}
|
|
357
888
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
889
|
+
return {
|
|
890
|
+
isLoading,
|
|
891
|
+
error,
|
|
892
|
+
data,
|
|
893
|
+
hasData,
|
|
894
|
+
fetch,
|
|
895
|
+
reset,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
`
|
|
899
|
+
}];
|
|
900
|
+
},
|
|
901
|
+
hook(name, basePath) {
|
|
902
|
+
const filePath = join(basePath, `use${toPascalCase(name)}.ts`);
|
|
903
|
+
return [{
|
|
904
|
+
filePath,
|
|
905
|
+
content: `import { signal, onMounted, onUnmounted } from '@lytjs/core';
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* ${toPascalCase(name)} Hook
|
|
909
|
+
*/
|
|
910
|
+
export function use${toPascalCase(name)}() {
|
|
911
|
+
const state = signal(null);
|
|
361
912
|
|
|
362
|
-
|
|
913
|
+
onMounted(() => {
|
|
914
|
+
// Setup code on mount
|
|
915
|
+
});
|
|
363
916
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
917
|
+
onUnmounted(() => {
|
|
918
|
+
// Cleanup on unmount
|
|
919
|
+
});
|
|
367
920
|
|
|
368
|
-
|
|
921
|
+
return {
|
|
922
|
+
state,
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
`
|
|
926
|
+
}];
|
|
927
|
+
},
|
|
928
|
+
util(name, basePath) {
|
|
929
|
+
const filePath = join(basePath, `${name}.ts`);
|
|
930
|
+
return [{
|
|
931
|
+
filePath,
|
|
932
|
+
content: `/**
|
|
933
|
+
* ${toPascalCase(name)} Utility Functions
|
|
934
|
+
*/
|
|
369
935
|
|
|
370
|
-
|
|
371
|
-
${
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
936
|
+
/**
|
|
937
|
+
* ${toPascalCase(name)} function
|
|
938
|
+
*
|
|
939
|
+
* @param input - The input value
|
|
940
|
+
* @returns The processed result
|
|
941
|
+
*/
|
|
942
|
+
export function ${toCamelCase(name)}(input: any) {
|
|
943
|
+
// TODO: Implement function
|
|
944
|
+
return input;
|
|
945
|
+
}
|
|
946
|
+
`
|
|
947
|
+
}];
|
|
948
|
+
},
|
|
949
|
+
middleware(name, basePath) {
|
|
950
|
+
const filePath = join(basePath, `${name}.ts`);
|
|
951
|
+
return [{
|
|
952
|
+
filePath,
|
|
953
|
+
content: `import type { NavigationGuard } from '@lytjs/router';
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* ${toPascalCase(name)} Middleware
|
|
957
|
+
*/
|
|
958
|
+
export const ${toCamelCase(name)}Middleware: NavigationGuard = (to, from, next) => {
|
|
959
|
+
// Middleware logic
|
|
960
|
+
console.log('Middleware:', to.path);
|
|
961
|
+
next();
|
|
962
|
+
};
|
|
963
|
+
`
|
|
964
|
+
}];
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
function toPascalCase(str) {
|
|
968
|
+
return str.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
969
|
+
}
|
|
970
|
+
function toCamelCase(str) {
|
|
971
|
+
const pascalCase = toPascalCase(str);
|
|
972
|
+
return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
|
|
973
|
+
}
|
|
974
|
+
function resolveTargetDir(type) {
|
|
975
|
+
const cwd = process.cwd();
|
|
976
|
+
switch (type) {
|
|
977
|
+
case "component":
|
|
978
|
+
return join(cwd, "src", "components");
|
|
979
|
+
case "page":
|
|
980
|
+
return join(cwd, "src", "pages");
|
|
981
|
+
case "store":
|
|
982
|
+
return join(cwd, "src", "stores");
|
|
983
|
+
case "directive":
|
|
984
|
+
return join(cwd, "src", "directives");
|
|
985
|
+
case "composable":
|
|
986
|
+
return join(cwd, "src", "composables");
|
|
987
|
+
case "util":
|
|
988
|
+
return join(cwd, "src", "utils");
|
|
989
|
+
case "middleware":
|
|
990
|
+
return join(cwd, "src", "middleware");
|
|
991
|
+
case "hook":
|
|
992
|
+
return join(cwd, "src", "hooks");
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
async function add(type, name, options = {}) {
|
|
996
|
+
const targetDir = resolveTargetDir(type);
|
|
997
|
+
const fullPath = resolve(targetDir);
|
|
998
|
+
if (!exists(join(process.cwd(), "package.json"))) {
|
|
999
|
+
logger.error("No package.json found. Are you in a LytJS project directory?");
|
|
1000
|
+
process.exit(1);
|
|
1001
|
+
}
|
|
1002
|
+
const template = TEMPLATES2[type];
|
|
1003
|
+
if (!template) {
|
|
1004
|
+
logger.error(`Unknown type: ${type}. Supported types: component, page, store`);
|
|
1005
|
+
process.exit(1);
|
|
1006
|
+
}
|
|
1007
|
+
const files = template(name, fullPath);
|
|
1008
|
+
for (const file of files) {
|
|
1009
|
+
if (exists(file.filePath) && !options.force) {
|
|
1010
|
+
logger.warning(`File already exists: ${file.filePath}`);
|
|
1011
|
+
logger.info("Use --force to overwrite.");
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
ensureDir(resolve(file.filePath, ".."));
|
|
1015
|
+
writeFile(file.filePath, file.content);
|
|
1016
|
+
logger.success(`Created ${type}: ${file.filePath}`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
var TEMPLATES3 = {
|
|
1020
|
+
component: (data, withStyles, withTest, template, lang) => {
|
|
1021
|
+
const styleImport = withStyles ? `
|
|
1022
|
+
import './${data.kebabName}.styles.css';` : "";
|
|
1023
|
+
const tsOnly = lang === "ts";
|
|
1024
|
+
if (template === "sfc") {
|
|
1025
|
+
return `<template>
|
|
1026
|
+
<div class="${data.kebabName}">
|
|
1027
|
+
<slot>
|
|
1028
|
+
${data.pascalName} Component
|
|
1029
|
+
</slot>
|
|
1030
|
+
</div>
|
|
1031
|
+
</template>
|
|
1032
|
+
|
|
1033
|
+
<script setup${tsOnly ? ' lang="ts"' : ""}>
|
|
1034
|
+
${tsOnly ? `import { ref } from '@lytjs/reactivity';
|
|
1035
|
+
` : ""}
|
|
1036
|
+
${tsOnly ? `
|
|
1037
|
+
export interface ${data.pascalName}Props {
|
|
1038
|
+
className?: string;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const props = defineProps<${data.pascalName}Props>();
|
|
1042
|
+
` : ""}
|
|
381
1043
|
|
|
382
|
-
|
|
1044
|
+
const title = ref('${data.pascalName}');
|
|
1045
|
+
</script>
|
|
383
1046
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
1047
|
+
<style scoped>
|
|
1048
|
+
.${data.kebabName} {
|
|
1049
|
+
/* Component styles */
|
|
1050
|
+
}
|
|
1051
|
+
</style>
|
|
1052
|
+
`;
|
|
1053
|
+
}
|
|
1054
|
+
const testImport = withTest ? `
|
|
1055
|
+
import { describe, it, expect } from 'vitest';
|
|
1056
|
+
import { ${data.pascalName} } from './${data.kebabName}';
|
|
1057
|
+
|
|
1058
|
+
describe('${data.pascalName}', () => {
|
|
1059
|
+
it('should render', () => {
|
|
1060
|
+
// Add test here
|
|
1061
|
+
expect(true).toBe(true);
|
|
1062
|
+
});
|
|
1063
|
+
});` : "";
|
|
1064
|
+
const propsDecl = tsOnly ? `['className', 'children']` : `[]`;
|
|
1065
|
+
return `/**
|
|
1066
|
+
* ${data.pascalName} \u7EC4\u4EF6
|
|
1067
|
+
*
|
|
1068
|
+
* @description ${data.description}
|
|
1069
|
+
* @created ${data.date}
|
|
1070
|
+
*/
|
|
387
1071
|
|
|
388
|
-
|
|
1072
|
+
import { h, defineComponent } from '@lytjs/core';${styleImport}
|
|
389
1073
|
|
|
390
|
-
|
|
391
|
-
|
|
1074
|
+
${tsOnly ? `export interface ${data.pascalName}Props {
|
|
1075
|
+
className?: string;
|
|
1076
|
+
children?: any;
|
|
1077
|
+
}
|
|
392
1078
|
|
|
393
|
-
|
|
1079
|
+
` : ""}${template === "functional" ? `export function ${data.pascalName}(${tsOnly ? `props: ${data.pascalName}Props` : "props"}) {
|
|
1080
|
+
const { className = '', children } = props;
|
|
394
1081
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
</
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
1082
|
+
return (
|
|
1083
|
+
<div className={\`${data.kebabName} \${className}\`}>
|
|
1084
|
+
{children || '${data.pascalName} Component'}
|
|
1085
|
+
</div>
|
|
1086
|
+
);
|
|
1087
|
+
}` : `export const ${data.pascalName} = defineComponent({
|
|
1088
|
+
name: '${data.pascalName}',
|
|
1089
|
+
props: ${propsDecl},
|
|
1090
|
+
setup(props) {
|
|
1091
|
+
const { className = '', children } = props;
|
|
1092
|
+
|
|
1093
|
+
return () => (
|
|
1094
|
+
<div className={\`${data.kebabName} \${className}\`}>
|
|
1095
|
+
{children || '${data.pascalName} Component'}
|
|
1096
|
+
</div>
|
|
1097
|
+
);
|
|
1098
|
+
},
|
|
1099
|
+
});`}
|
|
1100
|
+
|
|
1101
|
+
export default ${data.pascalName};${testImport}
|
|
1102
|
+
`;
|
|
1103
|
+
},
|
|
1104
|
+
page: (data, withStyles, withTest, template, lang) => {
|
|
1105
|
+
const styleImport = withStyles ? `
|
|
1106
|
+
import './${data.kebabName}.styles.css';` : "";
|
|
1107
|
+
const tsOnly = lang === "ts";
|
|
1108
|
+
if (template === "sfc") {
|
|
1109
|
+
return `<template>
|
|
1110
|
+
<div class="${data.kebabName}-page">
|
|
1111
|
+
<h1>{title}</h1>
|
|
1112
|
+
<p>Page content for ${data.pascalName}</p>
|
|
1113
|
+
<slot />
|
|
421
1114
|
</div>
|
|
422
1115
|
</template>
|
|
423
1116
|
|
|
424
|
-
<script lang="ts">
|
|
425
|
-
import {
|
|
426
|
-
|
|
1117
|
+
<script setup${tsOnly ? ' lang="ts"' : ""}>
|
|
1118
|
+
${tsOnly ? `import { ref } from '@lytjs/reactivity';
|
|
1119
|
+
` : ""}
|
|
1120
|
+
${tsOnly ? `
|
|
1121
|
+
export interface ${data.pascalName}PageProps {
|
|
1122
|
+
title?: string;
|
|
1123
|
+
}
|
|
427
1124
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
},
|
|
433
|
-
})
|
|
1125
|
+
const props = defineProps<${data.pascalName}PageProps>();
|
|
1126
|
+
` : ""}
|
|
1127
|
+
|
|
1128
|
+
const title = ref(props.title || '${data.pascalName}');
|
|
434
1129
|
</script>
|
|
435
1130
|
|
|
436
1131
|
<style scoped>
|
|
437
|
-
.
|
|
438
|
-
|
|
439
|
-
padding: 20px;
|
|
1132
|
+
.${data.kebabName}-page {
|
|
1133
|
+
padding: 2rem;
|
|
440
1134
|
}
|
|
441
1135
|
</style>
|
|
442
|
-
|
|
1136
|
+
`;
|
|
1137
|
+
}
|
|
1138
|
+
const testImport = withTest ? `
|
|
1139
|
+
import { describe, it, expect } from 'vitest';
|
|
1140
|
+
import { ${data.pascalName}Page } from './${data.kebabName}';
|
|
1141
|
+
|
|
1142
|
+
describe('${data.pascalName}Page', () => {
|
|
1143
|
+
it('should render', () => {
|
|
1144
|
+
// Add test here
|
|
1145
|
+
expect(true).toBe(true);
|
|
1146
|
+
});
|
|
1147
|
+
});` : "";
|
|
1148
|
+
return `/**
|
|
1149
|
+
* ${data.pascalName} \u9875\u9762
|
|
1150
|
+
*
|
|
1151
|
+
* @description ${data.description}
|
|
1152
|
+
* @created ${data.date}
|
|
1153
|
+
*/
|
|
443
1154
|
|
|
444
|
-
|
|
445
|
-
|
|
1155
|
+
import { h, ${tsOnly ? "signal" : "signal"} } from '@lytjs/core';
|
|
1156
|
+
${styleImport}
|
|
446
1157
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
<p>\u8FD9\u662F\u9996\u9875\u5185\u5BB9</p>
|
|
451
|
-
</div>
|
|
452
|
-
\`,
|
|
453
|
-
})
|
|
454
|
-
`}function Pe(){return`import { defineComponent } from '@lytjs/lytjs'
|
|
1158
|
+
${tsOnly ? `export interface ${data.pascalName}PageProps {
|
|
1159
|
+
title?: string;
|
|
1160
|
+
}
|
|
455
1161
|
|
|
456
|
-
export
|
|
457
|
-
|
|
1162
|
+
` : ""}export function ${data.pascalName}Page(${tsOnly ? `props: ${data.pascalName}PageProps` : "props"}) {
|
|
1163
|
+
const { title = '${data.pascalName}' } = props;
|
|
458
1164
|
|
|
459
|
-
|
|
460
|
-
<div
|
|
461
|
-
<h1
|
|
462
|
-
<p
|
|
1165
|
+
return (
|
|
1166
|
+
<div className="${data.kebabName}-page">
|
|
1167
|
+
<h1>{title}</h1>
|
|
1168
|
+
<p>Page content for ${data.pascalName}</p>
|
|
463
1169
|
</div>
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
`}function ke(){return`import { defineComponent } from '@lytjs/lytjs'
|
|
467
|
-
|
|
468
|
-
export default defineComponent({
|
|
469
|
-
name: 'Header',
|
|
470
|
-
|
|
471
|
-
template: \`
|
|
472
|
-
<header class="header">
|
|
473
|
-
<nav>
|
|
474
|
-
<a href="/">\u9996\u9875</a>
|
|
475
|
-
<a href="/about">\u5173\u4E8E</a>
|
|
476
|
-
</nav>
|
|
477
|
-
</header>
|
|
478
|
-
\`,
|
|
479
|
-
})
|
|
480
|
-
`}function Ae(){return`import { createRouter, createWebHistory } from '@lytjs/router'
|
|
481
|
-
import HomePage from '../pages/index'
|
|
482
|
-
import AboutPage from '../pages/about'
|
|
483
|
-
|
|
484
|
-
export const router = createRouter({
|
|
485
|
-
history: createWebHistory(),
|
|
486
|
-
routes: [
|
|
487
|
-
{
|
|
488
|
-
path: '/',
|
|
489
|
-
component: HomePage,
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
path: '/about',
|
|
493
|
-
component: AboutPage,
|
|
494
|
-
},
|
|
495
|
-
],
|
|
496
|
-
})
|
|
497
|
-
`}function Re(){return`import { createStore } from '@lytjs/store'
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
498
1172
|
|
|
499
|
-
export
|
|
500
|
-
|
|
501
|
-
count: 0,
|
|
502
|
-
message: 'Hello Lyt!',
|
|
1173
|
+
export default ${data.pascalName}Page;${testImport}
|
|
1174
|
+
`;
|
|
503
1175
|
},
|
|
1176
|
+
service: (data, _withStyles, _withTest, _template, lang) => {
|
|
1177
|
+
const tsOnly = lang === "ts";
|
|
1178
|
+
return `/**
|
|
1179
|
+
* ${data.pascalName} \u670D\u52A1
|
|
1180
|
+
*
|
|
1181
|
+
* @description ${data.description}
|
|
1182
|
+
* @created ${data.date}
|
|
1183
|
+
*/
|
|
504
1184
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
1185
|
+
${tsOnly ? `export interface ${data.pascalName}ServiceOptions {
|
|
1186
|
+
baseUrl?: string;
|
|
1187
|
+
timeout?: number;
|
|
1188
|
+
}
|
|
509
1189
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
1190
|
+
` : ""}${tsOnly ? `export class ${data.pascalName}Service {
|
|
1191
|
+
private baseUrl: string;
|
|
1192
|
+
private timeout: number;
|
|
1193
|
+
` : `export class ${data.pascalName}Service {
|
|
1194
|
+
`}
|
|
1195
|
+
constructor(${tsOnly ? `options: ${data.pascalName}ServiceOptions = {}` : "options = {}"}) {
|
|
1196
|
+
this.baseUrl = options.baseUrl || '/api';
|
|
1197
|
+
this.timeout = options.timeout || 30000;
|
|
1198
|
+
}
|
|
514
1199
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
1200
|
+
async getAll()${tsOnly ? ": Promise<any[]>" : ""} {
|
|
1201
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s\`, {
|
|
1202
|
+
method: 'GET',
|
|
1203
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1204
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1205
|
+
});
|
|
1206
|
+
return response.json();
|
|
1207
|
+
}
|
|
520
1208
|
|
|
521
|
-
|
|
522
|
-
|
|
1209
|
+
async getById(id)${tsOnly ? ": Promise<any>" : ""} {
|
|
1210
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s/\${id}\`, {
|
|
1211
|
+
method: 'GET',
|
|
1212
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1213
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1214
|
+
});
|
|
1215
|
+
return response.json();
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
async create(${tsOnly ? "data: any" : "data"})${tsOnly ? ": Promise<any>" : ""} {
|
|
1219
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s\`, {
|
|
1220
|
+
method: 'POST',
|
|
1221
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1222
|
+
body: JSON.stringify(data),
|
|
1223
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1224
|
+
});
|
|
1225
|
+
return response.json();
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
async update(id)${tsOnly ? ": Promise<any>" : ""} {
|
|
1229
|
+
const response = await fetch(\`\${this.baseUrl}/${data.kebabName}s/\${id}\`, {
|
|
1230
|
+
method: 'PUT',
|
|
1231
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1232
|
+
body: JSON.stringify(data),
|
|
1233
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1234
|
+
});
|
|
1235
|
+
return response.json();
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
async delete(id)${tsOnly ? ": Promise<void>" : ""} {
|
|
1239
|
+
await fetch(\`\${this.baseUrl}/${data.kebabName}s/\${id}\`, {
|
|
1240
|
+
method: 'DELETE',
|
|
1241
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
export default ${data.pascalName}Service;
|
|
1247
|
+
`;
|
|
523
1248
|
},
|
|
524
|
-
|
|
525
|
-
|
|
1249
|
+
hook: (data, _withStyles, _withTest, _template, lang) => {
|
|
1250
|
+
const tsOnly = lang === "ts";
|
|
1251
|
+
return `/**
|
|
1252
|
+
* ${data.pascalName} Hook
|
|
1253
|
+
*
|
|
1254
|
+
* @description ${data.description}
|
|
1255
|
+
* @created ${data.date}
|
|
1256
|
+
*/
|
|
1257
|
+
|
|
1258
|
+
import { signal, effect } from '@lytjs/reactivity';
|
|
526
1259
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
padding: 0;
|
|
530
|
-
box-sizing: border-box;
|
|
1260
|
+
${tsOnly ? `export interface ${data.pascalName}Options {
|
|
1261
|
+
immediate?: boolean;
|
|
531
1262
|
}
|
|
532
1263
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
1264
|
+
export interface ${data.pascalName}Return {
|
|
1265
|
+
data: ReturnType<typeof signal>;
|
|
1266
|
+
loading: ReturnType<typeof signal>;
|
|
1267
|
+
error: ReturnType<typeof signal>;
|
|
1268
|
+
execute: () => Promise<void>;
|
|
1269
|
+
reset: () => void;
|
|
539
1270
|
}
|
|
540
1271
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
1272
|
+
` : ""}export function use${data.pascalName}(${tsOnly ? `options: ${data.pascalName}Options = {}` : "options = {}"})${tsOnly ? `: ${data.pascalName}Return` : ""} {
|
|
1273
|
+
const { immediate = false } = options;
|
|
1274
|
+
|
|
1275
|
+
const data = signal<any>(null);
|
|
1276
|
+
const loading = signal(false);
|
|
1277
|
+
const error = signal<Error | null>(null);
|
|
1278
|
+
|
|
1279
|
+
async function execute() {
|
|
1280
|
+
loading.value = true;
|
|
1281
|
+
error.value = null;
|
|
1282
|
+
|
|
1283
|
+
try {
|
|
1284
|
+
const result = await new Promise(resolve => setTimeout(() => resolve(null), 100));
|
|
1285
|
+
data.value = result;
|
|
1286
|
+
} catch (e) {
|
|
1287
|
+
error.value = e as Error;
|
|
1288
|
+
} finally {
|
|
1289
|
+
loading.value = false;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
function reset() {
|
|
1294
|
+
data.value = null;
|
|
1295
|
+
loading.value = false;
|
|
1296
|
+
error.value = null;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
if (immediate) {
|
|
1300
|
+
execute();
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return {
|
|
1304
|
+
data,
|
|
1305
|
+
loading,
|
|
1306
|
+
error,
|
|
1307
|
+
execute,
|
|
1308
|
+
reset,
|
|
1309
|
+
};
|
|
544
1310
|
}
|
|
545
1311
|
|
|
546
|
-
|
|
547
|
-
|
|
1312
|
+
export default use${data.pascalName};
|
|
1313
|
+
`;
|
|
1314
|
+
},
|
|
1315
|
+
store: (data, _withStyles, _withTest, _template, lang) => {
|
|
1316
|
+
const tsOnly = lang === "ts";
|
|
1317
|
+
return `/**
|
|
1318
|
+
* ${data.pascalName} Store
|
|
1319
|
+
*
|
|
1320
|
+
* @description ${data.description}
|
|
1321
|
+
* @created ${data.date}
|
|
1322
|
+
*/
|
|
1323
|
+
|
|
1324
|
+
import { signal, computed } from '@lytjs/reactivity';
|
|
1325
|
+
|
|
1326
|
+
${tsOnly ? `export interface ${data.pascalName}State {
|
|
1327
|
+
items: any[];
|
|
1328
|
+
selectedId: string | null;
|
|
1329
|
+
loading: boolean;
|
|
1330
|
+
error: Error | null;
|
|
548
1331
|
}
|
|
549
|
-
`}function Me(){return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
550
|
-
<rect width="32" height="32" rx="6" fill="#42b883"/>
|
|
551
|
-
<text x="16" y="22" text-anchor="middle" fill="white" font-size="18" font-weight="bold">L</text>
|
|
552
|
-
</svg>
|
|
553
|
-
`}function Ee(){return JSON.stringify({root:!0,env:{browser:!0,es2021:!0,node:!0},extends:["eslint:recommended"],parserOptions:{ecmaVersion:"latest",sourceType:"module"},rules:{"no-unused-vars":"warn","no-console":"warn"}},null,2)+`
|
|
554
|
-
`}function X(e,t){if(!F.existsSync(e))throw new Error(`\u6A21\u677F\u76EE\u5F55\u4E0D\u5B58\u5728: ${e}`);let n=F.readdirSync(e,{withFileTypes:!0});for(let r of n){let s=h.join(e,r.name),a=h.join(t,r.name);if(!(r.name===".DS_Store"||r.name==="node_modules"))if(r.isDirectory())X(s,a);else{$(h.dirname(a));let p=S(s);w(a,p),i.success(` \u521B\u5EFA ${h.relative(t,a)}`)}}}async function Ie(e){let{name:t,template:n}=e,r=h.resolve(process.cwd(),t);if(i.info(`\u6B63\u5728\u521B\u5EFA Lyt \u9879\u76EE: ${o(t,"brightCyan")}`),i.info(`\u4F7F\u7528\u6A21\u677F: ${o(n,"brightCyan")} (${G[n].description})`),I(r))throw i.error(`\u76EE\u5F55 "${t}" \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0\u6216\u5220\u9664\u5DF2\u6709\u76EE\u5F55`),new Error(`Directory "${t}" already exists`);$(r);let s=h.resolve(__dirname,".."),a=h.join(s,"templates",n);X(a,r);let p=h.join(r,"package.json");if(F.existsSync(p)){let d=S(p),u=JSON.parse(d);u.name=t,w(p,JSON.stringify(u,null,2)+`
|
|
555
|
-
`),i.success(` \u66F4\u65B0 package.json (name: ${t})`)}console.log(""),i.success(`\u9879\u76EE ${o(t,"brightCyan")} \u521B\u5EFA\u6210\u529F\uFF01`),console.log(""),console.log(" \u8BF7\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u542F\u52A8\u9879\u76EE\uFF1A"),console.log(""),console.log(` ${o("cd","brightGreen")} ${t}`),console.log(` ${o("npm install","brightGreen")}`),console.log(` ${o("npm run dev","brightGreen")}`),console.log("")}async function W(e){var c;if(((c=G[e.template])==null?void 0:c.type)==="example")return Ie(e);let{name:t,template:n,ts:r,router:s,store:a,eslint:p}=e,d=h.resolve(process.cwd(),t);if(i.info(`\u6B63\u5728\u521B\u5EFA Lyt \u9879\u76EE: ${o(t,"brightCyan")}`),i.info(`\u4F7F\u7528\u6A21\u677F: ${o(n,"brightCyan")}`),I(d))throw i.error(`\u76EE\u5F55 "${t}" \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0\u6216\u5220\u9664\u5DF2\u6709\u76EE\u5F55`),new Error(`Directory "${t}" already exists`);$(d);let u=[{filePath:"package.json",content:xe(e)},{filePath:"index.html",content:$e(e)},{filePath:"lytx.config.ts",content:we(e)},{filePath:"src/main.ts",content:Se(e)},{filePath:"src/App.lyt",content:je()},{filePath:"src/pages/index.ts",content:Ce()},{filePath:"src/pages/about.ts",content:Pe()},{filePath:"src/components/Header.ts",content:ke()},{filePath:"src/styles/main.css",content:Te()},{filePath:"public/favicon.svg",content:Me()}];r&&u.push({filePath:"tsconfig.json",content:ve(e)}),s&&u.push({filePath:"src/router/index.ts",content:Ae()}),a&&u.push({filePath:"src/store/index.ts",content:Re()}),p&&u.push({filePath:".eslintrc.json",content:Ee()});for(let m of u){let l=h.join(d,m.filePath);w(l,m.content),i.success(` \u521B\u5EFA ${m.filePath}`)}console.log(""),i.success(`\u9879\u76EE ${o(t,"brightCyan")} \u521B\u5EFA\u6210\u529F\uFF01`),console.log(""),console.log(" \u8BF7\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u542F\u52A8\u9879\u76EE\uFF1A"),console.log(""),console.log(` ${o("cd","brightGreen")} ${t}`),console.log(` ${o("npm install","brightGreen")}`),console.log(` ${o("npm run dev","brightGreen")}`),console.log("")}import*as te from"http";import*as A from"fs";import*as x from"path";import*as Q from"http";import*as T from"fs";import*as j from"path";import*as ee from"crypto";var L=class{constructor(){this.clients=[]}handleUpgrade(t,n,r){let s=t.headers["sec-websocket-key"];if(!s){n.destroy();return}let a=ee.createHash("sha1").update(s+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");n.write(`HTTP/1.1 101 Switching Protocols\r
|
|
556
|
-
Upgrade: websocket\r
|
|
557
|
-
Connection: Upgrade\r
|
|
558
|
-
Sec-WebSocket-Accept: ${a}\r
|
|
559
|
-
\r
|
|
560
|
-
`);let p={socket:n,isAlive:!0};n.on("close",()=>{this.clients=this.clients.filter(d=>d!==p)}),n.on("error",()=>{this.clients=this.clients.filter(d=>d!==p)}),this.clients.push(p)}broadcast(t){let n=[];for(let r of this.clients)try{if(!r.isAlive){n.push(r);continue}let s=Buffer.from(t,"utf-8"),a=this.createFrame(129,s);r.socket.write(a)}catch(s){n.push(r)}for(let r of n){this.clients=this.clients.filter(s=>s!==r);try{r.socket.destroy()}catch(s){}}}createFrame(t,n){let r=n.length,s;r<126?s=2:r<65536?s=4:s=10;let a=Buffer.alloc(s+r);return a[0]=t,r<126?a[1]=r:r<65536?(a[1]=126,a.writeUInt16BE(r,2)):(a[1]=127,a.writeUInt32BE(0,2),a.writeUInt32BE(r,6)),n.copy(a,s),a}getClientCount(){return this.clients.length}closeAll(){for(let t of this.clients)try{t.socket.destroy()}catch(n){}this.clients=[]}};function Fe(e){let t=new L,n=[],r=[],s=null,a=!1;function p(u){let c=j.extname(u).toLowerCase();if(c===".css")return"css";let m=j.basename(u);return m.startsWith("lytx.config")||m==="tsconfig.json"||m==="package.json"?"reload":c===".ts"||c===".tsx"||c===".lyt"||c===".js"||c===".jsx"?"update":"reload"}function d(u){try{let c=T.readdirSync(u,{withFileTypes:!0});for(let m of c){let l=j.join(u,m.name);if(m.isDirectory()){if(m.name==="node_modules"||m.name===".git"||m.name==="dist")continue;d(l)}else if(m.isFile()){let f=j.extname(m.name).toLowerCase();if(new Set([".ts",".tsx",".js",".jsx",".css",".html",".json",".lyt"]).has(f))try{let P=T.watch(l,{persistent:!1},K=>{if(K==="change"){let E=j.relative(e,l);for(let k of n)try{k(E)}catch(O){}let R=p(l),C={type:R,path:`/${E}`};if(R==="css")try{C.content=T.readFileSync(l,"utf-8")}catch(k){}t.broadcast(JSON.stringify(C))}});r.push(P)}catch(P){}}}}catch(c){}}return{start(u){a||(a=!0,s=Q.createServer((c,m)=>{m.writeHead(426,{"Content-Type":"text/plain"}),m.end("Upgrade Required")}),s.on("upgrade",(c,m,l)=>{t.handleUpgrade(c,m,l)}),s.listen(u,()=>{}),d(e))},stop(){a=!1;for(let u of r)try{u.close()}catch(c){}if(r.length=0,t.closeAll(),s){try{s.close()}catch(u){}s=null}},onFileChange(u){n.push(u)},notifyClient(u){t.broadcast(JSON.stringify(u))}}}function _(e){let t=new L;return e.on("upgrade",(n,r,s)=>{t.handleUpgrade(n,r,s)}),{broadcast(n){t.broadcast(n)},getClientCount(){return t.getClientCount()}}}function J(){return`(function() {
|
|
561
|
-
'use strict';
|
|
562
|
-
|
|
563
|
-
var ws = null;
|
|
564
|
-
var reconnectTimer = null;
|
|
565
|
-
var reconnectAttempts = 0;
|
|
566
|
-
var maxReconnectAttempts = 10;
|
|
567
|
-
|
|
568
|
-
function connect() {
|
|
569
|
-
var protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
570
|
-
var wsUrl = protocol + '//' + location.host + '/__hmr__';
|
|
571
|
-
ws = new WebSocket(wsUrl);
|
|
572
|
-
|
|
573
|
-
ws.onopen = function() {
|
|
574
|
-
reconnectAttempts = 0;
|
|
575
|
-
console.log('[HMR] Connected');
|
|
576
|
-
};
|
|
577
1332
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
1333
|
+
` : ""}export function create${data.pascalName}Store() {
|
|
1334
|
+
const state = signal${tsOnly ? `<${data.pascalName}State>` : ""}({
|
|
1335
|
+
items: [],
|
|
1336
|
+
selectedId: null,
|
|
1337
|
+
loading: false,
|
|
1338
|
+
error: null,
|
|
1339
|
+
});
|
|
581
1340
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1341
|
+
const selectedItem = computed(() => {
|
|
1342
|
+
const currentState = state.value;
|
|
1343
|
+
return currentState.items.find(item => item.id === currentState.selectedId);
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
const itemCount = computed(() => state.value.items.length);
|
|
1347
|
+
|
|
1348
|
+
function setItems(items) {
|
|
1349
|
+
state.value = { ...state.value, items };
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function selectItem(id) {
|
|
1353
|
+
state.value = { ...state.value, selectedId: id };
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function addItem(item) {
|
|
1357
|
+
state.value = {
|
|
1358
|
+
...state.value,
|
|
1359
|
+
items: [...state.value.items, item],
|
|
592
1360
|
};
|
|
1361
|
+
}
|
|
593
1362
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
1363
|
+
function updateItem(id, updates) {
|
|
1364
|
+
state.value = {
|
|
1365
|
+
...state.value,
|
|
1366
|
+
items: state.value.items.map(item =>
|
|
1367
|
+
item.id === id ? { ...item, ...updates } : item
|
|
1368
|
+
),
|
|
597
1369
|
};
|
|
1370
|
+
}
|
|
598
1371
|
|
|
599
|
-
|
|
600
|
-
|
|
1372
|
+
function removeItem(id) {
|
|
1373
|
+
state.value = {
|
|
1374
|
+
...state.value,
|
|
1375
|
+
items: state.value.items.filter(item => item.id !== id),
|
|
1376
|
+
selectedId: state.value.selectedId === id ? null : state.value.selectedId,
|
|
601
1377
|
};
|
|
602
1378
|
}
|
|
603
1379
|
|
|
604
|
-
function
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
reconnectAttempts++;
|
|
610
|
-
var delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
|
|
611
|
-
console.log('[HMR] Reconnecting in ' + delay + 'ms (attempt ' + reconnectAttempts + ')');
|
|
612
|
-
reconnectTimer = setTimeout(connect, delay);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
function handleCSSUpdate(update) {
|
|
616
|
-
console.log('[HMR] CSS update:', update.path);
|
|
617
|
-
// \u67E5\u627E\u6240\u6709 link[rel="stylesheet"] \u5E76\u91CD\u65B0\u52A0\u8F7D
|
|
618
|
-
var links = document.querySelectorAll('link[rel="stylesheet"]');
|
|
619
|
-
links.forEach(function(link) {
|
|
620
|
-
var href = link.getAttribute('href');
|
|
621
|
-
if (href && href.indexOf(update.path) !== -1) {
|
|
622
|
-
var newLink = document.createElement('link');
|
|
623
|
-
newLink.rel = 'stylesheet';
|
|
624
|
-
newLink.href = href + (href.indexOf('?') !== -1 ? '&' : '?') + 't=' + Date.now();
|
|
625
|
-
link.parentNode.replaceChild(newLink, link);
|
|
626
|
-
}
|
|
627
|
-
});
|
|
1380
|
+
function setLoading(loading) {
|
|
1381
|
+
state.value = { ...state.value, loading };
|
|
1382
|
+
}
|
|
628
1383
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
var style = document.createElement('style');
|
|
632
|
-
style.textContent = update.content;
|
|
633
|
-
document.head.appendChild(style);
|
|
634
|
-
}
|
|
1384
|
+
function setError(error) {
|
|
1385
|
+
state.value = { ...state.value, error };
|
|
635
1386
|
}
|
|
636
1387
|
|
|
637
|
-
function
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
} else {
|
|
645
|
-
// \u56DE\u9000\u5230\u5168\u91CF\u5237\u65B0
|
|
646
|
-
console.log('[HMR] Full reload (module.hot not available)');
|
|
647
|
-
location.reload();
|
|
648
|
-
}
|
|
1388
|
+
function reset() {
|
|
1389
|
+
state.value = {
|
|
1390
|
+
items: [],
|
|
1391
|
+
selectedId: null,
|
|
1392
|
+
loading: false,
|
|
1393
|
+
error: null,
|
|
1394
|
+
};
|
|
649
1395
|
}
|
|
650
1396
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
${o("\u53C2\u6570:","brightGreen")}
|
|
666
|
-
${o("<type>","brightYellow")} \u751F\u6210\u7C7B\u578B (component, store, page, api)
|
|
667
|
-
${o("<name>","brightYellow")} \u540D\u79F0
|
|
668
|
-
|
|
669
|
-
${o("\u9009\u9879:","brightGreen")}
|
|
670
|
-
${o("--ai","brightYellow")} \u4F7F\u7528 AI \u751F\u6210\uFF08\u9700\u8981\u914D\u7F6E API Key\uFF09
|
|
671
|
-
${o("--no-ai","brightYellow")} \u4E0D\u4F7F\u7528 AI\uFF08\u6A21\u677F\u751F\u6210\uFF09
|
|
672
|
-
${o("-t, --type <type>","brightYellow")} \u7EC4\u4EF6\u7C7B\u578B (button, input, form, card, list, table, modal, dropdown, tabs, navigation, custom)
|
|
673
|
-
${o("-o, --output <path>","brightYellow")}\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84
|
|
674
|
-
${o("--no-style","brightYellow")} \u4E0D\u6DFB\u52A0\u6837\u5F0F
|
|
675
|
-
${o("--api-key <key>","brightYellow")} AI API Key
|
|
676
|
-
${o("--model <model>","brightYellow")} AI \u6A21\u578B\u540D\u79F0
|
|
677
|
-
${o("--provider <name>","brightYellow")} AI \u63D0\u4F9B\u5546 (openai, anthropic, custom)
|
|
678
|
-
${o("--base-url <url>","brightYellow")} AI API \u57FA\u7840 URL
|
|
679
|
-
|
|
680
|
-
${o("\u793A\u4F8B:","brightGreen")}
|
|
681
|
-
${o("$","dim")} lytx generate component MyButton
|
|
682
|
-
${o("$","dim")} lytx generate component MyButton --type button
|
|
683
|
-
${o("$","dim")} lytx generate component MyButton --type button --ai
|
|
684
|
-
${o("$","dim")} lytx generate store counter
|
|
685
|
-
${o("$","dim")} lytx generate page Home
|
|
686
|
-
${o("$","dim")} lytx generate api users
|
|
687
|
-
`;function Ye(e){let t={type:"component",name:"",useAI:!1,style:!0};for(let n=0;n<e.length;n++){let r=e[n];r==="--ai"?t.useAI=!0:r==="--no-ai"?t.useAI=!1:r==="-t"||r==="--type"?t.componentType=e[++n]:r==="-o"||r==="--output"?t.outputPath=e[++n]:r==="--no-style"?t.style=!1:r==="--api-key"?t.apiKey=e[++n]:r==="--model"?t.model=e[++n]:r==="--provider"?t.provider=e[++n]:r==="--base-url"?t.baseUrl=e[++n]:r==="-d"||r==="--description"?t.description=e[++n]:!t.type&&["component","store","page","api"].includes(r)?t.type=r:!t.name&&!r.startsWith("-")&&(t.name=r)}return t}function Ue(e,t){let n=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),r=t.componentType||"functional",s={functional:`<!-- ${e} \u7EC4\u4EF6 -->
|
|
688
|
-
<template>
|
|
689
|
-
<div class="${n}">
|
|
690
|
-
<slot></slot>
|
|
691
|
-
</div>
|
|
692
|
-
</template>
|
|
1397
|
+
return {
|
|
1398
|
+
state,
|
|
1399
|
+
selectedItem,
|
|
1400
|
+
itemCount,
|
|
1401
|
+
setItems,
|
|
1402
|
+
selectItem,
|
|
1403
|
+
addItem,
|
|
1404
|
+
updateItem,
|
|
1405
|
+
removeItem,
|
|
1406
|
+
setLoading,
|
|
1407
|
+
setError,
|
|
1408
|
+
reset,
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
693
1411
|
|
|
694
|
-
<
|
|
695
|
-
|
|
696
|
-
|
|
1412
|
+
${tsOnly ? `export type ${data.pascalName}Store = ReturnType<typeof create${data.pascalName}Store>;
|
|
1413
|
+
` : ""}export default create${data.pascalName}Store;
|
|
1414
|
+
`;
|
|
1415
|
+
},
|
|
1416
|
+
layout: (data, withStyles, _withTest, _template, lang) => {
|
|
1417
|
+
const tsOnly = lang === "ts";
|
|
1418
|
+
const styleImport = withStyles ? `
|
|
1419
|
+
import './${data.kebabName}.styles.css';` : "";
|
|
1420
|
+
return `/**
|
|
1421
|
+
* ${data.pascalName} \u5E03\u5C40
|
|
1422
|
+
*
|
|
1423
|
+
* @description ${data.description}
|
|
1424
|
+
* @created ${data.date}
|
|
1425
|
+
*/
|
|
697
1426
|
|
|
698
|
-
|
|
699
|
-
.${n} {
|
|
700
|
-
/* \u6837\u5F0F */
|
|
701
|
-
}
|
|
702
|
-
</style>
|
|
703
|
-
`,button:`<!-- ${e} \u6309\u94AE\u7EC4\u4EF6 -->
|
|
704
|
-
<template>
|
|
705
|
-
<button class="${n}" :disabled="disabled" @click="handleClick">
|
|
706
|
-
<slot></slot>
|
|
707
|
-
</button>
|
|
708
|
-
</template>
|
|
1427
|
+
import { h, defineComponent } from '@lytjs/core';${styleImport}
|
|
709
1428
|
|
|
710
|
-
|
|
711
|
-
|
|
1429
|
+
${tsOnly ? `export interface ${data.pascalName}LayoutProps {
|
|
1430
|
+
children?: any;
|
|
1431
|
+
}
|
|
712
1432
|
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1433
|
+
` : ""}export const ${data.pascalName}Layout = defineComponent({
|
|
1434
|
+
name: '${data.pascalName}Layout',
|
|
1435
|
+
setup(props) {
|
|
1436
|
+
return () => (
|
|
1437
|
+
<div className="${data.kebabName}-layout">
|
|
1438
|
+
<header className="${data.kebabName}-header">
|
|
1439
|
+
<slot name="header">
|
|
1440
|
+
<h1>${data.pascalName}</h1>
|
|
1441
|
+
</slot>
|
|
1442
|
+
</header>
|
|
1443
|
+
<main className="${data.kebabName}-main">
|
|
1444
|
+
<slot />
|
|
1445
|
+
</main>
|
|
1446
|
+
<footer className="${data.kebabName}-footer">
|
|
1447
|
+
<slot name="footer" />
|
|
1448
|
+
</footer>
|
|
1449
|
+
</div>
|
|
1450
|
+
);
|
|
1451
|
+
},
|
|
718
1452
|
});
|
|
719
1453
|
|
|
720
|
-
|
|
1454
|
+
export default ${data.pascalName}Layout;
|
|
1455
|
+
`;
|
|
1456
|
+
},
|
|
1457
|
+
middleware: (data, _withStyles, _withTest, _template, lang) => {
|
|
1458
|
+
const tsOnly = lang === "ts";
|
|
1459
|
+
return `/**
|
|
1460
|
+
* ${data.pascalName} \u4E2D\u95F4\u4EF6
|
|
1461
|
+
*
|
|
1462
|
+
* @description ${data.description}
|
|
1463
|
+
* @created ${data.date}
|
|
1464
|
+
*/
|
|
721
1465
|
|
|
722
|
-
|
|
723
|
-
|
|
1466
|
+
${tsOnly ? `import type { Request, Response, NextFunction } from 'express';
|
|
1467
|
+
` : ""}
|
|
1468
|
+
export function ${data.camelName}Middleware(${tsOnly ? `req: Request, res: Response, next: NextFunction` : "req, res, next"}) {
|
|
1469
|
+
try {
|
|
1470
|
+
console.log('[${data.pascalName}] Middleware executed');
|
|
1471
|
+
next();
|
|
1472
|
+
} catch (error) {
|
|
1473
|
+
next(error);
|
|
1474
|
+
}
|
|
724
1475
|
}
|
|
725
|
-
</script>
|
|
726
1476
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
color: white;
|
|
734
|
-
cursor: pointer;
|
|
735
|
-
font-size: 14px;
|
|
1477
|
+
export default ${data.camelName}Middleware;
|
|
1478
|
+
`;
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
function toPascalCase2(name) {
|
|
1482
|
+
return name.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
736
1483
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1484
|
+
function toCamelCase2(name) {
|
|
1485
|
+
const pascal = toPascalCase2(name);
|
|
1486
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
740
1487
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
background: #9ca3af;
|
|
744
|
-
cursor: not-allowed;
|
|
1488
|
+
function toKebabCase(name) {
|
|
1489
|
+
return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
745
1490
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
1491
|
+
async function generate(options) {
|
|
1492
|
+
const {
|
|
1493
|
+
type,
|
|
1494
|
+
name,
|
|
1495
|
+
path: basePath = "./src",
|
|
1496
|
+
withStyles = false,
|
|
1497
|
+
withTest = false,
|
|
1498
|
+
description = "",
|
|
1499
|
+
template = "default",
|
|
1500
|
+
language = "ts"
|
|
1501
|
+
} = options;
|
|
1502
|
+
const templateData = {
|
|
1503
|
+
name,
|
|
1504
|
+
pascalName: toPascalCase2(name),
|
|
1505
|
+
kebabName: toKebabCase(name),
|
|
1506
|
+
camelName: toCamelCase2(name),
|
|
1507
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
1508
|
+
description: description || `${toPascalCase2(name)} ${type}`
|
|
1509
|
+
};
|
|
1510
|
+
const typeDirs = {
|
|
1511
|
+
component: "components",
|
|
1512
|
+
page: "pages",
|
|
1513
|
+
service: "services",
|
|
1514
|
+
hook: "hooks",
|
|
1515
|
+
store: "stores",
|
|
1516
|
+
layout: "layouts",
|
|
1517
|
+
middleware: "middleware"
|
|
1518
|
+
};
|
|
1519
|
+
const targetDir = path.join(
|
|
1520
|
+
process.cwd(),
|
|
1521
|
+
basePath,
|
|
1522
|
+
typeDirs[type] || "components"
|
|
1523
|
+
);
|
|
1524
|
+
await ensureDir(targetDir);
|
|
1525
|
+
const templateFn = TEMPLATES3[type];
|
|
1526
|
+
if (!templateFn) {
|
|
1527
|
+
logger.error(`Unknown type: ${type}`);
|
|
1528
|
+
logger.info("Available types: component, page, service, hook, store, layout, middleware");
|
|
1529
|
+
process.exit(1);
|
|
1530
|
+
}
|
|
1531
|
+
const extension = template === "sfc" ? "lyt" : language;
|
|
1532
|
+
const filename = `${templateData.kebabName}${type === "page" ? ".page" : ""}.${extension}`;
|
|
1533
|
+
const filePath = path.join(targetDir, filename);
|
|
1534
|
+
const content = templateFn(templateData, withStyles, withTest, template, language);
|
|
1535
|
+
await writeFile(filePath, content);
|
|
1536
|
+
logger.success(`Generated ${type}: ${filePath}`);
|
|
1537
|
+
if (withStyles && template !== "sfc") {
|
|
1538
|
+
const styleContent = `/**
|
|
1539
|
+
* ${templateData.pascalName} Styles
|
|
1540
|
+
*/
|
|
763
1541
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
default: 'text'
|
|
772
|
-
},
|
|
773
|
-
placeholder: {
|
|
774
|
-
type: String,
|
|
775
|
-
default: ''
|
|
776
|
-
},
|
|
777
|
-
disabled: {
|
|
778
|
-
type: Boolean,
|
|
779
|
-
default: false
|
|
1542
|
+
.${templateData.kebabName} {
|
|
1543
|
+
/* Component styles */
|
|
1544
|
+
}
|
|
1545
|
+
`;
|
|
1546
|
+
const stylePath = path.join(targetDir, `${templateData.kebabName}.styles.css`);
|
|
1547
|
+
await writeFile(stylePath, styleContent);
|
|
1548
|
+
logger.success(`Generated styles: ${stylePath}`);
|
|
780
1549
|
}
|
|
1550
|
+
if (withTest && template !== "sfc") {
|
|
1551
|
+
logger.info("Test file included in generated component");
|
|
1552
|
+
}
|
|
1553
|
+
logger.info("\nNext steps:");
|
|
1554
|
+
logger.info(` cd ${targetDir}`);
|
|
1555
|
+
logger.info(` Import your ${type}: import { ${template === "sfc" ? "default" : templateData.pascalName} } from './${templateData.kebabName}'`);
|
|
1556
|
+
logger.info("\nAvailable options:");
|
|
1557
|
+
logger.info(" --template=sfc : Single File Component (.lyt)");
|
|
1558
|
+
logger.info(" --template=functional : Functional component");
|
|
1559
|
+
logger.info(" --language=js : JavaScript output");
|
|
1560
|
+
}
|
|
1561
|
+
var PLUGIN_TEMPLATES = {
|
|
1562
|
+
default: "Default plugin template with TypeScript",
|
|
1563
|
+
minimal: "Minimal plugin without extra dependencies",
|
|
1564
|
+
withConfig: "Plugin template with configuration schema"
|
|
1565
|
+
};
|
|
1566
|
+
function getTemplateContent(template, pluginName) {
|
|
1567
|
+
const packageName = `@lytjs/plugin-${pluginName}`;
|
|
1568
|
+
const files = {};
|
|
1569
|
+
files["package.json"] = JSON.stringify({
|
|
1570
|
+
name: packageName,
|
|
1571
|
+
version: "0.1.0",
|
|
1572
|
+
description: `LytJS plugin: ${pluginName}`,
|
|
1573
|
+
main: "dist/index.cjs",
|
|
1574
|
+
module: "dist/index.mjs",
|
|
1575
|
+
types: "dist/index.d.ts",
|
|
1576
|
+
exports: {
|
|
1577
|
+
".": {
|
|
1578
|
+
import: "./dist/index.mjs",
|
|
1579
|
+
require: "./dist/index.cjs",
|
|
1580
|
+
types: "./dist/index.d.ts"
|
|
1581
|
+
}
|
|
1582
|
+
},
|
|
1583
|
+
files: ["dist"],
|
|
1584
|
+
scripts: {
|
|
1585
|
+
build: "tsup",
|
|
1586
|
+
dev: "tsup --watch",
|
|
1587
|
+
test: "vitest",
|
|
1588
|
+
lint: "eslint src",
|
|
1589
|
+
"prepublishOnly": "npm run build"
|
|
1590
|
+
},
|
|
1591
|
+
keywords: ["lytjs", "plugin"],
|
|
1592
|
+
license: "MIT",
|
|
1593
|
+
peerDependencies: {
|
|
1594
|
+
"@lytjs/core": ">=6.0.0"
|
|
1595
|
+
}
|
|
1596
|
+
}, null, 2);
|
|
1597
|
+
files["tsconfig.json"] = JSON.stringify({
|
|
1598
|
+
extends: "@lytjs/core/tsconfig.json",
|
|
1599
|
+
compilerOptions: {
|
|
1600
|
+
outDir: "./dist",
|
|
1601
|
+
rootDir: "./src",
|
|
1602
|
+
declaration: true,
|
|
1603
|
+
declarationMap: true
|
|
1604
|
+
},
|
|
1605
|
+
include: ["src"],
|
|
1606
|
+
exclude: ["node_modules", "dist", "tests"]
|
|
1607
|
+
}, null, 2);
|
|
1608
|
+
files["tsup.config.ts"] = `import { defineConfig } from 'tsup';
|
|
1609
|
+
|
|
1610
|
+
export default defineConfig({
|
|
1611
|
+
entry: { index: 'src/index.ts' },
|
|
1612
|
+
format: ['esm', 'cjs'],
|
|
1613
|
+
dts: true,
|
|
1614
|
+
sourcemap: true,
|
|
1615
|
+
clean: true,
|
|
1616
|
+
splitting: false,
|
|
1617
|
+
treeshake: true,
|
|
1618
|
+
minify: false,
|
|
1619
|
+
external: ['@lytjs/core'],
|
|
781
1620
|
});
|
|
1621
|
+
`;
|
|
1622
|
+
if (template === "withConfig") {
|
|
1623
|
+
files["src/index.ts"] = `/**
|
|
1624
|
+
* @lytjs/plugin-${pluginName}
|
|
1625
|
+
*
|
|
1626
|
+
* A LytJS plugin with configuration schema support.
|
|
1627
|
+
*/
|
|
782
1628
|
|
|
783
|
-
|
|
1629
|
+
import { definePlugin } from '@lytjs/core';
|
|
1630
|
+
import type { ConfigSchema } from '@lytjs/core';
|
|
784
1631
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1632
|
+
/**
|
|
1633
|
+
* Plugin options schema
|
|
1634
|
+
*/
|
|
1635
|
+
const optionsSchema: ConfigSchema<${pluginName.replace(/-/g, "")}Options> = {
|
|
1636
|
+
type: 'object',
|
|
1637
|
+
properties: {
|
|
1638
|
+
debug: {
|
|
1639
|
+
type: 'boolean',
|
|
1640
|
+
description: 'Enable debug mode',
|
|
1641
|
+
default: false,
|
|
1642
|
+
},
|
|
1643
|
+
option1: {
|
|
1644
|
+
type: 'string',
|
|
1645
|
+
description: 'First option',
|
|
1646
|
+
default: 'default value',
|
|
1647
|
+
},
|
|
1648
|
+
},
|
|
1649
|
+
additionalProperties: false,
|
|
1650
|
+
};
|
|
788
1651
|
|
|
789
|
-
|
|
790
|
-
|
|
1652
|
+
export interface ${pluginName.replace(/-/g, "")}Options {
|
|
1653
|
+
debug?: boolean;
|
|
1654
|
+
option1?: string;
|
|
791
1655
|
}
|
|
792
|
-
</script>
|
|
793
1656
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1657
|
+
/**
|
|
1658
|
+
* Create the plugin with configuration schema
|
|
1659
|
+
*/
|
|
1660
|
+
export function create${pluginName.replace(/-/g, "").replace(/^\w/, (c) => c.toUpperCase())}(options?: ${pluginName.replace(/-/g, "")}Options) {
|
|
1661
|
+
return definePlugin({
|
|
1662
|
+
name: '${packageName}',
|
|
1663
|
+
version: '0.1.0',
|
|
1664
|
+
description: 'A LytJS plugin',
|
|
1665
|
+
schema: optionsSchema,
|
|
1666
|
+
install: (app, opts) => {
|
|
1667
|
+
const config = opts || {};
|
|
1668
|
+
console.log('[${packageName}] Installing with config:', config);
|
|
1669
|
+
},
|
|
1670
|
+
});
|
|
801
1671
|
}
|
|
802
1672
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
<div class="${n}-body">
|
|
815
|
-
<slot></slot>
|
|
816
|
-
</div>
|
|
817
|
-
<div class="${n}-footer" if="$slots.footer">
|
|
818
|
-
<slot name="footer"></slot>
|
|
819
|
-
</div>
|
|
820
|
-
</div>
|
|
821
|
-
</template>
|
|
1673
|
+
/**
|
|
1674
|
+
* Direct plugin export (without schema)
|
|
1675
|
+
*/
|
|
1676
|
+
export default create${pluginName.replace(/-/g, "").replace(/^\w/, (c) => c.toUpperCase())}();
|
|
1677
|
+
`;
|
|
1678
|
+
} else {
|
|
1679
|
+
files["src/index.ts"] = `/**
|
|
1680
|
+
* @lytjs/plugin-${pluginName}
|
|
1681
|
+
*
|
|
1682
|
+
* A LytJS plugin.
|
|
1683
|
+
*/
|
|
822
1684
|
|
|
823
|
-
|
|
824
|
-
// \u7EC4\u4EF6\u903B\u8F91
|
|
825
|
-
</script>
|
|
1685
|
+
import { definePlugin } from '@lytjs/core';
|
|
826
1686
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1687
|
+
/**
|
|
1688
|
+
* Create the plugin
|
|
1689
|
+
*/
|
|
1690
|
+
export function create${pluginName.replace(/-/g, "").replace(/^\w/, (c) => c.toUpperCase())}() {
|
|
1691
|
+
return definePlugin({
|
|
1692
|
+
name: '${packageName}',
|
|
1693
|
+
version: '0.1.0',
|
|
1694
|
+
description: 'A LytJS plugin',
|
|
1695
|
+
install: (app) => {
|
|
1696
|
+
console.log('[${packageName}] Plugin installed');
|
|
1697
|
+
},
|
|
1698
|
+
});
|
|
833
1699
|
}
|
|
834
1700
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1701
|
+
/**
|
|
1702
|
+
* Direct plugin export
|
|
1703
|
+
*/
|
|
1704
|
+
export default create${pluginName.replace(/-/g, "").replace(/^\w/, (c) => c.toUpperCase())}();
|
|
1705
|
+
`;
|
|
1706
|
+
}
|
|
1707
|
+
if (template === "minimal") {
|
|
1708
|
+
files["src/types.ts"] = `/**
|
|
1709
|
+
* Plugin types
|
|
1710
|
+
*/
|
|
840
1711
|
|
|
841
|
-
|
|
842
|
-
|
|
1712
|
+
export interface PluginOptions {
|
|
1713
|
+
// Define your plugin options here
|
|
843
1714
|
}
|
|
1715
|
+
`;
|
|
1716
|
+
}
|
|
1717
|
+
files["README.md"] = `# @lytjs/plugin-${pluginName}
|
|
844
1718
|
|
|
845
|
-
|
|
846
|
-
border-top: 1px solid #e5e7eb;
|
|
847
|
-
}
|
|
848
|
-
</style>
|
|
849
|
-
`};return s[r]||s.functional}function Ne(e){let t=e.charAt(0).toLowerCase()+e.slice(1);return`/**
|
|
850
|
-
* ${e} Store
|
|
851
|
-
*/
|
|
1719
|
+
A LytJS plugin.
|
|
852
1720
|
|
|
853
|
-
|
|
1721
|
+
## Installation
|
|
854
1722
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
count: 0
|
|
859
|
-
},
|
|
1723
|
+
\`\`\`bash
|
|
1724
|
+
npm install @lytjs/plugin-${pluginName}
|
|
1725
|
+
\`\`\`
|
|
860
1726
|
|
|
861
|
-
|
|
862
|
-
// \u8BA1\u7B97\u5C5E\u6027
|
|
863
|
-
double: state => state.count * 2
|
|
864
|
-
},
|
|
1727
|
+
## Usage
|
|
865
1728
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
state.count++;
|
|
870
|
-
},
|
|
1729
|
+
\`\`\`typescript
|
|
1730
|
+
import { createApp } from '@lytjs/core';
|
|
1731
|
+
import myPlugin from '@lytjs/plugin-${pluginName}';
|
|
871
1732
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
});
|
|
877
|
-
`}function ze(e){let t=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();return`<!-- ${e} \u9875\u9762 -->
|
|
878
|
-
<template>
|
|
879
|
-
<div class="${t}-page">
|
|
880
|
-
<h1>${e}</h1>
|
|
881
|
-
<slot></slot>
|
|
882
|
-
</div>
|
|
883
|
-
</template>
|
|
1733
|
+
const app = createApp(App);
|
|
1734
|
+
app.use(myPlugin);
|
|
1735
|
+
\`\`\`
|
|
884
1736
|
|
|
885
|
-
|
|
886
|
-
import { ref, computed } from '@lytjs/reactivity';
|
|
1737
|
+
## API
|
|
887
1738
|
|
|
888
|
-
|
|
889
|
-
</script>
|
|
1739
|
+
### createPlugin(options?)
|
|
890
1740
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1741
|
+
Create the plugin with options.
|
|
1742
|
+
|
|
1743
|
+
## License
|
|
1744
|
+
|
|
1745
|
+
MIT
|
|
1746
|
+
`;
|
|
1747
|
+
files[".gitignore"] = `# Logs
|
|
1748
|
+
logs
|
|
1749
|
+
*.log
|
|
1750
|
+
npm-debug.log*
|
|
1751
|
+
yarn-debug.log*
|
|
1752
|
+
pnpm-debug.log*
|
|
899
1753
|
|
|
900
|
-
|
|
901
|
-
|
|
1754
|
+
node_modules
|
|
1755
|
+
dist
|
|
1756
|
+
*.local
|
|
902
1757
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1758
|
+
# IDE
|
|
1759
|
+
.vscode
|
|
1760
|
+
.idea
|
|
1761
|
+
*.sw?
|
|
1762
|
+
`;
|
|
1763
|
+
return files;
|
|
1764
|
+
}
|
|
1765
|
+
async function createPlugin(name, options = {}) {
|
|
1766
|
+
const targetDir = resolve(process.cwd(), name);
|
|
1767
|
+
const template = options.template || "default";
|
|
1768
|
+
if (template !== "default" && template !== "minimal" && template !== "withConfig") {
|
|
1769
|
+
logger.error(`Unknown template: ${template}`);
|
|
1770
|
+
logger.info("Available templates: default, minimal, withConfig");
|
|
1771
|
+
process.exit(1);
|
|
1772
|
+
}
|
|
1773
|
+
if (existsSync(targetDir) && !isEmptyDir2(targetDir) && !options.force) {
|
|
1774
|
+
logger.error(`Directory "${name}" already exists and is not empty.`);
|
|
1775
|
+
logger.info("Use --force to overwrite.");
|
|
1776
|
+
process.exit(1);
|
|
1777
|
+
}
|
|
1778
|
+
logger.info(`Creating a new LytJS plugin in ${targetDir}...`);
|
|
1779
|
+
ensureDir(targetDir);
|
|
1780
|
+
const files = getTemplateContent(template, name);
|
|
1781
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
1782
|
+
const fullPath = join(targetDir, filePath);
|
|
1783
|
+
const fileDir = resolve(fullPath, "..");
|
|
1784
|
+
if (!existsSync(fileDir)) {
|
|
1785
|
+
mkdirSync(fileDir, { recursive: true });
|
|
1786
|
+
}
|
|
1787
|
+
writeFileSync(fullPath, content, "utf-8");
|
|
1788
|
+
logger.success(`Created: ${filePath}`);
|
|
1789
|
+
}
|
|
1790
|
+
if (!options.skipInstall) {
|
|
1791
|
+
logger.info("Installing dependencies...");
|
|
1792
|
+
try {
|
|
1793
|
+
execSync("pnpm install", { cwd: targetDir, stdio: "inherit" });
|
|
1794
|
+
logger.success("Dependencies installed!");
|
|
1795
|
+
} catch (_error) {
|
|
1796
|
+
logger.warning("Failed to install dependencies automatically.");
|
|
1797
|
+
logger.info('Please run "pnpm install" manually.');
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
logger.success(`Plugin "${name}" created successfully!`);
|
|
1801
|
+
logger.info("");
|
|
1802
|
+
logger.bold("Next steps:");
|
|
1803
|
+
logger.info(` cd ${name}`);
|
|
1804
|
+
logger.info(" pnpm dev # Start development");
|
|
1805
|
+
logger.info(" pnpm build # Build for production");
|
|
1806
|
+
logger.info(" pnpm test # Run tests");
|
|
1807
|
+
}
|
|
1808
|
+
async function buildPlugin(options = {}) {
|
|
1809
|
+
const cwd = process.cwd();
|
|
1810
|
+
const outDir = options.outDir || "dist";
|
|
1811
|
+
const pkgPath = join(cwd, "package.json");
|
|
1812
|
+
if (!existsSync(pkgPath)) {
|
|
1813
|
+
logger.error("No package.json found. Are you in a plugin directory?");
|
|
1814
|
+
process.exit(1);
|
|
1815
|
+
}
|
|
1816
|
+
logger.info("Building plugin...");
|
|
1817
|
+
try {
|
|
1818
|
+
const buildCmd = "tsup";
|
|
1819
|
+
const args = ["--entry", "src/index.ts", "--outDir", outDir, "--format", "esm,cjs", "--dts", "--sourcemap"];
|
|
1820
|
+
if (options.minify) {
|
|
1821
|
+
args.push("--minify");
|
|
1822
|
+
}
|
|
1823
|
+
execSync(`${buildCmd} ${args.join(" ")}`, { cwd, stdio: "inherit" });
|
|
1824
|
+
logger.success("Build completed!");
|
|
1825
|
+
logger.info(`Output: ${join(cwd, outDir)}`);
|
|
1826
|
+
} catch (_error) {
|
|
1827
|
+
logger.error("Build failed. Make sure tsup is installed: pnpm add -D tsup");
|
|
1828
|
+
process.exit(1);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
async function validatePlugin(options = {}) {
|
|
1832
|
+
const cwd = process.cwd();
|
|
1833
|
+
logger.info("Validating plugin...");
|
|
1834
|
+
const errors = [];
|
|
1835
|
+
const warnings = [];
|
|
1836
|
+
const pkgPath = join(cwd, "package.json");
|
|
1837
|
+
if (!existsSync(pkgPath)) {
|
|
1838
|
+
errors.push("package.json not found");
|
|
1839
|
+
} else {
|
|
1840
|
+
try {
|
|
1841
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1842
|
+
if (!pkg.name) errors.push("package.json: name is required");
|
|
1843
|
+
if (!pkg.version) errors.push("package.json: version is required");
|
|
1844
|
+
if (!pkg.main) errors.push("package.json: main is required");
|
|
1845
|
+
if (!pkg.module) errors.push("package.json: module is required");
|
|
1846
|
+
if (!pkg.types) errors.push("package.json: types is required");
|
|
1847
|
+
if (!pkg.exports) {
|
|
1848
|
+
warnings.push("package.json: exports field is recommended");
|
|
1849
|
+
}
|
|
1850
|
+
if (!pkg.peerDependencies || !pkg.peerDependencies["@lytjs/core"]) {
|
|
1851
|
+
errors.push("package.json: @lytjs/core peerDependency is required");
|
|
1852
|
+
}
|
|
1853
|
+
if (!pkg.keywords || !pkg.keywords.includes("lytjs")) {
|
|
1854
|
+
warnings.push('package.json: "lytjs" keyword is recommended');
|
|
1855
|
+
}
|
|
1856
|
+
} catch (err) {
|
|
1857
|
+
errors.push(`package.json: Invalid JSON - ${err}`);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
const srcPath = join(cwd, "src");
|
|
1861
|
+
if (!existsSync(srcPath)) {
|
|
1862
|
+
errors.push("src directory not found");
|
|
1863
|
+
} else {
|
|
1864
|
+
const indexPath = join(srcPath, "index.ts");
|
|
1865
|
+
if (!existsSync(indexPath)) {
|
|
1866
|
+
errors.push("src/index.ts not found");
|
|
1867
|
+
} else {
|
|
1868
|
+
const content = readFileSync(indexPath, "utf-8");
|
|
1869
|
+
if (!content.includes("definePlugin") && !content.includes("EnhancedPlugin")) {
|
|
1870
|
+
warnings.push("src/index.ts: Consider using definePlugin or EnhancedPlugin");
|
|
1871
|
+
}
|
|
1872
|
+
if (!content.includes("export")) {
|
|
1873
|
+
warnings.push("src/index.ts: No exports found");
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
const tsconfigPath = join(cwd, "tsconfig.json");
|
|
1878
|
+
if (!existsSync(tsconfigPath)) {
|
|
1879
|
+
warnings.push("tsconfig.json not found (recommended)");
|
|
1880
|
+
}
|
|
1881
|
+
const tsupConfigPath = join(cwd, "tsup.config.ts");
|
|
1882
|
+
if (!existsSync(tsupConfigPath)) {
|
|
1883
|
+
warnings.push("tsup.config.ts not found (recommended for builds)");
|
|
1884
|
+
}
|
|
1885
|
+
let hasErrors = errors.length > 0;
|
|
1886
|
+
let hasWarnings = warnings.length > 0;
|
|
1887
|
+
if (hasErrors) {
|
|
1888
|
+
logger.error("Validation failed with errors:");
|
|
1889
|
+
for (const err of errors) {
|
|
1890
|
+
logger.error(` - ${err}`);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
if (hasWarnings && !options.strict) {
|
|
1894
|
+
logger.warning("Warnings:");
|
|
1895
|
+
for (const warn of warnings) {
|
|
1896
|
+
logger.warning(` - ${warn}`);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
if (!hasErrors && !hasWarnings) {
|
|
1900
|
+
logger.success("Plugin validation passed!");
|
|
1901
|
+
} else if (hasWarnings && !hasErrors) {
|
|
1902
|
+
logger.success("Plugin validation passed (with warnings)");
|
|
1903
|
+
if (options.strict) {
|
|
1904
|
+
process.exit(1);
|
|
1905
|
+
}
|
|
1906
|
+
} else {
|
|
1907
|
+
if (options.warningsAsErrors && hasWarnings) {
|
|
1908
|
+
logger.error("Strict mode: treating warnings as errors");
|
|
1909
|
+
}
|
|
1910
|
+
process.exit(1);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
function isEmptyDir2(dir) {
|
|
1914
|
+
if (!existsSync(dir)) return true;
|
|
1915
|
+
const files = __require("fs").readdirSync(dir);
|
|
1916
|
+
return files.length === 0;
|
|
1917
|
+
}
|
|
1918
|
+
function listPluginTemplates() {
|
|
1919
|
+
logger.bold("Available plugin templates:");
|
|
1920
|
+
for (const [name, description] of Object.entries(PLUGIN_TEMPLATES)) {
|
|
1921
|
+
logger.info(` ${name.padEnd(12)} - ${description}`);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
// src/commands/run.ts
|
|
1926
|
+
var VERSION = "6.0.0";
|
|
1927
|
+
async function runCli(rawArgs = process.argv.slice(2)) {
|
|
1928
|
+
const { command, args, options } = parseArgs(rawArgs);
|
|
1929
|
+
if (options.help || command === "help") {
|
|
1930
|
+
showHelp();
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
if (options.version || command === "version" || command === "-v" || command === "--version") {
|
|
1934
|
+
console.log(`LytJS CLI v${VERSION}`);
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
switch (command) {
|
|
1938
|
+
case "create":
|
|
1939
|
+
await create(args[0] || "my-lytjs-app", {
|
|
1940
|
+
template: options.template,
|
|
1941
|
+
force: options.force
|
|
910
1942
|
});
|
|
911
1943
|
break;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1944
|
+
case "templates":
|
|
1945
|
+
listTemplates();
|
|
1946
|
+
break;
|
|
1947
|
+
case "dev":
|
|
1948
|
+
await dev({
|
|
1949
|
+
port: options.port ? parseInt(options.port, 10) : void 0,
|
|
1950
|
+
host: options.host,
|
|
1951
|
+
open: options.open
|
|
919
1952
|
});
|
|
920
1953
|
break;
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
message: 'Update ${e}'
|
|
1954
|
+
case "build":
|
|
1955
|
+
await build({
|
|
1956
|
+
outDir: options.outDir,
|
|
1957
|
+
ssr: options.ssr,
|
|
1958
|
+
minify: options.minify !== "false"
|
|
927
1959
|
});
|
|
928
1960
|
break;
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
message: 'Delete ${e}'
|
|
1961
|
+
case "test":
|
|
1962
|
+
await test({
|
|
1963
|
+
watch: options.watch !== "false",
|
|
1964
|
+
coverage: options.coverage,
|
|
1965
|
+
grep: options.grep
|
|
935
1966
|
});
|
|
936
1967
|
break;
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1968
|
+
case "add":
|
|
1969
|
+
const addTypes = ["component", "page", "store", "directive", "composable", "util", "middleware", "hook"];
|
|
1970
|
+
if (!args[0] || !addTypes.includes(args[0])) {
|
|
1971
|
+
logger.error("Usage: lyt add <type> <name>");
|
|
1972
|
+
logger.info("Types: component, page, store, directive, composable, util, middleware, hook");
|
|
1973
|
+
logger.info("Example: lyt add component Button");
|
|
1974
|
+
process.exit(1);
|
|
1975
|
+
}
|
|
1976
|
+
await add(args[0], args[1] || "Unnamed", {
|
|
1977
|
+
force: options.force
|
|
1978
|
+
});
|
|
1979
|
+
break;
|
|
1980
|
+
case "generate":
|
|
1981
|
+
case "g":
|
|
1982
|
+
const genTypes = ["component", "page", "service", "hook", "store"];
|
|
1983
|
+
if (!args[0] || !genTypes.includes(args[0])) {
|
|
1984
|
+
logger.error("Usage: lyt generate <type> <name>");
|
|
1985
|
+
logger.info("Types: component, page, service, hook, store");
|
|
1986
|
+
logger.info("Example: lyt generate component Button");
|
|
1987
|
+
process.exit(1);
|
|
1988
|
+
}
|
|
1989
|
+
await generate({
|
|
1990
|
+
type: args[0],
|
|
1991
|
+
name: args[1] || "Unnamed",
|
|
1992
|
+
path: options.path,
|
|
1993
|
+
withStyles: options.styles,
|
|
1994
|
+
withTest: options.test,
|
|
1995
|
+
withStorybook: options.storybook
|
|
942
1996
|
});
|
|
1997
|
+
break;
|
|
1998
|
+
case "plugin":
|
|
1999
|
+
if (!args[0]) {
|
|
2000
|
+
logger.error("Usage: lyt plugin <create|build|validate|templates>");
|
|
2001
|
+
logger.info("Example: lyt plugin create my-plugin");
|
|
2002
|
+
process.exit(1);
|
|
2003
|
+
}
|
|
2004
|
+
const subCommand = args[0];
|
|
2005
|
+
switch (subCommand) {
|
|
2006
|
+
case "create":
|
|
2007
|
+
await createPlugin(args[1] || "my-plugin", {
|
|
2008
|
+
template: options.template,
|
|
2009
|
+
force: options.force,
|
|
2010
|
+
skipInstall: options.skipInstall
|
|
2011
|
+
});
|
|
2012
|
+
break;
|
|
2013
|
+
case "build":
|
|
2014
|
+
await buildPlugin({
|
|
2015
|
+
outDir: options.outDir,
|
|
2016
|
+
minify: options.minify,
|
|
2017
|
+
sourcemap: options.sourcemap
|
|
2018
|
+
});
|
|
2019
|
+
break;
|
|
2020
|
+
case "validate":
|
|
2021
|
+
await validatePlugin({
|
|
2022
|
+
strict: options.strict,
|
|
2023
|
+
warningsAsErrors: options.warningsAsErrors
|
|
2024
|
+
});
|
|
2025
|
+
break;
|
|
2026
|
+
case "templates":
|
|
2027
|
+
listPluginTemplates();
|
|
2028
|
+
break;
|
|
2029
|
+
default:
|
|
2030
|
+
logger.error(`Unknown plugin sub-command: ${subCommand}`);
|
|
2031
|
+
logger.info("Supported sub-commands: create, build, validate, templates");
|
|
2032
|
+
process.exit(1);
|
|
2033
|
+
}
|
|
2034
|
+
break;
|
|
2035
|
+
default:
|
|
2036
|
+
if (command) {
|
|
2037
|
+
logger.error(`Unknown command: ${command}`);
|
|
2038
|
+
logger.info('Run "lyt --help" for usage information.');
|
|
2039
|
+
process.exit(1);
|
|
2040
|
+
} else {
|
|
2041
|
+
showHelp();
|
|
2042
|
+
}
|
|
943
2043
|
}
|
|
944
2044
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
${
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
2045
|
+
function parseArgs(args) {
|
|
2046
|
+
let command = args[0] ?? "";
|
|
2047
|
+
const positional = [];
|
|
2048
|
+
const options = {};
|
|
2049
|
+
if (command.startsWith("--") || command.startsWith("-")) {
|
|
2050
|
+
command = "";
|
|
2051
|
+
}
|
|
2052
|
+
const startIndex = command ? 1 : 0;
|
|
2053
|
+
for (let i = startIndex; i < args.length; i++) {
|
|
2054
|
+
const arg = args[i] ?? "";
|
|
2055
|
+
if (arg.startsWith("--")) {
|
|
2056
|
+
const parts = arg.slice(2).split("=");
|
|
2057
|
+
const key = parts[0];
|
|
2058
|
+
const value = parts[1];
|
|
2059
|
+
if (value !== void 0 && key) {
|
|
2060
|
+
options[key] = value;
|
|
2061
|
+
} else if (key && i + 1 < args.length && !(args[i + 1] ?? "").startsWith("-")) {
|
|
2062
|
+
const nextArg = args[++i];
|
|
2063
|
+
if (nextArg) {
|
|
2064
|
+
options[key] = nextArg;
|
|
2065
|
+
}
|
|
2066
|
+
} else if (key) {
|
|
2067
|
+
options[key] = true;
|
|
2068
|
+
}
|
|
2069
|
+
} else if (arg.startsWith("-")) {
|
|
2070
|
+
const key = arg.slice(1);
|
|
2071
|
+
if (key) {
|
|
2072
|
+
options[key] = true;
|
|
2073
|
+
}
|
|
2074
|
+
} else {
|
|
2075
|
+
positional.push(arg);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
return { command, args: positional, options };
|
|
2079
|
+
}
|
|
2080
|
+
function showHelp() {
|
|
2081
|
+
console.log(`
|
|
2082
|
+
${logger.bold("LytJS CLI")} v${VERSION}
|
|
2083
|
+
|
|
2084
|
+
${logger.bold("Usage:")}
|
|
2085
|
+
lyt <command> [options]
|
|
2086
|
+
|
|
2087
|
+
${logger.bold("Commands:")}
|
|
2088
|
+
create <name> Create a new LytJS project
|
|
2089
|
+
templates List available templates
|
|
2090
|
+
dev Start development server
|
|
2091
|
+
build Build for production
|
|
2092
|
+
test Run tests
|
|
2093
|
+
add <type> <name> Generate a component, page, store, directive, composable, etc.
|
|
2094
|
+
generate, g Advanced code generation (component, page, service, hook, store)
|
|
2095
|
+
plugin <subcmd> Plugin development commands
|
|
2096
|
+
help Show this help message
|
|
2097
|
+
|
|
2098
|
+
${logger.bold("Options:")}
|
|
2099
|
+
--version, -v Show version number
|
|
2100
|
+
--help Show help
|
|
2101
|
+
|
|
2102
|
+
${logger.bold("Create Options:")}
|
|
2103
|
+
--template <name> Use a specific template
|
|
2104
|
+
--force Overwrite existing directory
|
|
2105
|
+
|
|
2106
|
+
${logger.bold("Dev Options:")}
|
|
2107
|
+
--port <number> Specify port (default: 5173)
|
|
2108
|
+
--host <host> Specify host (default: localhost)
|
|
2109
|
+
--open Open browser on start
|
|
2110
|
+
|
|
2111
|
+
${logger.bold("Build Options:")}
|
|
2112
|
+
--outDir <dir> Output directory (default: dist)
|
|
2113
|
+
--ssr Build for SSR
|
|
2114
|
+
--minify false Disable minification
|
|
2115
|
+
|
|
2116
|
+
${logger.bold("Test Options:")}
|
|
2117
|
+
--watch false Run tests once (no watch mode)
|
|
2118
|
+
--coverage Generate coverage report
|
|
2119
|
+
--grep <pattern> Filter tests by pattern
|
|
2120
|
+
|
|
2121
|
+
${logger.bold("Generate Options:")}
|
|
2122
|
+
--path <dir> Output directory (default: ./src)
|
|
2123
|
+
--styles Generate CSS styles file
|
|
2124
|
+
--test Generate test file
|
|
2125
|
+
--storybook Generate Storybook story file
|
|
2126
|
+
|
|
2127
|
+
${logger.bold("Plugin Options:")}
|
|
2128
|
+
--template <name> Use a specific plugin template (default, minimal, withConfig)
|
|
2129
|
+
--force Overwrite existing directory
|
|
2130
|
+
--skipInstall Skip installing dependencies
|
|
2131
|
+
--outDir <dir> Output directory (default: dist)
|
|
2132
|
+
--minify Minify output
|
|
2133
|
+
--sourcemap Generate sourcemaps
|
|
2134
|
+
--strict Strict validation mode
|
|
2135
|
+
--warningsAsErrors Treat warnings as errors
|
|
2136
|
+
|
|
2137
|
+
${logger.bold("Examples:")}
|
|
2138
|
+
lyt create my-app
|
|
2139
|
+
lyt create my-app --template minimal
|
|
2140
|
+
lyt create my-app --template router
|
|
2141
|
+
lyt create my-app --template full
|
|
2142
|
+
lyt dev --port 3000
|
|
2143
|
+
lyt build --ssr
|
|
2144
|
+
lyt add component Button
|
|
2145
|
+
lyt add page About
|
|
2146
|
+
lyt add store user
|
|
2147
|
+
lyt directive click-outside
|
|
2148
|
+
lyt add composable fetch-data
|
|
2149
|
+
lyt add util format
|
|
2150
|
+
lyt add hook window-size
|
|
2151
|
+
lyt generate component Button --styles --test
|
|
2152
|
+
lyt generate page Dashboard --path ./src/pages
|
|
2153
|
+
lyt generate service User --path ./src/services
|
|
2154
|
+
lyt generate hook useCounter
|
|
2155
|
+
lyt generate store Auth --path ./src/stores
|
|
2156
|
+
lyt g page Login
|
|
2157
|
+
lyt plugin create my-plugin
|
|
2158
|
+
lyt plugin create my-plugin --template withConfig
|
|
2159
|
+
lyt plugin build
|
|
2160
|
+
lyt plugin validate
|
|
2161
|
+
`);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
// src/index.ts
|
|
2165
|
+
if (__require.main === module) {
|
|
2166
|
+
runCli().catch(console.error);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
export { add, build, buildPlugin, create, createPlugin, detectPackageManager, dev, ensureDir, exists, generate, getAddCommand, getInstallCommand, getRunCommand, listPluginTemplates, listTemplates, logger, readFile, runCli, test, validatePlugin, writeFile };
|
|
2170
|
+
//# sourceMappingURL=index.mjs.map
|
|
2171
|
+
//# sourceMappingURL=index.mjs.map
|