@lumiflora/fsdk 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -0
- package/dist/commands/add-component.d.ts +7 -0
- package/dist/commands/add-component.d.ts.map +1 -0
- package/dist/commands/add-component.js +93 -0
- package/dist/commands/add-page.d.ts +8 -0
- package/dist/commands/add-page.d.ts.map +1 -0
- package/dist/commands/add-page.js +98 -0
- package/dist/commands/add-store.d.ts +6 -0
- package/dist/commands/add-store.d.ts.map +1 -0
- package/dist/commands/add-store.js +204 -0
- package/dist/commands/completion.d.ts +8 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +271 -0
- package/dist/commands/create-app.d.ts +10 -0
- package/dist/commands/create-app.d.ts.map +1 -0
- package/dist/commands/create-app.js +174 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/preview.d.ts +8 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +162 -0
- package/dist/commands/sync-template.d.ts +6 -0
- package/dist/commands/sync-template.d.ts.map +1 -0
- package/dist/commands/sync-template.js +94 -0
- package/dist/commands/validate.d.ts +7 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +136 -0
- package/dist/completion/completion-scripts.d.ts +4 -0
- package/dist/completion/completion-scripts.d.ts.map +1 -0
- package/dist/completion/completion-scripts.js +193 -0
- package/dist/completion/index.d.ts +2 -0
- package/dist/completion/index.d.ts.map +1 -0
- package/dist/completion/index.js +1 -0
- package/dist/core/config-loader.d.ts +91 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +118 -0
- package/dist/core/hot-reload.d.ts +24 -0
- package/dist/core/hot-reload.d.ts.map +1 -0
- package/dist/core/hot-reload.js +97 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/plugin-system.d.ts +32 -0
- package/dist/core/plugin-system.d.ts.map +1 -0
- package/dist/core/plugin-system.js +69 -0
- package/dist/core/template-engine.d.ts +22 -0
- package/dist/core/template-engine.d.ts.map +1 -0
- package/dist/core/template-engine.js +89 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +214 -0
- package/dist/plugins/add-component-plugin.d.ts +4 -0
- package/dist/plugins/add-component-plugin.d.ts.map +1 -0
- package/dist/plugins/add-component-plugin.js +23 -0
- package/dist/plugins/add-page-plugin.d.ts +4 -0
- package/dist/plugins/add-page-plugin.d.ts.map +1 -0
- package/dist/plugins/add-page-plugin.js +23 -0
- package/dist/plugins/add-store-plugin.d.ts +4 -0
- package/dist/plugins/add-store-plugin.d.ts.map +1 -0
- package/dist/plugins/add-store-plugin.js +23 -0
- package/dist/plugins/core-plugin.d.ts +4 -0
- package/dist/plugins/core-plugin.d.ts.map +1 -0
- package/dist/plugins/core-plugin.js +22 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +4 -0
- package/dist/utils/command-helpers.d.ts +15 -0
- package/dist/utils/command-helpers.d.ts.map +1 -0
- package/dist/utils/command-helpers.js +40 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +32 -0
- package/dist/utils/path.d.ts +9 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +66 -0
- package/dist/utils/spinner.d.ts +23 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +61 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @lumiflora/fsdk
|
|
2
|
+
|
|
3
|
+
Vue 3 前端项目脚手架 CLI 工具,快速创建包含最佳技术栈的项目骨架。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @lumiflora/fsdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# 交互式创建项目
|
|
15
|
+
fsdk create
|
|
16
|
+
|
|
17
|
+
# 指定参数创建
|
|
18
|
+
fsdk create my-project --template full --package-manager pnpm
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 命令
|
|
22
|
+
|
|
23
|
+
### create - 创建新项目
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
fsdk create [project-name] [options]
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--template <name> 模板名称 (base|full) [default: base]
|
|
30
|
+
--package-manager <pm> 包管理器 (npm|pnpm|yarn|bun) [default: pnpm]
|
|
31
|
+
--eslint 启用 ESLint [default: true]
|
|
32
|
+
--no-git 跳过 git 初始化
|
|
33
|
+
--no-install 跳过依赖安装
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### add-page - 添加页面
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
fsdk add-page [options]
|
|
40
|
+
|
|
41
|
+
Options:
|
|
42
|
+
--page-name <name> 页面名称
|
|
43
|
+
--router-path <path> 路由路径
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### add-component - 添加组件
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
fsdk add-component [options]
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
--name <name> 组件名称
|
|
53
|
+
--type <type> 组件类型 (page|common|business) [default: common]
|
|
54
|
+
--dir <dir> 子目录
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### add-store - 添加状态管理
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
fsdk add-store [options]
|
|
61
|
+
|
|
62
|
+
Options:
|
|
63
|
+
--name <name> Store 名称
|
|
64
|
+
--type <type> Store 类型 (pinia|redux) [default: pinia]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### validate - 校验配置和模板
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
fsdk validate [options]
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
--config 仅校验配置
|
|
74
|
+
--templates 仅校验模板
|
|
75
|
+
--strict 严格校验
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### preview - 预览模板
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
fsdk preview [options]
|
|
82
|
+
|
|
83
|
+
Options:
|
|
84
|
+
--port <port> 端口号 [default: 3000]
|
|
85
|
+
--host <host> 主机地址 [default: localhost]
|
|
86
|
+
--open 打开浏览器
|
|
87
|
+
--template <name> 模板名称
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### sync-template - 同步模板文件
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
fsdk sync-template [options]
|
|
94
|
+
|
|
95
|
+
Options:
|
|
96
|
+
--template <name> 模板名称
|
|
97
|
+
--force 强制同步
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### completion - 生成 Shell 补全
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
fsdk completion [shell] [options]
|
|
104
|
+
|
|
105
|
+
Options:
|
|
106
|
+
--install 自动安装到 shell 配置
|
|
107
|
+
|
|
108
|
+
# 示例
|
|
109
|
+
fsdk completion --install # 自动配置当前 shell
|
|
110
|
+
fsdk completion zsh --install
|
|
111
|
+
fsdk completion bash --install
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 模板
|
|
115
|
+
|
|
116
|
+
| 模板 | 说明 |
|
|
117
|
+
|------|------|
|
|
118
|
+
| `base` | Vue 3.5 + Vite 6 基础版 |
|
|
119
|
+
| `full` | Vue 3.5 + Vite 6 + Element Plus + Pinia + Vue Router + SCSS + ESLint |
|
|
120
|
+
|
|
121
|
+
## 技术栈
|
|
122
|
+
|
|
123
|
+
| 技术 | 版本 | 用途 |
|
|
124
|
+
|------|------|------|
|
|
125
|
+
| Vue | ^3.5.0 | 前端框架 |
|
|
126
|
+
| Vue Router | ^4.4.0 | 路由(文件路由系统) |
|
|
127
|
+
| Pinia | ^2.2.0 | 状态管理 |
|
|
128
|
+
| Element Plus | ^2.8.0 | UI 组件库 |
|
|
129
|
+
| Vite | ^6.0.0 | 构建工具 |
|
|
130
|
+
| TypeScript | ~5.6.0 | 类型系统 |
|
|
131
|
+
| ESLint | ^9.0.0 | 代码检查 |
|
|
132
|
+
|
|
133
|
+
## 开发
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# 本地开发
|
|
137
|
+
cd packages/cli
|
|
138
|
+
pnpm install
|
|
139
|
+
pnpm dev
|
|
140
|
+
|
|
141
|
+
# 构建
|
|
142
|
+
pnpm build
|
|
143
|
+
|
|
144
|
+
# 类型检查
|
|
145
|
+
pnpm typecheck
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-component.d.ts","sourceRoot":"","sources":["../../src/commands/add-component.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBhG"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { logger, toKebabCase } from '../utils/index.js';
|
|
5
|
+
export async function addComponent(cwd, options = {}) {
|
|
6
|
+
try {
|
|
7
|
+
const resolvedOptions = await resolveOptions(options);
|
|
8
|
+
const { type, name, dir } = resolvedOptions;
|
|
9
|
+
const componentDir = path.resolve(cwd, 'src', 'components', type, dir || '');
|
|
10
|
+
await fs.ensureDir(componentDir);
|
|
11
|
+
const componentTemplate = generateComponentTemplate(name, type);
|
|
12
|
+
await Promise.all([
|
|
13
|
+
fs.writeFile(path.resolve(componentDir, `${name}.vue`), componentTemplate.component),
|
|
14
|
+
fs.writeFile(path.resolve(componentDir, `${name}.less`), componentTemplate.styles),
|
|
15
|
+
fs.writeFile(path.resolve(componentDir, `index.ts`), componentTemplate.index),
|
|
16
|
+
]);
|
|
17
|
+
logger.success(`Component ${name} created at src/components/${type}/${dir || name}/`);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
logger.error('Failed to add component:', error);
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function resolveOptions(options) {
|
|
25
|
+
if (options.type && options.name) {
|
|
26
|
+
return {
|
|
27
|
+
type: options.type,
|
|
28
|
+
name: options.name,
|
|
29
|
+
dir: options.dir || '',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const responses = await prompts([
|
|
33
|
+
{
|
|
34
|
+
type: 'select',
|
|
35
|
+
name: 'type',
|
|
36
|
+
message: 'Select component type',
|
|
37
|
+
choices: [
|
|
38
|
+
{ title: 'Common', value: 'common', description: 'Shared components' },
|
|
39
|
+
{ title: 'Business', value: 'business', description: 'Business-specific components' },
|
|
40
|
+
{ title: 'Page', value: 'page', description: 'Page-specific components' },
|
|
41
|
+
],
|
|
42
|
+
initial: 0,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'text',
|
|
46
|
+
name: 'name',
|
|
47
|
+
message: 'What is the component name?',
|
|
48
|
+
initial: 'my-component',
|
|
49
|
+
validate: (value) => /^[A-Z][a-zA-Z0-9]*$/.test(value) || 'Start with uppercase letter',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
name: 'dir',
|
|
54
|
+
message: 'Subdirectory (optional):',
|
|
55
|
+
initial: '',
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
return {
|
|
59
|
+
type: options.type || responses.type,
|
|
60
|
+
name: options.name || responses.name,
|
|
61
|
+
dir: options.dir || responses.dir || '',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function generateComponentTemplate(name, type) {
|
|
65
|
+
const component = `<template>
|
|
66
|
+
<div class="${toKebabCase(name)}">
|
|
67
|
+
<slot></slot>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script setup lang="ts">
|
|
72
|
+
defineOptions({
|
|
73
|
+
name: '${name}',
|
|
74
|
+
inheritAttrs: true,
|
|
75
|
+
});
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<style scoped lang="less">
|
|
79
|
+
.${toKebabCase(name)} {
|
|
80
|
+
display: block;
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
83
|
+
`;
|
|
84
|
+
const styles = `// ${name} component styles
|
|
85
|
+
.${toKebabCase(name)} {
|
|
86
|
+
// component styles
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
const index = `export { default as ${name} } from './${name}.vue';
|
|
90
|
+
export type { } from './${name}.vue';
|
|
91
|
+
`;
|
|
92
|
+
return { component, styles, index };
|
|
93
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface AddPageOptions {
|
|
2
|
+
routerPath?: string;
|
|
3
|
+
pageName?: string;
|
|
4
|
+
type?: 'page' | 'component';
|
|
5
|
+
}
|
|
6
|
+
export declare function addPage(cwd: string, options?: AddPageOptions): Promise<void>;
|
|
7
|
+
export declare function syncPageRoutes(cwd: string): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=add-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-page.d.ts","sourceRoot":"","sources":["../../src/commands/add-page.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;CAC7B;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCtF;AA8DD,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { logger, toPascalCase, toKebabCase } from '../utils/index.js';
|
|
5
|
+
import fg from 'fast-glob';
|
|
6
|
+
export async function addPage(cwd, options = {}) {
|
|
7
|
+
// Early check: if pageName is provided, verify directory doesn't exist
|
|
8
|
+
if (options.pageName) {
|
|
9
|
+
const pageDir = path.resolve(cwd, 'src', 'views', options.pageName);
|
|
10
|
+
if (fs.existsSync(pageDir)) {
|
|
11
|
+
logger.error(`Page directory ${options.pageName} already exists`);
|
|
12
|
+
throw new Error(`Page directory ${options.pageName} already exists`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const resolvedOptions = await resolveOptions(options);
|
|
17
|
+
const { pageName, routerPath } = resolvedOptions;
|
|
18
|
+
const pageDir = path.resolve(cwd, 'src', 'views', pageName);
|
|
19
|
+
if (fs.existsSync(pageDir)) {
|
|
20
|
+
logger.error(`Page directory ${pageName} already exists`);
|
|
21
|
+
throw new Error(`Page directory ${pageName} already exists`);
|
|
22
|
+
}
|
|
23
|
+
await fs.ensureDir(pageDir);
|
|
24
|
+
const pageTemplate = generatePageTemplate(pageName, routerPath);
|
|
25
|
+
await Promise.all([
|
|
26
|
+
fs.writeFile(path.resolve(pageDir, 'index.vue'), pageTemplate.page),
|
|
27
|
+
fs.writeFile(path.resolve(pageDir, 'index.less'), pageTemplate.styles),
|
|
28
|
+
]);
|
|
29
|
+
logger.success(`Page ${pageName} created at src/views/${pageName}/`);
|
|
30
|
+
logger.info(`Router path: ${routerPath}`);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger.error('Failed to add page:', error);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function resolveOptions(options) {
|
|
38
|
+
if (options.pageName && options.routerPath) {
|
|
39
|
+
return {
|
|
40
|
+
pageName: options.pageName,
|
|
41
|
+
routerPath: options.routerPath,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const responses = await prompts([
|
|
45
|
+
{
|
|
46
|
+
type: 'text',
|
|
47
|
+
name: 'pageName',
|
|
48
|
+
message: 'What is the page name?',
|
|
49
|
+
initial: 'about',
|
|
50
|
+
validate: (value) => /^[a-z][a-z0-9-]*$/.test(value) || 'Start with lowercase letter, only lowercase letters, numbers, and hyphens',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'text',
|
|
54
|
+
name: 'routerPath',
|
|
55
|
+
message: 'Enter the router path:',
|
|
56
|
+
initial: (prev) => `/${toKebabCase(prev)}`,
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
return {
|
|
60
|
+
pageName: options.pageName || responses.pageName,
|
|
61
|
+
routerPath: options.routerPath || responses.routerPath,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function generatePageTemplate(pageName, routerPath) {
|
|
65
|
+
const componentName = toPascalCase(pageName);
|
|
66
|
+
const page = `<template>
|
|
67
|
+
<div class="${pageName}-page">
|
|
68
|
+
<h1>${componentName}</h1>
|
|
69
|
+
<p>Router path: ${routerPath}</p>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
// ${componentName} page component
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<style scoped lang="less">
|
|
78
|
+
.${pageName}-page {
|
|
79
|
+
padding: 20px;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
82
|
+
`;
|
|
83
|
+
const styles = `// ${componentName} page styles
|
|
84
|
+
.${pageName}-page {
|
|
85
|
+
min-height: 100vh;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
return { page, styles };
|
|
89
|
+
}
|
|
90
|
+
export async function syncPageRoutes(cwd) {
|
|
91
|
+
const viewsDir = path.resolve(cwd, 'src', 'views');
|
|
92
|
+
if (!fs.existsSync(viewsDir)) {
|
|
93
|
+
logger.warning('Views directory does not exist');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const pageDirs = await fg.glob('*', { cwd: viewsDir, onlyDirectories: true });
|
|
97
|
+
logger.success(`Found ${pageDirs.length} pages to sync routes`);
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-store.d.ts","sourceRoot":"","sources":["../../src/commands/add-store.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC1B;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBxF"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { logger, toPascalCase } from '../utils/index.js';
|
|
5
|
+
export async function addStore(cwd, options = {}) {
|
|
6
|
+
try {
|
|
7
|
+
const resolvedOptions = await resolveOptions(options);
|
|
8
|
+
const { name, type } = resolvedOptions;
|
|
9
|
+
const storeDir = path.resolve(cwd, 'src', 'stores');
|
|
10
|
+
await fs.ensureDir(storeDir);
|
|
11
|
+
const storeTemplate = generateStoreTemplate(name, type);
|
|
12
|
+
await Promise.all([
|
|
13
|
+
fs.writeFile(path.resolve(storeDir, `${name}.ts`), storeTemplate.store),
|
|
14
|
+
fs.writeFile(path.resolve(storeDir, `${name}.test.ts`), storeTemplate.test),
|
|
15
|
+
]);
|
|
16
|
+
logger.success(`Store ${name} created at src/stores/`);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
logger.error('Failed to add store:', error);
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function resolveOptions(options) {
|
|
24
|
+
if (options.name && options.type) {
|
|
25
|
+
return {
|
|
26
|
+
name: options.name,
|
|
27
|
+
type: options.type,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const responses = await prompts([
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
name: 'name',
|
|
34
|
+
message: 'What is the store name?',
|
|
35
|
+
initial: 'user',
|
|
36
|
+
validate: (value) => /^[a-z][a-z0-9]*$/.test(value) || 'Start with lowercase letter',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: 'select',
|
|
40
|
+
name: 'type',
|
|
41
|
+
message: 'Select store type',
|
|
42
|
+
choices: [
|
|
43
|
+
{ title: 'Pinia', value: 'pinia', description: 'Vue 3 state management' },
|
|
44
|
+
{ title: 'Redux', value: 'redux', description: 'Predictable state container' },
|
|
45
|
+
],
|
|
46
|
+
initial: 0,
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
return {
|
|
50
|
+
name: options.name || responses.name,
|
|
51
|
+
type: options.type || responses.type,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function generateStoreTemplate(name, type) {
|
|
55
|
+
if (type === 'pinia') {
|
|
56
|
+
return generatePiniaTemplate(name);
|
|
57
|
+
}
|
|
58
|
+
return generateReduxTemplate(name);
|
|
59
|
+
}
|
|
60
|
+
function generatePiniaTemplate(name) {
|
|
61
|
+
const store = `import { defineStore } from 'pinia';
|
|
62
|
+
import { ref, computed } from 'vue';
|
|
63
|
+
|
|
64
|
+
export const use${toPascalCase(name)}Store = defineStore('${name}', () => {
|
|
65
|
+
// State
|
|
66
|
+
const data = ref<unknown>(null);
|
|
67
|
+
const loading = ref(false);
|
|
68
|
+
const error = ref<string | null>(null);
|
|
69
|
+
|
|
70
|
+
// Getters
|
|
71
|
+
const hasData = computed(() => data.value !== null);
|
|
72
|
+
|
|
73
|
+
// Actions
|
|
74
|
+
async function fetchData() {
|
|
75
|
+
loading.value = true;
|
|
76
|
+
error.value = null;
|
|
77
|
+
try {
|
|
78
|
+
// Replace with actual API call when implementing real data fetching
|
|
79
|
+
// Example: data.value = await api.get('/endpoint');
|
|
80
|
+
data.value = null;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
error.value = e instanceof Error ? e.message : 'Unknown error';
|
|
83
|
+
throw e;
|
|
84
|
+
} finally {
|
|
85
|
+
loading.value = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function setData(newData: unknown) {
|
|
90
|
+
data.value = newData;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function reset() {
|
|
94
|
+
data.value = null;
|
|
95
|
+
error.value = null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
data,
|
|
100
|
+
loading,
|
|
101
|
+
error,
|
|
102
|
+
hasData,
|
|
103
|
+
fetchData,
|
|
104
|
+
setData,
|
|
105
|
+
reset,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
`;
|
|
109
|
+
const test = `import { setActivePinia, createPinia } from 'pinia';
|
|
110
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
111
|
+
import { use${toPascalCase(name)}Store } from './${name}';
|
|
112
|
+
|
|
113
|
+
describe('${name} store', () => {
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
setActivePinia(createPinia());
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should initialize with default values', () => {
|
|
119
|
+
const store = use${toPascalCase(name)}Store();
|
|
120
|
+
expect(store.data).toBeNull();
|
|
121
|
+
expect(store.loading).toBe(false);
|
|
122
|
+
expect(store.error).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should set data', () => {
|
|
126
|
+
const store = use${toPascalCase(name)}Store();
|
|
127
|
+
store.setData({ test: 'value' });
|
|
128
|
+
expect(store.data).toEqual({ test: 'value' });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should reset state', () => {
|
|
132
|
+
const store = use${toPascalCase(name)}Store();
|
|
133
|
+
store.setData({ test: 'value' });
|
|
134
|
+
store.reset();
|
|
135
|
+
expect(store.data).toBeNull();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
`;
|
|
139
|
+
return { store, test };
|
|
140
|
+
}
|
|
141
|
+
function generateReduxTemplate(name) {
|
|
142
|
+
const store = `import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
143
|
+
|
|
144
|
+
interface ${toPascalCase(name)}State {
|
|
145
|
+
data: unknown;
|
|
146
|
+
loading: boolean;
|
|
147
|
+
error: string | null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const initialState: ${toPascalCase(name)}State = {
|
|
151
|
+
data: null,
|
|
152
|
+
loading: false,
|
|
153
|
+
error: null,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const ${name}Slice = createSlice({
|
|
157
|
+
name: '${name}',
|
|
158
|
+
initialState,
|
|
159
|
+
reducers: {
|
|
160
|
+
setData: (state, action: PayloadAction<unknown>) => {
|
|
161
|
+
state.data = action.payload;
|
|
162
|
+
},
|
|
163
|
+
setLoading: (state, action: PayloadAction<boolean>) => {
|
|
164
|
+
state.loading = action.payload;
|
|
165
|
+
},
|
|
166
|
+
setError: (state, action: PayloadAction<string | null>) => {
|
|
167
|
+
state.error = action.payload;
|
|
168
|
+
},
|
|
169
|
+
reset: () => initialState,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export const { setData, setLoading, setError, reset } = ${name}Slice.actions;
|
|
174
|
+
export default ${name}Slice.reducer;
|
|
175
|
+
|
|
176
|
+
export const ${name}Store = configureStore({
|
|
177
|
+
reducer: {
|
|
178
|
+
${name}: ${name}Slice.reducer,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export type RootState = ReturnType<typeof ${name}Store.getState>;
|
|
183
|
+
export type AppDispatch = typeof ${name}Store.dispatch;
|
|
184
|
+
`;
|
|
185
|
+
const test = `import { describe, it, expect } from 'vitest';
|
|
186
|
+
import { ${name}Slice } from './${name}';
|
|
187
|
+
|
|
188
|
+
describe('${name} slice', () => {
|
|
189
|
+
it('should return the initial state', () => {
|
|
190
|
+
expect(${name}Slice.reducer(undefined, { type: 'unknown' })).toEqual({
|
|
191
|
+
data: null,
|
|
192
|
+
loading: false,
|
|
193
|
+
error: null,
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle setData', () => {
|
|
198
|
+
const actual = ${name}Slice.reducer(undefined, ${name}Slice.actions.setData({ test: 'value' }));
|
|
199
|
+
expect(actual.data).toEqual({ test: 'value' });
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
`;
|
|
203
|
+
return { store, test };
|
|
204
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ShellType = 'bash' | 'zsh' | 'fish';
|
|
2
|
+
export interface CompletionOptions {
|
|
3
|
+
shell?: ShellType;
|
|
4
|
+
output?: string;
|
|
5
|
+
install?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateCompletion(options?: CompletionOptions): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/commands/completion.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEhD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAoLD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BvF"}
|