create-fc-uniapp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process'
3
+
4
+ // 导入主程序
5
+ import('../dist/index.js').catch((err) => {
6
+ console.error('Failed to load CLI:', err)
7
+ process.exit(1)
8
+ })
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env node
2
+ import { intro, text, multiselect, cancel, isCancel, log, spinner } from '@clack/prompts';
3
+ import { bold, green, red, yellow, cyan } from 'kolorist';
4
+ import { exec } from 'child_process';
5
+ import { promises as fs } from 'fs';
6
+ import { join } from 'path';
7
+ import { existsSync } from 'fs';
8
+ import dayjs from 'dayjs';
9
+ import minimist from 'minimist';
10
+ import { createFilesFromResponse } from './request.js';
11
+ import { pkg } from './info.js';
12
+
13
+ // 配置
14
+ const TEMPLATE_REPO = 'https://gitee.com/lfcleo/fc-uniapp.git';
15
+ const DEFAULT_BRANCH = 'main';
16
+ const AVAILABLE_MODULES = [
17
+ { value: 'request', label: '网络请求' },
18
+ { value: 'upload', label: '文件上传' },
19
+ { value: 'download', label: '文件下载' },
20
+ { value: 'websocket', label: 'WebSocket' },
21
+ { value: 'utils', label: '常用工具方法' },
22
+ { value: 'theme', label: '主题切换' },
23
+ { value: 'locale', label: '多语言' },
24
+ { value: 'updated', label: '版本更新(App/小程序)' },
25
+ { value: 'authorize', label: '系统权限(App)' },
26
+ { value: 'jgpush', label: '极光推送(App)' },
27
+ ];
28
+ const logger = {
29
+ success: (message) => console.log(`${green(bold(message))}`),
30
+ error: (message) => console.error(`${red(bold(message))}`),
31
+ warn: (message) => console.log(`${yellow(bold(message))}`),
32
+ info: (message) => console.log(`${cyan(bold(message))}`)
33
+ };
34
+
35
+ // 验证项目名称
36
+ function checkProjectName(name) {
37
+ if (!name.trim()) return '项目名称不能为空';
38
+ if (existsSync(join(process.cwd(), name))) {
39
+ return `目录 ${yellow(name)} 已存在,请选择其他名称`;
40
+ }
41
+ const reg = /^[a-zA-Z0-9_-]+$/;
42
+ if (!reg.test(name) || name.startsWith('-') || name.endsWith('-')) {
43
+ return `项目名称 ${yellow(name)} 不符合规范,请使用字母、数字、连字符或下划线`;
44
+ }
45
+ return '';
46
+ }
47
+
48
+ // 克隆仓库
49
+ async function cloneRepo(projectDir, branch = DEFAULT_BRANCH) {
50
+ return new Promise((resolve, reject) => {
51
+ exec(`git clone --depth=1 -b ${branch} ${TEMPLATE_REPO} "${projectDir}"`, (error) => {
52
+ if (error) {
53
+ reject(new Error(`模板下载失败: ${error.message}`));
54
+ return;
55
+ }
56
+ fs.rm(join(projectDir, '.git'), { recursive: true, force: true }).then(resolve).catch(reject);
57
+ });
58
+ });
59
+ }
60
+
61
+ // 更新 package.json
62
+ async function updatePackageJson(projectDir, projectName) {
63
+ const pkgPath = join(projectDir, 'package.json');
64
+ if (!existsSync(pkgPath)) return;
65
+
66
+ const pkgContent = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
67
+ const newPkg = {
68
+ name: projectName.toLowerCase().replace(/\s+/g, '-'),
69
+ version: '1.0.0',
70
+ description: `${projectName} - fc-uniapp project`,
71
+ unibest: { createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss') },
72
+ ...pkgContent
73
+ };
74
+
75
+ ['unibest-version', 'unibest-update-time', 'metadata'].forEach(key => delete newPkg[key]);
76
+
77
+ await fs.writeFile(pkgPath, JSON.stringify(newPkg, null, 2));
78
+ }
79
+
80
+ // 安装模块
81
+ async function installModules(projectDir, modules) {
82
+ if (modules.length === 0) return '没有选择任何模块';
83
+
84
+ const s = spinner();
85
+ s.start(`正在安装模块: ${modules.join(', ')}`);
86
+
87
+ try {
88
+ const result = await createFilesFromResponse(projectDir, modules);
89
+
90
+ if (result.success) {
91
+ s.stop(`模块安装成功`);
92
+ return '模块安装成功';
93
+ } else {
94
+ s.stop('模块安装部分失败');
95
+ const failedModules = Object.entries(result.failures)
96
+ .filter(([_, failed]) => failed)
97
+ .map(([module]) => module);
98
+
99
+ logger.error(`以下模块安装失败: ${failedModules.join(', ')}`);
100
+ return `部分模块安装失败: ${failedModules.join(', ')}`;
101
+ }
102
+ } catch (error) {
103
+ s.stop();
104
+ logger.error(`模块安装过程中出现错误: ${error.message}`);
105
+ throw new Error(`模块安装失败: ${error.message}`);
106
+ }
107
+ }
108
+
109
+ // 主创建函数
110
+ async function createProject(projectName, selectedModules) {
111
+ const projectDir = join(process.cwd(), projectName);
112
+
113
+ try {
114
+ await cloneRepo(projectDir);
115
+ await updatePackageJson(projectDir, projectName);
116
+ if (selectedModules?.length > 0) await installModules(projectDir, selectedModules);
117
+ return projectDir;
118
+ } catch (error) {
119
+ if (existsSync(projectDir)) {
120
+ try {
121
+ await fs.rm(projectDir, { recursive: true, force: true });
122
+ } catch (rmError) {
123
+ logger.warn(`清理失败目录时出错: ${rmError.message}`);
124
+ }
125
+ }
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ // 主函数
131
+ async function main() {
132
+ const args = minimist(process.argv.slice(2));
133
+
134
+ // 显示版本或帮助
135
+ if (args.v || args.version) {
136
+ console.log(pkg.version);
137
+ return;
138
+ }
139
+
140
+ if (args.h || args.help) {
141
+ console.log(green('\nfc-uniapp 项目创建工具'));
142
+ console.log(`用法: npm create fc-uniapp [项目名称]\n`);
143
+ console.log('选项:');
144
+ console.log(' -m, --modules <items> 预选模块(逗号分隔)');
145
+ console.log(' -v, --version 显示版本');
146
+ console.log(' -h, --help 显示帮助\n');
147
+ console.log('示例:');
148
+ console.log(' npm create fc-uniapp my-project -m request');
149
+ return;
150
+ }
151
+
152
+ // 启动提示
153
+ intro(bold(green('fc-uniapp 快速创建工具')));
154
+
155
+ // 获取项目名称
156
+ let projectName = args._[0];
157
+
158
+ if (!projectName) {
159
+ const result = await text({
160
+ message: `请输入项目名称${green("[只能包含字母、数字、下划线和短横线]")}`,
161
+ placeholder: 'my-app',
162
+ validate: value => checkProjectName(value) || undefined
163
+ });
164
+
165
+ if (isCancel(result)) {
166
+ cancel('操作已取消');
167
+ process.exit(0);
168
+ }
169
+ projectName = result;
170
+ } else {
171
+ const error = checkProjectName(projectName);
172
+ if (error) {
173
+ logger.error(error);
174
+ process.exit(1);
175
+ }
176
+ }
177
+
178
+ // 获取模块选择
179
+ let selectedModules = [];
180
+ const modulesArg = args.m || args.modules;
181
+
182
+ if (modulesArg) {
183
+ selectedModules = Array.isArray(modulesArg)
184
+ ? modulesArg.flat().map(m => m.trim())
185
+ : modulesArg.split(',').map(m => m.trim());
186
+ logger.info(`已选择模块: ${selectedModules.join(', ')}`);
187
+ } else {
188
+ const moduleResult = await multiselect({
189
+ message: `请选择需要的模块:${yellow("(多选,↑/↓ 切换,空格选择,回车确认)")}${green("[可不选,项目生成后也可添加]")}`,
190
+ options: AVAILABLE_MODULES,
191
+ required: false,
192
+ initialValues: []
193
+ });
194
+
195
+ if (isCancel(moduleResult)) {
196
+ cancel('操作已取消');
197
+ process.exit(0);
198
+ }
199
+ selectedModules = moduleResult;
200
+ }
201
+
202
+ try {
203
+ await createProject(projectName, selectedModules);
204
+ log.success(`项目 ${projectName} 创建成功!`);
205
+ logger.info('\n下一步操作:');
206
+ logger.info(` cd ${cyan(projectName)}`);
207
+ logger.info(` npm i`);
208
+ logger.info(` 使用HBuilderX运行项目`);
209
+ } catch (error) {
210
+ logger.error(`创建失败: ${error.message}`);
211
+ process.exit(1);
212
+ }
213
+ }
214
+
215
+ // 错误处理
216
+ process.on('unhandledRejection', (reason) => {
217
+ logger.error(`致命错误: ${reason.message || reason}`);
218
+ process.exit(1);
219
+ });
220
+
221
+ // 执行主函数
222
+ main().catch(err => {
223
+ logger.error(err.message);
224
+ process.exit(1);
225
+ });
package/dist/info.js ADDED
@@ -0,0 +1,10 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import path from 'node:path';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const pkgPath = path.join(__dirname, '../package.json');
9
+ const pkgContent = readFileSync(pkgPath, 'utf8');
10
+ export const pkg = JSON.parse(pkgContent);
@@ -0,0 +1,91 @@
1
+ import { promises as fs } from 'fs';
2
+ import { dirname, resolve } from 'path';
3
+
4
+ // 网络请求URL
5
+ const apiUrl = "https://mock.apipost.net/mock/570d229f6088000/?apipost_id=2fd1a15d3c950f";
6
+
7
+ // 模块注释配置
8
+ const MODULE_COMMENTS = {
9
+ request: [7, 33],
10
+ upload: [8, 35],
11
+ download: [9, 37],
12
+ websocket: [10, 39],
13
+ locale: [4, 27],
14
+ updated: [5, 29],
15
+ utils: [6, 31],
16
+ authorize: [12, 42],
17
+ jgpush: [13, 44]
18
+ };
19
+
20
+ // 网络请求
21
+ async function httpRequest(module) {
22
+ try {
23
+ const response = await fetch(apiUrl, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify({ modules: module })
27
+ });
28
+
29
+ if (!response.ok) throw new Error(`请求失败,状态码: ${response.status}`);
30
+ return await response.json();
31
+ } catch (error) {
32
+ console.error(`模块 ${module} 请求失败:`, error.message);
33
+ return null;
34
+ }
35
+ }
36
+
37
+ // 创建文件
38
+ async function createFile(projectDir, filePath, content) {
39
+ const fullPath = resolve(projectDir, filePath);
40
+ await fs.mkdir(dirname(fullPath), { recursive: true });
41
+ await fs.writeFile(fullPath, content || '');
42
+ }
43
+
44
+ // 修改注释
45
+ async function toggleComments(projectDir, indices) {
46
+ const indexPath = resolve(projectDir, 'fcuni/index.ts');
47
+ if (!(await fs.stat(indexPath).catch(() => false))) return;
48
+
49
+ const content = await fs.readFile(indexPath, 'utf8');
50
+ const lines = content.split('\n');
51
+
52
+ for (const index of indices) {
53
+ const lineNum = index - 1;
54
+ if (!lines[lineNum]) continue;
55
+
56
+ // 移除行首注释
57
+ lines[lineNum] = lines[lineNum].replace(/^\s*\/\//, '').trim();
58
+ }
59
+
60
+ await fs.writeFile(indexPath, lines.join('\n'));
61
+ }
62
+
63
+ // 主函数
64
+ export async function createFilesFromResponse(projectDir, modules) {
65
+ const results = { success: true, failures: {} };
66
+
67
+ for (const module of modules) {
68
+ try {
69
+ const response = await httpRequest(module);
70
+ if (!response) throw new Error('无响应数据');
71
+
72
+ // 创建文件
73
+ await Promise.all(
74
+ Object.entries(response).map(([file, content]) =>
75
+ createFile(projectDir, file, content)
76
+ )
77
+ );
78
+
79
+ // 处理注释
80
+ if (MODULE_COMMENTS[module]) {
81
+ await toggleComments(projectDir, MODULE_COMMENTS[module]);
82
+ }
83
+ } catch (error) {
84
+ console.error(`模块 ${module} 处理失败:`, error.message);
85
+ results.failures[module] = true;
86
+ results.success = false;
87
+ }
88
+ }
89
+
90
+ return results;
91
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "create-fc-uniapp",
3
+ "version": "1.0.0",
4
+ "description": "创建 fc-uniapp 快速开发框架",
5
+ "bin": {
6
+ "fc-uniapp": "bin/index.js",
7
+ "create-fc-uniapp": "bin/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [
14
+ "fcuni",
15
+ "fc-uniapp",
16
+ "fcuniapp",
17
+ "uniapp",
18
+ "uni"
19
+ ],
20
+ "author": "L",
21
+ "license": "Apache-2.0",
22
+ "type": "module",
23
+ "files": [
24
+ "bin",
25
+ "dist"
26
+ ],
27
+ "dependencies": {
28
+ "@clack/prompts": "^0.11.0",
29
+ "dayjs": "^1.11.18",
30
+ "ejs": "^3.1.10",
31
+ "fs-extra": "^11.3.0",
32
+ "kolorist": "^1.8.0",
33
+ "minimist": "^1.2.8",
34
+ "node-fetch": "^3.3.2"
35
+ },
36
+ "devDependencies": {
37
+ "@types/ejs": "^3.1.5",
38
+ "@types/fs-extra": "^11.0.4",
39
+ "@types/minimist": "^1.2.5",
40
+ "@types/node": "^24.5.0",
41
+ "cross-env": "^7.0.3",
42
+ "tsup": "^8.5.0",
43
+ "typescript": "^5.9.0"
44
+ }
45
+ }