1 | "use strict";
|
2 | const tslib_1 = require("tslib");
|
3 | const fs = require("fs");
|
4 | const lodash_1 = require("lodash");
|
5 | const moment = require("moment");
|
6 | const os_1 = require("os");
|
7 | const path_1 = require("path");
|
8 | const rimraf_1 = require("rimraf");
|
9 | const typescript_lazy_get_decorator_1 = require("typescript-lazy-get-decorator");
|
10 | const ext_1 = require("../const/ext");
|
11 | const addConfig_1 = require("../fns/add-cmd/addConfig");
|
12 | const cmdName_1 = require("../fns/cmdName");
|
13 | const execLocal_1 = require("../fns/execLocal");
|
14 | const getBin_1 = require("../fns/getBin");
|
15 | const mkTsconfig_1 = require("../fns/mkTsconfig");
|
16 | const readJson_1 = require("../fns/readJson");
|
17 | const unlinkSafe_1 = require("../fns/unlinkSafe");
|
18 | const xSpawn_1 = require("../fns/xSpawn");
|
19 | const BuildTarget_1 = require("../interfaces/BuildTarget");
|
20 | const cli_serialiser_1 = require("../lib/cli-serialiser");
|
21 | const Log_1 = require("../lib/Log");
|
22 | const tmp_1 = require("../lib/tmp");
|
23 | const command = cmdName_1.cmdName(__filename);
|
24 | const defaultUmdName = (() => {
|
25 | try {
|
26 | return readJson_1.readJson("./package.json" ).name;
|
27 | }
|
28 | catch (_a) {
|
29 | return undefined;
|
30 | }
|
31 | })();
|
32 | const tscBin = getBin_1.getBin('typescript', 'tsc');
|
33 | const rollupCmdFile = require.resolve(`../lib/build/cmd.${ext_1.ext}`);
|
34 | const cmd = {
|
35 | builder(argv) {
|
36 | return addConfig_1.addConfig(argv, command)
|
37 | .option('entry', {
|
38 | alias: 'e',
|
39 | default: 'src/index.ts',
|
40 | describe: 'Entry file',
|
41 | type: 'string'
|
42 | })
|
43 | .option('externals', {
|
44 | alias: 'x',
|
45 | coerce: lodash_1.castArray,
|
46 | default: [],
|
47 | describe: 'External dependencies',
|
48 | type: 'array'
|
49 | })
|
50 | .option('ignore-umd-externals', {
|
51 | alias: 'iux',
|
52 | coerce(v) {
|
53 | let out = ['tslib'];
|
54 | if (v && v.length) {
|
55 | out.push(...v);
|
56 | out = lodash_1.uniq(out);
|
57 | }
|
58 | return out;
|
59 | },
|
60 | default: ['tslib'],
|
61 | describe: 'Override the externals option and always bundle the following packages in UMD',
|
62 | type: 'array'
|
63 | })
|
64 | .option('license-banner', {
|
65 | alias: 'lb',
|
66 | default: false,
|
67 | describe: 'Include the license as a banner in FESM & UMD bundles',
|
68 | type: 'boolean'
|
69 | })
|
70 | .option('out', {
|
71 | alias: 'o',
|
72 | default: 'dist',
|
73 | describe: 'Output directory',
|
74 | type: 'string'
|
75 | })
|
76 | .option('rollup', {
|
77 | alias: 'r',
|
78 | default: 'rollup.config.js',
|
79 | describe: 'Path ro rollup config file',
|
80 | type: 'string'
|
81 | })
|
82 | .option('targets', {
|
83 | alias: 't',
|
84 | choices: BuildTarget_1.allBuildTargets,
|
85 | coerce: lodash_1.castArray,
|
86 | default: BuildTarget_1.allBuildTargets,
|
87 | describe: 'Build targets',
|
88 | type: 'array'
|
89 | })
|
90 | .option('umd-name', {
|
91 | alias: 'u',
|
92 | default: defaultUmdName,
|
93 | describe: 'UMD global variable name',
|
94 | type: 'string'
|
95 | })
|
96 | .option('tsconfig', {
|
97 | alias: 'ts',
|
98 | coerce(path) {
|
99 | let out = {};
|
100 | let last;
|
101 | do {
|
102 | last = readJson_1.readJson(path);
|
103 | if (!last) {
|
104 | throw new Error(`Path not found: ${path}`);
|
105 | }
|
106 | out = lodash_1.omit(lodash_1.merge(lodash_1.cloneDeep(last), out), ['extends']);
|
107 | if (last.extends) {
|
108 | path = last.extends;
|
109 | }
|
110 | } while (last && last.extends);
|
111 | return out;
|
112 | },
|
113 | describe: 'Path to tsconfig file to inherit from',
|
114 | type: 'string'
|
115 | });
|
116 | },
|
117 | command,
|
118 | describe: 'Build the project',
|
119 | handler(c) {
|
120 | const start = Date.now();
|
121 | validate(c);
|
122 | Log_1.Log.info(`Clearing ${c.out}`);
|
123 | rimraf_1.sync(c.out);
|
124 | Log_1.Log.success(`${c.out} cleared.`);
|
125 | const tmpTsconfigs = [];
|
126 | try {
|
127 | buildCJsOrDeclaration(c, tmpTsconfigs);
|
128 | buildESM(c, tmpTsconfigs);
|
129 | buildRollup(c);
|
130 | Log_1.Log.info('Writing package.json');
|
131 | new PackageJsonBuilder(c).write();
|
132 | Log_1.Log.success('Wrote package.json');
|
133 | Log_1.Log.success(`Build finished in ${getDuration(start)}`);
|
134 | }
|
135 | catch (e) {
|
136 | Log_1.Log.err(`Build errored in ${getDuration(start)}`);
|
137 | throw e;
|
138 | }
|
139 | finally {
|
140 | tmpTsconfigs.forEach(unlinkSafe_1.unlinkSafe);
|
141 | }
|
142 | }
|
143 | };
|
144 | function validate(c) {
|
145 | if (c.targets.includes(BuildTarget_1.BuildTarget.UMD) && !c.umdName) {
|
146 | throw new Error('umd-name is required when one of the targets is UMD');
|
147 | }
|
148 | if (!c.targets.length) {
|
149 | throw new Error('No targets specified');
|
150 | }
|
151 | for (const t of c.targets) {
|
152 | if (!BuildTarget_1.isBuildTarget(t)) {
|
153 | throw new Error(`Unknown build target: ${JSON.stringify(t)}`);
|
154 | }
|
155 | }
|
156 | }
|
157 | function buildRollup(c) {
|
158 | const incUMD = c.targets.includes(BuildTarget_1.BuildTarget.UMD);
|
159 | const incFESM5 = c.targets.includes(BuildTarget_1.BuildTarget.FESM5);
|
160 | const incFESM2015 = c.targets.includes(BuildTarget_1.BuildTarget.FESM2015);
|
161 | const banner = (() => {
|
162 | if (c.lb) {
|
163 | let e = null;
|
164 | for (const p of ['LICENSE', 'LICENSE.txt']) {
|
165 | try {
|
166 | const contents = fs.readFileSync(p, 'utf8');
|
167 | return [
|
168 | '/*!',
|
169 | contents,
|
170 | '*/',
|
171 | '',
|
172 | ''
|
173 | ].join(os_1.EOL);
|
174 | }
|
175 | catch (err) {
|
176 | e = err;
|
177 | }
|
178 | }
|
179 | if (e) {
|
180 | Log_1.Log.warn(`Unable to set license banner: ${e.stack || e.toString() || e.message}`);
|
181 | }
|
182 | }
|
183 | return null;
|
184 | })();
|
185 | if (!incUMD && !incFESM5 && !incFESM2015) {
|
186 | Log_1.Log.info("Skipping UMD" );
|
187 | Log_1.Log.info("Skipping FESM5" );
|
188 | Log_1.Log.info("Skipping FESM2015" );
|
189 | return;
|
190 | }
|
191 | const stdConf = lodash_1.merge({ output: { sourcemap: true } }, loadRollupConfig(c));
|
192 | stdConf.input = c.entry;
|
193 | stdConf.plugins = [];
|
194 | if (c.externals && c.externals.length) {
|
195 | if (Array.isArray(stdConf.external)) {
|
196 | stdConf.external.unshift(...c.externals);
|
197 | stdConf.external = lodash_1.uniq(c.externals);
|
198 | }
|
199 | else {
|
200 | stdConf.external = c.externals;
|
201 | }
|
202 | }
|
203 | if (banner && !lodash_1.has(stdConf, 'output.banner')) {
|
204 | lodash_1.set(stdConf, 'output.banner', banner);
|
205 | }
|
206 | lodash_1.set(stdConf, 'output.name', c.umdName);
|
207 | lodash_1.set(stdConf, 'output.amd.id', readJson_1.readJson("./package.json" ).name);
|
208 | const execOpts = {
|
209 | cwd: process.cwd(),
|
210 | stdio: 'inherit'
|
211 | };
|
212 | const stdArgs = [
|
213 | '--opts',
|
214 | cli_serialiser_1.CLISerialiser.serialise(stdConf),
|
215 | '--tsconfig',
|
216 | cli_serialiser_1.CLISerialiser.serialise(c.tsconfig),
|
217 | '--ignored-externals',
|
218 | cli_serialiser_1.CLISerialiser.serialise(c.ignoreUmdExternals),
|
219 | '--dist',
|
220 | c.out
|
221 | ];
|
222 | if (incFESM2015) {
|
223 | throwIfErrored(execLocal_1.execLocal(rollupCmdFile, stdArgs.concat('--formats', BuildTarget_1.BuildTarget.FESM2015), execOpts));
|
224 | }
|
225 | else {
|
226 | Log_1.Log.info("Skipping FESM2015" );
|
227 | }
|
228 | if (incFESM5 || incUMD) {
|
229 | const formats = [];
|
230 | if (incFESM5) {
|
231 | formats.push(BuildTarget_1.BuildTarget.FESM5);
|
232 | }
|
233 | else {
|
234 | Log_1.Log.info("Skipping FESM5" );
|
235 | }
|
236 | if (incUMD) {
|
237 | formats.push(BuildTarget_1.BuildTarget.UMD);
|
238 | }
|
239 | else {
|
240 | Log_1.Log.info("Skipping UMD" );
|
241 | }
|
242 | throwIfErrored(execLocal_1.execLocal(rollupCmdFile, stdArgs.concat('--formats', ...formats), execOpts));
|
243 | }
|
244 | else {
|
245 | Log_1.Log.info("Skipping FESM5" );
|
246 | Log_1.Log.info("Skipping UMD" );
|
247 | }
|
248 | }
|
249 | class PackageJsonBuilder {
|
250 | constructor(cfg) {
|
251 | this.cfg = cfg;
|
252 | this.pkgJson = readJson_1.readJson("./package.json" ) || {};
|
253 | }
|
254 | get _base() {
|
255 | const ext$ = path_1.extname(this.cfg.entry);
|
256 | return path_1.basename(this.cfg.entry, ext$);
|
257 | }
|
258 | get _baseJS() {
|
259 | return this._base + '.js';
|
260 | }
|
261 | get browser() {
|
262 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.UMD)) {
|
263 | return '_bundle/umd.js';
|
264 | }
|
265 | return null;
|
266 | }
|
267 | get esm2015() {
|
268 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.ESM2015)) {
|
269 | return `_bundle/esm2015/${this._baseJS}`;
|
270 | }
|
271 | return this.fesm5;
|
272 | }
|
273 | get esm5() {
|
274 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.ESM5)) {
|
275 | return `_bundle/esm5/${this._baseJS}`;
|
276 | }
|
277 | return this.fesm5;
|
278 | }
|
279 | get fesm2015() {
|
280 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.FESM2015)) {
|
281 | return '_bundle/fesm2015.js';
|
282 | }
|
283 | return null;
|
284 | }
|
285 | get fesm5() {
|
286 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.FESM5)) {
|
287 | return '_bundle/fesm5.js';
|
288 | }
|
289 | return null;
|
290 | }
|
291 | get jsdelivr() {
|
292 | if (this.browser) {
|
293 | return `_bundle/umd.min.js`;
|
294 | }
|
295 | return null;
|
296 | }
|
297 | get main() {
|
298 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.CJS)) {
|
299 | return this._baseJS;
|
300 | }
|
301 | else if (this.browser) {
|
302 | return this.browser;
|
303 | }
|
304 | else {
|
305 | throw new Error('Unable to resolve main file');
|
306 | }
|
307 | }
|
308 | get module() {
|
309 | return this.fesm5 || this.esm5;
|
310 | }
|
311 | get types() {
|
312 | if (this.cfg.targets.includes(BuildTarget_1.BuildTarget.DECLARATION)) {
|
313 | return `${this._base}.d.ts`;
|
314 | }
|
315 | return null;
|
316 | }
|
317 | write() {
|
318 | for (const p of ['main', 'browser', 'jsdelivr', 'fesm5', 'esm5', 'fesm2015', 'esm2015', 'types', 'module']) {
|
319 | if (this[p]) {
|
320 | this.pkgJson[p] = this[p];
|
321 | }
|
322 | else {
|
323 | delete this.pkgJson[p];
|
324 | }
|
325 | }
|
326 | if (this.types) {
|
327 | this.pkgJson.typings = this.types;
|
328 | }
|
329 | else {
|
330 | delete this.pkgJson.typings;
|
331 | }
|
332 | fs.writeFileSync("./package.json" , JSON.stringify(this.pkgJson, null, 2 ));
|
333 | this.write = lodash_1.noop;
|
334 | }
|
335 | }
|
336 | tslib_1.__decorate([
|
337 | typescript_lazy_get_decorator_1.LazyGetter(),
|
338 | tslib_1.__metadata("design:type", String),
|
339 | tslib_1.__metadata("design:paramtypes", [])
|
340 | ], PackageJsonBuilder.prototype, "_base", null);
|
341 | tslib_1.__decorate([
|
342 | typescript_lazy_get_decorator_1.LazyGetter(),
|
343 | tslib_1.__metadata("design:type", String),
|
344 | tslib_1.__metadata("design:paramtypes", [])
|
345 | ], PackageJsonBuilder.prototype, "_baseJS", null);
|
346 | tslib_1.__decorate([
|
347 | typescript_lazy_get_decorator_1.LazyGetter(),
|
348 | tslib_1.__metadata("design:type", Object),
|
349 | tslib_1.__metadata("design:paramtypes", [])
|
350 | ], PackageJsonBuilder.prototype, "browser", null);
|
351 | tslib_1.__decorate([
|
352 | typescript_lazy_get_decorator_1.LazyGetter(),
|
353 | tslib_1.__metadata("design:type", Object),
|
354 | tslib_1.__metadata("design:paramtypes", [])
|
355 | ], PackageJsonBuilder.prototype, "esm2015", null);
|
356 | tslib_1.__decorate([
|
357 | typescript_lazy_get_decorator_1.LazyGetter(),
|
358 | tslib_1.__metadata("design:type", Object),
|
359 | tslib_1.__metadata("design:paramtypes", [])
|
360 | ], PackageJsonBuilder.prototype, "esm5", null);
|
361 | tslib_1.__decorate([
|
362 | typescript_lazy_get_decorator_1.LazyGetter(),
|
363 | tslib_1.__metadata("design:type", Object),
|
364 | tslib_1.__metadata("design:paramtypes", [])
|
365 | ], PackageJsonBuilder.prototype, "fesm2015", null);
|
366 | tslib_1.__decorate([
|
367 | typescript_lazy_get_decorator_1.LazyGetter(),
|
368 | tslib_1.__metadata("design:type", Object),
|
369 | tslib_1.__metadata("design:paramtypes", [])
|
370 | ], PackageJsonBuilder.prototype, "fesm5", null);
|
371 | tslib_1.__decorate([
|
372 | typescript_lazy_get_decorator_1.LazyGetter(),
|
373 | tslib_1.__metadata("design:type", Object),
|
374 | tslib_1.__metadata("design:paramtypes", [])
|
375 | ], PackageJsonBuilder.prototype, "jsdelivr", null);
|
376 | tslib_1.__decorate([
|
377 | typescript_lazy_get_decorator_1.LazyGetter(),
|
378 | tslib_1.__metadata("design:type", Object),
|
379 | tslib_1.__metadata("design:paramtypes", [])
|
380 | ], PackageJsonBuilder.prototype, "main", null);
|
381 | tslib_1.__decorate([
|
382 | typescript_lazy_get_decorator_1.LazyGetter(),
|
383 | tslib_1.__metadata("design:type", Object),
|
384 | tslib_1.__metadata("design:paramtypes", [])
|
385 | ], PackageJsonBuilder.prototype, "module", null);
|
386 | tslib_1.__decorate([
|
387 | typescript_lazy_get_decorator_1.LazyGetter(),
|
388 | tslib_1.__metadata("design:type", Object),
|
389 | tslib_1.__metadata("design:paramtypes", [])
|
390 | ], PackageJsonBuilder.prototype, "types", null);
|
391 | function loadRollupConfig(c) {
|
392 | try {
|
393 | if (c.rollup) {
|
394 | const fullPath = path_1.join(process.cwd(), c.rollup);
|
395 | const contents = fs.readFileSync(fullPath, 'utf8');
|
396 | const reg = /export\s+default/g;
|
397 | if (reg.test(contents)) {
|
398 | try {
|
399 | const newContents = contents.replace(reg, 'module.exports = ');
|
400 | fs.writeFileSync(fullPath, newContents);
|
401 | return lodash_1.cloneDeep(require(fullPath));
|
402 | }
|
403 | finally {
|
404 | fs.writeFileSync(fullPath, contents);
|
405 | }
|
406 | }
|
407 | else {
|
408 | return lodash_1.cloneDeep(require(fullPath));
|
409 | }
|
410 | }
|
411 | }
|
412 | catch (_a) {
|
413 |
|
414 | }
|
415 | return {};
|
416 | }
|
417 | function buildESM(c, tmpTsConfigs) {
|
418 | if (c.targets.includes(BuildTarget_1.BuildTarget.ESM5)) {
|
419 | Log_1.Log.info('Building ESM5');
|
420 | spawnTsc(makeTmpTsconfig(c, tmpTsConfigs, {
|
421 | compilerOptions: {
|
422 | declaration: false,
|
423 | module: 'es2015',
|
424 | outDir: path_1.join(c.out, '_bundle', 'esm5'),
|
425 | target: 'es5'
|
426 | }
|
427 | }));
|
428 | Log_1.Log.success('Built ESM5');
|
429 | }
|
430 | else {
|
431 | Log_1.Log.info('Skipping ESM5');
|
432 | }
|
433 | if (c.targets.includes(BuildTarget_1.BuildTarget.ESM2015)) {
|
434 | Log_1.Log.info('Building ESM2015');
|
435 | spawnTsc(makeTmpTsconfig(c, tmpTsConfigs, {
|
436 | compilerOptions: {
|
437 | declaration: false,
|
438 | module: 'es2015',
|
439 | outDir: path_1.join(c.out, '_bundle', 'esm2015'),
|
440 | target: 'esnext'
|
441 | }
|
442 | }));
|
443 | Log_1.Log.success('Built ESM2015');
|
444 | }
|
445 | else {
|
446 | Log_1.Log.info('Skipping ESM2015');
|
447 | }
|
448 | }
|
449 | function buildCJsOrDeclaration(c, tmpTsconfigs) {
|
450 | const needsDeclaration = c.targets.includes(BuildTarget_1.BuildTarget.DECLARATION);
|
451 | if (c.targets.includes(BuildTarget_1.BuildTarget.CJS)) {
|
452 | if (!needsDeclaration) {
|
453 | Log_1.Log.info('Skipping declaration');
|
454 | }
|
455 | const building = needsDeclaration ? 'CommonJS + declaration' : 'CommonJS';
|
456 | Log_1.Log.info(`Building ${building}`);
|
457 | spawnTsc(makeTmpTsconfig(c, tmpTsconfigs, {
|
458 | compilerOptions: {
|
459 | declaration: needsDeclaration,
|
460 | module: 'commonjs',
|
461 | outDir: c.out
|
462 | }
|
463 | }));
|
464 | Log_1.Log.success(`Built ${building}`);
|
465 | }
|
466 | else if (needsDeclaration) {
|
467 | Log_1.Log.info('Skipping commonjs');
|
468 | Log_1.Log.info('Building declaration');
|
469 | spawnTsc(makeTmpTsconfig(c, tmpTsconfigs, {
|
470 | compilerOptions: {
|
471 | declaration: true,
|
472 | emitDeclarationOnly: true,
|
473 | module: 'commonjs',
|
474 | outDir: c.out,
|
475 | sourceMap: false
|
476 | }
|
477 | }));
|
478 | Log_1.Log.success('Built declaration');
|
479 | }
|
480 | else {
|
481 | Log_1.Log.info('Skipping commonjs');
|
482 | Log_1.Log.info('Skipping declaration');
|
483 | }
|
484 | }
|
485 | function makeTmpTsconfig(c, cleanupArray, overrides) {
|
486 | const path = tmp_1.tmp.fileSync({
|
487 | dir: process.cwd(),
|
488 | postfix: '.json',
|
489 | prefix: ".alobuild-tsconfig-"
|
490 | }).name;
|
491 | const tsconfig = mkTsconfig_1.mkTsconfig(lodash_1.merge(lodash_1.cloneDeep(c.tsconfig), overrides || {}));
|
492 | delete tsconfig.compilerOptions.outFile;
|
493 | fs.writeFileSync(path, JSON.stringify(tsconfig, null, 2 ));
|
494 | cleanupArray.push(path);
|
495 | return path;
|
496 | }
|
497 | function spawnTsc(tsconfigPath) {
|
498 | const proc = xSpawn_1.xSpawnSync(process.execPath, [tscBin, '-p', tsconfigPath], { stdio: 'inherit' });
|
499 | throwIfErrored(proc);
|
500 | }
|
501 | function throwIfErrored(res) {
|
502 | if (res.status !== 0) {
|
503 | throw new Error(`Process exited with code ${res.status}`);
|
504 | }
|
505 | }
|
506 | function getDuration(startPoint) {
|
507 | const d = moment.duration(Date.now() - startPoint);
|
508 | return [
|
509 | d.hours().toString(),
|
510 | lodash_1.padStart(d.minutes().toString(), 2 , "0" ),
|
511 | lodash_1.padStart(d.seconds().toString(), 2 , "0" )
|
512 | ].join(':') + `.${lodash_1.padStart(d.milliseconds().toString(), 3 /* PAD_MS */, "0" /* PAD_CHAR */)}`;
|
513 | }
|
514 |
|
515 | fs.readdirSync(process.cwd(), 'utf8')
|
516 | .filter(p => p.startsWith(".alobuild-tsconfig-" ) && p.endsWith('.json'))
|
517 | .forEach(unlinkSafe_1.unlinkSafe);
|
518 | module.exports = cmd;
|