UNPKG

quickly-build

Version:

mistong web local build script.

482 lines (391 loc) 14.8 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var fs = require('fs'); var path = require('path'); var simpleGit = require('simple-git'); var rimraf = require('rimraf'); var child_process = require('child_process'); var readline = require('readline'); var chalk = require('chalk'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var simpleGit__default = /*#__PURE__*/_interopDefaultLegacy(simpleGit); var rimraf__default = /*#__PURE__*/_interopDefaultLegacy(rimraf); var readline__default = /*#__PURE__*/_interopDefaultLegacy(readline); var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } const consoleRed = str => console.log(chalk__default['default'].red(str)); const consoleGreen = str => console.log(chalk__default['default'].green(str)); const consoleYellow = str => console.log(chalk__default['default'].blue(str)); const bashCmd = bash => new Promise(res => { var _job$stdout, _job$stderr; const job = child_process.exec(bash, { cwd: process.cwd() }); console.log(process.cwd()); (_job$stdout = job.stdout) === null || _job$stdout === void 0 ? void 0 : _job$stdout.on('data', data => { console.info(`stdout: ${data}`); }); (_job$stderr = job.stderr) === null || _job$stderr === void 0 ? void 0 : _job$stderr.on('data', /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (data) { // console.log(data); if (/Error/i.test(data)) { const arr = data.match(/Error:(.|\s|\S)*?(?<=(}|,))/gi); if (arr) { arr.forEach(item => consoleRed(item)); } } if (/stack/.test(data)) { const arr = data.match(/stack:(.|\s|\S)*?(?<=(}|,))/g); if (arr) { arr.forEach(item => consoleRed(item)); } } else { consoleYellow(data); } }); return function (_x) { return _ref.apply(this, arguments); }; }()); job.on('close', code => { res(undefined); }); }); // 交互式选择 const readlineJob = txt => new Promise((reslove, reject) => { const rl = readline__default['default'].createInterface({ input: process.stdin, output: process.stdout, prompt: txt }); rl.prompt(); rl.on('line', line => { const res = line.trim(); reslove({ env: res, rl }); // rl.close(); }).on('close', () => { process.exit(0); }); }); // promise错误重试 const retry = (fnc, time = 1, retryErrorCb) => { return new Promise((resolve, reject) => { let index = 0; const job = () => { Promise.resolve(fnc(index)).then(res => { resolve(res); }).catch(err => { index++; retryErrorCb && retryErrorCb(err, index); index < time ? job() : reject(err); }); }; job(); }); }; const git = simpleGit__default['default'](); // 检查当前打包分支的文件,再切换回分支 const checkAndBack = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (currentBranch, distBranchName) { const _yield$git$status = yield git.status(), files = _yield$git$status.files; const filePath = []; for (let i = 0; i < files.length; i++) { filePath.push(files[i].path); } if (filePath.length) { yield git.add(filePath); yield git.commit(`build: add files`); } yield git.checkout(currentBranch); if (distBranchName) { yield git.branch(['-D', distBranchName]); } consoleGreen(`✔️ checkout back: '${currentBranch}'` + (distBranchName ? ` and remove dist branch: '${distBranchName}'` : '')); }); return function checkAndBack(_x, _x2) { return _ref.apply(this, arguments); }; }(); // 修改package.json文件 const writePackageJson = () => { // npm install 时执行的钩子 需要排除 const npmHooksScripts = ['preinstall', 'postinstall', 'prepare', 'install']; const dataStr = fs__default['default'].readFileSync(path__default['default'].join(__dirname, '../temp/package.hbs')); const originalDataStr = fs__default['default'].readFileSync(path__default['default'].join(process.cwd(), './package.json')); const data = JSON.parse(dataStr.toString()); const originalData = JSON.parse(originalDataStr.toString()); const writeData = _objectSpread2(_objectSpread2(_objectSpread2({}, originalData), data), {}, { scripts: _objectSpread2(_objectSpread2({}, originalData.scripts), data.scripts) }); delete writeData.husky; npmHooksScripts.forEach(item => { if (writeData.scripts[item]) { delete writeData.scripts[item]; } }); const writeDataStr = JSON.stringify(writeData, null, 2); fs__default['default'].writeFileSync(path__default['default'].join(process.cwd(), './package.json'), writeDataStr); }; // 命令合计 const gitCommitList = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator(function* (buildEnv, retryTimes, onJobError) { const _yield$git$branch = yield git.branch({}), all = _yield$git$branch.all, currentBranch = _yield$git$branch.current; buildEnv = buildEnv.trim(); // 检查:不能在含有dist的分支进行打包 if (/_dist_/.test(currentBranch)) { console.warn(`⚠️ ${currentBranch} build not allow.`); onJobError(new Error(`${currentBranch} build not allowed`)); return currentBranch; } // 检查当前版本的名字 const nowName = /^trunk_/.test(currentBranch) && currentBranch.replace('trunk_', '') || /^release_/.test(currentBranch) && currentBranch.replace('release_', '') || currentBranch; const distName = `${buildEnv === 'sit' || /test/.test(buildEnv) ? 'trunk_dist_' : buildEnv === 'pre' ? 'release_pre_dist_' : 'release_dist_'}${nowName}`; const find = all.find(item => item === distName); // 删除当前打包分支 重新创建 if (find) { yield git.branch(['-D', find]); } yield git.checkout(['-b', distName]); consoleGreen(`✔️ switch to ${distName} .`); // 读取package.json模板 写入到文件 writePackageJson(); // 读取.gitignore模板 写入到文件 const igonreData = fs__default['default'].readFileSync(path__default['default'].join(__dirname, '../temp/ignore.hbs')); fs__default['default'].writeFileSync(path__default['default'].join(process.cwd(), './.gitignore'), igonreData); // 删除package-lock.json try { const lockPath = path__default['default'].join(process.cwd(), 'package-lock.json'); if (fs__default['default'].existsSync(lockPath)) { yield new Promise((res, rej) => { rimraf__default['default'](lockPath, err => { if (err) consoleRed(err); res(undefined); }); }); } } catch (error) {} // 进行提交 yield git.add('.'); try { yield git.commit(`build: ${currentBranch} and auto push`); } catch (e) { onJobError(e); yield checkAndBack(currentBranch, distName); return distName; } try { // 推送失败尝试3次 yield retry( /*#__PURE__*/function () { var _ref3 = _asyncToGenerator(function* (retryTime) { try { yield git.push('origin', distName, ['-f']); // 强制推送 打包分支只需要最新的dist输出 } catch (e) { yield new Promise((resolve, reject) => { setTimeout(resolve, 500); }); throw new Error('retry'); } }); return function (_x6) { return _ref3.apply(this, arguments); }; }(), retryTimes, (err, num) => { consoleRed(`⚠️ push fail times: ${num}`); }); consoleGreen('✔️ commit pushed success.'); } catch (e) { consoleRed('❌ pushed fail, process exit.'); onJobError(e); // 推送失败 不删除 dist分支 yield checkAndBack(currentBranch); // process.exit(1); return distName; } // 切回当前分支 yield checkAndBack(currentBranch, distName); return distName; }); return function gitCommitList(_x3, _x4, _x5) { return _ref2.apply(this, arguments); }; }(); // 服务器node版本太低 , 本地打包后切换到另一个 xxx_dist_xxx分支,秒发布 const buildJob = /*#__PURE__*/function () { var _ref4 = _asyncToGenerator(function* ({ buildEnv, getBuildBashWithEnv = env => `npm run build:${env}`, pushRetryTimes = 3, outPutDir = './dist', onJobError = v => {} }) { consoleGreen(`start build env: '${buildEnv}' .`); // 检查是否有未提交的内容 const statusRes = yield git.status(); const files = statusRes.files; let notCommit = false; for (let i = 0; i < files.length; i++) { const path = files[i].path; consoleRed(`❌ has file not commited, path: ${path}.`); notCommit = true; } if (notCommit) { onJobError(new Error('❌ has file not commited')); return ''; } // 编译 const bash = getBuildBashWithEnv(buildEnv); yield bashCmd(bash); try { fs__default['default'].statSync(path__default['default'].join(process.cwd(), outPutDir)); consoleGreen('✔️ 编译完成'); } catch (e) { onJobError(e); consoleRed('❌ build fail.'); return ''; } const distName = yield gitCommitList(buildEnv, pushRetryTimes, onJobError); try { yield new Promise((res, rej) => { rimraf__default['default'](path__default['default'].join(process.cwd(), outPutDir), err => { if (err) rej(err); res(undefined); }); }); } catch (error) {} consoleGreen(`✔️ ${outPutDir} removed.`); return distName; }); return function buildJob(_x7) { return _ref4.apply(this, arguments); }; }(); class QuickBuild { constructor(config) { var _this = this; this.quickBuildConfig = void 0; this.start = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* (env) { let buildEnv; const argv2 = process.argv[2]; const _this$quickBuildConfi = _this.quickBuildConfig, onJobSuccess = _this$quickBuildConfi.onJobSuccess, onJobError = _this$quickBuildConfi.onJobError; const st = Date.now(); const nextTickDoJob = (v, env) => { process.nextTick(() => { const buildTime = Date.now() - st; const min = Math.floor(buildTime / 60000); const str = min > 0 ? `${min} min ${((buildTime - min * 60000) / 1000).toFixed(1)}s` : `${(buildTime / 1000).toFixed(1)}s`; consoleGreen(`✔️ build job use time: ${str}...`); if (onJobSuccess) { onJobSuccess(v, env); } }); }; if (!env) { const _this$quickBuildConfi2 = _this.quickBuildConfig.environments, environments = _this$quickBuildConfi2 === void 0 ? ['sit', 'pre', 'prod'] : _this$quickBuildConfi2; if (argv2) { // 从node argv取参数 if (environments.includes(argv2)) { buildEnv = argv2; const distName = yield buildJob(_objectSpread2(_objectSpread2({}, _this.quickBuildConfig), {}, { buildEnv })); nextTickDoJob(null, buildEnv); return distName; } else { return consoleRed(`${argv2} not in config.environments`); } } const _yield$readlineJob = yield readlineJob(`enter a build env [ '${environments.join("' | '")}' ] > `), res = _yield$readlineJob.env, rlRes = _yield$readlineJob.rl; buildEnv = res; const distName = yield buildJob(_objectSpread2(_objectSpread2({}, _this.quickBuildConfig), {}, { buildEnv, onJobError })); nextTickDoJob(rlRes, buildEnv); return distName; } else { buildEnv = env; const distName = yield buildJob(_objectSpread2(_objectSpread2({}, _this.quickBuildConfig), {}, { buildEnv })); nextTickDoJob(null, buildEnv); return distName; } }); return function (_x) { return _ref.apply(this, arguments); }; }(); const _environments = config.environments; if (_environments) { const check = _environments.filter(item => typeof item !== "string"); if (check.length) { throw new Error(`config.environments must be Array<string> !`); } } this.quickBuildConfig = config; } } var main = { QuickBuild, git }; exports.QuickBuild = QuickBuild; exports.default = main;