1 | ## 介绍
|
2 |
|
3 | miniprogram-ci 是从[微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/devtools.html)中抽离的关于小程序/小游戏项目代码的编译模块。
|
4 |
|
5 | 使用前需要使用小程序管理员身份访问"[微信公众平台](https://mp.weixin.qq.com)-开发-开发设置"后下载代码上传密钥,并配置 IP 白名单,才能进行上传、预览操作。
|
6 |
|
7 | ## 注意事项
|
8 |
|
9 | 1. 代码上传密钥拥有预览、上传代码的权限
|
10 | 2. 代码上传密钥不会明文存储在微信公众平台上,一旦遗失必须重置,请妥善保管
|
11 | 3. 未配置IP白名单的,将无法使用 miniprogram-ci 进行预览和上传
|
12 | 4. 可选择不对IP进行限制,但务必明白风险
|
13 |
|
14 | ## 功能
|
15 |
|
16 | miniprogram-ci 目前提供以下能力:
|
17 | 1. 上传代码,对应小程序开发者工具的上传
|
18 | 1. 预览代码,对应小程序开发者工具的预览
|
19 | 1. 构建 npm,对应小程序开发者工具的: 菜单-工具-构建npm
|
20 | 1. 代理,配置 miniprogram-ci 的网络请求代理方式
|
21 | 1. 支持获取最近上传版本的 sourceMap
|
22 | 1. 支持 node 脚本调用方式和 命令行 调用方式
|
23 |
|
24 |
|
25 | ## 脚本调用
|
26 |
|
27 | ```shell script
|
28 | npm install miniprogram-ci --save
|
29 | ```
|
30 |
|
31 | ### 项目对象
|
32 |
|
33 | 项目对象是本模块主要的入参,可以依据下边的定义自行实现
|
34 |
|
35 | **项目对象的定义:**
|
36 |
|
37 | ```typescript
|
38 | interface IProject {
|
39 | appid: string
|
40 | type: string
|
41 | projectPath: string
|
42 | privateKey: string
|
43 | attr(): Promise<IProjectAttr>
|
44 | stat(prefix: string, filePath: string): IStat | undefined
|
45 | getFile(prefix: string, filePath: string): Promise<Buffer>
|
46 | getFileList(prefix: string, extName: string): string[]
|
47 | updateFiles: () => void
|
48 | }
|
49 | ```
|
50 |
|
51 | | 键 | 类型 | 说明 |
|
52 | | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
|
53 | | appid | 属性 | 小程序/小游戏项目的 appid |
|
54 | | type | 属性 | 项目的类型,有效值 miniProgram/miniProgramPlugin/miniGame/miniGamePlugin |
|
55 | | projectPath | 属性 | 项目的路径,即 project.config.json 所在的目录 |
|
56 | | privateKey | 属性 | 私钥,在获取项目属性和上传时用于鉴权使用,在 [微信公众平台](https://mp.weixin.qq.com) 上使用小程序管理员登录后下载 |
|
57 | | attr | 异步方法 | 项目的属性,如指定了 privateKey 则会使用真实的项目属性 |
|
58 | | stat | 同步方法 | 特定目录下前缀下(prefix)文件路径 (filePath) 的 stat, 如果不存在则返回 undefined |
|
59 | | getFile | 异步方法 | 特定目录下前缀下(prefix)文件路径 (filePath) 的 Buffer |
|
60 | | getFileList | 同步方法 | 特定目录下前缀下(prefix)文件路径 (filePath) 下的文件列表 |
|
61 | | updateFile | 同步方法 | 更新项目文件 |
|
62 |
|
63 | 也可以通过指定项目路径来创建该对象
|
64 |
|
65 | ```javascript
|
66 | const ci = require('miniprogram-ci')
|
67 | const project = new ci.Project({
|
68 | appid: 'wxsomeappid',
|
69 | type: 'miniProgram',
|
70 | projectPath: 'the/project/path',
|
71 | privateKeyPath: 'the/privatekey/path',
|
72 | ignores: ['node_modules/**/*'],
|
73 | })
|
74 | ```
|
75 |
|
76 | | 键 | 类型 | 必填 | 说明 |
|
77 | | -------------- | -------- | ---- | -------------------------------------------------------------------------------------------------------- |
|
78 | | appid | string | 是 | 合法的小程序/小游戏 appid |
|
79 | | projectPath | string | 是 | 项目路径,即 project.config.json 所在的目录 |
|
80 | | privateKeyPath | string | 是 | 私钥的路径 |
|
81 | | type | string | 否 | 显示指明当前的项目类型, 默认为 miniProgram,有效值 miniProgram/miniProgramPlugin/miniGame/miniGamePlugin |
|
82 | | ignores | string[] | 否 | 指定需要排除的规则 |
|
83 |
|
84 | ### 上传
|
85 |
|
86 | ```javascript
|
87 | const ci = require('miniprogram-ci')
|
88 | ;(async () => {
|
89 | const project = new ci.Project({
|
90 | appid: 'wxsomeappid',
|
91 | type: 'miniProgram',
|
92 | projectPath: 'the/project/path',
|
93 | privateKeyPath: 'the/path/to/privatekey',
|
94 | ignores: ['node_modules/**/*'],
|
95 | })
|
96 | const uploadResult = await ci.upload({
|
97 | project,
|
98 | version: '1.1.1',
|
99 | desc: 'hello',
|
100 | setting: {
|
101 | es6: true,
|
102 | },
|
103 | onProgressUpdate: console.log,
|
104 | })
|
105 | console.log(uploadResult)
|
106 | })()
|
107 | ```
|
108 | #### 参数
|
109 |
|
110 | | 键 | 类型 | 必填 | 说明 |
|
111 | | ---------------- | -------- | ---- | ------------------------------------- |
|
112 | | project | IProject | 是 | #项目对象 |
|
113 | | version | string | 是 | 自定义版本号 |
|
114 | | desc | string | 否 | 自定义备注 |
|
115 | | setting | object | 否 | #编译设置 |
|
116 | | onProgressUpdate | function | 否 | 进度更新监听函数 |
|
117 | | robot | number | 否 | 指定使用哪一个 ci 机器人,可选值:1 ~ 30 |
|
118 |
|
119 | #### 返回
|
120 |
|
121 | | 键 | 类型 | 必填 | 说明 |
|
122 | | ---------------- | -------- | ---- | ------------------------------------- |
|
123 | | subPackageInfo | Array<{name:string, size:number}> | 否 | 小程序包信息, `name` 为 `__FULL__` 时表示整个小程序包, `name` 为 `__APP__` 时表示小程序主包,其他情况都表示分包 |
|
124 | | pluginInfo | Array<{name:string, version: string, size:number}> | 否 | 小程序插件信息 |
|
125 | | devPluginId | string | 否 | 插件开发模式下,上传版本的插件 id |
|
126 |
|
127 |
|
128 | ### 预览
|
129 |
|
130 | ```javascript
|
131 | const ci = require('miniprogram-ci')
|
132 | ;(async () => {
|
133 | const project = new ci.Project({
|
134 | appid: 'wxsomeappid',
|
135 | type: 'miniProgram',
|
136 | projectPath: 'the/project/path',
|
137 | privateKeyPath: 'the/path/to/privatekey',
|
138 | ignores: ['node_modules/**/*'],
|
139 | })
|
140 | const previewResult = await ci.preview({
|
141 | project,
|
142 | desc: 'hello',
|
143 | setting: {
|
144 | es6: true,
|
145 | },
|
146 | qrcodeFormat: 'image',
|
147 | qrcodeOutputDest: '/path/to/qrcode/file/destination.jpg',
|
148 | onProgressUpdate: console.log,
|
149 | // pagePath: 'pages/index/index', // 预览页面
|
150 | // searchQuery: 'a=1&b=2', // 预览参数 [注意!]这里的`&`字符在命令行中应写成转义字符`\&`
|
151 | })
|
152 | console.log(previewResult)
|
153 | })()
|
154 | ```
|
155 |
|
156 | #### 参数
|
157 |
|
158 | | 键 | 类型 | 必填 | 说明 |
|
159 | | ---------------- | -------- | ---- | --------------------------------------------------------------------|
|
160 | | project | IProject | 是 | #项目对象 |
|
161 | | desc | string | 否 | 自定义备注 |
|
162 | | setting | object | 否 | #编译设置 |
|
163 | | onProgressUpdate | function | 否 | 进度更新监听函数 |
|
164 | | robot | number | 否 | 指定使用哪一个 ci 机器人,可选值:1 ~ 30 |
|
165 | | qrcodeFormat | string | 否 | 返回二维码文件的格式 `"image"` 或 `"base64"`, 默认值 `"terminal"` 供调试用 |
|
166 | | qrcodeOutputDest | string | 是 | 二维码文件保存路径 |
|
167 | | pagePath: | string | 否 | 预览页面路径 |
|
168 | | searchQuery: | string | 否 | 预览页面路径启动参数 |
|
169 |
|
170 | #### 返回
|
171 |
|
172 | | 键 | 类型 | 必填 | 说明 |
|
173 | | ---------------- | -------- | ---- | ------------------------------------- |
|
174 | | subPackageInfo | Array<{name:string, size:number}> | 否 | 小程序包信息, `name` 为 `__FULL__` 时表示整个小程序包, `name` 为 `__APP__` 时表示小程序主包,其他情况都表示分包 |
|
175 | | pluginInfo | Array<{name:string, version: string, size:number}> | 否 | 小程序插件信息 |
|
176 |
|
177 |
|
178 |
|
179 | ### 拉取最近上传版本的sourceMap
|
180 |
|
181 | ```javascript
|
182 | const ci = require('miniprogram-ci')
|
183 | ;(async () => {
|
184 | const project = new ci.Project({
|
185 | appid: 'wxsomeappid',
|
186 | type: 'miniProgram',
|
187 | projectPath: 'the/project/path',
|
188 | privateKeyPath: 'the/path/to/privatekey',
|
189 | ignores: ['node_modules/**/*'],
|
190 | })
|
191 | await ci.getDevSourceMap({
|
192 | project,
|
193 | robot: 1,
|
194 | sourceMapSavePath: './sm.zip'
|
195 | })
|
196 | })()
|
197 | ```
|
198 |
|
199 | #### 入参
|
200 |
|
201 | | 键 | 类型 | 必填 | 说明 |
|
202 | | ---------------- | -------- | ---- | --------------------------------------------------------------------|
|
203 | | project | IProject | 是 | #项目对象 |
|
204 | | robot | number | 是 | 指定使用哪一个 ci 机器人,可选值:1 ~ 30 |
|
205 | | sourceMapSavePath | string | 是 | 保存的路径 |
|
206 |
|
207 |
|
208 | ### 构建npm
|
209 | 对应开发者工具[构建npm](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)功能。
|
210 |
|
211 | ```javascript
|
212 | const ci = require('miniprogram-ci')
|
213 | ;(async () => {
|
214 | const project = new ci.Project({
|
215 | appid: 'wxsomeappid',
|
216 | type: 'miniProgram',
|
217 | projectPath: 'the/project/path',
|
218 | privateKeyPath: 'the/path/to/privatekey',
|
219 | ignores: ['node_modules/**/*'],
|
220 | })
|
221 | // 在有需要的时候构建npm
|
222 | const warning = await ci.packNpm(project, {
|
223 | ignores: ['pack_npm_ignore_list'],
|
224 | reporter: (infos) => { console.log(infos) }
|
225 | })
|
226 | console.warn(warning)
|
227 | // 可对warning进行格式化
|
228 | /*
|
229 | warning.map((it, index) => {
|
230 | return `${index + 1}. ${it.msg}
|
231 | \t> code: ${it.code}
|
232 | \t@ ${it.jsPath}:${it.startLine}-${it.endLine}`
|
233 | }).join('---------------\n')
|
234 | */
|
235 | // 完成构建npm之后,可用ci.preview或者ci.upload
|
236 | })()
|
237 | ```
|
238 |
|
239 | | 参数 | 类型 | 必填 | 说明 |
|
240 | | ---------------- | -------- | ---- | --------------------------------------------------------------------|
|
241 | | project | IProject | 是 | 项目对象 |
|
242 | | options.ignores | string[] | 否 | 指定构建npm需要排除的规则 |
|
243 | | options.reporter | function | 否 | 构建回调信息 |
|
244 |
|
245 | ### 自定义 node_modules 位置的构建 npm
|
246 | 有的时候,需要被构建模块对应的 `node_modules` 可能并不在小程序项目内,所以提供了一个新的接口来支持这个需求。
|
247 | 例如有如下项目结构
|
248 |
|
249 | ```text
|
250 | ├── lib # lib目录存放要被构建的 node_modules
|
251 | │ ├── node_modules
|
252 | │ │ └── is-object
|
253 | │ └── package.json
|
254 | └── miniprogram-project # 这里是小程序项目路径
|
255 | ├── miniprogram # 我们希望最终把 miniprogram_npm 构建在 miniprogram/ 目录之下
|
256 | │ ├── app.js
|
257 | │ ├── app.json
|
258 | │ ├── app.wxss
|
259 | │ ├── pages
|
260 | │ │ ├── index
|
261 | │ │ └── logs
|
262 | │ └── sitemap.json
|
263 | └── project.config.json
|
264 | ```
|
265 |
|
266 | 于是可以这样调用
|
267 |
|
268 | ```javascript
|
269 | let packResult = await ci.packNpmManually({
|
270 | packageJsonPath: './lib/package.json',
|
271 | miniprogramNpmDistDir: './miniprogram-project/miniprogram/',
|
272 | })
|
273 |
|
274 | console.log('pack done, packResult:', packResult)
|
275 | // 输出 pack done, packResult: { miniProgramPackNum: 0, otherNpmPackNum: 1, warnList: [] }
|
276 | ```
|
277 |
|
278 | 得到的最终项目
|
279 |
|
280 | ```text
|
281 | .
|
282 | ├── lib
|
283 | │ ├── node_modules
|
284 | │ │ └── is-object
|
285 | │ └── package.json
|
286 | └── miniprogram-project
|
287 | ├── miniprogram
|
288 | │ ├── app.js
|
289 | │ ├── app.json
|
290 | │ ├── app.wxss
|
291 | │ ├── miniprogram_npm # <--- 这就是构建出来的由 lib/node_modules 里 miniprogram_npm 了
|
292 | │ ├── pages
|
293 | │ └── sitemap.json
|
294 | └── project.config.json
|
295 | ```
|
296 |
|
297 | | 参数 | 类型 | 必填 | 说明 |
|
298 | | ---------------- | -------- | ---- | --------------------------------------------------------------------|
|
299 | | options.packageJsonPath | string | 是 | 希望被构建的`node_modules` 对应的 `package.json` 的路径 |
|
300 | | options.miniprogramNpmDistDir | string | 是 | 被构建 `miniprogram_npm` 的目标位置目标位置 |
|
301 | | options.ignores | string[] | 否 | 指定需要排除的规则 |
|
302 |
|
303 |
|
304 | ### 代理
|
305 |
|
306 | miniprogram-ci 使用了 [get-proxy](https://www.npmjs.com/package/get-proxy) 模块来自动获取代理地址。
|
307 | 如果不适用`ci.proxy()`方法或者`--proxy`参数来指定代理,那么 miniprogram-ci 将会按照以下顺序去获取 https 代理地址
|
308 |
|
309 | 1. 获取环境变量中的 `HTTPS_PROXY`
|
310 | 2. 获取环境变量中的 `https_proxy`
|
311 | 3. 获取环境变量中的 `HTTP_PROXY`
|
312 | 4. 获取环境变量中的 `http_proxy`
|
313 | 5. 获取npm配置的 `https-proxy`
|
314 | 6. 获取npm配置的 `http-proxy`
|
315 | 7. 获取npm配置的 `proxy`
|
316 | 8. 把 `sercerce_wechat.com` 加入到环境变量中的 `no_proxy`,miniprogram-ci 将不通过代理发送网络请求
|
317 |
|
318 | ```javascript
|
319 | const ci = require('miniprogram-ci')
|
320 | ci.proxy('YOUR_PROXY_URL')
|
321 | ```
|
322 |
|
323 |
|
324 | ## 命令行调用
|
325 |
|
326 | ```shell script
|
327 | npm install -g miniprogram-ci
|
328 | ```
|
329 |
|
330 | ```shell script
|
331 | #help
|
332 | miniprogram-ci --help
|
333 |
|
334 | #preview
|
335 | miniprogram-ci \
|
336 | preview \
|
337 | --pp ./demo-proj/ \
|
338 | --pkp ./private.YOUR_APPID.key \
|
339 | --appid YOUR_APPID \
|
340 | --uv PACKAGE_VERSION \
|
341 | -r 1 \
|
342 | --enable-es6 true \
|
343 | --proxy YOUR_PROXY \
|
344 | --qrcode-format image \
|
345 | --qrcode-output-dest '/tmp/x.jpg' \
|
346 |
|
347 | #upload
|
348 |
|
349 | miniprogram-ci \
|
350 | upload \
|
351 | --pp ./demo-proj/ \
|
352 | --pkp ./private.YOUR_APPID.key \
|
353 | --appid YOUR_APPID \
|
354 | --uv PACKAGE_VERSION \
|
355 | -r 1 \
|
356 | --enable-es6 true \
|
357 |
|
358 | #pack-npm
|
359 | miniprogram-ci \
|
360 | pack-npm \
|
361 | --pp ./YOUR_PROJECT/ \
|
362 | --pkp ./private.YOUR_APPID.key \
|
363 | --appid YOUR_APPID \
|
364 |
|
365 | #pack-npm-manually
|
366 | miniprogram-ci \
|
367 | pack-npm-manually \
|
368 | --pack-npm-manually-package-json-path PACKAGE_JSON_PATH \
|
369 | --pack-npm-manually-miniprogram-npm-dist-dir DISTPATH
|
370 |
|
371 | #Proxy
|
372 | export HTTPS_PROXY = YOUR_PROXY_URL # 可以在shell脚本里声明临时proxy
|
373 |
|
374 | miniprogram-ci \
|
375 | upload \
|
376 | --pp ./demo-proj/ \
|
377 | --pkp ./private.YOUR_APPID.key \
|
378 | --appid YOUR_APPID \
|
379 | --uv PACKAGE_VERSION \
|
380 | -r 1 \
|
381 | --enable-es6 true \
|
382 | --proxy YOUR_PROXY_URL # 也可以使用这个参数声明proxy
|
383 |
|
384 | #get dev source map
|
385 | miniprogram-ci \
|
386 | get-dev-source-map \
|
387 | --pp ./demo-proj/ \
|
388 | --pkp ./private.YOUR_APPID.key \
|
389 | --appid YOUR_APPID \
|
390 | -r 1 \ # 获取具体哪个robot最近上传的版本的sourceMap
|
391 | --source-map-save-path ./sourcemap.zip #保存路径,推荐zip结尾,最后得到的是一个zip包
|
392 | ```
|
393 |
|
394 |
|
395 | ### 编译设置
|
396 |
|
397 | | 键 | 类型 | 说明 |
|
398 | | -------------- | ------- | ----------------------------------------------------------- |
|
399 | | es6 | boolean | 对应于微信开发者工具的 "es6 转 es5" |
|
400 | | es7 | boolean | 对应于微信开发者工具的 "增强编译" |
|
401 | | minifyJS | boolean | 上传时压缩 JS 代码 |
|
402 | | minifyWXML | boolean | 上传时压缩 WXML 代码 |
|
403 | | minifyWXSS | boolean | 上传时压缩 WXSS 代码 |
|
404 | | minify | boolean | 上传时压缩所有代码,对应于微信开发者工具的 "上传时压缩代码" |
|
405 | | codeProtect | boolean | 对应于微信开发者工具的 "上传时进行代码保护" |
|
406 | | autoPrefixWXSS | boolean | 对应于微信开发者工具的 "上传时样式自动补全" |
|
407 |
|
408 |
|
409 | ## Tips
|
410 |
|
411 | `miniprogram-ci` 从 `1.0.28` 开始支持第三方平台开发的上传和预览,调用方式与普通开发模式无异。
|
412 | 使用第三方平台开发模式时,应注意:
|
413 | - 请确保项目中存在正确的 `ext.json`
|
414 | - 密钥文件是第三方平台绑定的开发小程序 `appid` 的密钥文件
|
415 | - ip白名单是第三方平台绑定的开发小程序 `appid` 的 ip 白名单
|
416 | - 调用传入的 `appid` 是第三方平台绑定的开发小程序 `appid`
|
417 | 关于第三方平台开发模式,请参考[这里](https://developers.weixin.qq.com/miniprogram/dev/devtools/ext.html)
|