qdmp-cli 0.1.27 → 0.1.29
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 +6 -1
- package/actions.js +21 -6
- package/constants.js +1 -12
- package/package.json +2 -6
- package/utils/common.js +7 -95
- package/utils/gitClone.js +6 -4
- package/utils/projectLoader.js +36 -0
package/README.md
CHANGED
|
@@ -26,12 +26,17 @@ npm install -g qdmp-cli
|
|
|
26
26
|
{
|
|
27
27
|
"appId": "your_app_id",
|
|
28
28
|
"devAppId": "your_dev_app_id",
|
|
29
|
+
"loader": "EMP"
|
|
29
30
|
}
|
|
30
31
|
```
|
|
31
32
|
### 初始化项目
|
|
32
33
|
```bash
|
|
33
|
-
qdmp-cli create
|
|
34
|
+
qdmp-cli create <app-name> -t default
|
|
34
35
|
```
|
|
36
|
+
新建项目使用官方 2.0(EMP)模板,创建后验证配置。`package.json.version` 是业务包版本,模板中的 `1.0.0` 不代表 1.0 小程序。保留模板的 loader,不通过改写该字段转换工程。
|
|
37
|
+
|
|
38
|
+
关联 AppID 后执行 `qdmp-cli getMe` 核对本地与平台应用类型;上传前也会校验一致性。仅支持 2.0(EMP)项目,不再支持 SPA/effuse 工程;构建必须完成 EMP 编译。
|
|
39
|
+
|
|
35
40
|
### 拉取配置
|
|
36
41
|
```bash
|
|
37
42
|
qdmp-cli init -a appId
|
package/actions.js
CHANGED
|
@@ -19,10 +19,11 @@ import {
|
|
|
19
19
|
preUpload,
|
|
20
20
|
cleanWorkSpaceFolder,
|
|
21
21
|
} from "./utils/common.js";
|
|
22
|
+
import { readProjectLoader, assertMatchingLoader, assertEMPTemplate } from "./utils/projectLoader.js";
|
|
22
23
|
import { assertCompilerRuntime } from "./utils/compilerRuntime.js";
|
|
23
24
|
import { getAppInfo, uploadVersion, uploadApp } from "./api.js";
|
|
24
25
|
|
|
25
|
-
function createProject(appName, template) {
|
|
26
|
+
async function createProject(appName, template) {
|
|
26
27
|
printLogo();
|
|
27
28
|
if (template) {
|
|
28
29
|
console.log("\n" + chalk.dim("─".repeat(60)) + "\n");
|
|
@@ -35,7 +36,9 @@ function createProject(appName, template) {
|
|
|
35
36
|
info(` ${chalk.dim("仓库:")} ${chalk.green(template.value)}`);
|
|
36
37
|
info(`⏳ 正在创建 ${chalk.bold(appName)} 项目...`);
|
|
37
38
|
console.log("\n" + chalk.dim("─".repeat(60)) + "\n");
|
|
38
|
-
download(appName, template.value);
|
|
39
|
+
const targetPath = await download(appName, template.value);
|
|
40
|
+
assertEMPTemplate(targetPath);
|
|
41
|
+
success("已验证 2.0(EMP)模板;package.json.version 不代表小程序运行时版本。");
|
|
39
42
|
} else {
|
|
40
43
|
listAction();
|
|
41
44
|
error(`未找到模板`);
|
|
@@ -50,9 +53,13 @@ export const initAction = async (appName, option) => {
|
|
|
50
53
|
if (appName.match(invalidChars)) {
|
|
51
54
|
error("名称不能包含中文及特殊字符");
|
|
52
55
|
}
|
|
53
|
-
const inputTemplate = option.template ||
|
|
56
|
+
const inputTemplate = option.template || "default";
|
|
54
57
|
const template = TEMPLATES.find((t) => t.name === inputTemplate);
|
|
55
|
-
|
|
58
|
+
try {
|
|
59
|
+
await createProject(appName, template);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
error(e.message);
|
|
62
|
+
}
|
|
56
63
|
};
|
|
57
64
|
|
|
58
65
|
export const listAction = () => {
|
|
@@ -100,6 +107,9 @@ export const getMyInfoAction = async (option) => {
|
|
|
100
107
|
const appInfo = await getAppInfo(appId, option.env);
|
|
101
108
|
info(`当前用户:${nickname}`);
|
|
102
109
|
info(`当前授权应用:${appInfo?.name} - ${appId}`);
|
|
110
|
+
const localLoader = readProjectLoader();
|
|
111
|
+
info(`小程序类型:${appInfo?.loader === "EMP" ? "2.0(EMP)" : "不支持"}`);
|
|
112
|
+
assertMatchingLoader(localLoader, appInfo?.loader);
|
|
103
113
|
} catch (e) {
|
|
104
114
|
error(e?.message);
|
|
105
115
|
}
|
|
@@ -117,6 +127,7 @@ export async function uploadAction(option) {
|
|
|
117
127
|
if (!remoteApp) {
|
|
118
128
|
error("未找到应用,请检查appId是否正确");
|
|
119
129
|
}
|
|
130
|
+
assertMatchingLoader(readProjectLoader(), remoteApp.loader);
|
|
120
131
|
const description = option.description?.trim()
|
|
121
132
|
?? (process.stdin.isTTY ? await inquirerVersionDescription() : "");
|
|
122
133
|
if (Array.from(description).length > 200) {
|
|
@@ -167,9 +178,12 @@ export const initConfigAction = async (option) => {
|
|
|
167
178
|
const appId = option.appId;
|
|
168
179
|
if (!appId) return error("未配置应用ID,请使用-a参数指定应用ID");
|
|
169
180
|
const appInfo = await getAppInfo(appId, option.env);
|
|
181
|
+
if (!appInfo || appInfo.loader !== "EMP") {
|
|
182
|
+
throw new Error("仅支持平台 EMP 应用,未写入配置");
|
|
183
|
+
}
|
|
170
184
|
const config = {
|
|
171
185
|
name: appInfo.name,
|
|
172
|
-
appId: appInfo.appId,
|
|
186
|
+
[option.env === "dev" ? "devAppId" : "appId"]: appInfo.appId,
|
|
173
187
|
loader: appInfo.loader,
|
|
174
188
|
latest: appInfo.latest,
|
|
175
189
|
};
|
|
@@ -187,6 +201,7 @@ export const buildAction = async () => {
|
|
|
187
201
|
return;
|
|
188
202
|
}
|
|
189
203
|
const config = JSON.parse(fs.readFileSync(qdmpJsonPath, "utf8"));
|
|
204
|
+
readProjectLoader();
|
|
190
205
|
switch (config.loader) {
|
|
191
206
|
case "EMP":
|
|
192
207
|
await assertCompilerRuntime();
|
|
@@ -226,7 +241,7 @@ export const buildAction = async () => {
|
|
|
226
241
|
|
|
227
242
|
default:
|
|
228
243
|
error(
|
|
229
|
-
`不支持的构建类型:${
|
|
244
|
+
`不支持的构建类型:${config.loader ?? "未配置"},请检查 qdmp.json;2.0 必须使用 EMP,不能改用 H5 绕过`,
|
|
230
245
|
);
|
|
231
246
|
break;
|
|
232
247
|
}
|
package/constants.js
CHANGED
|
@@ -6,18 +6,7 @@ export const TEMPLATES = [
|
|
|
6
6
|
emoji: "🟢",
|
|
7
7
|
fullUrl: true,
|
|
8
8
|
},
|
|
9
|
-
|
|
10
|
-
// name: "qdmp",
|
|
11
|
-
// value: "frontend/miniapp-taro-template",
|
|
12
|
-
// desc: "千岛小程序默认模版(1.0)",
|
|
13
|
-
// emoji: "🟡",
|
|
14
|
-
// },
|
|
15
|
-
// {
|
|
16
|
-
// name: "h5",
|
|
17
|
-
// value: "frontend/h5-template",
|
|
18
|
-
// desc: "h5模版",
|
|
19
|
-
// emoji: "🔵",
|
|
20
|
-
// },
|
|
9
|
+
|
|
21
10
|
];
|
|
22
11
|
|
|
23
12
|
export const WORKSPACE_DIR = "qdmp-output";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qdmp-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"description": "qdmp-cli",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -45,9 +45,5 @@
|
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": "^20.19.0 || >=22.12.0"
|
|
48
|
-
}
|
|
49
|
-
"publishConfig": {
|
|
50
|
-
"registry": "https://registry.npmjs.org/",
|
|
51
|
-
"access": "public"
|
|
52
|
-
}
|
|
48
|
+
}
|
|
53
49
|
}
|
package/utils/common.js
CHANGED
|
@@ -68,20 +68,6 @@ export const isUrlAccessible = async (url) => {
|
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
/** 重命名文件夹
|
|
72
|
-
* @param {*} dirname 旧文件夹名
|
|
73
|
-
* @param {*} newname 新文件夹名
|
|
74
|
-
*/
|
|
75
|
-
export async function renameDistFolder(dirname, newname) {
|
|
76
|
-
try {
|
|
77
|
-
if (fs.existsSync(newname)) return true;
|
|
78
|
-
fs.renameSync(dirname, newname);
|
|
79
|
-
return true;
|
|
80
|
-
} catch (e) {
|
|
81
|
-
error("重命名文件夹失败:", e);
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
71
|
/** 创建工作文件夹
|
|
86
72
|
* @param {*} dirname 文件夹名
|
|
87
73
|
*/
|
|
@@ -105,69 +91,15 @@ export async function createWorkSpaceFolder(dirname = "") {
|
|
|
105
91
|
// 压缩项目
|
|
106
92
|
async function compressProject(output, dirname = "", loader, appId) {
|
|
107
93
|
try {
|
|
108
|
-
if (loader !== "EMP")
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (!fs.existsSync(h5Path)) {
|
|
113
|
-
throw new Error("h5 目录不存在");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// 添加文件夹时指定目标路径为 h5
|
|
117
|
-
zip.addLocalFolder(h5Path, "h5");
|
|
118
|
-
zip.writeZip(path.join(dirname, `${output}.zip`));
|
|
119
|
-
} else {
|
|
120
|
-
// 直接压缩dist
|
|
121
|
-
const zip = new AdmZip();
|
|
122
|
-
zip.addLocalFolder(path.join(dirname, "dist"), `${appId}`);
|
|
123
|
-
zip.writeZip(path.join(dirname, `${output}.zip`));
|
|
124
|
-
}
|
|
94
|
+
if (loader !== "EMP") throw new Error("仅支持 EMP 产物");
|
|
95
|
+
const zip = new AdmZip();
|
|
96
|
+
zip.addLocalFolder(path.join(dirname, "dist"), `${appId}`);
|
|
97
|
+
zip.writeZip(path.join(dirname, `${output}.zip`));
|
|
125
98
|
return true;
|
|
126
99
|
} catch (error) {
|
|
127
100
|
return false;
|
|
128
101
|
}
|
|
129
102
|
}
|
|
130
|
-
/** 复制文件夹
|
|
131
|
-
* @param {*} dirname 文件夹名
|
|
132
|
-
*/
|
|
133
|
-
async function copyDistFolder(dirname) {
|
|
134
|
-
try {
|
|
135
|
-
info("正在查找 index.html 文件...");
|
|
136
|
-
let indexPath = null;
|
|
137
|
-
function findIndexHtml(dir) {
|
|
138
|
-
const files = fs.readdirSync(dir);
|
|
139
|
-
for (const file of files) {
|
|
140
|
-
const fullPath = path.join(dir, file);
|
|
141
|
-
const stat = fs.statSync(fullPath);
|
|
142
|
-
if (stat.isDirectory()) {
|
|
143
|
-
const found = findIndexHtml(fullPath);
|
|
144
|
-
if (found) return found;
|
|
145
|
-
} else if (file === "index.html") {
|
|
146
|
-
return fullPath;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
indexPath = findIndexHtml(dirname);
|
|
152
|
-
if (!indexPath) {
|
|
153
|
-
throw new Error("未找到index.html文件");
|
|
154
|
-
}
|
|
155
|
-
success("找到 index.html 文件");
|
|
156
|
-
|
|
157
|
-
const parentDir = path.dirname(indexPath);
|
|
158
|
-
const targetDir = path.join(WORKSPACE_DIR, "dist");
|
|
159
|
-
|
|
160
|
-
info("正在复制文件...");
|
|
161
|
-
// 直接复制整个目录
|
|
162
|
-
fs.cpSync(parentDir, targetDir, { recursive: true });
|
|
163
|
-
success("文件复制完成");
|
|
164
|
-
return true;
|
|
165
|
-
} catch (e) {
|
|
166
|
-
error(e);
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
103
|
function hasCompiledMainDir(dirname) {
|
|
172
104
|
try {
|
|
173
105
|
return fs.statSync(path.join(dirname, "main")).isDirectory();
|
|
@@ -250,7 +182,6 @@ async function copyAndSetEmpDistFolder(appId, code) {
|
|
|
250
182
|
* 预上传准备工作
|
|
251
183
|
* 1. 创建工作空间
|
|
252
184
|
* 2. 复制构建产物
|
|
253
|
-
* 3. 重命名文件夹(非EMP构建模式)
|
|
254
185
|
* 4. 压缩项目
|
|
255
186
|
* @param {string} loader - 构建加载器类型
|
|
256
187
|
* @param {string} targetName - 目标文件名
|
|
@@ -258,6 +189,7 @@ async function copyAndSetEmpDistFolder(appId, code) {
|
|
|
258
189
|
*/
|
|
259
190
|
export async function preUpload(loader, targetName, appId, code) {
|
|
260
191
|
try {
|
|
192
|
+
if (loader !== "EMP") throw new Error("仅支持 2.0(EMP)小程序上传");
|
|
261
193
|
// 1. 创建工作空间文件夹
|
|
262
194
|
if (!(await createWorkSpaceFolder(WORKSPACE_DIR))) {
|
|
263
195
|
error("创建工作空间失败");
|
|
@@ -265,10 +197,7 @@ export async function preUpload(loader, targetName, appId, code) {
|
|
|
265
197
|
}
|
|
266
198
|
|
|
267
199
|
// 2. 根据构建类型复制对应产物
|
|
268
|
-
const isCopySuccess =
|
|
269
|
-
loader === "EMP"
|
|
270
|
-
? await copyAndSetEmpDistFolder(appId, code)
|
|
271
|
-
: await copyAndRenameDistFolder();
|
|
200
|
+
const isCopySuccess = await copyAndSetEmpDistFolder(appId, code);
|
|
272
201
|
|
|
273
202
|
if (!isCopySuccess) {
|
|
274
203
|
error("复制构建产物失败");
|
|
@@ -278,7 +207,7 @@ export async function preUpload(loader, targetName, appId, code) {
|
|
|
278
207
|
// 3. 复制 qdmp.json 到产物目录
|
|
279
208
|
const qdmpJsonPath = path.resolve(process.cwd(), "qdmp.json");
|
|
280
209
|
if (fs.existsSync(qdmpJsonPath)) {
|
|
281
|
-
const distDir =
|
|
210
|
+
const distDir = "dist";
|
|
282
211
|
fs.copyFileSync(qdmpJsonPath, path.join(WORKSPACE_DIR, distDir, "qdmp.json"));
|
|
283
212
|
info("已将 qdmp.json 复制到产物目录");
|
|
284
213
|
}
|
|
@@ -299,23 +228,6 @@ export async function preUpload(loader, targetName, appId, code) {
|
|
|
299
228
|
}
|
|
300
229
|
}
|
|
301
230
|
|
|
302
|
-
/**
|
|
303
|
-
* 复制并创建标准H5文件夹结构
|
|
304
|
-
* 对于非EMP构建模式,复制dist内容并重命名为h5
|
|
305
|
-
* @returns {Promise<boolean>} 操作是否成功
|
|
306
|
-
*/
|
|
307
|
-
async function copyAndRenameDistFolder() {
|
|
308
|
-
try {
|
|
309
|
-
if (!(await copyDistFolder("dist"))) {
|
|
310
|
-
return false;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return renameDistFolder(`${WORKSPACE_DIR}/dist`, `${WORKSPACE_DIR}/h5`);
|
|
314
|
-
} catch (err) {
|
|
315
|
-
error("复制并重命名文件夹过程出错:", err);
|
|
316
|
-
return false;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
231
|
/** 清理工作文件夹
|
|
320
232
|
* @param {*} dirname 文件夹名
|
|
321
233
|
*/
|
package/utils/gitClone.js
CHANGED
|
@@ -23,15 +23,16 @@ export const download = async (projectName, repo) => {
|
|
|
23
23
|
`文件夹 ${projectName} 已存在,是否创建新名称: ${name}`
|
|
24
24
|
);
|
|
25
25
|
if (confirm) {
|
|
26
|
-
clone(repo, name);
|
|
26
|
+
await clone(repo, name);
|
|
27
27
|
} else {
|
|
28
28
|
error(`文件夹 ${projectName} 已存在, 请更名后重新创建`);
|
|
29
29
|
}
|
|
30
30
|
} else {
|
|
31
|
-
clone(repo, name);
|
|
31
|
+
await clone(repo, name);
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
return path.join(process.cwd(), name);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
throw err;
|
|
35
36
|
}
|
|
36
37
|
};
|
|
37
38
|
|
|
@@ -81,5 +82,6 @@ const clone = async (repo, name) => {
|
|
|
81
82
|
spinner.succeed(chalk.green(`模版创建成功!请进入 ${name} 目录开始开发`));
|
|
82
83
|
} catch (err) {
|
|
83
84
|
spinner.fail(chalk.red(`模版创建失败: ${err.message}`));
|
|
85
|
+
throw err;
|
|
84
86
|
}
|
|
85
87
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export function readProjectLoader(cwd = process.cwd()) {
|
|
5
|
+
const config = JSON.parse(fs.readFileSync(path.join(cwd, "qdmp.json"), "utf8"));
|
|
6
|
+
if (config.loader !== "EMP") {
|
|
7
|
+
throw new Error("仅支持 2.0(EMP)小程序;qdmp.json 的 loader 必须为 EMP。不支持 SPA/effuse 工程,不能只修改 loader 转换工程。");
|
|
8
|
+
}
|
|
9
|
+
const packagePath = path.join(cwd, "package.json");
|
|
10
|
+
if (fs.existsSync(packagePath)) {
|
|
11
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
12
|
+
for (const field of ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"]) {
|
|
13
|
+
for (const [name, version] of Object.entries(pkg[field] || {})) {
|
|
14
|
+
if (/effuse|effues/i.test(name) || /effuse|effues/i.test(String(version))) {
|
|
15
|
+
throw new Error(`不支持 effuse/effues 依赖:${name}。请使用官方 2.0 模板迁移业务,不要仅修改 loader。`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return config.loader;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function assertMatchingLoader(localLoader, remoteLoader) {
|
|
24
|
+
if (localLoader !== "EMP" || remoteLoader !== "EMP") {
|
|
25
|
+
throw new Error(`应用类型不一致或不受支持(仅支持 EMP):本地 ${localLoader},平台 ${remoteLoader ?? "未提供"}。请关联匹配的应用或完成工程迁移,不能仅修改 loader。`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function assertEMPTemplate(cwd) {
|
|
30
|
+
if (readProjectLoader(cwd) !== "EMP") {
|
|
31
|
+
throw new Error("创建结果不是 2.0(EMP)模板,请检查官方模板来源。");
|
|
32
|
+
}
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
|
|
34
|
+
if (!pkg.scripts?.build) throw new Error("模板缺少 build 脚本。");
|
|
35
|
+
// package.json.version 是业务包版本,不是小程序运行时版本。
|
|
36
|
+
}
|