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

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,
}