@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.
Files changed (43) hide show
  1. package/dist/create.cjs +607 -0
  2. package/dist/create.cjs.map +1 -0
  3. package/dist/create.d.mts +2 -0
  4. package/dist/create.d.ts +2 -0
  5. package/dist/create.mjs +605 -0
  6. package/dist/create.mjs.map +1 -0
  7. package/dist/index.cjs +2212 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.mts +208 -0
  10. package/dist/index.d.ts +208 -0
  11. package/dist/index.mjs +1999 -886
  12. package/dist/index.mjs.map +1 -0
  13. package/dist/lyt.cjs +2212 -0
  14. package/dist/lyt.cjs.map +1 -0
  15. package/dist/lyt.d.mts +1 -0
  16. package/dist/lyt.d.ts +1 -0
  17. package/dist/lyt.mjs +2171 -0
  18. package/dist/lyt.mjs.map +1 -0
  19. package/lyt-cli.js +3 -0
  20. package/package.json +34 -31
  21. package/README.md +0 -227
  22. package/dist/bin/cli.cjs +0 -1058
  23. package/dist/bin/cli.js +0 -2
  24. package/dist/bin/cli.mjs +0 -1058
  25. package/dist/index.js +0 -1058
  26. package/dist/types/bin/cli.d.ts +0 -8
  27. package/dist/types/bin/cli.d.ts.map +0 -1
  28. package/dist/types/build.d.ts +0 -30
  29. package/dist/types/build.d.ts.map +0 -1
  30. package/dist/types/create.d.ts +0 -19
  31. package/dist/types/create.d.ts.map +0 -1
  32. package/dist/types/dev.d.ts +0 -24
  33. package/dist/types/dev.d.ts.map +0 -1
  34. package/dist/types/generate.d.ts +0 -14
  35. package/dist/types/generate.d.ts.map +0 -1
  36. package/dist/types/hmr.d.ts +0 -55
  37. package/dist/types/hmr.d.ts.map +0 -1
  38. package/dist/types/index.d.ts +0 -20
  39. package/dist/types/index.d.ts.map +0 -1
  40. package/dist/types/scaffold.d.ts +0 -67
  41. package/dist/types/scaffold.d.ts.map +0 -1
  42. package/dist/types/utils.d.ts +0 -92
  43. 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*as ie from"fs";import*as ae from"path";import*as v from"fs";import*as U from"path";var ce="\x1B[0m",pe={black:"\x1B[30m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",brightRed:"\x1B[91m",brightGreen:"\x1B[92m",brightYellow:"\x1B[93m",brightBlue:"\x1B[94m",brightMagenta:"\x1B[95m",brightCyan:"\x1B[96m",brightWhite:"\x1B[97m",bgBlack:"\x1B[40m",bgRed:"\x1B[41m",bgGreen:"\x1B[42m",bgYellow:"\x1B[43m",bgBlue:"\x1B[44m",bgMagenta:"\x1B[45m",bgCyan:"\x1B[46m",bgWhite:"\x1B[47m",bold:"\x1B[1m",dim:"\x1B[2m",italic:"\x1B[3m",underline:"\x1B[4m",strikethrough:"\x1B[9m"};function o(e,t){let n=pe[t];return n?`${n}${e}${ce}`:e}function q(e){let t=e.slice(2),n={command:"",args:[],options:{},raw:t},r=0;for(;r<t.length;){let s=t[r];if(s==="--help"||s==="-h")n.options.help=!0,r++;else if(s==="--version"||s==="-v")n.options.version=!0,r++;else if(s.startsWith("--")){let a=s.indexOf("=");if(a!==-1){let p=s.slice(2,a),d=s.slice(a+1);n.options[p]=d}else{let p=s.slice(2),d=t[r+1];d&&!d.startsWith("-")?(n.options[p]=d,r++):n.options[p]=!0}r++}else if(s.startsWith("-")&&s.length>1){let a=s.slice(1),p=t[r+1];p&&!p.startsWith("-")?(n.options[a]=p,r++):n.options[a]=!0,r++}else n.command?(n.args.push(s),r++):(n.command=s,r++)}return n}function $(e){v.existsSync(e)||v.mkdirSync(e,{recursive:!0})}function I(e){return v.existsSync(e)}function S(e){return v.readFileSync(e,"utf-8")}function w(e,t){let n=U.dirname(e);$(n),typeof t=="string"?v.writeFileSync(e,t,"utf-8"):v.writeFileSync(e,t)}var i={info(e){console.log(`${o("[INFO]","blue")} ${e}`)},warn(e){console.log(`${o("[WARN]","yellow")} ${e}`)},error(e){console.error(`${o("[ERROR]","red")} ${e}`)},success(e){console.log(`${o("[SUCCESS]","green")} ${e}`)}};import*as H from"path";function de(){return`<!DOCTYPE html>
3
- <html lang="zh-CN">
4
- <head>
5
- <meta charset="UTF-8" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Lyt App</title>
8
- <link rel="stylesheet" href="/src/style.css" />
9
- </head>
10
- <body>
11
- <div id="app"></div>
12
- <script type="module" src="/src/main.ts"></script>
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
- // \u521B\u5EFA\u5E94\u7528\u5B9E\u4F8B
19
- const app = createApp(App);
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
- // \u5C06\u5E94\u7528\u6302\u8F7D\u5230 #app \u5143\u7D20
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
- `}function me(){return`import { defineComponent, ref, computed } from '@lytjs/lytjs';
24
-
25
- // \u5B9A\u4E49\u6839\u7EC4\u4EF6
26
- const App = defineComponent({
27
- name: 'App',
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
- const reset = () => {
103
- count.value = 0;
104
- };
105
-
106
- return {
107
- count,
108
- double,
109
- features,
110
- increment,
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
- export default App;
118
- `}function ge(){return`/* Lyt.js \u5E94\u7528\u6837\u5F0F */
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
- :root {
121
- --primary-color: #42b883;
122
- --primary-hover: #35a06f;
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
- margin: 0;
134
- padding: 0;
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
- body {
139
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
140
- color: var(--text-color);
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
- min-height: 100vh;
147
- display: flex;
148
- flex-direction: column;
318
+ text-align: center;
319
+ padding: 2rem;
149
320
  }
150
321
 
151
- .app-header {
152
- background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));
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
- .logo {
159
- font-size: 1.8rem;
160
- font-weight: 700;
326
+ .nav a {
327
+ margin: 0 1rem;
328
+ color: #42b883;
329
+ text-decoration: none;
161
330
  }
162
331
 
163
- .logo-text {
164
- letter-spacing: 2px;
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
- .app-main {
168
- flex: 1;
169
- padding: 3rem 2rem;
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
- .welcome-section {
349
+ <style scoped>
350
+ .app {
176
351
  text-align: center;
352
+ padding: 2rem;
177
353
  }
178
354
 
179
- .welcome-section h1 {
180
- font-size: 2.5rem;
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
- .welcome-section p {
186
- font-size: 1.2rem;
187
- color: var(--secondary-color);
188
- margin-bottom: 2rem;
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
- .counter-section {
192
- background: var(--card-bg);
193
- border-radius: 12px;
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
- .counter-section h2 {
201
- margin-bottom: 1rem;
202
- color: var(--text-color);
203
- }
397
+ <script setup lang="ts">
398
+ </script>
204
399
 
205
- .count-display,
206
- .double-display {
207
- font-size: 1.25rem;
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
- .button-group {
212
- display: flex;
213
- justify-content: center;
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
- .btn {
220
- padding: 0.75rem 1.5rem;
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
- .btn:hover {
231
- transform: translateY(-2px);
232
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
233
- }
419
+ // Actions
420
+ function increment() {
421
+ count.value++;
422
+ }
234
423
 
235
- .btn:active {
236
- transform: translateY(0);
237
- }
424
+ function decrement() {
425
+ count.value--;
426
+ }
238
427
 
239
- .btn-primary {
240
- background: var(--primary-color);
241
- }
428
+ function reset() {
429
+ count.value = 0;
430
+ }
242
431
 
243
- .btn-primary:hover {
244
- background: var(--primary-hover);
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
- .btn-secondary {
248
- background: var(--secondary-color);
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
- .btn-secondary:hover {
252
- background: #5d6470;
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
- .btn-success {
256
- background: var(--success-color);
257
- }
475
+ import fs from 'fs';
476
+ import path from 'path';
477
+ import { fileURLToPath } from 'url';
478
+ import http from 'http';
258
479
 
259
- .btn-success:hover {
260
- background: #059669;
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
- .features-section {
264
- margin-top: 2rem;
265
- text-align: left;
485
+ interface RenderOptions {
486
+ url: string;
487
+ template: string;
488
+ manifest?: Record<string, string[]>;
266
489
  }
267
490
 
268
- .features-section h2 {
269
- text-align: center;
270
- margin-bottom: 1.5rem;
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
- .features-section ul {
275
- list-style: none;
276
- padding: 0;
277
- max-width: 600px;
278
- margin: 0 auto;
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
- .features-section li {
282
- padding: 0.75rem 1rem;
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
- .app-footer {
291
- padding: 1.5rem 2rem;
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
- @media (max-width: 640px) {
299
- .welcome-section h1 {
300
- font-size: 2rem;
301
- }
513
+ // Load index.html template
514
+ let template: string;
302
515
 
303
- .app-main {
304
- padding: 2rem 1rem;
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
- # Build outputs
315
- dist/
316
- build/
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
- # IDE
321
- .vscode/
322
- .idea/
323
- *.swp
324
- *.swo
736
+ <script setup lang="ts">
737
+ defineProps<{
738
+ /** Component props */
739
+ }>();
325
740
 
326
- # OS
327
- .DS_Store
328
- Thumbs.db
741
+ defineEmits<{
742
+ /** Component events */
743
+ }>();
744
+ </script>
329
745
 
330
- # Environment
331
- .env.local
332
- .env.*.local
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
- # Cache
335
- *.cache
336
- .cache/
337
- `}function be(e){return`# ${e}
765
+ <script setup lang="ts">
766
+ // Page logic here
767
+ </script>
338
768
 
339
- \u8FD9\u662F\u4E00\u4E2A\u4F7F\u7528 [Lyt.js](https://gitee.com/lytjs/lytjs) \u6846\u67B6\u521B\u5EFA\u7684\u9879\u76EE\u3002
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
- ## \u7279\u6027
796
+ function decrement() {
797
+ count.value--;
798
+ }
342
799
 
343
- - \u26A1 **\u96F6\u4F9D\u8D56** - \u7EAF\u539F\u751F\u5B9E\u73B0\uFF0C\u4E0D\u4F9D\u8D56\u4EFB\u4F55\u7B2C\u4E09\u65B9\u5E93
344
- - \u{1F680} **\u8D85\u8F7B\u91CF** - \u6838\u5FC3\u4EC5 34.56KB\uFF0C\u6781\u901F\u52A0\u8F7D
345
- - \u{1F3A8} **Vue 3 \u517C\u5BB9** - API \u9AD8\u5EA6\u517C\u5BB9\uFF0C\u8FC1\u79FB\u6210\u672C\u4F4E
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
- ## \u5FEB\u901F\u5F00\u59CB
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
- ### \u5B89\u88C5\u4F9D\u8D56
835
+ updated(el, binding) {
836
+ // Directive updated
837
+ },
351
838
 
352
- \`\`\`bash
353
- npm install
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
- ### \u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668
883
+ function reset() {
884
+ isLoading.value = false;
885
+ error.value = null;
886
+ data.value = null;
887
+ }
357
888
 
358
- \`\`\`bash
359
- npm run dev
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
- ### \u6784\u5EFA\u751F\u4EA7\u7248\u672C
913
+ onMounted(() => {
914
+ // Setup code on mount
915
+ });
363
916
 
364
- \`\`\`bash
365
- npm run build
366
- \`\`\`
917
+ onUnmounted(() => {
918
+ // Cleanup on unmount
919
+ });
367
920
 
368
- ## \u9879\u76EE\u7ED3\u6784
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
- ${e}/
372
- \u251C\u2500\u2500 index.html # HTML \u5165\u53E3
373
- \u251C\u2500\u2500 package.json # \u9879\u76EE\u914D\u7F6E
374
- \u251C\u2500\u2500 tsconfig.json # TypeScript \u914D\u7F6E
375
- \u251C\u2500\u2500 src/
376
- \u2502 \u251C\u2500\u2500 main.ts # \u5E94\u7528\u5165\u53E3
377
- \u2502 \u251C\u2500\u2500 App.ts # \u6839\u7EC4\u4EF6
378
- \u2502 \u2514\u2500\u2500 style.css # \u5168\u5C40\u6837\u5F0F
379
- \u2514\u2500\u2500 README.md # \u9879\u76EE\u6587\u6863
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
- ## \u5B66\u4E60\u8D44\u6E90
1044
+ const title = ref('${data.pascalName}');
1045
+ </script>
383
1046
 
384
- - \u{1F4D6} [Lyt.js \u5B98\u65B9\u6587\u6863](https://gitee.com/lytjs/lytjs)
385
- - \u{1F4A1} [\u5FEB\u901F\u5F00\u59CB\u6307\u5357](https://gitee.com/lytjs/lytjs)
386
- - \u{1F527} [API \u53C2\u8003](https://gitee.com/lytjs/lytjs)
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
- ## \u793E\u533A\u652F\u6301
1072
+ import { h, defineComponent } from '@lytjs/core';${styleImport}
389
1073
 
390
- - \u{1F310} Gitee: [https://gitee.com/lytjs/lytjs](https://gitee.com/lytjs/lytjs)
391
- - \u{1F4AC} Issues: [https://gitee.com/lytjs/lytjs/issues](https://gitee.com/lytjs/lytjs/issues)
1074
+ ${tsOnly ? `export interface ${data.pascalName}Props {
1075
+ className?: string;
1076
+ children?: any;
1077
+ }
392
1078
 
393
- ## License
1079
+ ` : ""}${template === "functional" ? `export function ${data.pascalName}(${tsOnly ? `props: ${data.pascalName}Props` : "props"}) {
1080
+ const { className = '', children } = props;
394
1081
 
395
- MIT
396
- `}async function Z(e,t={}){let n=t.template||"spa",r=H.resolve(process.cwd(),e);i.info(`\u6B63\u5728\u521B\u5EFA Lyt \u9879\u76EE: ${o(e,"brightCyan")}`),i.info(`\u4F7F\u7528\u6A21\u677F: ${o(n,"brightCyan")}`),I(r)&&(i.error(`\u76EE\u5F55 "${e}" \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0\u6216\u5220\u9664\u5DF2\u6709\u76EE\u5F55`),process.exit(1)),$(r);let s=[{filePath:"index.html",content:de()},{filePath:"src/main.ts",content:ue()},{filePath:"src/App.ts",content:me()},{filePath:"src/style.css",content:ge()},{filePath:"package.json",content:fe(e)},{filePath:"tsconfig.json",content:he()},{filePath:".gitignore",content:ye()},{filePath:"README.md",content:be(e)}];for(let a of s){let p=H.join(r,a.filePath);w(p,a.content),i.success(` \u521B\u5EFA ${a.filePath}`)}console.log(""),i.success(`\u9879\u76EE ${o(e,"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")} ${e}`),console.log(` ${o("npm install","brightGreen")}`),console.log(` ${o("npm run dev","brightGreen")}`),console.log("")}import*as F from"fs";import*as h from"path";var G={spa:{description:"\u57FA\u7840 SPA \u5355\u9875\u5E94\u7528\uFF08\u8BA1\u6570\u5668\u793A\u4F8B\uFF09",type:"builtin"},ssr:{description:"SSR \u670D\u52A1\u7AEF\u6E32\u67D3\u5E94\u7528",type:"builtin"},ssg:{description:"SSG \u9759\u6001\u7AD9\u70B9\u751F\u6210",type:"builtin"},"todo-app":{description:"Todo \u5F85\u529E\u4E8B\u9879\u5E94\u7528\uFF08\u54CD\u5E94\u5F0F + LocalStorage \u6301\u4E45\u5316\uFF09",type:"example"},"admin-dashboard":{description:"Admin Dashboard \u4F01\u4E1A\u7EA7\u540E\u53F0\u7BA1\u7406\u7CFB\u7EDF\uFF0810 \u4E2A\u9875\u9762\uFF09",type:"example"}};function N(){let e={};for(let[t,n]of Object.entries(G))e[t]=n.description;return e}function z(e){return e in G}function xe(e){let t={name:e.name,version:"0.1.0",private:!0,type:"module",scripts:{dev:"lytx dev",build:"lytx build",preview:"lytx preview"},dependencies:{"@lytjs/lytjs":"latest"}};return e.ts&&(t.devDependencies={"@lytjs/cli":"latest",typescript:"^5.0.1"}),e.eslint&&(t.devDependencies=t.devDependencies||{},t.devDependencies.eslint="^8.0.0",t.scripts=t.scripts||{},t.scripts.lint="eslint src --ext .ts,.js"),e.router&&(t.dependencies["@lytjs/router"]="latest"),e.store&&(t.dependencies["@lytjs/store"]="latest"),JSON.stringify(t,null,2)+`
397
- `}function ve(e){let t={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"]};return e.template==="ssr"&&(t.compilerOptions.types=["node"]),JSON.stringify(t,null,2)+`
398
- `}function $e(e){let t=e.ts?".ts":".js";return`<!DOCTYPE html>
399
- <html lang="zh-CN">
400
- <head>
401
- <meta charset="UTF-8" />
402
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
403
- <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
404
- <title>${e.name}</title>
405
- </head>
406
- <body>
407
- <div id="app"></div>
408
- <script type="module" src="/src/main${t}"></script>
409
- </body>
410
- </html>
411
- `}function we(e){let t=["// Lytx \u914D\u7F6E\u6587\u4EF6","import { defineConfig } from '@lytjs/lytjs'","","export default defineConfig({"," // \u6784\u5EFA\u6A21\u5F0F",` mode: '${e.template}',`];return e.router&&(t.push(" // \u8DEF\u7531\u914D\u7F6E"),t.push(" router: {"),t.push(" historyMode: true,"),t.push(" },")),e.store&&(t.push(" // \u72B6\u6001\u7BA1\u7406\u914D\u7F6E"),t.push(" store: {"),t.push(" strict: true,"),t.push(" },")),t.push("})"),t.push(""),t.join(`
412
- `)}function Se(e){let t=e.ts?".ts":".js",n=["import { createApp } from '@lytjs/lytjs'","import App from './App.lyt'","import './styles/main.css'"];return e.router&&n.push("import { router } from './router'"),e.store&&n.push("import { store } from './store'"),n.push(""),n.push("// \u521B\u5EFA\u5E94\u7528\u5B9E\u4F8B"),n.push("const app = createApp(App)"),e.router&&n.push("app.use(router)"),e.store&&n.push("app.use(store)"),n.push(""),n.push("// \u5C06\u5E94\u7528\u6302\u8F7D\u5230 #app \u5143\u7D20"),n.push("app.mount('#app')"),n.join(`
413
- `)+`
414
- `}function je(){return`<template>
415
- <div class="app">
416
- <Header />
417
- <main>
418
- <h1>Hello Lyt!</h1>
419
- <p>\u6B22\u8FCE\u4F7F\u7528 Lyt \u6846\u67B6</p>
420
- </main>
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 { defineComponent } from '@lytjs/lytjs'
426
- import Header from './components/Header'
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
- export default defineComponent({
429
- name: 'App',
430
- components: {
431
- Header,
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
- .app {
438
- text-align: center;
439
- padding: 20px;
1132
+ .${data.kebabName}-page {
1133
+ padding: 2rem;
440
1134
  }
441
1135
  </style>
442
- `}function Ce(){return`import { defineComponent } from '@lytjs/lytjs'
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
- export default defineComponent({
445
- name: 'HomePage',
1155
+ import { h, ${tsOnly ? "signal" : "signal"} } from '@lytjs/core';
1156
+ ${styleImport}
446
1157
 
447
- template: \`
448
- <div class="page-home">
449
- <h1>\u9996\u9875</h1>
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 default defineComponent({
457
- name: 'AboutPage',
1162
+ ` : ""}export function ${data.pascalName}Page(${tsOnly ? `props: ${data.pascalName}PageProps` : "props"}) {
1163
+ const { title = '${data.pascalName}' } = props;
458
1164
 
459
- template: \`
460
- <div class="page-about">
461
- <h1>\u5173\u4E8E</h1>
462
- <p>\u8FD9\u662F\u5173\u4E8E\u9875\u9762</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 const store = createStore({
500
- state: {
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
- mutations: {
506
- increment(state: any) {
507
- state.count++
508
- },
1185
+ ${tsOnly ? `export interface ${data.pascalName}ServiceOptions {
1186
+ baseUrl?: string;
1187
+ timeout?: number;
1188
+ }
509
1189
 
510
- setMessage(state: any, message: string) {
511
- state.message = message
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
- actions: {
516
- async fetchMessage({ commit }: any) {
517
- commit('setMessage', 'Fetched from API')
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
- getters: {
522
- doubleCount: (state: any) => state.count * 2,
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
- `}function Te(){return`/* \u5168\u5C40\u6837\u5F0F */
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
- margin: 0;
529
- padding: 0;
530
- box-sizing: border-box;
1260
+ ${tsOnly ? `export interface ${data.pascalName}Options {
1261
+ immediate?: boolean;
531
1262
  }
532
1263
 
533
- body {
534
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
535
- 'Helvetica Neue', Arial, sans-serif;
536
- -webkit-font-smoothing: antialiased;
537
- -moz-osx-font-smoothing: grayscale;
538
- color: #2c3e50;
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
- a {
542
- color: #42b883;
543
- text-decoration: none;
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
- a:hover {
547
- text-decoration: underline;
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
- ws.onmessage = function(event) {
579
- try {
580
- var update = JSON.parse(event.data);
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
- if (update.type === 'css') {
583
- handleCSSUpdate(update);
584
- } else if (update.type === 'update') {
585
- handleModuleUpdate(update);
586
- } else if (update.type === 'reload') {
587
- handleFullReload(update);
588
- }
589
- } catch (e) {
590
- console.error('[HMR] Failed to parse update:', e);
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
- ws.onclose = function() {
595
- console.log('[HMR] Disconnected');
596
- scheduleReconnect();
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
- ws.onerror = function() {
600
- ws.close();
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 scheduleReconnect() {
605
- if (reconnectAttempts >= maxReconnectAttempts) {
606
- console.log('[HMR] Max reconnect attempts reached');
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
- // \u5982\u679C\u6709\u5185\u8054 CSS \u5185\u5BB9\uFF0C\u76F4\u63A5\u6CE8\u5165
630
- if (update.content) {
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 handleModuleUpdate(update) {
638
- console.log('[HMR] Module update:', update.path);
639
- // \u5C1D\u8BD5\u70ED\u66F4\u65B0\u6A21\u5757
640
- if (typeof module !== 'undefined' && module.hot) {
641
- module.hot.accept(update.path, function() {
642
- console.log('[HMR] Module accepted:', update.path);
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
- function handleFullReload(update) {
652
- console.log('[HMR] Full reload:', update.path);
653
- location.reload();
654
- }
655
-
656
- // \u542F\u52A8\u8FDE\u63A5
657
- connect();
658
- })();`}var D=null;async function Oe(){if(!D)try{D=await import("esbuild")}catch(e){i.error("\u7F3A\u5C11\u4F9D\u8D56: esbuild"),i.error(""),i.error(" Lyt CLI \u7684\u5F00\u53D1\u670D\u52A1\u5668\u9700\u8981 esbuild \u6765\u5B9E\u65F6\u7F16\u8BD1 TypeScript\u3002"),i.error(""),i.error(" \u8BF7\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u5B89\u88C5:"),i.error(` ${o("npm install esbuild --save-dev","brightGreen")}`),i.error(""),i.error(" \u5982\u679C\u60A8\u4F7F\u7528 pnpm:"),i.error(` ${o("pnpm add esbuild -D","brightGreen")}`),i.error(""),process.exit(1)}return D}function He(e,t){try{return D.transformSync(e,{loader:"ts",target:"es2018",format:"esm",sourcemap:"inline"}).code}catch(n){let r=n instanceof Error?n.message:String(n);return console.error(`[Lyt CLI] \u7F16\u8BD1\u9519\u8BEF ${t||""}:`,r),e}}function Ge(e,t){var u;let n=((u=e.url)==null?void 0:u.split("?")[0])||"/";if(n==="/"&&(n="/index.html"),n.startsWith("/node_modules/")||n.startsWith("/@")){let c;if(n.startsWith("/@")&&!n.startsWith("/node_modules/")?c=x.join(t,"node_modules",n.slice(1)):c=x.join(t,n.slice(1)),n.match(/^\/(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/)&&!n.includes(".")){let m=x.join(c,"package.json");if(A.existsSync(m))try{let l=JSON.parse(S(m)),f=l.module||l.main||"index.js";l.exports&&(typeof l.exports=="string"?f=l.exports:typeof l.exports=="object"&&l.exports["."]&&(typeof l.exports["."]=="string"?f=l.exports["."]:l.exports["."].import?f=l.exports["."].import:l.exports["."].default?f=l.exports["."].default:Object.values(l.exports["."])[0]&&(f=Object.values(l.exports["."])[0]))),f.startsWith("./")&&(f=f.slice(2)),c=x.join(c,f)}catch(l){}}if(A.existsSync(c)&&A.statSync(c).isFile()){let m=x.extname(c).toLowerCase(),f={".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".json":"application/json; charset=utf-8"}[m]||"application/octet-stream",b=S(c);return{statusCode:200,headers:{"Content-Type":f},body:b}}}let r=x.join(t,n);if(!r.startsWith(t))return{statusCode:403,headers:{"Content-Type":"text/plain; charset=utf-8"},body:"403 Forbidden"};if(!A.existsSync(r)||!A.statSync(r).isFile())return{statusCode:404,headers:{"Content-Type":"text/plain; charset=utf-8"},body:"404 Not Found"};let s=x.extname(r).toLowerCase(),p={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".ts":"application/typescript; charset=utf-8",".json":"application/json; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".txt":"text/plain; charset=utf-8"}[s]||"application/octet-stream",d=S(r);return(s===".ts"||s===".tsx")&&(d=He(d,n)),s===".html"&&(d=d.replace("</head>",`<script>${J()}</script>
659
- </head>`)),{statusCode:200,headers:{"Content-Type":p},body:d}}async function V(e={}){let t=e.port||3e3,n=x.resolve(e.root||process.cwd()),r=e.hmr!==!1;await Oe();let s=te.createServer((d,u)=>{let c=Ge(d,n);u.writeHead(c.statusCode,c.headers),u.end(c.body)}),a=r?_(s):null;s.listen(t,()=>{console.log(""),console.log(o(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557","brightCyan")),console.log(o(" \u2551","brightCyan")+o(" Lyt \u5F00\u53D1\u670D\u52A1\u5668\u5DF2\u542F\u52A8 ","brightWhite")+o("\u2551","brightCyan")),console.log(o(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D","brightCyan")),console.log(""),console.log(` ${o("\u279C","brightGreen")} \u672C\u5730\u8BBF\u95EE: ${o(`http://localhost:${t}`,"brightBlue")}`),console.log(` ${o("\u279C","brightGreen")} \u7F51\u7EDC\u8BBF\u95EE: ${o(`http://127.0.0.1:${t}`,"brightBlue")}`),console.log(` ${o("\u279C","brightGreen")} \u9879\u76EE\u76EE\u5F55: ${o(n,"brightBlue")}`),console.log(` ${o("\u279C","brightGreen")} \u70ED\u66F4\u65B0: ${o(r?"\u5DF2\u5F00\u542F":"\u5DF2\u5173\u95ED",r?"brightGreen":"brightRed")}`),console.log(""),console.log(` ${o("\u6309 Ctrl+C \u505C\u6B62\u670D\u52A1\u5668","dim")}`),console.log("")}),s.on("error",d=>{d.code==="EADDRINUSE"?(i.error(`\u7AEF\u53E3 ${t} \u5DF2\u88AB\u5360\u7528\uFF0C\u8BF7\u4F7F\u7528 --port \u6307\u5B9A\u5176\u4ED6\u7AEF\u53E3`),process.exit(1)):(i.error(`\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${d.message}`),process.exit(1))});let p=()=>{i.info("\u6B63\u5728\u5173\u95ED\u5F00\u53D1\u670D\u52A1\u5668..."),s.close(()=>{i.success("\u670D\u52A1\u5668\u5DF2\u5173\u95ED"),process.exit(0)}),setTimeout(()=>{i.warn("\u670D\u52A1\u5668\u5173\u95ED\u8D85\u65F6\uFF0C\u5F3A\u5236\u9000\u51FA"),process.exit(1)},5e3)};process.on("SIGINT",p),process.on("SIGTERM",p)}import*as y from"fs";import*as g from"path";var B=null;async function Le(){if(!B)try{B=await import("esbuild")}catch(e){i.error("\u7F3A\u5C11\u4F9D\u8D56: esbuild"),i.error(""),i.error(" Lyt CLI \u7684\u6784\u5EFA\u529F\u80FD\u9700\u8981 esbuild\u3002"),i.error(""),i.error(" \u8BF7\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u5B89\u88C5:"),i.error(` ${o("npm install esbuild --save-dev","brightGreen")}`),i.error(""),i.error(" \u5982\u679C\u60A8\u4F7F\u7528 pnpm:"),i.error(` ${o("pnpm add esbuild -D","brightGreen")}`),i.error(""),process.exit(1)}return B}async function oe(e={}){let t=Date.now(),n=g.resolve(e.root||process.cwd()),r=g.resolve(n,e.outDir||"dist"),s=e.entry||"index.html",a=e.minify||!1;await Le(),i.info("\u5F00\u59CB\u6784\u5EFA\u9879\u76EE..."),i.info(` \u6839\u76EE\u5F55: ${o(n,"brightCyan")}`),i.info(` \u8F93\u51FA\u76EE\u5F55: ${o(r,"brightCyan")}`),i.info(` \u538B\u7F29: ${o(a?"\u5F00\u542F":"\u5173\u95ED",a?"brightGreen":"brightYellow")}`);let p=g.join(n,s);y.existsSync(p)||(i.error(`\u5165\u53E3\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`),process.exit(1));let d=S(p),u=/<script\s+type="module"\s+src="([^"]+)"\s*><\/script>/g,c,m=[];for(;(c=u.exec(d))!==null;)m.push(c[1]);m.length===0&&i.warn("\u672A\u5728\u5165\u53E3 HTML \u4E2D\u627E\u5230\u6A21\u5757\u811A\u672C\u5F15\u7528");let l={inputFiles:0,outputFiles:0,totalSize:0,buildTime:0};$(r),$(g.join(r,"assets"));for(let b of m){let P=g.join(n,b);if(!y.existsSync(P)){i.warn(`\u811A\u672C\u6587\u4EF6\u4E0D\u5B58\u5728: ${b}`);continue}i.info(`\u6B63\u5728\u6253\u5305: ${o(b,"brightYellow")}`);let E=`${g.basename(b,g.extname(b))}.bundle.js`,R=g.join(r,"assets",E);try{await B.build({entryPoints:[P],bundle:!0,minify:a,target:"es2018",format:"esm",outfile:R,sourcemap:!0,external:O=>O.startsWith("@lytjs/"),drop:a?["console"]:[],metafile:!0});let C=y.readFileSync(R,"utf-8");l.totalSize+=Buffer.byteLength(C,"utf-8"),l.outputFiles++;let k=R+".map";if(y.existsSync(k)){let O=y.readFileSync(k,"utf-8");l.totalSize+=Buffer.byteLength(O,"utf-8"),l.outputFiles++}d=d.replace(`<script type="module" src="${b}"></script>`,`<script src="/assets/${E}"></script>`),l.inputFiles++}catch(C){let k=C instanceof Error?C.message:String(C);i.error(`\u6253\u5305\u5931\u8D25 ${b}: ${k}`),process.exit(1)}}De(n,r,l);let f=g.join(r,"index.html");w(f,d),l.outputFiles++,l.totalSize+=Buffer.byteLength(d,"utf-8"),l.buildTime=Date.now()-t,console.log(""),i.success("\u6784\u5EFA\u5B8C\u6210\uFF01"),console.log(""),console.log(` ${o("\u8F93\u5165\u6587\u4EF6:","brightWhite")} ${l.inputFiles} \u4E2A`),console.log(` ${o("\u8F93\u51FA\u6587\u4EF6:","brightWhite")} ${l.outputFiles} \u4E2A`),console.log(` ${o("\u603B\u5927\u5C0F:","brightWhite")} ${Be(l.totalSize)}`),console.log(` ${o("\u6784\u5EFA\u8017\u65F6:","brightWhite")} ${l.buildTime}ms`),console.log(` ${o("\u8F93\u51FA\u76EE\u5F55:","brightWhite")} ${o(r,"brightCyan")}`),console.log("")}function De(e,t,n){let r=g.join(e,"src");if(!y.existsSync(r))return;function s(a,p){let d=y.readdirSync(a,{withFileTypes:!0});for(let u of d){let c=g.join(a,u.name);if(u.isDirectory()){if(u.name==="node_modules")continue;s(c,p)}else if(u.isFile()){let m=g.extname(u.name);if([".ts",".tsx",".js",".jsx"].includes(m))continue;let l=g.relative(p,c),f=g.join(t,l);if(/\.(png|jpe?g|gif|svg|ico|woff2?|ttf|eot|mp[34]|webm|avi|pdf|zip|gz)$/i.test(c)){let P=y.readFileSync(c);w(f,P)}else w(f,y.readFileSync(c).toString("utf-8"));n.outputFiles++,n.totalSize+=y.statSync(c).size}}}s(r,r)}function Be(e){if(e===0)return"0 B";let t=["B","KB","MB","GB"],n=1024,r=Math.floor(Math.log(e)/Math.log(n));return`${(e/Math.pow(n,r)).toFixed(2)} ${t[r]}`}import*as M from"fs";import*as ne from"path";var Y=`
660
- ${o("lytx generate","brightCyan")} - \u751F\u6210\u4EE3\u7801\uFF08\u7EC4\u4EF6\u3001Store\u3001\u9875\u9762\u3001API\uFF09
661
-
662
- ${o("\u7528\u6CD5:","brightGreen")}
663
- lytx generate <type> <name> [options]
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
- <script setup>
695
- // \u7EC4\u4EF6\u903B\u8F91
696
- </script>
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
- <style scoped>
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
- <script setup>
711
- import { defineProps, defineEmits } from '@lytjs/core';
1429
+ ${tsOnly ? `export interface ${data.pascalName}LayoutProps {
1430
+ children?: any;
1431
+ }
712
1432
 
713
- const props = defineProps({
714
- disabled: {
715
- type: Boolean,
716
- default: false
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
- const emit = defineEmits(['click']);
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
- function handleClick(event) {
723
- emit('click', event);
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
- <style scoped>
728
- .${n} {
729
- padding: 8px 16px;
730
- border: none;
731
- border-radius: 4px;
732
- background: #3b82f6;
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
- .${n}:hover:not(:disabled) {
739
- background: #2563eb;
1484
+ function toCamelCase2(name) {
1485
+ const pascal = toPascalCase2(name);
1486
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
740
1487
  }
741
-
742
- .${n}:disabled {
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
- </style>
747
- `,input:`<!-- ${e} \u8F93\u5165\u6846\u7EC4\u4EF6 -->
748
- <template>
749
- <div class="${n}">
750
- <input
751
- :value="modelValue"
752
- :type="type"
753
- :placeholder="placeholder"
754
- :disabled="disabled"
755
- @input="handleInput"
756
- @change="handleChange"
757
- />
758
- </div>
759
- </template>
760
-
761
- <script setup>
762
- import { defineProps, defineEmits } from '@lytjs/core';
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
- const props = defineProps({
765
- modelValue: {
766
- type: [String, Number],
767
- default: ''
768
- },
769
- type: {
770
- type: String,
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
- const emit = defineEmits(['update:modelValue', 'change']);
1629
+ import { definePlugin } from '@lytjs/core';
1630
+ import type { ConfigSchema } from '@lytjs/core';
784
1631
 
785
- function handleInput(event) {
786
- emit('update:modelValue', event.target.value);
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
- function handleChange(event) {
790
- emit('change', event.target.value);
1652
+ export interface ${pluginName.replace(/-/g, "")}Options {
1653
+ debug?: boolean;
1654
+ option1?: string;
791
1655
  }
792
- </script>
793
1656
 
794
- <style scoped>
795
- .${n} input {
796
- padding: 8px 12px;
797
- border: 1px solid #d1d5db;
798
- border-radius: 4px;
799
- font-size: 14px;
800
- outline: none;
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
- .${n} input:focus {
804
- border-color: #3b82f6;
805
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
806
- }
807
- </style>
808
- `,card:`<!-- ${e} \u5361\u7247\u7EC4\u4EF6 -->
809
- <template>
810
- <div class="${n}">
811
- <div class="${n}-header" if="$slots.header">
812
- <slot name="header"></slot>
813
- </div>
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
- <script setup>
824
- // \u7EC4\u4EF6\u903B\u8F91
825
- </script>
1685
+ import { definePlugin } from '@lytjs/core';
826
1686
 
827
- <style scoped>
828
- .${n} {
829
- background: white;
830
- border-radius: 8px;
831
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
832
- overflow: hidden;
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
- .${n}-header,
836
- .${n}-body,
837
- .${n}-footer {
838
- padding: 16px;
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
- .${n}-header {
842
- border-bottom: 1px solid #e5e7eb;
1712
+ export interface PluginOptions {
1713
+ // Define your plugin options here
843
1714
  }
1715
+ `;
1716
+ }
1717
+ files["README.md"] = `# @lytjs/plugin-${pluginName}
844
1718
 
845
- .${n}-footer {
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
- import { createStore } from '@lytjs/store';
1721
+ ## Installation
854
1722
 
855
- export const ${t}Store = createStore('${e}', {
856
- state: {
857
- // \u72B6\u6001\u5B9A\u4E49
858
- count: 0
859
- },
1723
+ \`\`\`bash
1724
+ npm install @lytjs/plugin-${pluginName}
1725
+ \`\`\`
860
1726
 
861
- getters: {
862
- // \u8BA1\u7B97\u5C5E\u6027
863
- double: state => state.count * 2
864
- },
1727
+ ## Usage
865
1728
 
866
- actions: {
867
- // \u65B9\u6CD5\u5B9A\u4E49
868
- increment(state) {
869
- state.count++;
870
- },
1729
+ \`\`\`typescript
1730
+ import { createApp } from '@lytjs/core';
1731
+ import myPlugin from '@lytjs/plugin-${pluginName}';
871
1732
 
872
- decrement(state) {
873
- state.count--;
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
- <script setup>
886
- import { ref, computed } from '@lytjs/reactivity';
1737
+ ## API
887
1738
 
888
- // \u9875\u9762\u903B\u8F91
889
- </script>
1739
+ ### createPlugin(options?)
890
1740
 
891
- <style scoped>
892
- .${t}-page {
893
- padding: 20px;
894
- }
895
- </style>
896
- `}function We(e){return`/**
897
- * ${e} API
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
- export default async function handler(req, res) {
901
- const method = req.method;
1754
+ node_modules
1755
+ dist
1756
+ *.local
902
1757
 
903
- switch (method) {
904
- case 'GET':
905
- // \u83B7\u53D6\u8D44\u6E90
906
- res.json({
907
- success: true,
908
- message: 'Get ${e}',
909
- data: []
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
- case 'POST':
914
- // \u521B\u5EFA\u8D44\u6E90
915
- res.status(201).json({
916
- success: true,
917
- message: 'Create ${e}',
918
- data: {}
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
- case 'PUT':
923
- // \u66F4\u65B0\u8D44\u6E90
924
- res.json({
925
- success: true,
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
- case 'DELETE':
931
- // \u5220\u9664\u8D44\u6E90
932
- res.json({
933
- success: true,
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
- default:
939
- res.status(405).json({
940
- success: false,
941
- message: 'Method not allowed'
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
- `}async function re(e){let t=Ye(e);t.type||(i.error("\u8BF7\u63D0\u4F9B\u751F\u6210\u7C7B\u578B (component, store, page, api)"),console.log(""),console.log(Y),process.exit(1)),t.name||(i.error("\u8BF7\u63D0\u4F9B\u540D\u79F0"),console.log(""),console.log(Y),process.exit(1)),i.info(`\u751F\u6210 ${t.type}: ${t.name}`),i.info(`\u4F7F\u7528 AI: ${t.useAI?"Yes":"No"}`);let n,r;switch(t.type){case"component":n=Ue(t.name,t),r=`./src/components/${t.name}.lyt`;break;case"store":n=Ne(t.name),r=`./src/stores/${t.name}.js`;break;case"page":n=ze(t.name),r=`./src/pages/${t.name}.lyt`;break;case"api":n=We(t.name),r=`./src/api/${t.name}.js`;break}let s=t.outputPath||r,a=ne.dirname(s);M.existsSync(a)||M.mkdirSync(a,{recursive:!0}),M.writeFileSync(s,n,"utf-8"),i.success(`\u6587\u4EF6\u5DF2\u4FDD\u5B58: ${s}`),t.useAI&&(console.log(""),console.log(o("\u63D0\u793A:","brightYellow")),console.log(" AI \u751F\u6210\u529F\u80FD\u9700\u8981\u5728\u9879\u76EE\u4E2D\u5B89\u88C5 @lytjs/ai"),console.log(" \u8BF7\u5148\u8FD0\u884C: lyt-ai init \u521D\u59CB\u5316\u914D\u7F6E"),console.log(""))}var _e="5.0.5",le="lytx",Je="Lyt.js \u6846\u67B6\u547D\u4EE4\u884C\u5DE5\u5177\uFF08\u589E\u5F3A\u7248\uFF09",se=`
946
- ${o(le,"brightCyan")} - ${Je}
947
-
948
- ${o("\u7528\u6CD5:","brightGreen")}
949
- lytx <command> [options] [args]
950
-
951
- ${o("\u547D\u4EE4:","brightGreen")}
952
- ${o("create","brightYellow")} <name> \u521B\u5EFA\u4E00\u4E2A\u65B0\u7684 Lyt \u9879\u76EE
953
- ${o("dev","brightYellow")} \u542F\u52A8\u672C\u5730\u5F00\u53D1\u670D\u52A1\u5668
954
- ${o("build","brightYellow")} \u6784\u5EFA\u751F\u4EA7\u7248\u672C
955
- ${o("preview","brightYellow")} \u9884\u89C8\u6784\u5EFA\u7ED3\u679C
956
- ${o("generate","brightYellow")} <type> <name> \u751F\u6210\u4EE3\u7801\uFF08\u7EC4\u4EF6\u3001Store\u3001\u9875\u9762\u3001API\uFF09
957
-
958
- ${o("\u5168\u5C40\u9009\u9879:","brightGreen")}
959
- ${o("-h, --help","brightYellow")} \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
960
- ${o("-v, --version","brightYellow")} \u663E\u793A\u7248\u672C\u53F7
961
-
962
- ${o("\u793A\u4F8B:","brightGreen")}
963
- ${o("$","dim")} lytx create my-app
964
- ${o("$","dim")} lytx create my-app --template spa --ts --router --store
965
- ${o("$","dim")} lytx create my-todo --template todo-app
966
- ${o("$","dim")} lytx create my-admin --template admin-dashboard
967
- ${o("$","dim")} lytx dev
968
- ${o("$","dim")} lytx dev --port 8080 --hmr
969
- ${o("$","dim")} lytx build
970
- ${o("$","dim")} lytx build --mode ssr
971
- ${o("$","dim")} lytx preview --port 4173
972
-
973
- `;function Ve(){let e=N(),t="";for(let[n,r]of Object.entries(e))t+=` ${o(n,"brightYellow")} \u2014 ${r}
974
- `;return`
975
- ${o("lytx create","brightCyan")} - \u521B\u5EFA\u65B0\u7684 Lyt \u9879\u76EE
976
-
977
- ${o("\u7528\u6CD5:","brightGreen")}
978
- lytx create <name> [options]
979
-
980
- ${o("\u53C2\u6570:","brightGreen")}
981
- ${o("<name>","brightYellow")} \u9879\u76EE\u540D\u79F0\uFF08\u540C\u65F6\u4F5C\u4E3A\u76EE\u5F55\u540D\uFF09
982
-
983
- ${o("\u9009\u9879:","brightGreen")}
984
- ${o("--template <tpl>","brightYellow")} \u9879\u76EE\u6A21\u677F\uFF08\u9ED8\u8BA4: spa\uFF09
985
- ${t}
986
- ${o("--ts","brightYellow")} \u4F7F\u7528 TypeScript\uFF08\u4EC5\u5185\u7F6E\u6A21\u677F\uFF09
987
- ${o("--router","brightYellow")} \u5305\u542B\u8DEF\u7531\uFF08\u4EC5\u5185\u7F6E\u6A21\u677F\uFF09
988
- ${o("--store","brightYellow")} \u5305\u542B\u72B6\u6001\u7BA1\u7406\uFF08\u4EC5\u5185\u7F6E\u6A21\u677F\uFF09
989
- ${o("--eslint","brightYellow")} \u5305\u542B ESLint \u914D\u7F6E\uFF08\u4EC5\u5185\u7F6E\u6A21\u677F\uFF09
990
-
991
- ${o("\u793A\u4F8B:","brightGreen")}
992
- ${o("$","dim")} lytx create my-app
993
- ${o("$","dim")} lytx create my-app --template spa --ts --router --store
994
- ${o("$","dim")} lytx create my-todo --template todo-app
995
- ${o("$","dim")} lytx create my-admin --template admin-dashboard
996
- ${o("$","dim")} lytx create my-app --template ssr --ts
997
-
998
- `}var Ke=`
999
- ${o("lytx dev","brightCyan")} - \u542F\u52A8\u672C\u5730\u5F00\u53D1\u670D\u52A1\u5668
1000
-
1001
- ${o("\u7528\u6CD5:","brightGreen")}
1002
- lytx dev [options]
1003
-
1004
- ${o("\u9009\u9879:","brightGreen")}
1005
- ${o("-p, --port <port>","brightYellow")} \u670D\u52A1\u7AEF\u53E3\uFF08\u9ED8\u8BA4: 3000\uFF09
1006
- ${o("--hmr","brightYellow")} \u5F00\u542F\u70ED\u66F4\u65B0\uFF08\u9ED8\u8BA4: \u5F00\u542F\uFF09
1007
- ${o("--no-hmr","brightYellow")} \u5173\u95ED\u70ED\u66F4\u65B0
1008
-
1009
- ${o("\u529F\u80FD:","brightGreen")}
1010
- - \u9759\u6001\u6587\u4EF6\u670D\u52A1
1011
- - TypeScript \u5373\u65F6\u7F16\u8BD1
1012
- - \u70ED\u6A21\u5757\u66FF\u6362\uFF08HMR\uFF09
1013
- - WebSocket \u5B9E\u65F6\u901A\u4FE1
1014
-
1015
- ${o("\u793A\u4F8B:","brightGreen")}
1016
- ${o("$","dim")} lytx dev
1017
- ${o("$","dim")} lytx dev --port 8080
1018
- ${o("$","dim")} lytx dev --no-hmr
1019
-
1020
- `,qe=`
1021
- ${o("lytx build","brightCyan")} - \u6784\u5EFA\u751F\u4EA7\u7248\u672C
1022
-
1023
- ${o("\u7528\u6CD5:","brightGreen")}
1024
- lytx build [options]
1025
-
1026
- ${o("\u9009\u9879:","brightGreen")}
1027
- ${o("--mode <mode>","brightYellow")} \u6784\u5EFA\u6A21\u5F0F\uFF08\u9ED8\u8BA4: spa\uFF09
1028
- \u53EF\u9009\u503C: spa, ssr, ssg
1029
- ${o("--minify","brightYellow")} \u538B\u7F29\u4EE3\u7801\uFF08\u53BB\u9664\u7A7A\u767D\u548C\u6CE8\u91CA\uFF09
1030
- ${o("-o, --outDir <dir>","brightYellow")} \u8F93\u51FA\u76EE\u5F55\uFF08\u9ED8\u8BA4: dist\uFF09
1031
- ${o("--entry <file>","brightYellow")} \u5165\u53E3\u6587\u4EF6\uFF08\u9ED8\u8BA4: index.html\uFF09
1032
-
1033
- ${o("\u529F\u80FD:","brightGreen")}
1034
- - TypeScript \u7F16\u8BD1
1035
- - \u6A21\u5757\u6253\u5305\uFF08\u5185\u8054\u4F9D\u8D56\uFF09
1036
- - \u53BB\u9664 console.log
1037
- - Source Map \u751F\u6210
1038
- - \u9759\u6001\u8D44\u6E90\u590D\u5236
1039
-
1040
- ${o("\u793A\u4F8B:","brightGreen")}
1041
- ${o("$","dim")} lytx build
1042
- ${o("$","dim")} lytx build --mode ssr
1043
- ${o("$","dim")} lytx build --minify --outDir ./output
1044
-
1045
- `,Ze=`
1046
- ${o("lytx preview","brightCyan")} - \u9884\u89C8\u6784\u5EFA\u7ED3\u679C
1047
-
1048
- ${o("\u7528\u6CD5:","brightGreen")}
1049
- lytx preview [options]
1050
-
1051
- ${o("\u9009\u9879:","brightGreen")}
1052
- ${o("-p, --port <port>","brightYellow")} \u670D\u52A1\u7AEF\u53E3\uFF08\u9ED8\u8BA4: 4173\uFF09
1053
-
1054
- ${o("\u793A\u4F8B:","brightGreen")}
1055
- ${o("$","dim")} lytx preview
1056
- ${o("$","dim")} lytx preview --port 5000
1057
-
1058
- `;function Xe(){console.log(""),console.log(` ${o(le,"brightCyan")} v${o(_e,"brightWhite")}`),console.log("")}function Qe(e){i.error(`\u672A\u77E5\u547D\u4EE4: ${o(e,"brightRed")}`),console.log(""),console.log(` \u8FD0\u884C ${o("lytx --help","brightCyan")} \u67E5\u770B\u53EF\u7528\u547D\u4EE4`),console.log("")}function et(e){return typeof e=="string"&&z(e)?e:"spa"}async function tt(e){if(e.options.help){console.log(Ve());return}e.args.length===0&&(i.error("\u8BF7\u63D0\u4F9B\u9879\u76EE\u540D\u79F0"),console.log(""),console.log(` \u7528\u6CD5: ${o("lytx create <name>","brightCyan")}`),console.log(""),console.log(` \u8FD0\u884C ${o("lytx create --help","brightCyan")} \u67E5\u770B\u66F4\u591A\u9009\u9879`),console.log(""),process.exit(1));let t=e.args[0],n=typeof e.options.template=="string"?e.options.template:"spa";if(!z(n)){i.error(`\u672A\u77E5\u6A21\u677F: ${o(n,"brightRed")}`),console.log(""),console.log(" \u53EF\u7528\u6A21\u677F\uFF1A");let s=N();for(let[a,p]of Object.entries(s))console.log(` ${o(a,"brightYellow")} \u2014 ${p}`);console.log(""),process.exit(1)}if(e.options.ts===!0||e.options.router===!0||e.options.store===!0||e.options.eslint===!0||typeof e.options.template=="string"&&["ssr","ssg","todo-app","admin-dashboard"].includes(e.options.template)){let s={name:t,template:et(e.options.template),ts:e.options.ts===!0,router:e.options.router===!0,store:e.options.store===!0,eslint:e.options.eslint===!0};await W(s)}else{let s={template:typeof e.options.template=="string"?e.options.template:"spa"};await Z(t,s)}}function ot(e){if(e.options.help){console.log(Ke);return}let t={port:typeof e.options.port=="string"?parseInt(e.options.port,10):typeof e.options.p=="string"?parseInt(e.options.p,10):3e3,hmr:e.options["no-hmr"]!==!0};(isNaN(t.port)||t.port<1||t.port>65535)&&(i.error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${e.options.port||e.options.p}`),process.exit(1)),V(t)}async function nt(e){if(e.options.help){console.log(qe);return}let t={minify:e.options.minify===!0,outDir:typeof e.options.outDir=="string"?e.options.outDir:typeof e.options.o=="string"?e.options.o:"dist",entry:typeof e.options.entry=="string"?e.options.entry:"index.html"};await oe(t)}function rt(e){if(e.options.help){console.log(Ze);return}let t=typeof e.options.port=="string"?parseInt(e.options.port,10):typeof e.options.p=="string"?parseInt(e.options.p,10):4173;(isNaN(t)||t<1||t>65535)&&(i.error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${e.options.port||e.options.p}`),process.exit(1));let n=ae.resolve(process.cwd(),"dist");ie.existsSync(n)||(i.error("\u672A\u627E\u5230\u6784\u5EFA\u8F93\u51FA\u76EE\u5F55 dist/\uFF0C\u8BF7\u5148\u8FD0\u884C lytx build"),process.exit(1)),V({port:t,root:n,hmr:!1})}async function st(){let e=q(process.argv);if(e.options.version){Xe();return}if(e.options.help&&!e.command){console.log(se);return}e.command||(console.log(se),process.exit(1));try{switch(e.command){case"create":await tt(e);break;case"dev":ot(e);break;case"build":await nt(e);break;case"preview":rt(e);break;case"generate":if(e.options.help){console.log(Y);break}await re(e.args);break;default:Qe(e.command),process.exit(1)}}catch(t){let n=t instanceof Error?t.message:String(t);i.error(`\u6267\u884C\u5931\u8D25: ${n}`),console.log(""),console.log(` ${o("\u63D0\u793A:","brightYellow")} \u8BF7\u68C0\u67E5\u8F93\u5165\u53C2\u6570\u6216\u8FD0\u884C ${o("lytx --help","brightCyan")} \u67E5\u770B\u5E2E\u52A9`),console.log(""),process.exit(1)}}st();export{_ as createHMREndpoint,Fe as createHMRServer,W as createProject,J as getHMRClientScript};
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