automan-cmd 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +56 -0
  3. package/bin/automan +3 -0
  4. package/bin/automan-build +3 -0
  5. package/bin/automan-config +3 -0
  6. package/bin/automan-create +3 -0
  7. package/bin/automan-publish +3 -0
  8. package/lib/automan-build.js +41 -0
  9. package/lib/automan-config.js +82 -0
  10. package/lib/automan-create.js +137 -0
  11. package/lib/automan-publish.js +331 -0
  12. package/lib/index.js +13 -0
  13. package/lib/install.js.tpl +47 -0
  14. package/lib/util.js +174 -0
  15. package/package.json +37 -0
  16. package/tpl/.babelrc +16 -0
  17. package/tpl/.browserslistrc +3 -0
  18. package/tpl/.eslintignore +2 -0
  19. package/tpl/.eslintrc.js +228 -0
  20. package/tpl/.gitignore.ejs +12 -0
  21. package/tpl/.postcssrc.js +12 -0
  22. package/tpl/README.md +1 -0
  23. package/tpl/changelog.md +1 -0
  24. package/tpl/editor/index.vue +45 -0
  25. package/tpl/icon.png +0 -0
  26. package/tpl/jsconfig.json +7 -0
  27. package/tpl/package.json.ejs +66 -0
  28. package/tpl/preview/app.vue +326 -0
  29. package/tpl/preview/attr/Data.vue +69 -0
  30. package/tpl/preview/attr/Resource.vue +79 -0
  31. package/tpl/preview/attr/com.vue +21 -0
  32. package/tpl/preview/attr/index.js +16 -0
  33. package/tpl/preview/components/Attribute.vue +365 -0
  34. package/tpl/preview/components/FitImg.vue +152 -0
  35. package/tpl/preview/components/ImgViewer.vue +80 -0
  36. package/tpl/preview/components/Loading.vue +55 -0
  37. package/tpl/preview/components/Toast.vue +111 -0
  38. package/tpl/preview/index.js +22 -0
  39. package/tpl/preview/index.tpl +13 -0
  40. package/tpl/preview/lib/ESlog.js +46 -0
  41. package/tpl/preview/lib/Util.js +57 -0
  42. package/tpl/preview/lib/fetch.js +139 -0
  43. package/tpl/preview/lib/index.js +15 -0
  44. package/tpl/preview/lib/vue/filters.js +53 -0
  45. package/tpl/preview/lib/vue/index.js +9 -0
  46. package/tpl/preview/lib/vue/mixin.js +166 -0
  47. package/tpl/preview/mint-ui/message-box/index.js +1503 -0
  48. package/tpl/preview/mint-ui/message-box/style.css +159 -0
  49. package/tpl/preview/mint-ui/popup/index.js +1046 -0
  50. package/tpl/preview/mint-ui/popup/style.css +115 -0
  51. package/tpl/preview/mint-ui/spinner/index.js +657 -0
  52. package/tpl/preview/mint-ui/spinner/style.css +227 -0
  53. package/tpl/preview/mint-ui/swipe/index.js +907 -0
  54. package/tpl/preview/mint-ui/swipe/style.css +43 -0
  55. package/tpl/preview/mint-ui/swipe-item/index.js +171 -0
  56. package/tpl/preview/mint-ui/swipe-item/style.css +1 -0
  57. package/tpl/preview/style.css +126 -0
  58. package/tpl/server.config.js +6 -0
  59. package/tpl/src/assets/css/index.scss +29 -0
  60. package/tpl/src/example.vue +165 -0
  61. package/tpl/src/index.vue.ejs +32 -0
  62. package/tpl/webpack.config.js.ejs +267 -0
  63. package/tpl/yarn.lock +6037 -0
