init commit

This commit is contained in:
qimengjie
2018-12-12 14:52:37 +08:00
commit 88921a9ac0
28 changed files with 1074 additions and 0 deletions

11
.babelrc Normal file
View File

@@ -0,0 +1,11 @@
{
"plugins": [
["module-resolver", {
"root": ["./src"],
"alias": {}
}]
],
"presets": [
["env", {"loose": true, "modules": "commonjs"}]
]
}

96
.eslintrc.js Normal file
View File

@@ -0,0 +1,96 @@
module.exports = {
'extends': [
'airbnb-base',
'plugin:promise/recommended'
],
'parserOptions': {
'ecmaVersion': 9,
'ecmaFeatures': {
'jsx': false
},
'sourceType': 'module'
},
'env': {
'es6': true,
'node': true,
'jest': true
},
'plugins': [
'import',
'node',
'promise'
],
'rules': {
'arrow-parens': 'off',
'comma-dangle': [
'error',
'only-multiline'
],
'complexity': ['error', 10],
'func-names': 'off',
'global-require': 'off',
'handle-callback-err': [
'error',
'^(err|error)$'
],
'import/no-unresolved': [
'error',
{
'caseSensitive': true,
'commonjs': true,
'ignore': ['^[^.]']
}
],
'import/prefer-default-export': 'off',
'linebreak-style': 'off',
'no-catch-shadow': 'error',
'no-continue': 'off',
'no-div-regex': 'warn',
'no-else-return': 'off',
'no-param-reassign': 'off',
'no-plusplus': 'off',
'no-shadow': 'off',
'no-multi-assign': 'off',
'no-underscore-dangle': 'off',
'node/no-deprecated-api': 'error',
'node/process-exit-as-throw': 'error',
'object-curly-spacing': [
'error',
'never'
],
'operator-linebreak': [
'error',
'after',
{
'overrides': {
':': 'before',
'?': 'before'
}
}
],
'prefer-arrow-callback': 'off',
'prefer-destructuring': 'off',
'prefer-template': 'off',
'quote-props': [
1,
'as-needed',
{
'unnecessary': true
}
],
'semi': [
'error',
'never'
]
},
'globals': {
'window': true,
'document': true,
'App': true,
'Page': true,
'Component': true,
'Behavior': true,
'wx': true,
'getCurrentPages': true,
}
}

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
.idea
.DS_Store
package-lock.json
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
miniprogram_dist
miniprogram_dev
node_modules
coverage

16
.npmignore Normal file
View File

@@ -0,0 +1,16 @@
.idea
.DS_Store
package-lock.json
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
test
tools
docs
miniprogram_dev
node_modules
coverage

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 wechat-miniprogram
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
README.md Normal file
View File

@@ -0,0 +1 @@
# miniprogram-picker

26
gulpfile.js Normal file
View File

@@ -0,0 +1,26 @@
const gulp = require('gulp')
const clean = require('gulp-clean')
const config = require('./tools/config')
const BuildTask = require('./tools/build')
const id = require('./package.json').name || 'miniprogram-custom-component'
// build task instance
// eslint-disable-next-line no-new
new BuildTask(id, config.entry)
// clean the generated folders and files
gulp.task('clean', gulp.series(() => gulp.src(config.distPath, {read: false, allowEmpty: true}).pipe(clean()), done => {
if (config.isDev) {
return gulp.src(config.demoDist, {read: false, allowEmpty: true})
.pipe(clean())
}
return done()
}))
// watch files and build
gulp.task('watch', gulp.series(`${id}-watch`))
// build for develop
gulp.task('dev', gulp.series(`${id}-dev`))
// build for publish
gulp.task('default', gulp.series(`${id}-default`))

