UNPKG

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