1 | import * as chalk from "chalk";
|
2 | import * as fse from "fs-extra";
|
3 | import * as inquirer from "inquirer";
|
4 | import * as _ from "lodash";
|
5 | import * as ora from "ora";
|
6 | import * as path from "path";
|
7 | import * as webpack from "webpack";
|
8 |
|
9 | import startServer from "./devServer";
|
10 | import { getEntryByKey, readCustomEntry, ALL_KEY, LAST_KEY } from "./inquirer";
|
11 | import { alertSuccess, isDirectory, printError, printSuccess, printWarn } from "./utils";
|
12 |
|
13 | import Git from "./utils/Git";
|
14 | import { genDevConfig, genProdConfig } from "./webpack";
|
15 |
|
16 |
|
17 | import { EZPackOptions } from "./EZPackInterface";
|
18 |
|
19 | export function validateConfig(config: EZPackOptions) {
|
20 | return config;
|
21 | }
|
22 |
|
23 | async 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 |
|
51 | interface BuildOption {
|
52 | analyse?: boolean;
|
53 | entry?: string;
|
54 | isLast?: boolean;
|
55 | isAll?: boolean;
|
56 | }
|
57 |
|
58 | export 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 |
|
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 |
|
148 |
|
149 |
|
150 |
|
151 | alertSuccess("Build successfully.");
|
152 | resolve();
|
153 | }
|
154 | });
|
155 | });
|
156 | }
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
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 |
|
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 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
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 | }
|