62
package.json Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "miniprogram-picker",
"version": "1.0.0",
"description": "",
"main": "miniprogram_dist/index.js",
"scripts": {
"dev": "gulp dev --develop",
"watch": "gulp watch --develop --watch",
"build": "gulp",
"dist": "npm run build",
"clean-dev": "gulp clean --develop",
"clean": "gulp clean",
"test": "jest ./test/* --silent --bail",
"coverage": "jest ./test/* --coverage --bail",
"lint": "eslint \"src/**/*.js\"",
"lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\""
},
"miniprogram": "miniprogram_dist",
"jest": {
"testEnvironment": "jsdom",
"testURL": "https://jest.test",
"collectCoverageFrom": [
"src/**/*.js"
],
"moduleDirectories": [
"node_modules",
"src"
]
},
"repository": {
"type": "git",
"url": "https://github.com/IceApriler/miniprogram-picker.git"
},
"author": "wechat-miniprogram",
"license": "MIT",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-module-resolver": "^3.1.1",
"babel-preset-env": "^1.7.0",
"colors": "^1.3.1",
"eslint": "^5.3.0",
"eslint-config-airbnb-base": "13.1.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^3.8.0",
"gulp": "^4.0.0",
"gulp-clean": "^0.4.0",
"gulp-if": "^2.0.2",
"gulp-install": "^1.1.0",
"gulp-less": "^3.5.0",
"gulp-rename": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"jest": "^23.5.0",
"miniprogram-simulate": "^0.1.3",
"through2": "^2.0.3",
"webpack": "^4.16.5",
"webpack-node-externals": "^1.7.2"
},
"dependencies": {}
}

22
src/index.js Normal file
View File

@@ -0,0 +1,22 @@
Component({
properties: {
prop: {
type: String,
value: 'index.properties'
},
},
data: {
flag: false,
},
lifetimes: {
attached() {
wx.getSystemInfo({
success: res => {
this.setData({
flag: true,
})
}
})
}
}
})

4
src/index.json Normal file
View File

@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

1
src/index.wxml Normal file
View File

@@ -0,0 +1 @@
<view class="index">{{prop}}-{{flag}}</view>

3
src/index.wxss Normal file
View File

@@ -0,0 +1,3 @@
.index {
color: green;
}

15
test/index.test.js Normal file
View File

@@ -0,0 +1,15 @@
const _ = require('./utils')
test('render', async () => {
const componentId = _.load('index', 'comp')
const component = _.render(componentId, {prop: 'index.test.properties'})
const parent = document.createElement('parent-wrapper')
component.attach(parent)
expect(_.match(component.dom, '<wx-view class="comp--index">index.test.properties-false</wx-view>')).toBe(true)
await _.sleep(10)
expect(_.match(component.dom, '<wx-view class="comp--index">index.test.properties-true</wx-view>')).toBe(true)
})

22
test/utils.js Normal file
View File

@@ -0,0 +1,22 @@
const path = require('path')
const simulate = require('miniprogram-simulate')
const config = require('../tools/config')
const srcPath = config.srcPath
const oldLoad = simulate.load
simulate.load = function (componentPath, ...args) {
componentPath = path.join(srcPath, componentPath)
return oldLoad(componentPath, ...args)
}
module.exports = simulate
// adjust the simulated wx api
const oldGetSystemInfoSync = global.wx.getSystemInfoSync
global.wx.getSystemInfoSync = function() {
const res = oldGetSystemInfoSync()
res.SDKVersion = '2.4.1'
return res
}

18
test/wx.test.js Normal file
View File

@@ -0,0 +1,18 @@
const _ = require('./utils')
test('wx.getSystemInfo', async () => {
wx.getSystemInfo({
success(res) {
expect(res.errMsg).toBe('getSystemInfo:ok')
},
complete(res) {
expect(res.errMsg).toBe('getSystemInfo:ok')
},
})
})
test('wx.getSystemInfoSync', async () => {
const info = wx.getSystemInfoSync()
expect(info.SDKVersion).toBe('2.4.1')
expect(info.version).toBe('6.6.3')
})

318
tools/build.js Normal file
View File

