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 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 [options] <app-name>
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 || TEMPLATES[0].name;
56
+ const inputTemplate = option.template || "default";
54
57
  const template = TEMPLATES.find((t) => t.name === inputTemplate);
55
- await createProject(appName, template);
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
- `不支持的构建类型:${option.loader} 如果是空请检查qdmp.json配置文件是否正确`,
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.27",
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
- const zip = new AdmZip();
110
- const h5Path = path.join(dirname, "h5");
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 = loader === "EMP" ? "dist" : "h5";
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
- } catch (error) {
34
- error(`${error.message}`);
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
+ }