UNPKG

18 kBJavaScriptView Raw
1"use strict";
2const tslib_1 = require("tslib");
3const fs = require("fs");
4const lodash_1 = require("lodash");
5const moment = require("moment");
6const os_1 = require("os");
7const path_1 = require("path");
8const rimraf_1 = require("rimraf");
9const typescript_lazy_get_decorator_1 = require("typescript-lazy-get-decorator");
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('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};
144function 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}
157function 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" /* SKIP_UMD */);
187 Log_1.Log.info("Skipping FESM5" /* SKIP_FESM5 */);
188 Log_1.Log.info("Skipping FESM2015" /* SKIP_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" /* PKG_JSON_PATH */).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" /* SKIP_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" /* SKIP_FESM5 */);
235 }
236 if (incUMD) {
237 formats.push(BuildTarget_1.BuildTarget.UMD);
238 }
239 else {
240 Log_1.Log.info("Skipping UMD" /* SKIP_UMD */);
241 }
242 throwIfErrored(execLocal_1.execLocal(rollupCmdFile, stdArgs.concat('--formats', ...formats), execOpts));
243 }
244 else {
245 Log_1.Log.info("Skipping FESM5" /* SKIP_FESM5 */);
246 Log_1.Log.info("Skipping UMD" /* SKIP_UMD */);
247 }
248}
249class PackageJsonBuilder {
250 constructor(cfg) {
251 this.cfg = cfg;
252 this.pkgJson = readJson_1.readJson("./package.json" /* PKG_JSON_PATH */) || {};
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" /* PKG_JSON_PATH */, JSON.stringify(this.pkgJson, null, 2 /* INDENT */));
333 this.write = lodash_1.noop;
334 }
335}
336tslib_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);
341tslib_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);
346tslib_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);
351tslib_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);
356tslib_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);
361tslib_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);
366tslib_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);
371tslib_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);
376tslib_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);
381tslib_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);
386tslib_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);
391function 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 //noop
414 }
415 return {};
416}
417function 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}
449function 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}
485function makeTmpTsconfig(c, cleanupArray, overrides) {
486 const path = tmp_1.tmp.fileSync({
487 dir: process.cwd(),
488 postfix: '.json',
489 prefix: ".alobuild-tsconfig-" /* TSCONFIG_PREFIX */
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 /* INDENT */));
494 cleanupArray.push(path);
495 return path;
496}
497function spawnTsc(tsconfigPath) {
498 const proc = xSpawn_1.xSpawnSync(process.execPath, [tscBin, '-p', tsconfigPath], { stdio: 'inherit' });
499 throwIfErrored(proc);
500}
501function throwIfErrored(res) {
502 if (res.status !== 0) {
503 throw new Error(`Process exited with code ${res.status}`);
504 }
505}
506function getDuration(startPoint) {
507 const d = moment.duration(Date.now() - startPoint);
508 return [
509 d.hours().toString(),
510 lodash_1.padStart(d.minutes().toString(), 2 /* PAD */, "0" /* PAD_CHAR */),
511 lodash_1.padStart(d.seconds().toString(), 2 /* PAD */, "0" /* PAD_CHAR */)
512 ].join(':') + `.${lodash_1.padStart(d.milliseconds().toString(), 3 /* PAD_MS */, "0" /* PAD_CHAR */)}`;
513}
514// Cleanup on startup
515fs.readdirSync(process.cwd(), 'utf8')
516 .filter(p => p.startsWith(".alobuild-tsconfig-" /* TSCONFIG_PREFIX */) && p.endsWith('.json'))
517 .forEach(unlinkSafe_1.unlinkSafe);
518module.exports = cmd;