@@ -0,0 +1,331 @@
1
+ const util = require('./util')
2
+ const build = require('./automan-build')
3
+ const commander = require('commander')
4
+ const fs = require('fs-extra')
5
+ const path = require('path')
6
+ const glob = require('glob')
7
+ const configUtil = require('./automan-config.js')
8
+ const axios = require('axios')
9
+ const chalk = require('chalk')
10
+ const FormData = require('form-data')
11
+
12
+ commander
13
+ .option('-t, --token [token]', '指定鉴权token')
14
+ .option('-r, --registry [registry]', '指定鉴权registry')
15
+ .option('--skip-build', '跳过构建')
16
+ .parse(process.argv)
17
+
18
+ let skipBuild = commander['skipBuild']
19
+ let token = commander['token']
20
+ // 增加一个host选项
21
+ let registry = commander['registry']
22
+
23
+ // const CWD = '/Users/arnan/Documents/code/ml-tpl'
24
+ const CWD = process.cwd()
25
+ let GET_TOKEN, CLI_KEY
26
+ let FIND_COMPONENT
27
+ let ADD_COMPONENT
28
+
29
+ async function initConfig() {
30
+ let host
31
+
32
+ if(registry){
33
+ host = registry
34
+ }else{
35
+ try {
36
+ host = await configUtil.getConfig('registry')
37
+ } catch (e) {}
38
+ }
39
+
40
+ host = host.replace(/(https?:\/\/[\w.\-:]+)(.*)$/, (m, p1, p2) => p1 + (p2.replace(/\/$/, '') || '/api'))
41
+ GET_TOKEN = `${host}/upload/getToken`
42
+ FIND_COMPONENT = `${host}/component/find`
43
+ ADD_COMPONENT = `${host}/component/add`
44
+ }
45
+
46
+ async function getAccessInfo() {
47
+ let key = ''
48
+ let tokenKey = 'token'
49
+ let from = 'input'
50
+
51
+ util.log('\n')
52
+ if (token) {
53
+ key = token
54
+ from = 'commander'
55
+ } else {
56
+ try {
57
+ key = await configUtil.getConfig(tokenKey)
58
+ if (key) from = 'config'
59
+ } catch (e) {}
60
+ }
61
+ if (from == 'config') {
62
+ from = !(await util.confirm(`取得奥特曼用户鉴权token【${key}】,是否使用?`)) ? 'input' : 'config'
63
+ }
64
+ if (from == 'input') {
65
+ key = await util.quiz('请输入您的奥特曼用户鉴权token (在【奥特曼后台-用户设置-秘钥】页面可获取)')
66
+ }
67
+ key = String(key || '').trim()
68
+
69
+ CLI_KEY = key
70
+ return key
71
+ }
72
+
73
+ async function genComponentJson(ns,visibilitylevel) {
74
+ let PACKAGE_JSON_PATH = path.resolve(CWD, 'package.json')
75
+ let pkg = await fs.readJson(PACKAGE_JSON_PATH)
76
+
77
+ return {
78
+ namespace: util.normalizeName(ns),
79
+ name: util.normalizeName(pkg.name),
80
+ type: +pkg.type || 0,
81
+ description: pkg.description,
82
+ version: pkg.version,
83
+ visibilitylevel: Number(visibilitylevel),
84
+ tags: (pkg.tags || []).filter(t => !isNaN(t)).map(t => ({
85
+ id: t
86
+ })),
87
+ built: pkg.built
88
+ }
89
+ }
90
+
91
+ async function ossUpload({
92
+ credentials,
93
+ COMPONENT_PATH,
94
+ package
95
+ } = {}) {
96
+ let mainFile;
97
+ await (async function () {
98
+ const componentFiles = glob.sync('{dist/**/*.*,README.md,icon.png,cover.png,changelog.md}', {
99
+ cwd: CWD,
100
+ absolute: false
101
+ })
102
+ for (let file of componentFiles) {
103
+ if (/\.tmp$/.test(file)) continue
104
+ util.log(`\n上传文件${file}`)
105
+ let filekey = `${COMPONENT_PATH.split(/[/@]/).join('/')}/${file.replace(/^dist[\/]/, '')}`
106
+ var fileurl = `${credentials.host}/${credentials.dir}${filekey}`
107
+
108
+ try {
109
+ if (/(index|editor)\.js$/.test(file)) {
110
+ if (/index\.js$/.test(file)) mainFile = fileurl
111
+ file = await setConstVar(path.resolve(CWD, file), {
112
+ namespace: package.namespace,
113
+ publicpath: `${credentials.host}/${credentials.dir}`,
114
+ version: package.version,
115
+ name: package.name
116
+ })
117
+ }
118
+
119
+ await upload(filekey, path.resolve(CWD, file), credentials)
120
+ } catch (err) {
121
+ util.logRed(err.message || err)
122
+ process.exit()
123
+ }
124
+ util.log(`上传成功: ${fileurl}`)
125
+ }
126
+ })()
127
+ return mainFile
128
+
129
+ async function setConstVar(filepath, {
130
+ publicpath,
131
+ namespace,
132
+ name,
133
+ version
134
+ }) {
135
+ let str = await fs.readFile(filepath, 'utf-8')
136
+
137
+ const tmp = filepath + '.tmp'
138
+ await fs.writeFile(tmp, str, 'utf-8')
139
+ return tmp
140
+ }
141
+
142
+ function upload(name, filepath, {
143
+ policy,
144
+ dir,
145
+ accessid,
146
+ host,
147
+ signature
148
+ }) {
149
+ let form = new FormData()
150
+ form.append('name',name)
151
+ form.append('key',dir + name)
152
+ form.append('policy',policy)
153
+ form.append('OSSAccessKeyId',accessid)
154
+ form.append('success_action_status','200')
155
+ form.append('signature',signature)
156
+ form.append(
157
+ 'file',
158
+ fs.createReadStream(filepath),
159
+ {
160
+ filename: name,
161
+ })
162
+
163
+ return axios({
164
+ url:host,
165
+ method:'post',
166
+ headers:form.getHeaders(),
167
+ data:form
168
+ }).then((res)=>{
169
+ }).catch((e)=>{
170
+ console.log(e)
171
+ })
172
+ }
173
+ }
174
+
175
+
176
+ async function checkComponent({
177
+ name,
178
+ namespace,
179
+ version
180
+ }) {
181
+
182
+ await axios({
183
+ url:FIND_COMPONENT,
184
+ headers:{
185
+ clikey:CLI_KEY
186
+ },
187
+ method:'POST',
188
+ data:{
189
+ name: `${namespace}/${name}`,
190
+ status: 'all',
191
+ version
192
+ }
193
+ }).then(({data}) => {
194
+ const list = data.data.list
195
+ if(data.code === 1){
196
+ if(list.length>0){
197
+ throw new Error(`请核对您的组件版本或者组件命名(package.json 'version'、'name'字段),组件库已有记录\n${list.map(com => `${com.name}@${com.version}`).join('\n')}`)
198
+ }
199
+ }else{
200
+ throw new Error('系统异常')
201
+ }
202
+ })
203
+ .catch(e => {
204
+ util.logRed(e.message || e)
205
+ process.exit()
206
+ })
207
+ }
208
+
209
+ async function addComponent(mainFile, package,visibilitylevel) {
210
+ await axios({
211
+ url: ADD_COMPONENT,
212
+ headers: {
213
+ clikey: CLI_KEY
214
+ },
215
+ method: 'POST',
216
+ data: {
217
+ name: `${package.namespace}/${package.name}`,
218
+ version: package.version,
219
+ desc: package.description,
220
+ type: package.type,
221
+ tags: package.tags,
222
+ path: mainFile.replace(/^http:/, 'https:'),
223
+ visibilitylevel:Number(visibilitylevel)
224
+ }
225
+ }).then(({data}) => {
226
+ if (data.code !== 1) {
227
+ throw new Error(data && data.msg || '添加组件失败')
228
+ }
229
+ })
230
+ .catch(e => {
231
+ util.logRed(e.message || e)
232
+ process.exit()
233
+ })
234
+ }
235
+
236
+ async function getCredentials() {
237
+ const credentials = await axios({
238
+ url:GET_TOKEN,
239
+ headers:{
240
+ clikey:CLI_KEY
241
+ }
242
+ })
243
+ .then(({data})=>{
244
+ if(data.code===1){
245
+ return data.data
246
+ }else{
247
+ throw new Error(data.msg || '获取oss token 失败')
248
+ }
249
+ })
250
+ .catch(e=>{
251
+ console.log(e)
252
+ process.exit()
253
+ })
254
+
255
+ return credentials
256
+ }
257
+
258
+ async function start() {
259
+ // 询问是否公开组件
260
+ // const visibilitylevel = Number(await util.confirm('是否设置该组件为所有人可见'))
261
+
262
+ //通过输入确定是否公开组件
263
+ async function pleaseAnswerMe () {
264
+ let answer
265
+ while (!(/^[YyNn]$/).test(answer = await getAnswer())) {
266
+ util.logRed('请输入Y/N(不区分大小写)来选择是否公开(官方)组件')
267
+ }
268
+
269
+ async function getAnswer () {
270
+ let _answer= (await util.quiz('请输入Y/N(不区分大小写)来选择是否公开(官方)组件')).trim()
271
+ return _answer
272
+ }
273
+
274
+ if(answer === 'Y' || answer === 'y'){
275
+ answer = true
276
+ }else{
277
+ answer = false
278
+ }
279
+
280
+ return answer
281
+ }
282
+
283
+ const visibilitylevel = await pleaseAnswerMe()
284
+
285
+ await initConfig()
286
+ await getAccessInfo()
287
+
288
+ util.logGreen('\n授权KEY')
289
+ util.log(CLI_KEY)
290
+
291
+ const credentials = await getCredentials()
292
+
293
+ if(visibilitylevel){
294
+ credentials.ns='main'
295
+ }
296
+
297
+ const package = await genComponentJson(credentials.ns,visibilitylevel)
298
+
299
+
300
+ await checkComponent(package)
301
+
302
+ if (!(skipBuild || package.built)) {
303
+ util.log('\n')
304
+ try {
305
+ await build({
306
+ namespace: package.namespace,
307
+ name: package.name,
308
+ version: package.version,
309
+ publicpath: `${credentials.host}/${credentials.dir}`
310
+ })
311
+
312
+ } catch (e) {
313
+ console.error(e)
314
+ process.exit()
315
+ }
316
+ }
317
+
318
+ let COMPONENT_PATH = `${package.namespace}/${package.name}@${package.version}`
319
+
320
+ let mainFile = await ossUpload({
321
+ credentials,
322
+ COMPONENT_PATH,
323
+ package
324
+ })
325
+
326
+ await addComponent(mainFile, package,visibilitylevel)
327
+ util.logGreen('\n发布成功')
328
+ util.logGreen(COMPONENT_PATH)
329
+ }
330
+ // start()
331
+ module.exports = start
package/lib/index.js ADDED
@@ -0,0 +1,13 @@
1
+ const process = require('process')
2
+ const commander = require('commander')
3
+ const package = require('../package.json')
4
+
5
+ commander
6
+ .version(package.version, '-v, --version')
7
+ .description(`奥特曼命令行工具 @${package.version}`)
8
+ .usage('<command> [options]')
9
+ .command('config <key> [val]', '全局配置')
10
+ .command('create <name>', '创建一个组件')
11
+ .command('build', '构建组件')
12
+ .command('publish [options]', '发布组件')
13
+ .parse(process.argv)
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {execSync, spawn} = require('child_process')
4
+ const os = require('os')
5
+
6
+ const versionDiff = (v1, v2) => {
7
+ const vers = [v1, v2].map(String).map(v => v.split('.').map(a => `0000${a}`).join('.'))
8
+ return vers[0] > vers[1] ? 1 : vers[0] == vers[1] ? 0 : -1
9
+ }
10
+
11
+ function asyncExec (cmd, args = []) {
12
+ const ls = spawn(cmd, args, { stdio: 'inherit' })
13
+ return new Promise((resolve, reject) => {
14
+ ls.on('error', reject)
15
+ ls.on('close', resolve)
16
+ })
17
+ }
18
+
19
+ const exist = (() => {
20
+ try {
21
+ execSync(os.platform() !== 'win32' ? 'type gods-pen-publish' : 'where gods-pen-publish')
22
+ return true
23
+ } catch (e) {
24
+ return false
25
+ }
26
+ })()
27
+
28
+ const outOfDate = (() => {
29
+ if (!exist) return false
30
+ let version = '0.0.0'
31
+ try {
32
+ version = execSync('gods-pen -v')
33
+ version = version.toString().trim()
34
+ } catch (e) {
35
+ }
36
+ return versionDiff(version, '1.0.6') <= 0
37
+ })()
38
+
39
+ ;(async () => {
40
+ console.log('开始执行组件发布,请按照提示操作')
41
+ if (outOfDate) {
42
+ console.log('gods-pen 工具版本过低或未安装,将为您升级到最新版本')
43
+ await asyncExec('npm', ['install', 'gods-pen-cli', '-g'])
44
+ }
45
+
46
+ await asyncExec('gods-pen', ['publish'])
47
+ })()
package/lib/util.js ADDED
@@ -0,0 +1,174 @@
1
+ const path = require('path')
2
+ const fs = require('fs-extra')
3
+ const inquirer = require('inquirer')
4
+ const chalk = require('chalk')
5
+ // const request = require('request-promise')
6
+ const axios = require('axios')
7
+ const runScript = require('runscript')
8
+ const exec = require('child_process').exec
9
+
10
+ const IS_WIN = process.platform === 'win32'
11
+ const PROCESS_REGEX = IS_WIN ? /^(.*)\s+(\d+)\s*$/ : /^\s*(\d+)\s+(.*)/
12
+
13
+ function quiz (msg, backup) {
14
+ return inquirer.prompt([{
15
+ name: 'input',
16
+ type: 'input',
17
+ default: backup,
18
+ message: msg,
19
+ }]).then(({input}) => input)
20
+ }
21
+
22
+ function log (msg, color) {
23
+ var msgStr = msg instanceof Error ? msg.message : typeof msg == 'object' ? JSON.stringify(msg, null, 2) : msg
24
+ if (color) {
25
+ msgStr = (chalk[color] || chalk['white'])(msgStr)
26
+ }
27
+ console.log(msgStr)
28
+ }
29
+
30
+ function logGreen (msg) {
31
+ log(msg, 'green')
32
+ }
33
+
34
+ function logRed (msg) {
35
+ log(msg, 'red')
36
+ }
37
+
38
+ function confirm (msg, backup = true) {
39
+ return inquirer.prompt([{
40
+ name: 'yes',
41
+ type: 'confirm',
42
+ default: backup,
43
+ message: msg,
44
+ }]).then(({yes}) => yes)
45
+ }
46
+
47
+ function select (msg, list, backup = 0) {
48
+ return inquirer.prompt([{
49
+ name: 'select',
50
+ type: 'list',
51
+ choices: list,
52
+ default: backup,
53
+ message: msg,
54
+ }]).then(({select}) => select)
55
+ }
56
+
57
+ function checks (msg, list) {
58
+ return inquirer.prompt([{
59
+ name: 'checks',
60
+ type: 'checkbox',
61
+ choices: list,
62
+ message: msg,
63
+ }]).then(({checks}) => checks)
64
+ }
65
+
66
+ function isNullOrUndefined (arg) {
67
+ return arg === null || arg === '' || arg === null
68
+ }
69
+
70
+ function asyncExec (cmd,obj) {
71
+ var ls = exec(cmd,obj,(error, stdout, stderr) => {
72
+ if (error) {
73
+ logRed(`构建异常: ${error}`)
74
+ process.exit(error.code)
75
+ }
76
+ })
77
+
78
+ return new Promise((resolve, reject) => {
79
+ ls.on('error', reject)
80
+ ls.on('close', resolve)
81
+ })
82
+ }
83
+
84
+ function normalizeName (name = '') {
85
+ return name.replace(/[-_]+(\w)/g, (m, p) => p.toUpperCase())
86
+ }
87
+
88
+ function getTags(host){
89
+ return axios.post(
90
+ `${host}/editor/tags/list`,
91
+ {
92
+ data:{
93
+ "categoryId":3,
94
+ "name":""
95
+ }
96
+ }
97
+ )
98
+ .then((data) => {
99
+ return data.data.data
100
+ })
101
+ .catch(e=>[])
102
+ }
103
+
104
+ async function findNodeProcess (filterFn) {
105
+ const command = IS_WIN ?
106
+ 'wmic Path win32_process Where "Name = \'node.exe\'" Get CommandLine,ProcessId' :
107
+ 'ps -eo "pid,args"'
108
+ const stdio = await runScript(command, { stdio: 'pipe' })
109
+ const processList = stdio.stdout.toString().split('\n')
110
+ .reduce((arr, line) => {
111
+ if (!!line && !line.includes('/bin/sh') && line.includes('node')) {
112
+ const m = line.match(PROCESS_REGEX)
113
+ if (m) {
114
+ const item = IS_WIN ? { pid: m[2], cmd: m[1] } : { pid: m[1], cmd: m[2] }
115
+ if (!filterFn || filterFn(item)) {
116
+ arr.push(item)
117
+ }
118
+ }
119
+ }
120
+ return arr
121
+ }, [])
122
+ return processList
123
+ }
124
+
125
+ function killProcess (pids, signal) {
126
+ pids = typeof pids === 'string' ? pids.split(',') : pids
127
+ pids.forEach(pid => {
128
+ try {
129
+ process.kill(pid, signal)
130
+ } catch (err) {
131
+ if (err.code !== 'ESRCH') {
132
+ throw err
133
+ }
134
+ }
135
+ })
136
+ }
137
+
138
+ async function commandExist (cmd) {
139
+ cmd = IS_WIN ? `where ${cmd}` : `type ${cmd}`
140
+ const stdio = await runScript(cmd, { stdio: 'pipe' }).catch(e => ({stderr: e.toString()}))
141
+ return !stdio.stderr
142
+ }
143
+
144
+ async function dirCheck (dir, {
145
+ msg = '%s 已存在,确定要覆盖它么?',
146
+ onlyCheck = false,
147
+ defaultRemove = true
148
+ } = {}) {
149
+ let exist = await fs.pathExists(dir)
150
+ if (!exist) return 'na'
151
+ if (onlyCheck) return 'keep'
152
+ let shouldDelete = await confirm(msg.replace(/%s/g, dir), defaultRemove)
153
+ if (!shouldDelete) return 'keep'
154
+ await fs.remove(dir)
155
+ return 'clear'
156
+ }
157
+
158
+ module.exports = {
159
+ quiz,
160
+ getTags,
161
+ confirm,
162
+ select,
163
+ checks,
164
+ log,
165
+ asyncExec,
166
+ logGreen,
167
+ logRed,
168
+ isNullOrUndefined,
169
+ normalizeName,
170
+ findNodeProcess,
171
+ killProcess,
172
+ commandExist,
173
+ dirCheck
174
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "automan-cmd",
3
+ "version": "2.1.5",
4
+ "description": "奥特曼脚手架",
5
+ "bin": {
6
+ "automan": "bin/automan",
7
+ "automan-build": "bin/automan-build",
8
+ "automan-create": "bin/automan-create",
9
+ "automan-config": "bin/automan-config",
10
+ "automan-publish": "bin/automan-publish"
11
+ },
12
+ "repository": {},
13
+ "engines": {
14
+ "node": ">=7.6.0"
15
+ },
16
+ "keywords": [],
17
+ "author": "GreatJiang",
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "archiver": "^3.1.1",
21
+ "axios": "^0.21.4",
22
+ "chalk": "^1.1.3",
23
+ "commander": "^2.20.3",
24
+ "ejs": "^2.7.4",
25
+ "form-data": "^4.0.0",
26
+ "fs-extra": "^6.0.1",
27
+ "glob": "^7.2.3",
28
+ "image-size": "^0.8.3",
29
+ "inquirer": "^6.5.2",
30
+ "open": "^7.4.2",
31
+ "ora": "^3.4.0",
32
+ "qs": "^6.12.3",
33
+ "rimraf": "^2.7.1",
34
+ "runscript": "^1.6.0",
35
+ "unzip": "^0.1.11"
36
+ }
37
+ }
package/tpl/.babelrc ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "presets": [
3
+ ["@babel/preset-env",{
4
+ "modules": false,
5
+ "bugfixes": true,
6
+ "targets": "android >= 4.4, ios >= 8, > 1%"
7
+ }]
8
+ ],
9
+ "plugins": [
10
+ "@babel/plugin-syntax-dynamic-import",
11
+ "@babel/plugin-proposal-optional-chaining",
12
+ "@babel/plugin-proposal-numeric-separator",
13
+ "@babel/plugin-proposal-class-properties",
14
+ "@babel/plugin-proposal-nullish-coalescing-operator"
15
+ ]
16
+ }
@@ -0,0 +1,3 @@
1
+ > 0.5%
2
+ Android > 4.3
3
+ iOS > 7
@@ -0,0 +1,2 @@
1
+ preview/mint-ui/
2
+ preview/lib/