-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
index.js
114 lines (95 loc) · 3.79 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
'use strict';
const fs = require('fs');
const walkSync = require('walk-sync');
const Plugin = require('broccoli-plugin');
const path = require('path');
const defaults = require('lodash.defaultsdeep');
const symlinkOrCopy = require('symlink-or-copy');
const MatcherCollection = require('matcher-collection');
const debug = require('debug')('broccoli-terser-sourcemap');
const queue = require('async-promise-queue');
const workerpool = require('workerpool');
const processFile = require('./lib/process-file');
const silent = process.argv.indexOf('--silent') !== -1;
const worker = queue.async.asyncify(doWork => doWork());
const MatchNothing = {
match() {
return false;
},
};
module.exports = class TerserWriter extends Plugin {
constructor(_inputNodes, options = {}) {
let inputNodes = Array.isArray(_inputNodes) ? _inputNodes : [_inputNodes];
super(inputNodes, {
name: options.name,
annotation: options.annotation,
needsCache: false,
});
this.options = defaults(options, {
terser: {
sourceMap: {},
},
});
// async prop is deprecated since terser.minify() is async by default
if ('async' in this.options) {
throw new Error('\n Passing `async` property inside `options` is deprecated.');
}
this.concurrency = Number(process.env.JOBS) || this.options.concurrency || Math.max(require('os').cpus().length - 1, 1);
// create a worker pool using an external worker script
this.pool = workerpool.pool(path.join(__dirname, 'lib', 'worker.js'), {
maxWorkers: this.concurrency,
workerType: 'auto',
});
let exclude = this.options.exclude;
if (Array.isArray(exclude)) {
this.excludes = new MatcherCollection(exclude);
} else {
this.excludes = MatchNothing;
}
}
async build() {
let pendingWork = [];
this.inputPaths.forEach(inputPath => {
walkSync(inputPath).forEach(relativePath => {
if (relativePath.slice(-1) === '/') {
return;
}
let inFile = path.join(inputPath, relativePath);
let outFile = path.join(this.outputPath, relativePath);
fs.mkdirSync(path.dirname(outFile), { recursive: true });
if (this._isJSExt(relativePath) && !this.excludes.match(relativePath)) {
// wrap this in a function so it doesn't actually run yet, and can be throttled
let terserOperation = () => this.processFile(inFile, outFile, relativePath, this.outputPath);
pendingWork.push(terserOperation);
} else if (relativePath.slice(-4) === '.map') {
if (this.excludes.match(`${relativePath.slice(0, -4)}.{js,mjs}`)) {
// ensure .map files for excluded JS paths are also copied forward
symlinkOrCopy.sync(inFile, outFile);
}
// skip, because it will get handled when its corresponding JS does
} else {
symlinkOrCopy.sync(inFile, outFile);
}
});
});
try {
await queue(worker, pendingWork, this.concurrency);
} finally {
// make sure to shut down the workers on both success and error case
this.pool.terminate();
}
}
_isJSExt(relativePath) {
return relativePath.slice(-3) === '.js' || relativePath.slice(-4) === '.mjs';
}
processFile(inFile, outFile, relativePath, outDir) {
// don't run this in the workerpool if concurrency is disabled (can set JOBS <= 1)
if (this.concurrency > 1) {
debug('running in workerpool, concurrency=%d', this.concurrency);
// each of these arguments is a string, which can be sent to the worker process as-is
return this.pool.exec('processFileParallel', [inFile, outFile, relativePath, outDir, silent, this.options]);
}
debug('not running in workerpool');
return processFile(inFile, outFile, relativePath, outDir, silent, this.options);
}
};