import * as chalk from "chalk"; import * as fse from "fs-extra"; import * as inquirer from "inquirer"; import * as _ from "lodash"; import * as ora from "ora"; import * as path from "path"; import * as webpack from "webpack"; import startServer from "./devServer"; import { getEntryByKey, readCustomEntry, ALL_KEY, LAST_KEY } from "./inquirer"; import { alertSuccess, isDirectory, printError, printSuccess, printWarn } from "./utils"; // import SentryUpload = require("sentry-uploader"); import Git from "./utils/Git"; import { genDevConfig, genProdConfig } from "./webpack"; // import { listFiles, removeFiles } from "./utils"; import { EZPackOptions } from "./EZPackInterface"; export function validateConfig(config: EZPackOptions) { return config; } async function selectEnv(envList: string[]): Promise { console.log(); envList.forEach((env, index) => { console.log(`${index + 1}: ${env}`); }); console.log(); let answers = await inquirer.prompt([ { type: "input", name: "num", message: "Please input the env number" } ]); if (!_.isFinite(+answers.num)) { printError("Invalid env num."); process.exit(1); } const env = envList[answers.num - 1]; if (env) { return env; } else { printError(`Env ${answers.num} not found.`); process.exit(1); return ""; } } interface BuildOption { analyse?: boolean; entry?: string; isLast?: boolean; isAll?: boolean; } export class EZPack { options: EZPackOptions; sentryRelease: string = ""; constructor(options: EZPackOptions) { this.options = options; } private getExtendWebpackConfig() { return this.options.extendWebpackConfig === undefined ? ({ options }) => Promise.resolve(options) : this.options.extendWebpackConfig; } async start() { const { webpack: webpackConfig, name, devServer } = this.options; const config = await this.getExtendWebpackConfig()({ options: genDevConfig(webpackConfig, name, devServer.port) }); devServer.name = name; startServer(config, devServer); } async build(opiton: BuildOption) { const currentRepo = new Git(); const currentBranch = await currentRepo.getCurrentBranch(); const isOnline = currentBranch === "master"; const { webpack: webpackConfig, disableEntrySelect } = this.options; let { entry } = webpackConfig; const entryLen = Object.keys(entry).length; const defaultEntryKeys: string[] = []; if (disableEntrySelect === true || opiton.isAll) { defaultEntryKeys.push(ALL_KEY); } if (opiton.isLast) { defaultEntryKeys.push(LAST_KEY); } if (opiton.entry) { defaultEntryKeys.push(opiton.entry); } // console.log(defaultEntryKeys); if (entryLen > 1) { entry = await (defaultEntryKeys.length > 0 ? getEntryByKey(defaultEntryKeys, webpackConfig.entry, webpackConfig.rootPath) : readCustomEntry(webpackConfig.entry, webpackConfig.rootPath, true)); if (!entry) { printWarn("No entry found."); return; } } this.sentryRelease = (+Date.now()).toString(); const config = await this.getExtendWebpackConfig()({ options: genProdConfig( { ...webpackConfig, entry }, this.options.name, isOnline, this.sentryRelease, opiton.analyse ) }); const spinner = ora("webpack building..."); spinner.start(); return new Promise((resolve, reject) => { webpack(config, (err, stats) => { spinner.stop(); if (err) { reject(err); } else if (stats.hasErrors()) { reject(stats.toJson().errors.join("\n")); printError(`Build error:`); } else { process.stdout.write( stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + "\n\n" ); console.log(chalk.cyan(" Build complete.\n")); // if (entryLen > 1) { // // only update auto-entry when having multiple entries // updateAutoEntry(); // } alertSuccess("Build successfully."); resolve(); } }); }); } // private async uploadSentry(isOnline: boolean) { // const sentryConfig = this.options.sentry; // if (sentryConfig) { // const distPath = this.options.webpack.outputPath; // const mapFiles = await listFiles(distPath, ".map"); // const { token, host } = sentryConfig; // const configKey = isOnline ? "online" : "uat"; // const uploader = new SentryUpload( // this.sentryRelease, // "sentry", // sentryConfig.projectName, // token[configKey], // host[configKey] // ); // await uploader.doUpload(path.join(distPath, "**/*.map"), distPath); // await removeFiles(mapFiles); // } // } async publish(opiton: BuildOption) { const { publish: publishOptions, webpack: webpackOptions, name: projectName } = this.options; const currentRepo = new Git(); const currentBranch = await currentRepo.getCurrentBranch(); const isOnline = currentBranch === "master"; let publishPath = ""; let mapPath = ""; let env = ""; if (isOnline) { const answers = await inquirer.prompt([ { type: "confirm", name: "isPublish", default: false, message: "Are you sure to publish online?" } ]); if (!answers.isPublish) { return; } env = "online"; const { mapDir, onlinePath } = publishOptions; publishPath = onlinePath; if (mapDir) { mapPath = path.join(publishPath, mapDir, "map.js"); } } else { const { uatPath, uatEnv, uatMapName, mapDir } = publishOptions; if (uatEnv && uatEnv.length > 0) { env = await selectEnv(uatEnv); publishPath = path.resolve(uatPath, env); } else { publishPath = uatPath; if (uatMapName) { env = await selectEnv(uatMapName); mapPath = path.join(uatPath, mapDir, env); } } } if (!(await isDirectory(publishPath))) { printError(`publish fail! ${publishPath} not exists.`); return; } const publishRepo = new Git(publishPath); // make sure publishPath is a clean git repository try { const stats = await publishRepo.status(); if (stats.files.length !== 0) { printError(`${publishPath} is not clean, check it.`); return; } } catch (e) { printError(`${publishPath} is not a git repository.`); return; } publishRepo.pull("origin", "master"); try { await fse.emptyDir(webpackOptions.outputPath); await this.build(opiton); } catch (e) { printError(e); return; } // remove upload map to sentry // try { // await this.uploadSentry(isOnline); // } catch (e) { // console.dir(e); // printError("Upload sentry fail."); // } if (mapPath) { await fse.move(path.join(webpackOptions.outputPath, "map.js"), mapPath, { overwrite: true }); } await fse.copy(webpackOptions.outputPath, publishPath); try { await publishRepo.add("./*"); await publishRepo.commit(`Auto publish ${projectName} ${currentBranch}.`); await publishRepo.pull("origin", "master", { "--rebase": "true" }); await publishRepo.push("master"); printSuccess(`Publish ${env} successfully.`); } catch (e) { printWarn("Build successfully, but auto publish fail."); return; } } }