├── .browserslistrc ├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── apis │ └── request.js ├── index.js ├── officals.js └── utils │ ├── globals.js │ └── promisify.js └── test ├── basic.js ├── helpers ├── eslintrc.json └── globals.js └── lint.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | # 小程序运行环境: https://developers.weixin.qq.com/miniprogram/dev/devtools/details.html 2 | 3 | iOS 8 4 | ChromeAndroid 53 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dev 2 | .DS_Store 3 | node_modules 4 | npm-debug.log 5 | 6 | # build 7 | /cjs 8 | !/cjs/eslintrc.json 9 | /esm 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "8" 5 | 6 | cache: 7 | directoreis: 8 | - $HOME/.npm 9 | 10 | before_install: 11 | - npm install --global npm@latest 12 | - npm --version 13 | 14 | install: 15 | - npm i 16 | 17 | script: 18 | - npm test 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wxio 2 | 3 | ## License 4 | Apache-2.0 © [yelo](https://github.com/imyelo), 2017 - present 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', {modules: false, loose: true}], 4 | ], 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wxio", 3 | "version": "0.4.1", 4 | "description": "", 5 | "main": "cjs/wxio.js", 6 | "module": "esm/wxio.js", 7 | "files": [ 8 | "cjs", 9 | "esm" 10 | ], 11 | "scripts": { 12 | "start": "rollup -c -w", 13 | "clean": "rimraf ./cjs ./esm", 14 | "prebuild": "npm run clean", 15 | "build": "rollup -c", 16 | "prepublishOnly": "npm run build", 17 | "pretest": "npm run build", 18 | "test": "ava" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "imyelo/wxio" 23 | }, 24 | "author": "yelo", 25 | "license": "Apache-2.0", 26 | "bugs": { 27 | "url": "https://github.com/imyelo/wxio/issues" 28 | }, 29 | "homepage": "https://github.com/imyelo/wxio#readme", 30 | "devDependencies": { 31 | "@babel/core": "^7.0.0-rc.2", 32 | "@babel/preset-env": "^7.0.0-rc.2", 33 | "ava": "^5.1.0", 34 | "eslint": "^5.4.0", 35 | "fs-extra": "^7.0.0", 36 | "npm-run-all": "^4.1.3", 37 | "rimraf": "^2.6.2", 38 | "rollup": "^2.79.2", 39 | "rollup-plugin-babel": "^4.0.0-beta.8", 40 | "rollup-plugin-commonjs": "^9.1.5", 41 | "rollup-plugin-node-resolve": "^3.3.0", 42 | "rollup-plugin-terser": "^7.0.2", 43 | "sinon": "^6.1.5" 44 | }, 45 | "dependencies": { 46 | "map-obj": "^2.0.0", 47 | "p-cancelable": "^0.3.0", 48 | "p-queue": "^2.3.0", 49 | "p-timeout": "^2.0.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | import babel from 'rollup-plugin-babel' 4 | import mergeWith from 'lodash/mergeWith' 5 | import partialRight from 'lodash/partialRight' 6 | import { terser } from 'rollup-plugin-terser' 7 | import pkg from './package.json' 8 | 9 | const deepMerge = partialRight(mergeWith, (dest, src) => { 10 | if (Array.isArray(dest)) { 11 | return dest.concat(src) 12 | } 13 | }) 14 | 15 | const baseConfig = { 16 | input: 'src/index.js', 17 | plugins: [ 18 | resolve(), 19 | commonjs(), 20 | // compile all files (including dependencies) with `babel.config.js` 21 | babel() 22 | ] 23 | } 24 | 25 | export default [ 26 | { 27 | output: { 28 | file: pkg.main, 29 | format: 'cjs', 30 | }, 31 | }, 32 | { 33 | output: { 34 | file: 'cjs/wxio.min.js', 35 | format: 'cjs', 36 | }, 37 | plugins: [ 38 | terser({ 39 | compress: { 40 | pure_getters: true, 41 | unsafe: true, 42 | unsafe_comps: true, 43 | warnings: false, 44 | }, 45 | }), 46 | ], 47 | }, 48 | { 49 | output: { 50 | file: pkg.module, 51 | format: 'esm', 52 | }, 53 | }, 54 | ].map((config) => deepMerge({}, baseConfig, config)) 55 | -------------------------------------------------------------------------------- /src/apis/request.js: -------------------------------------------------------------------------------- 1 | import Cancelable, { CancelError } from 'p-cancelable' 2 | import Queue from 'p-queue' 3 | import timeout from 'p-timeout' 4 | import globals from '../utils/globals' 5 | import promisify from '../utils/promisify' 6 | 7 | const CONCURRENCY = 10 8 | 9 | const queue = new Queue({ concurrency: CONCURRENCY }) 10 | const request = promisify(globals.wx.request) 11 | 12 | export default Cancelable.fn((onCancel, options = {}) => { 13 | let job, isCanceled 14 | onCancel(() => { 15 | if (job) { 16 | job.cancel() 17 | } else { 18 | isCanceled = true 19 | } 20 | }) 21 | return queue.add(() => { 22 | if (isCanceled) { 23 | return Promise.reject(new CancelError()) 24 | } 25 | job = request(options) 26 | if (options.timeout) { 27 | job = timeout(job, options.timeout) 28 | } 29 | return job 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import map from 'map-obj' 2 | import globals from './utils/globals' 3 | import promisify from './utils/promisify' 4 | import { APIS, PATTERN } from './officals' 5 | import request from './apis/request' 6 | 7 | function mapPatterns (original, rules) { 8 | return map(rules, (name, { pattern, moduleName }) => { 9 | switch (pattern) { 10 | case PATTERN.SYNC: 11 | case PATTERN.EVENT: 12 | return [name, original[name]] 13 | 14 | case PATTERN.ASYNC: 15 | return [name, promisify(original[name])] 16 | 17 | case PATTERN.MODULE: 18 | return [name, function () { 19 | const instance = original[name].apply(this, arguments) 20 | return mapPatterns(instance, APIS[moduleName]) 21 | }] 22 | 23 | default: 24 | throw new Error('Invalid pattern.') 25 | } 26 | }) 27 | } 28 | 29 | const wechat = mapPatterns(globals.wx, APIS.wx) 30 | 31 | wechat.request = request 32 | 33 | export default wechat 34 | -------------------------------------------------------------------------------- /src/officals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fetch from https://mp.weixin.qq.com/debug/wxadoc/dev/api/ 3 | */ 4 | 5 | export const PATTERN = { 6 | ASYNC: Symbol('ASYNC'), 7 | EVENT: Symbol('EVENT'), 8 | SYNC: Symbol('SYNC'), 9 | MODULE: Symbol('MODULE'), 10 | } 11 | 12 | export const APIS = { 13 | wx: { 14 | // - 网络 15 | // - 发起请求 16 | request: { 17 | pattern: PATTERN.ASYNC, 18 | }, 19 | // - 上传下载 20 | uploadFile: { 21 | pattern: PATTERN.ASYNC, 22 | }, 23 | downloadFile: { 24 | pattern: PATTERN.ASYNC, 25 | }, 26 | // - WebSocket 27 | connectSocket: { 28 | pattern: PATTERN.ASYNC, 29 | }, 30 | onSocketOpen: { 31 | pattern: PATTERN.EVENT, 32 | }, 33 | onSocketError: { 34 | pattern: PATTERN.EVENT, 35 | }, 36 | sendSocketMessage: { 37 | pattern: PATTERN.ASYNC, 38 | }, 39 | onSocketMessage: { 40 | pattern: PATTERN.EVENT, 41 | }, 42 | closeSocket: { 43 | pattern: PATTERN.ASYNC, 44 | }, 45 | onSocketClose: { 46 | pattern: PATTERN.EVENT, 47 | }, 48 | // TODO 49 | // - WebSocket 50 | // - (return) SocketTask 51 | // - * 52 | 53 | // - 媒体 54 | // - 图片 55 | chooseImage: { 56 | pattern: PATTERN.ASYNC, 57 | }, 58 | chooseMessageFile: { 59 | pattern: PATTERN.ASYNC, 60 | }, 61 | previewImage: { 62 | pattern: PATTERN.ASYNC, 63 | }, 64 | getImageInfo: { 65 | pattern: PATTERN.ASYNC, 66 | }, 67 | saveImageToPhotosAlbum: { 68 | pattern: PATTERN.ASYNC, 69 | }, 70 | // - 录音 71 | startRecord: { 72 | pattern: PATTERN.ASYNC, 73 | }, 74 | stopRecord: { 75 | pattern: PATTERN.SYNC, 76 | }, 77 | // - 录音管理 78 | getRecorderManager: { 79 | pattern: PATTERN.SYNC, 80 | }, 81 | // - 音频播放控制 82 | playVoice: { 83 | pattern: PATTERN.ASYNC, 84 | }, 85 | pauseVoice: { 86 | pattern: PATTERN.SYNC, 87 | }, 88 | stopVoice: { 89 | pattern: PATTERN.SYNC, 90 | }, 91 | // - 音乐播放控制 92 | getBackgroundAudioPlayerState: { 93 | pattern: PATTERN.ASYNC, 94 | }, 95 | playBackgroundAudio: { 96 | pattern: PATTERN.ASYNC, 97 | }, 98 | pauseBackgroundAudio: { 99 | pattern: PATTERN.SYNC, 100 | }, 101 | seekBackgroundAudio: { 102 | pattern: PATTERN.ASYNC, 103 | }, 104 | stopBackgroundAudio: { 105 | pattern: PATTERN.SYNC, 106 | }, 107 | onBackgroundAudioPlay: { 108 | pattern: PATTERN.EVENT, 109 | }, 110 | onBackgroundAudioPause: { 111 | pattern: PATTERN.EVENT, 112 | }, 113 | onBackgroundAudioStop: { 114 | pattern: PATTERN.EVENT, 115 | }, 116 | // - 背景音频播放管理 117 | getBackgroundAudioManager: { 118 | pattern: PATTERN.SYNC, 119 | }, 120 | // - 音频组件控制 121 | createAudioContext: { 122 | pattern: PATTERN.SYNC, 123 | }, 124 | createInnerAudioContext: { 125 | pattern: PATTERN.SYNC, 126 | }, 127 | // - 视频 128 | chooseVideo: { 129 | pattern: PATTERN.ASYNC, 130 | }, 131 | saveVideoToPhotosAlbum: { 132 | pattern: PATTERN.ASYNC, 133 | }, 134 | // - 视频组件控制 135 | createVideoContext: { 136 | pattern: PATTERN.SYNC, 137 | }, 138 | // - 相机组件控制 139 | createCameraContext: { 140 | pattern: PATTERN.SYNC, 141 | }, 142 | // TODO 143 | // - 相机组件控制 144 | // - (return) cameraContext 145 | // - * 146 | 147 | // - 文件 148 | saveFile: { 149 | pattern: PATTERN.ASYNC, 150 | }, 151 | getFileInfo: { 152 | pattern: PATTERN.ASYNC, 153 | }, 154 | getSavedFileList: { 155 | pattern: PATTERN.ASYNC, 156 | }, 157 | getSavedFileInfo: { 158 | pattern: PATTERN.ASYNC, 159 | }, 160 | removeSavedFile: { 161 | pattern: PATTERN.ASYNC, 162 | }, 163 | openDocument: { 164 | pattern: PATTERN.ASYNC, 165 | }, 166 | getFileSystemManager: { 167 | pattern: PATTERN.MODULE, 168 | moduleName: 'FileSystemManager', 169 | }, 170 | 171 | // - 数据缓存 172 | setStorage: { 173 | pattern: PATTERN.ASYNC, 174 | }, 175 | setStorageSync: { 176 | pattern: PATTERN.SYNC, 177 | }, 178 | getStorage: { 179 | pattern: PATTERN.ASYNC, 180 | }, 181 | getStorageSync: { 182 | pattern: PATTERN.SYNC, 183 | }, 184 | getStorageInfo: { 185 | pattern: PATTERN.ASYNC, 186 | }, 187 | getStorageInfoSync: { 188 | pattern: PATTERN.SYNC, 189 | }, 190 | removeStorage: { 191 | pattern: PATTERN.ASYNC, 192 | }, 193 | removeStorageSync: { 194 | pattern: PATTERN.SYNC, 195 | }, 196 | clearStorage: { 197 | pattern: PATTERN.SYNC, 198 | }, 199 | clearStorageSync: { 200 | pattern: PATTERN.SYNC, 201 | }, 202 | 203 | // - 位置 204 | // - 获取位置 205 | getLocation: { 206 | pattern: PATTERN.ASYNC, 207 | }, 208 | chooseLocation: { 209 | pattern: PATTERN.ASYNC, 210 | }, 211 | // - 查看位置 212 | openLocation: { 213 | pattern: PATTERN.ASYNC, 214 | }, 215 | // - 地图组件控制 216 | createMapContext: { 217 | pattern: PATTERN.SYNC, 218 | }, 219 | // TODO 220 | // - 地图组件控制 221 | // - (return) mapContext 222 | // - * 223 | 224 | // - 设备 225 | // - 系统信息 226 | getSystemInfo: { 227 | pattern: PATTERN.ASYNC, 228 | }, 229 | getSystemInfoSync: { 230 | pattern: PATTERN.SYNC, 231 | }, 232 | canIUse: { 233 | pattern: PATTERN.SYNC, 234 | }, 235 | // - 网络状态 236 | getNetworkType: { 237 | pattern: PATTERN.ASYNC, 238 | }, 239 | onNetworkStatusChange: { 240 | pattern: PATTERN.EVENT, 241 | }, 242 | // - 加速度计 243 | onAccelerometerChange: { 244 | pattern: PATTERN.EVENT, 245 | }, 246 | startAccelerometer: { 247 | pattern: PATTERN.ASYNC, 248 | }, 249 | stopAccelerometer: { 250 | pattern: PATTERN.ASYNC, 251 | }, 252 | // - 罗盘 253 | onCompassChange: { 254 | pattern: PATTERN.EVENT, 255 | }, 256 | startCompass: { 257 | pattern: PATTERN.ASYNC, 258 | }, 259 | stopCompass: { 260 | pattern: PATTERN.ASYNC, 261 | }, 262 | // - 拨打电话 263 | makePhoneCall: { 264 | pattern: PATTERN.SYNC, 265 | }, 266 | // - 扫码 267 | scanCode: { 268 | pattern: PATTERN.ASYNC, 269 | }, 270 | // - 剪贴板 271 | setClipboardData: { 272 | pattern: PATTERN.ASYNC, 273 | }, 274 | getClipboardData: { 275 | pattern: PATTERN.ASYNC, 276 | }, 277 | // - 蓝牙 278 | openBluetoothAdapter: { 279 | pattern: PATTERN.ASYNC, 280 | }, 281 | closeBluetoothAdapter: { 282 | pattern: PATTERN.ASYNC, 283 | }, 284 | getBluetoothAdapterState: { 285 | pattern: PATTERN.ASYNC, 286 | }, 287 | onBluetoothAdapterStateChange: { 288 | pattern: PATTERN.EVENT, 289 | }, 290 | startBluetoothDevicesDiscovery: { 291 | pattern: PATTERN.ASYNC, 292 | }, 293 | stopBluetoothDevicesDiscovery: { 294 | pattern: PATTERN.ASYNC, 295 | }, 296 | getBluetoothDevices: { 297 | pattern: PATTERN.ASYNC, 298 | }, 299 | getConnectedBluetoothDevices: { 300 | pattern: PATTERN.ASYNC, 301 | }, 302 | onBluetoothDeviceFound: { 303 | pattern: PATTERN.EVENT, 304 | }, 305 | createBLEConnection: { 306 | pattern: PATTERN.ASYNC, 307 | }, 308 | closeBLEConnection: { 309 | pattern: PATTERN.ASYNC, 310 | }, 311 | getBLEDeviceServices: { 312 | pattern: PATTERN.ASYNC, 313 | }, 314 | getBLEDeviceCharacteristics: { 315 | pattern: PATTERN.ASYNC, 316 | }, 317 | readBLECharacteristicValue: { 318 | pattern: PATTERN.ASYNC, 319 | }, 320 | writeBLECharacteristicValue: { 321 | pattern: PATTERN.ASYNC, 322 | }, 323 | notifyBLECharacteristicValueChange: { 324 | pattern: PATTERN.ASYNC, 325 | }, 326 | onBLEConnectionStateChange: { 327 | pattern: PATTERN.EVENT, 328 | }, 329 | onBLECharacteristicValueChange: { 330 | pattern: PATTERN.EVENT, 331 | }, 332 | // - iBeacon 333 | startBeaconDiscovery: { 334 | pattern: PATTERN.ASYNC, 335 | }, 336 | stopBeaconDiscovery: { 337 | pattern: PATTERN.ASYNC, 338 | }, 339 | getBeacons: { 340 | pattern: PATTERN.ASYNC, 341 | }, 342 | onBeaconUpdate: { 343 | pattern: PATTERN.EVENT, 344 | }, 345 | onBeaconServiceChange: { 346 | pattern: PATTERN.EVENT, 347 | }, 348 | // - 屏幕亮度 349 | setScreenBrightness: { 350 | pattern: PATTERN.ASYNC, 351 | }, 352 | getScreenBrightness: { 353 | pattern: PATTERN.ASYNC, 354 | }, 355 | setKeepScreenOn: { 356 | pattern: PATTERN.ASYNC, 357 | }, 358 | // - 用户截屏事件 359 | onUserCaptureScreen: { 360 | pattern: PATTERN.EVENT, 361 | }, 362 | // - 震动 363 | vibrateLong: { 364 | pattern: PATTERN.ASYNC, 365 | }, 366 | vibrateShort: { 367 | pattern: PATTERN.ASYNC, 368 | }, 369 | // - 手机联系人 370 | addPhoneContact: { 371 | pattern: PATTERN.ASYNC, 372 | }, 373 | 374 | // - 界面 375 | // - 交互反馈 376 | showToast: { 377 | pattern: PATTERN.ASYNC, 378 | }, 379 | showLoading: { 380 | pattern: PATTERN.ASYNC, 381 | }, 382 | hideToast: { 383 | pattern: PATTERN.SYNC, 384 | }, 385 | hideLoading: { 386 | pattern: PATTERN.SYNC, 387 | }, 388 | showModal: { 389 | pattern: PATTERN.ASYNC, 390 | }, 391 | showActionSheet: { 392 | pattern: PATTERN.ASYNC, 393 | }, 394 | // - 设置导航条 395 | setNavigationBarTitle: { 396 | pattern: PATTERN.ASYNC, 397 | }, 398 | showNavigationBarLoading: { 399 | pattern: PATTERN.SYNC, 400 | }, 401 | hideNavigationBarLoading: { 402 | pattern: PATTERN.SYNC, 403 | }, 404 | setNavigationBarColor: { 405 | pattern: PATTERN.ASYNC, 406 | }, 407 | // - 设置置顶信息 408 | setTopBarText: { 409 | pattern: PATTERN.ASYNC, 410 | }, 411 | // - 导航 412 | navigateTo: { 413 | pattern: PATTERN.ASYNC, 414 | }, 415 | redirectTo: { 416 | pattern: PATTERN.ASYNC, 417 | }, 418 | switchTab: { 419 | pattern: PATTERN.ASYNC, 420 | }, 421 | navigateBack: { 422 | pattern: PATTERN.SYNC, 423 | }, 424 | reLaunch: { 425 | pattern: PATTERN.ASYNC, 426 | }, 427 | // - 动画 428 | createAnimation: { 429 | pattern: PATTERN.SYNC, 430 | }, 431 | // - 位置 432 | pageScrollTo: { 433 | pattern: PATTERN.SYNC, 434 | }, 435 | // createSelectorQuery 归类于 436 | // - 绘图 437 | createCanvasContext: { 438 | pattern: PATTERN.SYNC, 439 | }, 440 | createContext: { 441 | pattern: PATTERN.SYNC, 442 | }, 443 | drawCanvas: { 444 | pattern: PATTERN.SYNC, 445 | }, 446 | canvasToTempFilePath: { 447 | pattern: PATTERN.ASYNC, 448 | }, 449 | canvasGetImageData: { 450 | pattern: PATTERN.ASYNC, 451 | }, 452 | canvasPutImageData: { 453 | pattern: PATTERN.ASYNC, 454 | }, 455 | // - 下拉刷新 456 | startPullDownRefresh: { 457 | pattern: PATTERN.ASYNC, 458 | }, 459 | stopPullDownRefresh: { 460 | pattern: PATTERN.SYNC, 461 | }, 462 | // TODO 463 | // - 动画 464 | // - (return) animation 465 | // - * 466 | // - 绘图 467 | // - (return) canvasContext 468 | // - * 469 | 470 | // - WXML 节点信息 471 | createSelectorQuery: { 472 | pattern: PATTERN.SYNC, 473 | }, 474 | // TODO 475 | // - (return) selectorQuery 476 | // - * 477 | // - (return) nodesRef 478 | // - * 479 | 480 | // - 第三方平台 481 | getExtConfig: { 482 | pattern: PATTERN.ASYNC, 483 | }, 484 | getExtConfigSync: { 485 | pattern: PATTERN.SYNC, 486 | }, 487 | 488 | // - 开放接口 489 | // - 登录 490 | login: { 491 | pattern: PATTERN.ASYNC, 492 | }, 493 | checkSession: { 494 | pattern: PATTERN.ASYNC, 495 | }, 496 | // - 授权 497 | authorize: { 498 | pattern: PATTERN.ASYNC, 499 | }, 500 | // - 用户信息 501 | getUserInfo: { 502 | pattern: PATTERN.ASYNC, 503 | }, 504 | // - 微信支付 505 | requestPayment: { 506 | pattern: PATTERN.ASYNC, 507 | }, 508 | // - 转发 509 | showShareMenu: { 510 | pattern: PATTERN.ASYNC, 511 | }, 512 | hideShareMenu: { 513 | pattern: PATTERN.ASYNC, 514 | }, 515 | updateShareMenu: { 516 | pattern: PATTERN.ASYNC, 517 | }, 518 | getShareInfo: { 519 | pattern: PATTERN.ASYNC, 520 | }, 521 | // - 收货地址 522 | chooseAddress: { 523 | pattern: PATTERN.ASYNC, 524 | }, 525 | // - 卡券 526 | addCard: { 527 | pattern: PATTERN.ASYNC, 528 | }, 529 | openCard: { 530 | pattern: PATTERN.ASYNC, 531 | }, 532 | // - 设置 533 | openSetting: { 534 | pattern: PATTERN.ASYNC, 535 | }, 536 | getSetting: { 537 | pattern: PATTERN.ASYNC, 538 | }, 539 | // - 微信运动 540 | getWeRunData: { 541 | pattern: PATTERN.ASYNC, 542 | }, 543 | // - 打开小程序 544 | navigateToMiniProgram: { 545 | pattern: PATTERN.ASYNC, 546 | }, 547 | navigateBackMiniProgram: { 548 | pattern: PATTERN.ASYNC, 549 | }, 550 | // - 获取发票抬头 551 | chooseInvoiceTitle: { 552 | pattern: PATTERN.ASYNC, 553 | }, 554 | // - 生物认证 555 | checkIsSupportSoterAuthentication: { 556 | pattern: PATTERN.ASYNC, 557 | }, 558 | startSoterAuthentication: { 559 | pattern: PATTERN.ASYNC, 560 | }, 561 | checkIsSoterEnrolledInDevice: { 562 | pattern: PATTERN.ASYNC, 563 | }, 564 | 565 | // - 数据 566 | // - 自定义分析 567 | reportAnalytics: { 568 | pattern: PATTERN.SYNC, 569 | }, 570 | 571 | // - 调试接口 572 | // - 打开 / 关闭调试 573 | setEnableDebug: { 574 | pattern: PATTERN.ASYNC, 575 | }, 576 | }, 577 | FileSystemManager: { 578 | access: { 579 | pattern: PATTERN.ASYNC, 580 | }, 581 | accessSync: { 582 | pattern: PATTERN.SYNC, 583 | }, 584 | appendFile: { 585 | pattern: PATTERN.ASYNC, 586 | }, 587 | appendFileSync: { 588 | pattern: PATTERN.SYNC, 589 | }, 590 | copyFile: { 591 | pattern: PATTERN.ASYNC, 592 | }, 593 | copyFileSync: { 594 | pattern: PATTERN.SYNC, 595 | }, 596 | getFileInfo: { 597 | pattern: PATTERN.ASYNC, 598 | }, 599 | getSavedFileList: { 600 | pattern: PATTERN.ASYNC, 601 | }, 602 | mkdir: { 603 | pattern: PATTERN.ASYNC, 604 | }, 605 | mkdirSync: { 606 | pattern: PATTERN.SYNC, 607 | }, 608 | readdir: { 609 | pattern: PATTERN.ASYNC, 610 | }, 611 | readdirSync: { 612 | pattern: PATTERN.SYNC, 613 | }, 614 | readFile: { 615 | pattern: PATTERN.ASYNC, 616 | }, 617 | readFileSync: { 618 | pattern: PATTERN.SYNC, 619 | }, 620 | removeSavedFile: { 621 | pattern: PATTERN.ASYNC, 622 | }, 623 | rename: { 624 | pattern: PATTERN.ASYNC, 625 | }, 626 | renameSync: { 627 | pattern: PATTERN.SYNC, 628 | }, 629 | rmdir: { 630 | pattern: PATTERN.ASYNC, 631 | }, 632 | rmdirSync: { 633 | pattern: PATTERN.SYNC, 634 | }, 635 | saveFile: { 636 | pattern: PATTERN.ASYNC, 637 | }, 638 | saveFileSync: { 639 | pattern: PATTERN.SYNC, 640 | }, 641 | stat: { 642 | pattern: PATTERN.ASYNC, 643 | }, 644 | statSync: { 645 | pattern: PATTERN.SYNC, 646 | }, 647 | unlink: { 648 | pattern: PATTERN.ASYNC, 649 | }, 650 | unlinkSync: { 651 | pattern: PATTERN.SYNC, 652 | }, 653 | unzip: { 654 | pattern: PATTERN.ASYNC, 655 | }, 656 | writeFile: { 657 | pattern: PATTERN.ASYNC, 658 | }, 659 | writeFileSync: { 660 | pattern: PATTERN.SYNC, 661 | }, 662 | }, 663 | } 664 | -------------------------------------------------------------------------------- /src/utils/globals.js: -------------------------------------------------------------------------------- 1 | export default { 2 | wx, 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/promisify.js: -------------------------------------------------------------------------------- 1 | import Cancelable from 'p-cancelable' 2 | 3 | export default function promisify (api) { 4 | return (options = {}) => new Cancelable((onCancel, resolve, reject) => { 5 | const job = api({ 6 | ...options, 7 | success (...fulfilled) { 8 | resolve(...fulfilled) 9 | }, 10 | fail (error) { 11 | if (error && error.errMsg) { 12 | return reject(new Error(error.errMsg)) 13 | } 14 | if (error instanceof Error) { 15 | return reject(error) 16 | } 17 | reject(new Error(error)) 18 | }, 19 | }) 20 | if (job && typeof job.abort === 'function') { 21 | onCancel(() => job.abort()) 22 | } 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | 2 | import './helpers/globals' 3 | import test from 'ava' 4 | import sinon from 'sinon' 5 | import wxio from '..' 6 | 7 | test('async function should returns a promise', async t => { 8 | let promise = wxio.setStorage() 9 | 10 | t.true(typeof promise.then === 'function') 11 | t.true(typeof promise.catch === 'function') 12 | t.true(typeof promise.cancel === 'function') 13 | 14 | t.pass() 15 | }) 16 | 17 | test('wxio should use the Promise class in global', async t => { 18 | let spy = global.Promise.prototype.someExtraMethod = sinon.spy() 19 | 20 | let promise = wxio.request() 21 | t.true(typeof promise.someExtraMethod === 'function') 22 | 23 | promise.someExtraMethod() 24 | t.true(spy.called) 25 | 26 | t.pass() 27 | }) 28 | 29 | test('wxio.request should returns a promise', async t => { 30 | let promise = wxio.request() 31 | 32 | t.true(typeof promise.then === 'function') 33 | t.true(typeof promise.catch === 'function') 34 | t.true(typeof promise.cancel === 'function') 35 | 36 | t.pass() 37 | }) 38 | 39 | test('module function', async t => { 40 | let manager = wxio.getFileSystemManager() 41 | let promise = manager.getFileInfo() 42 | 43 | t.true(typeof promise.then === 'function') 44 | t.true(typeof promise.catch === 'function') 45 | t.true(typeof promise.cancel === 'function') 46 | 47 | let message = await promise 48 | 49 | t.is(message, 'GET_FILE_INFO_SUCCESS') 50 | 51 | t.pass() 52 | }) 53 | -------------------------------------------------------------------------------- /test/helpers/eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 5 4 | }, 5 | "rules": { 6 | "no-undef": 2 7 | }, 8 | "env": { 9 | "node": true 10 | }, 11 | "globals": { 12 | "Symbol": true, 13 | "Set": true, 14 | "WeakSet": true, 15 | "Map": true, 16 | "WeakMap": true, 17 | "Reflect": true, 18 | "Promise": true, 19 | "wx": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/helpers/globals.js: -------------------------------------------------------------------------------- 1 | global.wx = function () {} 2 | 3 | global.wx.request = function ({ success }) { 4 | setTimeout(() => { 5 | success() 6 | }, 0) 7 | } 8 | 9 | global.wx.setStorage = function () {} 10 | 11 | global.wx.getFileSystemManager = function () { 12 | return { 13 | getFileInfo ({ success }) { 14 | success('GET_FILE_INFO_SUCCESS') 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/lint.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { Linter } from 'eslint' 3 | import fs from 'fs-extra' 4 | import path from 'path' 5 | import eslintrc from './helpers/eslintrc.json' 6 | 7 | test('cjs file should be verified by linter', async t => { 8 | let source = await fs.readFileSync(path.resolve(__dirname, '../cjs/wxio.js'), 'utf8') 9 | 10 | let linter = new Linter() 11 | 12 | let messages = linter.verify(source, eslintrc) 13 | 14 | t.deepEqual(messages, [], JSON.stringify(messages, null, 2)) 15 | 16 | t.pass() 17 | }) 18 | --------------------------------------------------------------------------------