From 88921a9ac045c212393cb1fb8fd9d5b72e07019e Mon Sep 17 00:00:00 2001 From: qimengjie Date: Wed, 12 Dec 2018 14:52:37 +0800 Subject: [PATCH] init commit --- .babelrc | 11 ++ .eslintrc.js | 96 +++++++++ .gitignore | 14 ++ .npmignore | 16 ++ LICENSE | 21 ++ README.md | 1 + gulpfile.js | 26 +++ package.json | 62 ++++++ src/index.js | 22 +++ src/index.json | 4 + src/index.wxml | 1 + src/index.wxss | 3 + test/index.test.js | 15 ++ test/utils.js | 22 +++ test/wx.test.js | 18 ++ tools/build.js | 318 ++++++++++++++++++++++++++++++ tools/checkcomponents.js | 87 ++++++++ tools/config.js | 67 +++++++ tools/demo/app.js | 1 + tools/demo/app.json | 11 ++ tools/demo/app.wxss | 0 tools/demo/package.json | 1 + tools/demo/pages/index/index.js | 1 + tools/demo/pages/index/index.json | 5 + tools/demo/pages/index/index.wxml | 1 + tools/demo/pages/index/index.wxss | 0 tools/demo/project.config.json | 37 ++++ tools/utils.js | 213 ++++++++++++++++++++ 28 files changed, 1074 insertions(+) create mode 100644 .babelrc create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 src/index.js create mode 100644 src/index.json create mode 100644 src/index.wxml create mode 100644 src/index.wxss create mode 100644 test/index.test.js create mode 100644 test/utils.js create mode 100644 test/wx.test.js create mode 100644 tools/build.js create mode 100644 tools/checkcomponents.js create mode 100644 tools/config.js create mode 100644 tools/demo/app.js create mode 100644 tools/demo/app.json create mode 100644 tools/demo/app.wxss create mode 100644 tools/demo/package.json create mode 100644 tools/demo/pages/index/index.js create mode 100644 tools/demo/pages/index/index.json create mode 100644 tools/demo/pages/index/index.wxml create mode 100644 tools/demo/pages/index/index.wxss create mode 100644 tools/demo/project.config.json create mode 100644 tools/utils.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..0b40319 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "plugins": [ + ["module-resolver", { + "root": ["./src"], + "alias": {} + }] + ], + "presets": [ + ["env", {"loose": true, "modules": "commonjs"}] + ] +} \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..1f42ef5 --- /dev/null +++ b/.eslintrc.js @@ -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, + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..abb2112 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..650e4a7 --- /dev/null +++ b/.npmignore @@ -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 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e78bcd8 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bdf4201 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# miniprogram-picker diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..3ecaab5 --- /dev/null +++ b/gulpfile.js @@ -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`)) diff --git a/package.json b/package.json new file mode 100644 index 0000000..440f004 --- /dev/null +++ b/package.json @@ -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": {} +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..933dd81 --- /dev/null +++ b/src/index.js @@ -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, + }) + } + }) + } + } +}) diff --git a/src/index.json b/src/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/src/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/src/index.wxml b/src/index.wxml new file mode 100644 index 0000000..7d4c23c --- /dev/null +++ b/src/index.wxml @@ -0,0 +1 @@ +{{prop}}-{{flag}} diff --git a/src/index.wxss b/src/index.wxss new file mode 100644 index 0000000..38bf189 --- /dev/null +++ b/src/index.wxss @@ -0,0 +1,3 @@ +.index { + color: green; +} diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..1e787bf --- /dev/null +++ b/test/index.test.js @@ -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, 'index.test.properties-false')).toBe(true) + + await _.sleep(10) + + expect(_.match(component.dom, 'index.test.properties-true')).toBe(true) +}) diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..a56c560 --- /dev/null +++ b/test/utils.js @@ -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 +} diff --git a/test/wx.test.js b/test/wx.test.js new file mode 100644 index 0000000..0209e56 --- /dev/null +++ b/test/wx.test.js @@ -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') +}) diff --git a/tools/build.js b/tools/build.js new file mode 100644 index 0000000..6a6634c --- /dev/null +++ b/tools/build.js @@ -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 diff --git a/tools/checkcomponents.js b/tools/checkcomponents.js new file mode 100644 index 0000000..53f3766 --- /dev/null +++ b/tools/checkcomponents.js @@ -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 +} diff --git a/tools/config.js b/tools/config.js new file mode 100644 index 0000000..da089f2 --- /dev/null +++ b/tools/config.js @@ -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 +} diff --git a/tools/demo/app.js b/tools/demo/app.js new file mode 100644 index 0000000..6241c06 --- /dev/null +++ b/tools/demo/app.js @@ -0,0 +1 @@ +App({}) diff --git a/tools/demo/app.json b/tools/demo/app.json new file mode 100644 index 0000000..4bc8a6f --- /dev/null +++ b/tools/demo/app.json @@ -0,0 +1,11 @@ +{ + "pages":[ + "pages/index/index" + ], + "window":{ + "backgroundTextStyle":"light", + "navigationBarBackgroundColor": "#fff", + "navigationBarTitleText": "WeChat", + "navigationBarTextStyle":"black" + } +} diff --git a/tools/demo/app.wxss b/tools/demo/app.wxss new file mode 100644 index 0000000..e69de29 diff --git a/tools/demo/package.json b/tools/demo/package.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/tools/demo/package.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tools/demo/pages/index/index.js b/tools/demo/pages/index/index.js new file mode 100644 index 0000000..ba76804 --- /dev/null +++ b/tools/demo/pages/index/index.js @@ -0,0 +1 @@ +Page({}) diff --git a/tools/demo/pages/index/index.json b/tools/demo/pages/index/index.json new file mode 100644 index 0000000..fda2d24 --- /dev/null +++ b/tools/demo/pages/index/index.json @@ -0,0 +1,5 @@ +{ + "usingComponents": { + "comp": "../../components/index" + } +} \ No newline at end of file diff --git a/tools/demo/pages/index/index.wxml b/tools/demo/pages/index/index.wxml new file mode 100644 index 0000000..ea63c1e --- /dev/null +++ b/tools/demo/pages/index/index.wxml @@ -0,0 +1 @@ + diff --git a/tools/demo/pages/index/index.wxss b/tools/demo/pages/index/index.wxss new file mode 100644 index 0000000..e69de29 diff --git a/tools/demo/project.config.json b/tools/demo/project.config.json new file mode 100644 index 0000000..2bdb6ce --- /dev/null +++ b/tools/demo/project.config.json @@ -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": [] + } + } +} \ No newline at end of file diff --git a/tools/utils.js b/tools/utils.js new file mode 100644 index 0000000..360a461 --- /dev/null +++ b/tools/utils.js @@ -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, +}