@@ -0,0 +1,318 @@
const path = require('path')
const gulp = require('gulp')
const clean = require('gulp-clean')
const less = require('gulp-less')
const rename = require('gulp-rename')
const gulpif = require('gulp-if')
const sourcemaps = require('gulp-sourcemaps')
const webpack = require('webpack')
const gulpInstall = require('gulp-install')
const config = require('./config')
const checkComponents = require('./checkcomponents')
const _ = require('./utils')
const wxssConfig = config.wxss || {}
const srcPath = config.srcPath
const distPath = config.distPath
/**
* get wxss stream
*/
function wxss(wxssFileList) {
if (!wxssFileList.length) return false
return gulp.src(wxssFileList, {cwd: srcPath, base: srcPath})
.pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.init()))
.pipe(gulpif(wxssConfig.less, less({paths: [srcPath]})))
.pipe(rename({extname: '.wxss'}))
.pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.write('./')))
.pipe(_.logger(wxssConfig.less ? 'generate' : undefined))
.pipe(gulp.dest(distPath))
}
/**
* get js stream
*/
function js(jsFileMap, scope) {
const webpackConfig = config.webpack
const webpackCallback = (err, stats) => {
if (!err) {
// eslint-disable-next-line no-console
console.log(stats.toString({
assets: true,
cached: false,
colors: true,
children: false,
errors: true,
warnings: true,
version: true,
modules: false,
publicPath: true,
}))
} else {
// eslint-disable-next-line no-console
console.log(err)
}
}
webpackConfig.entry = jsFileMap
webpackConfig.output.path = distPath
if (scope.webpackWatcher) {
scope.webpackWatcher.close()
scope.webpackWatcher = null
}
if (config.isWatch) {
scope.webpackWatcher = webpack(webpackConfig).watch({
ignored: /node_modules/,
}, webpackCallback)
} else {
webpack(webpackConfig).run(webpackCallback)
}
}
/**
* copy file
*/
function copy(copyFileList) {
if (!copyFileList.length) return false
return gulp.src(copyFileList, {cwd: srcPath, base: srcPath})
.pipe(_.logger())
.pipe(gulp.dest(distPath))
}
/**
* install packages
*/
function install() {
return gulp.series(async () => {
const demoDist = config.demoDist
const demoPackageJsonPath = path.join(demoDist, 'package.json')
const packageJson = _.readJson(path.resolve(__dirname, '../package.json'))
const dependencies = packageJson.dependencies || {}
await _.writeFile(demoPackageJsonPath, JSON.stringify({dependencies}, null, '\t')) // write dev demo's package.json
}, () => {
const demoDist = config.demoDist
const demoPackageJsonPath = path.join(demoDist, 'package.json')
return gulp.src(demoPackageJsonPath)
.pipe(gulpInstall({production: true}))
})
}
class BuildTask {
constructor(id, entry) {
if (!entry) return
this.id = id
this.entries = Array.isArray(config.entry) ? config.entry : [config.entry]
this.copyList = Array.isArray(config.copy) ? config.copy : []
this.componentListMap = {}
this.cachedComponentListMap = {}
this.init()
}
init() {
const id = this.id
/**
* clean the dist folder
*/
gulp.task(`${id}-clean-dist`, () => gulp.src(distPath, {read: false, allowEmpty: true}).pipe(clean()))
/**
* copy demo to the dev folder
*/
let isDemoExists = false
gulp.task(`${id}-demo`, gulp.series(async () => {
const demoDist = config.demoDist
isDemoExists = await _.checkFileExists(path.join(demoDist, 'project.config.json'))
}, done => {
if (!isDemoExists) {
const demoSrc = config.demoSrc
const demoDist = config.demoDist
return gulp.src('**/*', {cwd: demoSrc, base: demoSrc})
.pipe(gulp.dest(demoDist))
}
return done()
}))
/**
* install packages for dev
*/
gulp.task(`${id}-install`, install())
/**
* check custom components
*/
gulp.task(`${id}-component-check`, async () => {
const entries = this.entries
const mergeComponentListMap = {}
for (let i = 0, len = entries.length; i < len; i++) {
let entry = entries[i]
entry = path.join(srcPath, `${entry}.json`)
// eslint-disable-next-line no-await-in-loop
const newComponentListMap = await checkComponents(entry)
_.merge(mergeComponentListMap, newComponentListMap)
}
this.cachedComponentListMap = this.componentListMap
this.componentListMap = mergeComponentListMap
})
/**
* write json to the dist folder
*/
gulp.task(`${id}-component-json`, done => {
const jsonFileList = this.componentListMap.jsonFileList
if (jsonFileList && jsonFileList.length) {
return copy(this.componentListMap.jsonFileList)
}
return done()
})
/**
* copy wxml to the dist folder
*/
gulp.task(`${id}-component-wxml`, done => {
const wxmlFileList = this.componentListMap.wxmlFileList
if (wxmlFileList &&
wxmlFileList.length &&
!_.compareArray(this.cachedComponentListMap.wxmlFileList, wxmlFileList)) {
return copy(wxmlFileList)
}
return done()
})
/**
* generate wxss to the dist folder
*/
gulp.task(`${id}-component-wxss`, done => {
const wxssFileList = this.componentListMap.wxssFileList
if (wxssFileList &&
wxssFileList.length &&
!_.compareArray(this.cachedComponentListMap.wxssFileList, wxssFileList)) {
return wxss(wxssFileList, srcPath, distPath)
}
return done()
})
/**
* generate js to the dist folder
*/
gulp.task(`${id}-component-js`, done => {
const jsFileList = this.componentListMap.jsFileList
if (jsFileList &&
jsFileList.length &&
!_.compareArray(this.cachedComponentListMap.jsFileList, jsFileList)) {
js(this.componentListMap.jsFileMap, this)
}
return done()
})
/**
* copy resources to dist folder
*/
gulp.task(`${id}-copy`, gulp.parallel(done => {
const copyList = this.copyList
const copyFileList = copyList.map(dir => path.join(dir, '**/*.!(wxss)'))
if (copyFileList.length) return copy(copyFileList)
return done()
}, done => {
const copyList = this.copyList
const copyFileList = copyList.map(dir => path.join(dir, '**/*.wxss'))
if (copyFileList.length) return wxss(copyFileList, srcPath, distPath)
return done()
}))
/**
* watch json
*/
gulp.task(`${id}-watch-json`, () => gulp.watch(this.componentListMap.jsonFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-check`, gulp.parallel(`${id}-component-wxml`, `${id}-component-wxss`, `${id}-component-js`, `${id}-component-json`))))
/**
* watch wxml
*/
gulp.task(`${id}-watch-wxml`, () => {
this.cachedComponentListMap.wxmlFileList = null
return gulp.watch(this.componentListMap.wxmlFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-wxml`))
})
/**
* watch wxss
*/
gulp.task(`${id}-watch-wxss`, () => {
this.cachedComponentListMap.wxssFileList = null
return gulp.watch('**/*.wxss', {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-wxss`))
})
/**
* watch resources
*/
gulp.task(`${id}-watch-copy`, () => {
const copyList = this.copyList
const copyFileList = copyList.map(dir => path.join(dir, '**/*'))
const watchCallback = filePath => copy([filePath])
return gulp.watch(copyFileList, {cwd: srcPath, base: srcPath})
.on('change', watchCallback)
.on('add', watchCallback)
.on('unlink', watchCallback)
})
/**
* watch demo
*/
gulp.task(`${id}-watch-demo`, () => {
const demoSrc = config.demoSrc
const demoDist = config.demoDist
const watchCallback = filePath => gulp.src(filePath, {cwd: demoSrc, base: demoSrc})
.pipe(gulp.dest(demoDist))
return gulp.watch('**/*', {cwd: demoSrc, base: demoSrc})
.on('change', watchCallback)
.on('add', watchCallback)
.on('unlink', watchCallback)
})
/**
* watch installed packages
*/
gulp.task(`${id}-watch-install`, () => gulp.watch(path.resolve(__dirname, '../package.json'), install()))
/**
* build custom component
*/
gulp.task(`${id}-build`, gulp.series(`${id}-clean-dist`, `${id}-component-check`, gulp.parallel(`${id}-component-wxml`, `${id}-component-wxss`, `${id}-component-js`, `${id}-component-json`, `${id}-copy`)))
gulp.task(`${id}-watch`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`, gulp.parallel(`${id}-watch-wxml`, `${id}-watch-wxss`, `${id}-watch-json`, `${id}-watch-copy`, `${id}-watch-install`, `${id}-watch-demo`)))
gulp.task(`${id}-dev`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`))
gulp.task(`${id}-default`, gulp.series(`${id}-build`))
}
}
module.exports = BuildTask

87
tools/checkcomponents.js Normal file
View File

@@ -0,0 +1,87 @@
const path = require('path')
const _ = require('./utils')
const config = require('./config')
const srcPath = config.srcPath
/**
* get json path's info
*/
function getJsonPathInfo(jsonPath) {
const dirPath = path.dirname(jsonPath)
const fileName = path.basename(jsonPath, '.json')
const relative = path.relative(srcPath, dirPath)
const fileBase = path.join(relative, fileName)
return {
dirPath, fileName, relative, fileBase
}
}
/**
* check included components
*/
const checkProps = ['usingComponents', 'componentGenerics']
async function checkIncludedComponents(jsonPath, componentListMap) {
const json = _.readJson(jsonPath)
if (!json) throw new Error(`json is not valid: "${jsonPath}"`)
const {dirPath, fileName, fileBase} = getJsonPathInfo(jsonPath)
for (let i = 0, len = checkProps.length; i < len; i++) {
const checkProp = checkProps[i]
const checkPropValue = json[checkProp] || {}
const keys = Object.keys(checkPropValue)
for (let j = 0, jlen = keys.length; j < jlen; j++) {
const key = keys[j]
let value = typeof checkPropValue[key] === 'object' ? checkPropValue[key].default : checkPropValue[key]
if (!value) continue
value = _.transformPath(value, path.sep)
// check relative path
const componentPath = `${path.join(dirPath, value)}.json`
// eslint-disable-next-line no-await-in-loop
const isExists = await _.checkFileExists(componentPath)
if (isExists) {
// eslint-disable-next-line no-await-in-loop
await checkIncludedComponents(componentPath, componentListMap)
}
}
}
// checked
componentListMap.wxmlFileList.push(`${fileBase}.wxml`)
componentListMap.wxssFileList.push(`${fileBase}.wxss`)
componentListMap.jsonFileList.push(`${fileBase}.json`)
componentListMap.jsFileList.push(`${fileBase}.js`)
componentListMap.jsFileMap[fileBase] = `${path.join(dirPath, fileName)}.js`
}
module.exports = async function (entry) {
const componentListMap = {
wxmlFileList: [],
wxssFileList: [],
jsonFileList: [],
jsFileList: [],
jsFileMap: {}, // for webpack entry
}
const isExists = await _.checkFileExists(entry)
if (!isExists) {
const {dirPath, fileName, fileBase} = getJsonPathInfo(entry)
componentListMap.jsFileList.push(`${fileBase}.js`)
componentListMap.jsFileMap[fileBase] = `${path.join(dirPath, fileName)}.js`
return componentListMap
}
await checkIncludedComponents(entry, componentListMap)
return componentListMap
}

67
tools/config.js Normal file
View File

@@ -0,0 +1,67 @@
const path = require('path')
const webpack = require('webpack')
const nodeExternals = require('webpack-node-externals')
const isDev = process.argv.indexOf('--develop') >= 0
const isWatch = process.argv.indexOf('--watch') >= 0
const demoSrc = path.resolve(__dirname, './demo')
const demoDist = path.resolve(__dirname, '../miniprogram_dev')
const src = path.resolve(__dirname, '../src')
const dev = path.join(demoDist, 'components')
const dist = path.resolve(__dirname, '../miniprogram_dist')
module.exports = {
entry: ['index'],
isDev,
isWatch,
srcPath: src,
distPath: isDev ? dev : dist,
demoSrc,
demoDist,
wxss: {
less: false, // compile wxss with less
sourcemap: false, // source map for less
},
webpack: {
mode: 'production',
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
},
target: 'node',
externals: [nodeExternals()], // ignore node_modules
module: {
rules: [{
test: /\.js$/i,
use: [
'babel-loader',
'eslint-loader'
],
exclude: /node_modules/
}],
},
resolve: {
modules: [src, 'node_modules'],
extensions: ['.js', '.json'],
},
plugins: [
new webpack.DefinePlugin({}),
new webpack.optimize.LimitChunkCountPlugin({maxChunks: 1}),
],
optimization: {
minimize: false,
},
// devtool: 'nosources-source-map', // source map for js
performance: {
hints: 'warning',
assetFilter: assetFilename => assetFilename.endsWith('.js')
}
},
copy: ['./wxml', './wxss', './wxs', './images'], // will copy to dist
}

1
tools/demo/app.js Normal file
View File

@@ -0,0 +1 @@
App({})

11
tools/demo/app.json Normal file
View File

@@ -0,0 +1,11 @@
{
"pages":[
"pages/index/index"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}

0
tools/demo/app.wxss Normal file
View File

1
tools/demo/package.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
Page({})

View File

@@ -0,0 +1,5 @@
{
"usingComponents": {
"comp": "../../components/index"
}
}

View File

@@ -0,0 +1 @@
<comp></comp>

View File

View File

@@ -0,0 +1,37 @@
{
"description": "项目配置文件。",
"packOptions": {
"ignore": []
},
"setting": {
"urlCheck": true,
"es6": true,
"postcss": true,
"minified": true,
"newFeature": true,
"nodeModules": true
},
"compileType": "miniprogram",
"libVersion": "2.2.3",
"appid": "",
"projectname": "miniprogram-demo",
"isGameTourist": false,
"condition": {
"search": {
"current": -1,
"list": []
},
"conversation": {
"current": -1,
"list": []
},
"game": {
"currentL": -1,
"list": []
},
"miniprogram": {
"current": -1,
"list": []
}
}
}

213
tools/utils.js Normal file
View File

@@ -0,0 +1,213 @@
const fs = require('fs')
const path = require('path')
// eslint-disable-next-line no-unused-vars
const colors = require('colors')
const through = require('through2')
/**
* async function wrapper
*/
function wrap(func, scope) {
return function (...args) {
if (args.length) {
const temp = args.pop()
if (typeof temp !== 'function') {
args.push(temp)
}
}
return new Promise(function (resolve, reject) {
args.push(function (err, data) {
if (err) reject(err)
else resolve(data)
})
func.apply((scope || null), args)
})
}
}
const accessSync = wrap(fs.access)
const statSync = wrap(fs.stat)
const renameSync = wrap(fs.rename)
const mkdirSync = wrap(fs.mkdir)
const readFileSync = wrap(fs.readFile)
const writeFileSync = wrap(fs.writeFile)
/**
* transform path segment separator
*/
function transformPath(filePath, sep = '/') {
return filePath.replace(/[\\/]/g, sep)
}
/**
* check file exists
*/
async function checkFileExists(filePath) {
try {
await accessSync(filePath)
return true
} catch (err) {
return false
}
}
/**
* create folder
*/
async function recursiveMkdir(dirPath) {
const prevDirPath = path.dirname(dirPath)
try {
await accessSync(prevDirPath)
} catch (err) {
// prevDirPath is not exist
await recursiveMkdir(prevDirPath)
}
try {
await accessSync(dirPath)
const stat = await statSync(dirPath)
if (stat && !stat.isDirectory()) {
// dirPath already exists but is not a directory
await renameSync(dirPath, `${dirPath}.bak`) // rename to a file with the suffix ending in '.bak'
await mkdirSync(dirPath)
}
} catch (err) {
// dirPath is not exist
await mkdirSync(dirPath)
}
}
/**
* read json
*/
function readJson(filePath) {
try {
// eslint-disable-next-line import/no-dynamic-require
const content = require(filePath)
delete require.cache[require.resolve(filePath)]
return content
} catch (err) {
return null
}
}
/**
* read file
*/
async function readFile(filePath) {
try {
return await readFileSync(filePath, 'utf8')
} catch (err) {
// eslint-disable-next-line no-console
return console.error(err)
}
}
/**
* write file
*/
async function writeFile(filePath, data) {
try {
await recursiveMkdir(path.dirname(filePath))
return await writeFileSync(filePath, data, 'utf8')
} catch (err) {
// eslint-disable-next-line no-console
return console.error(err)
}
}
/**
* time format
*/
function format(time, reg) {
const date = typeof time === 'string' ? new Date(time) : time
const map = {}
map.yyyy = date.getFullYear()
map.yy = ('' + map.yyyy).substr(2)
map.M = date.getMonth() + 1
map.MM = (map.M < 10 ? '0' : '') + map.M
map.d = date.getDate()
map.dd = (map.d < 10 ? '0' : '') + map.d
map.H = date.getHours()
map.HH = (map.H < 10 ? '0' : '') + map.H
map.m = date.getMinutes()
map.mm = (map.m < 10 ? '0' : '') + map.m
map.s = date.getSeconds()
map.ss = (map.s < 10 ? '0' : '') + map.s
return reg.replace(/\byyyy|yy|MM|M|dd|d|HH|H|mm|m|ss|s\b/g, $1 => map[$1])
}
/**
* logger plugin
*/
function logger(action = 'copy') {
return through.obj(function (file, enc, cb) {
const type = path.extname(file.path).slice(1).toLowerCase()
// eslint-disable-next-line no-console
console.log(`[${format(new Date(), 'yyyy-MM-dd HH:mm:ss').grey}] [${action.green} ${type.green}] ${'=>'.cyan} ${file.path}`)
this.push(file)
cb()
})
}
/**
* compare arrays
*/
function compareArray(arr1, arr2) {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false
if (arr1.length !== arr2.length) return false
for (let i = 0, len = arr1.length; i < len; i++) {
if (arr1[i] !== arr2[i]) return false
}
return true
}
/**
* merge two object
*/
function merge(obj1, obj2) {
Object.keys(obj2).forEach(key => {
if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) {
obj1[key] = obj1[key].concat(obj2[key])
} else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
obj1[key] = Object.assign(obj1[key], obj2[key])
} else {
obj1[key] = obj2[key]
}
})
return obj1
}
/**
* get random id
*/
let seed = +new Date()
function getId() {
return ++seed
}
module.exports = {
wrap,
transformPath,
checkFileExists,
readJson,
readFile,
writeFile,
logger,
format,
compareArray,
merge,
getId,
}