UNPKG

7.14 kBPlain TextView Raw
1import * as chalk from "chalk";
2import * as fse from "fs-extra";
3import * as inquirer from "inquirer";
4import * as _ from "lodash";
5import * as ora from "ora";
6import * as path from "path";
7import * as webpack from "webpack";
8
9import startServer from "./devServer";
10import { getEntryByKey, readCustomEntry, ALL_KEY, LAST_KEY } from "./inquirer";
11import { alertSuccess, isDirectory, printError, printSuccess, printWarn } from "./utils";
12// import SentryUpload = require("sentry-uploader");
13import Git from "./utils/Git";
14import { genDevConfig, genProdConfig } from "./webpack";
15// import { listFiles, removeFiles } from "./utils";
16
17import { EZPackOptions } from "./EZPackInterface";
18
19export function validateConfig(config: EZPackOptions) {
20 return config;
21}
22
23async function selectEnv(envList: string[]): Promise<string> {
24 console.log();
25 envList.forEach((env, index) => {
26 console.log(`${index + 1}: ${env}`);
27 });
28 console.log();
29 let answers = await inquirer.prompt([
30 {
31 type: "input",
32 name: "num",
33 message: "Please input the env number"
34 }
35 ]);
36 if (!_.isFinite(+answers.num)) {
37 printError("Invalid env num.");
38 process.exit(1);
39 }
40
41 const env = envList[answers.num - 1];
42 if (env) {
43 return env;
44 } else {
45 printError(`Env ${answers.num} not found.`);
46 process.exit(1);
47 return "";
48 }
49}
50
51interface BuildOption {
52 analyse?: boolean;
53 entry?: string;
54 isLast?: boolean;
55 isAll?: boolean;
56}
57
58export class EZPack {
59 options: EZPackOptions;
60 sentryRelease: string = "";
61
62 constructor(options: EZPackOptions) {
63 this.options = options;
64 }
65
66 private getExtendWebpackConfig() {
67 return this.options.extendWebpackConfig === undefined
68 ? ({ options }) => Promise.resolve(options)
69 : this.options.extendWebpackConfig;
70 }
71
72 async start() {
73 const { webpack: webpackConfig, name, devServer } = this.options;
74
75 const config = await this.getExtendWebpackConfig()({
76 options: genDevConfig(webpackConfig, name, devServer.port)
77 });
78 devServer.name = name;
79 startServer(config, devServer);
80 }
81
82 async build(opiton: BuildOption) {
83 const currentRepo = new Git();
84 const currentBranch = await currentRepo.getCurrentBranch();
85 const isOnline = currentBranch === "master";
86 const { webpack: webpackConfig, disableEntrySelect } = this.options;
87 let { entry } = webpackConfig;
88 const entryLen = Object.keys(entry).length;
89
90 const defaultEntryKeys: string[] = [];
91 if (disableEntrySelect === true || opiton.isAll) {
92 defaultEntryKeys.push(ALL_KEY);
93 }
94 if (opiton.isLast) {
95 defaultEntryKeys.push(LAST_KEY);
96 }
97 if (opiton.entry) {
98 defaultEntryKeys.push(opiton.entry);
99 }
100 // console.log(defaultEntryKeys);
101 if (entryLen > 1) {
102 entry = await (defaultEntryKeys.length > 0
103 ? getEntryByKey(defaultEntryKeys, webpackConfig.entry, webpackConfig.rootPath)
104 : readCustomEntry(webpackConfig.entry, webpackConfig.rootPath, true));
105 if (!entry) {
106 printWarn("No entry found.");
107 return;
108 }
109 }
110 this.sentryRelease = (+Date.now()).toString();
111
112 const config = await this.getExtendWebpackConfig()({
113 options: genProdConfig(
114 {
115 ...webpackConfig,
116 entry
117 },
118 this.options.name,
119 isOnline,
120 this.sentryRelease,
121 opiton.analyse
122 )
123 });
124
125 const spinner = ora("webpack building...");
126 spinner.start();
127
128 return new Promise((resolve, reject) => {
129 webpack(config, (err, stats) => {
130 spinner.stop();
131 if (err) {
132 reject(err);
133 } else if (stats.hasErrors()) {
134 reject(stats.toJson().errors.join("\n"));
135 printError(`Build error:`);
136 } else {
137 process.stdout.write(
138 stats.toString({
139 colors: true,
140 modules: false,
141 children: false,
142 chunks: false,
143 chunkModules: false
144 }) + "\n\n"
145 );
146 console.log(chalk.cyan(" Build complete.\n"));
147 // if (entryLen > 1) {
148 // // only update auto-entry when having multiple entries
149 // updateAutoEntry();
150 // }
151 alertSuccess("Build successfully.");
152 resolve();
153 }
154 });
155 });
156 }
157
158 // private async uploadSentry(isOnline: boolean) {
159 // const sentryConfig = this.options.sentry;
160 // if (sentryConfig) {
161 // const distPath = this.options.webpack.outputPath;
162 // const mapFiles = await listFiles(distPath, ".map");
163
164 // const { token, host } = sentryConfig;
165 // const configKey = isOnline ? "online" : "uat";
166 // const uploader = new SentryUpload(
167 // this.sentryRelease,
168 // "sentry",
169 // sentryConfig.projectName,
170 // token[configKey],
171 // host[configKey]
172 // );
173
174 // await uploader.doUpload(path.join(distPath, "**/*.map"), distPath);
175 // await removeFiles(mapFiles);
176 // }
177 // }
178
179 async publish(opiton: BuildOption) {
180 const { publish: publishOptions, webpack: webpackOptions, name: projectName } = this.options;
181
182 const currentRepo = new Git();
183 const currentBranch = await currentRepo.getCurrentBranch();
184 const isOnline = currentBranch === "master";
185
186 let publishPath = "";
187 let mapPath = "";
188 let env = "";
189 if (isOnline) {
190 const answers = await inquirer.prompt([
191 {
192 type: "confirm",
193 name: "isPublish",
194 default: false,
195 message: "Are you sure to publish online?"
196 }
197 ]);
198
199 if (!answers.isPublish) {
200 return;
201 }
202
203 env = "online";
204
205 const { mapDir, onlinePath } = publishOptions;
206
207 publishPath = onlinePath;
208 if (mapDir) {
209 mapPath = path.join(publishPath, mapDir, "map.js");
210 }
211 } else {
212 const { uatPath, uatEnv, uatMapName, mapDir } = publishOptions;
213
214 if (uatEnv && uatEnv.length > 0) {
215 env = await selectEnv(uatEnv);
216 publishPath = path.resolve(uatPath, env);
217 } else {
218 publishPath = uatPath;
219
220 if (uatMapName) {
221 env = await selectEnv(uatMapName);
222 mapPath = path.join(uatPath, mapDir, env);
223 }
224 }
225 }
226
227 if (!(await isDirectory(publishPath))) {
228 printError(`publish fail! ${publishPath} not exists.`);
229 return;
230 }
231
232 const publishRepo = new Git(publishPath);
233 // make sure publishPath is a clean git repository
234 try {
235 const stats = await publishRepo.status();
236 if (stats.files.length !== 0) {
237 printError(`${publishPath} is not clean, check it.`);
238 return;
239 }
240 } catch (e) {
241 printError(`${publishPath} is not a git repository.`);
242 return;
243 }
244 publishRepo.pull("origin", "master");
245
246 try {
247 await fse.emptyDir(webpackOptions.outputPath);
248 await this.build(opiton);
249 } catch (e) {
250 printError(e);
251 return;
252 }
253
254 // remove upload map to sentry
255 // try {
256 // await this.uploadSentry(isOnline);
257 // } catch (e) {
258 // console.dir(e);
259 // printError("Upload sentry fail.");
260 // }
261
262 if (mapPath) {
263 await fse.move(path.join(webpackOptions.outputPath, "map.js"), mapPath, {
264 overwrite: true
265 });
266 }
267
268 await fse.copy(webpackOptions.outputPath, publishPath);
269
270 try {
271 await publishRepo.add("./*");
272 await publishRepo.commit(`Auto publish ${projectName} ${currentBranch}.`);
273 await publishRepo.pull("origin", "master", {
274 "--rebase": "true"
275 });
276 await publishRepo.push("master");
277 printSuccess(`Publish ${env} successfully.`);
278 } catch (e) {
279 printWarn("Build successfully, but auto publish fail.");
280 return;
281 }
282 }
283}