@rollup/plugin-terser
Advanced tools
Comparing version 0.3.0 to 0.4.0
@@ -5,3 +5,2 @@ 'use strict'; | ||
var process = require('process'); | ||
var worker_threads = require('worker_threads'); | ||
@@ -11,2 +10,3 @@ var smob = require('smob'); | ||
var url = require('url'); | ||
var async_hooks = require('async_hooks'); | ||
var os = require('os'); | ||
@@ -28,13 +28,16 @@ var events = require('events'); | ||
} | ||
async function runWorker() { | ||
if (worker_threads.isMainThread || !worker_threads.parentPort || !isWorkerContextSerialized(worker_threads.workerData)) { | ||
function runWorker() { | ||
if (worker_threads.isMainThread || !worker_threads.parentPort) { | ||
return; | ||
} | ||
try { | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
const options = eval2(`(${worker_threads.workerData.options})`); | ||
const result = await terser$1.minify(worker_threads.workerData.code, options); | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
worker_threads.parentPort.on('message', async (data) => { | ||
if (!isWorkerContextSerialized(data)) { | ||
return; | ||
} | ||
const options = eval2(`(${data.options})`); | ||
const result = await terser$1.minify(data.code, options); | ||
const output = { | ||
code: result.code || worker_threads.workerData.code, | ||
code: result.code || data.code, | ||
nameCache: options.nameCache | ||
@@ -48,10 +51,19 @@ }; | ||
} | ||
worker_threads.parentPort.postMessage(output); | ||
worker_threads.parentPort === null || worker_threads.parentPort === void 0 ? void 0 : worker_threads.parentPort.postMessage(output); | ||
}); | ||
} | ||
const taskInfo = Symbol('taskInfo'); | ||
const freeWorker = Symbol('freeWorker'); | ||
class WorkerPoolTaskInfo extends async_hooks.AsyncResource { | ||
constructor(callback) { | ||
super('WorkerPoolTaskInfo'); | ||
this.callback = callback; | ||
} | ||
catch (e) { | ||
process.exit(1); | ||
done(err, result) { | ||
this.runInAsyncScope(this.callback, null, err, result); | ||
this.emitDestroy(); | ||
} | ||
} | ||
const symbol = Symbol.for('FreeWoker'); | ||
class WorkerPool extends events.EventEmitter { | ||
@@ -61,24 +73,19 @@ constructor(options) { | ||
this.tasks = []; | ||
this.workers = 0; | ||
this.workers = []; | ||
this.freeWorkers = []; | ||
this.maxInstances = options.maxWorkers || os.cpus().length; | ||
this.filePath = options.filePath; | ||
this.on(symbol, () => { | ||
this.on(freeWorker, () => { | ||
if (this.tasks.length > 0) { | ||
this.run(); | ||
const { context, cb } = this.tasks.shift(); | ||
this.runTask(context, cb); | ||
} | ||
}); | ||
} | ||
add(context, cb) { | ||
this.tasks.push({ | ||
context, | ||
cb | ||
}); | ||
if (this.workers >= this.maxInstances) { | ||
return; | ||
} | ||
this.run(); | ||
get numWorkers() { | ||
return this.workers.length; | ||
} | ||
async addAsync(context) { | ||
addAsync(context) { | ||
return new Promise((resolve, reject) => { | ||
this.add(context, (err, output) => { | ||
this.runTask(context, (err, output) => { | ||
if (err) { | ||
@@ -96,39 +103,48 @@ reject(err); | ||
} | ||
run() { | ||
if (this.tasks.length === 0) { | ||
return; | ||
close() { | ||
for (let i = 0; i < this.workers.length; i++) { | ||
const worker = this.workers[i]; | ||
worker.terminate(); | ||
} | ||
const task = this.tasks.shift(); | ||
if (typeof task === 'undefined') { | ||
return; | ||
} | ||
this.workers += 1; | ||
let called = false; | ||
const callCallback = (err, output) => { | ||
if (called) { | ||
return; | ||
} | ||
called = true; | ||
this.workers -= 1; | ||
task.cb(err, output); | ||
this.emit(symbol); | ||
}; | ||
const worker = new worker_threads.Worker(this.filePath, { | ||
workerData: { | ||
code: task.context.code, | ||
options: serializeJavascript(task.context.options) | ||
} | ||
} | ||
addNewWorker() { | ||
const worker = new worker_threads.Worker(this.filePath); | ||
worker.on('message', (result) => { | ||
var _a; | ||
(_a = worker[taskInfo]) === null || _a === void 0 ? void 0 : _a.done(null, result); | ||
worker[taskInfo] = null; | ||
this.freeWorkers.push(worker); | ||
this.emit(freeWorker); | ||
}); | ||
worker.on('message', (data) => { | ||
callCallback(null, data); | ||
}); | ||
worker.on('error', (err) => { | ||
callCallback(err); | ||
}); | ||
worker.on('exit', (code) => { | ||
if (code !== 0) { | ||
callCallback(new Error(`Minify worker stopped with exit code ${code}`)); | ||
if (worker[taskInfo]) { | ||
worker[taskInfo].done(err, null); | ||
} | ||
else { | ||
this.emit('error', err); | ||
} | ||
this.workers.splice(this.workers.indexOf(worker), 1); | ||
this.addNewWorker(); | ||
}); | ||
this.workers.push(worker); | ||
this.freeWorkers.push(worker); | ||
this.emit(freeWorker); | ||
} | ||
runTask(context, cb) { | ||
if (this.freeWorkers.length === 0) { | ||
this.tasks.push({ context, cb }); | ||
if (this.numWorkers < this.maxInstances) { | ||
this.addNewWorker(); | ||
} | ||
return; | ||
} | ||
const worker = this.freeWorkers.pop(); | ||
if (worker) { | ||
worker[taskInfo] = new WorkerPoolTaskInfo(cb); | ||
worker.postMessage({ | ||
code: context.code, | ||
options: serializeJavascript(context.options) | ||
}); | ||
} | ||
} | ||
} | ||
@@ -138,9 +154,15 @@ | ||
const { maxWorkers, ...options } = input; | ||
const workerPool = new WorkerPool({ | ||
filePath: url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.js', document.baseURI).href))), | ||
maxWorkers | ||
}); | ||
let workerPool; | ||
let numOfChunks = 0; | ||
let numOfWorkersUsed = 0; | ||
return { | ||
name: 'terser', | ||
async renderChunk(code, chunk, outputOptions) { | ||
if (!workerPool) { | ||
workerPool = new WorkerPool({ | ||
filePath: url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.js', document.baseURI).href))), | ||
maxWorkers | ||
}); | ||
} | ||
numOfChunks += 1; | ||
const defaultOptions = { | ||
@@ -194,2 +216,13 @@ sourceMap: outputOptions.sourcemap === true || typeof outputOptions.sourcemap === 'string' | ||
} | ||
finally { | ||
numOfChunks -= 1; | ||
if (numOfChunks === 0) { | ||
numOfWorkersUsed = workerPool.numWorkers; | ||
workerPool.close(); | ||
workerPool = null; | ||
} | ||
} | ||
}, | ||
get numOfWorkersUsed() { | ||
return numOfWorkersUsed; | ||
} | ||
@@ -196,0 +229,0 @@ }; |
@@ -1,6 +0,6 @@ | ||
import process from 'process'; | ||
import { isMainThread, parentPort, workerData, Worker } from 'worker_threads'; | ||
import { isMainThread, parentPort, Worker } from 'worker_threads'; | ||
import { isObject, hasOwnProperty, merge } from 'smob'; | ||
import { minify } from 'terser'; | ||
import { fileURLToPath } from 'url'; | ||
import { AsyncResource } from 'async_hooks'; | ||
import { cpus } from 'os'; | ||
@@ -22,13 +22,16 @@ import { EventEmitter } from 'events'; | ||
} | ||
async function runWorker() { | ||
if (isMainThread || !parentPort || !isWorkerContextSerialized(workerData)) { | ||
function runWorker() { | ||
if (isMainThread || !parentPort) { | ||
return; | ||
} | ||
try { | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
const options = eval2(`(${workerData.options})`); | ||
const result = await minify(workerData.code, options); | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
parentPort.on('message', async (data) => { | ||
if (!isWorkerContextSerialized(data)) { | ||
return; | ||
} | ||
const options = eval2(`(${data.options})`); | ||
const result = await minify(data.code, options); | ||
const output = { | ||
code: result.code || workerData.code, | ||
code: result.code || data.code, | ||
nameCache: options.nameCache | ||
@@ -42,10 +45,19 @@ }; | ||
} | ||
parentPort.postMessage(output); | ||
parentPort === null || parentPort === void 0 ? void 0 : parentPort.postMessage(output); | ||
}); | ||
} | ||
const taskInfo = Symbol('taskInfo'); | ||
const freeWorker = Symbol('freeWorker'); | ||
class WorkerPoolTaskInfo extends AsyncResource { | ||
constructor(callback) { | ||
super('WorkerPoolTaskInfo'); | ||
this.callback = callback; | ||
} | ||
catch (e) { | ||
process.exit(1); | ||
done(err, result) { | ||
this.runInAsyncScope(this.callback, null, err, result); | ||
this.emitDestroy(); | ||
} | ||
} | ||
const symbol = Symbol.for('FreeWoker'); | ||
class WorkerPool extends EventEmitter { | ||
@@ -55,24 +67,19 @@ constructor(options) { | ||
this.tasks = []; | ||
this.workers = 0; | ||
this.workers = []; | ||
this.freeWorkers = []; | ||
this.maxInstances = options.maxWorkers || cpus().length; | ||
this.filePath = options.filePath; | ||
this.on(symbol, () => { | ||
this.on(freeWorker, () => { | ||
if (this.tasks.length > 0) { | ||
this.run(); | ||
const { context, cb } = this.tasks.shift(); | ||
this.runTask(context, cb); | ||
} | ||
}); | ||
} | ||
add(context, cb) { | ||
this.tasks.push({ | ||
context, | ||
cb | ||
}); | ||
if (this.workers >= this.maxInstances) { | ||
return; | ||
} | ||
this.run(); | ||
get numWorkers() { | ||
return this.workers.length; | ||
} | ||
async addAsync(context) { | ||
addAsync(context) { | ||
return new Promise((resolve, reject) => { | ||
this.add(context, (err, output) => { | ||
this.runTask(context, (err, output) => { | ||
if (err) { | ||
@@ -90,39 +97,48 @@ reject(err); | ||
} | ||
run() { | ||
if (this.tasks.length === 0) { | ||
return; | ||
close() { | ||
for (let i = 0; i < this.workers.length; i++) { | ||
const worker = this.workers[i]; | ||
worker.terminate(); | ||
} | ||
const task = this.tasks.shift(); | ||
if (typeof task === 'undefined') { | ||
return; | ||
} | ||
this.workers += 1; | ||
let called = false; | ||
const callCallback = (err, output) => { | ||
if (called) { | ||
return; | ||
} | ||
called = true; | ||
this.workers -= 1; | ||
task.cb(err, output); | ||
this.emit(symbol); | ||
}; | ||
const worker = new Worker(this.filePath, { | ||
workerData: { | ||
code: task.context.code, | ||
options: serializeJavascript(task.context.options) | ||
} | ||
} | ||
addNewWorker() { | ||
const worker = new Worker(this.filePath); | ||
worker.on('message', (result) => { | ||
var _a; | ||
(_a = worker[taskInfo]) === null || _a === void 0 ? void 0 : _a.done(null, result); | ||
worker[taskInfo] = null; | ||
this.freeWorkers.push(worker); | ||
this.emit(freeWorker); | ||
}); | ||
worker.on('message', (data) => { | ||
callCallback(null, data); | ||
}); | ||
worker.on('error', (err) => { | ||
callCallback(err); | ||
}); | ||
worker.on('exit', (code) => { | ||
if (code !== 0) { | ||
callCallback(new Error(`Minify worker stopped with exit code ${code}`)); | ||
if (worker[taskInfo]) { | ||
worker[taskInfo].done(err, null); | ||
} | ||
else { | ||
this.emit('error', err); | ||
} | ||
this.workers.splice(this.workers.indexOf(worker), 1); | ||
this.addNewWorker(); | ||
}); | ||
this.workers.push(worker); | ||
this.freeWorkers.push(worker); | ||
this.emit(freeWorker); | ||
} | ||
runTask(context, cb) { | ||
if (this.freeWorkers.length === 0) { | ||
this.tasks.push({ context, cb }); | ||
if (this.numWorkers < this.maxInstances) { | ||
this.addNewWorker(); | ||
} | ||
return; | ||
} | ||
const worker = this.freeWorkers.pop(); | ||
if (worker) { | ||
worker[taskInfo] = new WorkerPoolTaskInfo(cb); | ||
worker.postMessage({ | ||
code: context.code, | ||
options: serializeJavascript(context.options) | ||
}); | ||
} | ||
} | ||
} | ||
@@ -132,9 +148,15 @@ | ||
const { maxWorkers, ...options } = input; | ||
const workerPool = new WorkerPool({ | ||
filePath: fileURLToPath(import.meta.url), | ||
maxWorkers | ||
}); | ||
let workerPool; | ||
let numOfChunks = 0; | ||
let numOfWorkersUsed = 0; | ||
return { | ||
name: 'terser', | ||
async renderChunk(code, chunk, outputOptions) { | ||
if (!workerPool) { | ||
workerPool = new WorkerPool({ | ||
filePath: fileURLToPath(import.meta.url), | ||
maxWorkers | ||
}); | ||
} | ||
numOfChunks += 1; | ||
const defaultOptions = { | ||
@@ -188,2 +210,13 @@ sourceMap: outputOptions.sourcemap === true || typeof outputOptions.sourcemap === 'string' | ||
} | ||
finally { | ||
numOfChunks -= 1; | ||
if (numOfChunks === 0) { | ||
numOfWorkersUsed = workerPool.numWorkers; | ||
workerPool.close(); | ||
workerPool = null; | ||
} | ||
} | ||
}, | ||
get numOfWorkersUsed() { | ||
return numOfWorkersUsed; | ||
} | ||
@@ -190,0 +223,0 @@ }; |
{ | ||
"name": "@rollup/plugin-terser", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"publishConfig": { | ||
@@ -5,0 +5,0 @@ "access": "public" |
@@ -12,6 +12,5 @@ import { fileURLToPath } from 'url'; | ||
const workerPool = new WorkerPool({ | ||
filePath: fileURLToPath(import.meta.url), | ||
maxWorkers | ||
}); | ||
let workerPool: WorkerPool | null | undefined; | ||
let numOfChunks = 0; | ||
let numOfWorkersUsed = 0; | ||
@@ -22,2 +21,11 @@ return { | ||
async renderChunk(code: string, chunk: RenderedChunk, outputOptions: NormalizedOutputOptions) { | ||
if (!workerPool) { | ||
workerPool = new WorkerPool({ | ||
filePath: fileURLToPath(import.meta.url), | ||
maxWorkers | ||
}); | ||
} | ||
numOfChunks += 1; | ||
const defaultOptions: Options = { | ||
@@ -85,5 +93,16 @@ sourceMap: outputOptions.sourcemap === true || typeof outputOptions.sourcemap === 'string' | ||
return Promise.reject(e); | ||
} finally { | ||
numOfChunks -= 1; | ||
if (numOfChunks === 0) { | ||
numOfWorkersUsed = workerPool.numWorkers; | ||
workerPool.close(); | ||
workerPool = null; | ||
} | ||
} | ||
}, | ||
get numOfWorkersUsed() { | ||
return numOfWorkersUsed; | ||
} | ||
}; | ||
} |
@@ -0,3 +1,8 @@ | ||
import type { AsyncResource } from 'async_hooks'; | ||
import type { Worker } from 'worker_threads'; | ||
import type { MinifyOptions } from 'terser'; | ||
import type { taskInfo } from './constants'; | ||
export interface Options extends MinifyOptions { | ||
@@ -15,2 +20,8 @@ nameCache?: Record<string, any>; | ||
interface WorkerPoolTaskInfo extends AsyncResource { | ||
done(err: Error | null, result: any): void; | ||
} | ||
export type WorkerWithTaskInfo = Worker & { [taskInfo]?: WorkerPoolTaskInfo | null }; | ||
export interface WorkerContextSerialized { | ||
@@ -17,0 +28,0 @@ code: string; |
@@ -0,1 +1,2 @@ | ||
import { AsyncResource } from 'async_hooks'; | ||
import { Worker } from 'worker_threads'; | ||
@@ -7,2 +8,4 @@ import { cpus } from 'os'; | ||
import { freeWorker, taskInfo } from './constants'; | ||
import type { | ||
@@ -13,7 +16,17 @@ WorkerCallback, | ||
WorkerPoolOptions, | ||
WorkerPoolTask | ||
WorkerPoolTask, | ||
WorkerWithTaskInfo | ||
} from './type'; | ||
const symbol = Symbol.for('FreeWoker'); | ||
class WorkerPoolTaskInfo extends AsyncResource { | ||
constructor(private callback: WorkerCallback) { | ||
super('WorkerPoolTaskInfo'); | ||
} | ||
done(err: Error | null, result: any) { | ||
this.runInAsyncScope(this.callback, null, err, result); | ||
this.emitDestroy(); | ||
} | ||
} | ||
export class WorkerPool extends EventEmitter { | ||
@@ -26,3 +39,4 @@ protected maxInstances: number; | ||
protected workers = 0; | ||
protected workers: WorkerWithTaskInfo[] = []; | ||
protected freeWorkers: WorkerWithTaskInfo[] = []; | ||
@@ -35,5 +49,6 @@ constructor(options: WorkerPoolOptions) { | ||
this.on(symbol, () => { | ||
this.on(freeWorker, () => { | ||
if (this.tasks.length > 0) { | ||
this.run(); | ||
const { context, cb } = this.tasks.shift()!; | ||
this.runTask(context, cb); | ||
} | ||
@@ -43,18 +58,9 @@ }); | ||
add(context: WorkerContext, cb: WorkerCallback) { | ||
this.tasks.push({ | ||
context, | ||
cb | ||
}); | ||
if (this.workers >= this.maxInstances) { | ||
return; | ||
} | ||
this.run(); | ||
get numWorkers(): number { | ||
return this.workers.length; | ||
} | ||
async addAsync(context: WorkerContext): Promise<WorkerOutput> { | ||
addAsync(context: WorkerContext): Promise<WorkerOutput> { | ||
return new Promise((resolve, reject) => { | ||
this.add(context, (err, output) => { | ||
this.runTask(context, (err, output) => { | ||
if (err) { | ||
@@ -75,49 +81,52 @@ reject(err); | ||
private run() { | ||
if (this.tasks.length === 0) { | ||
return; | ||
close() { | ||
for (let i = 0; i < this.workers.length; i++) { | ||
const worker = this.workers[i]; | ||
worker.terminate(); | ||
} | ||
} | ||
const task = this.tasks.shift(); | ||
private addNewWorker() { | ||
const worker: WorkerWithTaskInfo = new Worker(this.filePath); | ||
if (typeof task === 'undefined') { | ||
return; | ||
} | ||
worker.on('message', (result) => { | ||
worker[taskInfo]?.done(null, result); | ||
worker[taskInfo] = null; | ||
this.freeWorkers.push(worker); | ||
this.emit(freeWorker); | ||
}); | ||
this.workers += 1; | ||
let called = false; | ||
const callCallback = (err: Error | null, output?: WorkerOutput) => { | ||
if (called) { | ||
return; | ||
worker.on('error', (err) => { | ||
if (worker[taskInfo]) { | ||
worker[taskInfo].done(err, null); | ||
} else { | ||
this.emit('error', err); | ||
} | ||
called = true; | ||
this.workers.splice(this.workers.indexOf(worker), 1); | ||
this.addNewWorker(); | ||
}); | ||
this.workers -= 1; | ||
this.workers.push(worker); | ||
this.freeWorkers.push(worker); | ||
this.emit(freeWorker); | ||
} | ||
task.cb(err, output); | ||
this.emit(symbol); | ||
}; | ||
const worker = new Worker(this.filePath, { | ||
workerData: { | ||
code: task.context.code, | ||
options: serializeJavascript(task.context.options) | ||
private runTask(context: WorkerContext, cb: WorkerCallback) { | ||
if (this.freeWorkers.length === 0) { | ||
this.tasks.push({ context, cb }); | ||
if (this.numWorkers < this.maxInstances) { | ||
this.addNewWorker(); | ||
} | ||
}); | ||
return; | ||
} | ||
worker.on('message', (data) => { | ||
callCallback(null, data); | ||
}); | ||
worker.on('error', (err) => { | ||
callCallback(err); | ||
}); | ||
worker.on('exit', (code) => { | ||
if (code !== 0) { | ||
callCallback(new Error(`Minify worker stopped with exit code ${code}`)); | ||
} | ||
}); | ||
const worker = this.freeWorkers.pop(); | ||
if (worker) { | ||
worker[taskInfo] = new WorkerPoolTaskInfo(cb); | ||
worker.postMessage({ | ||
code: context.code, | ||
options: serializeJavascript(context.options) | ||
}); | ||
} | ||
} | ||
} |
@@ -1,3 +0,2 @@ | ||
import process from 'process'; | ||
import { isMainThread, parentPort, workerData } from 'worker_threads'; | ||
import { isMainThread, parentPort } from 'worker_threads'; | ||
@@ -25,17 +24,21 @@ import { hasOwnProperty, isObject } from 'smob'; | ||
export async function runWorker() { | ||
if (isMainThread || !parentPort || !isWorkerContextSerialized(workerData)) { | ||
export function runWorker() { | ||
if (isMainThread || !parentPort) { | ||
return; | ||
} | ||
try { | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
const options = eval2(`(${workerData.options})`); | ||
parentPort.on('message', async (data: WorkerContextSerialized) => { | ||
if (!isWorkerContextSerialized(data)) { | ||
return; | ||
} | ||
const result = await minify(workerData.code, options); | ||
const options = eval2(`(${data.options})`); | ||
const result = await minify(data.code, options); | ||
const output: WorkerOutput = { | ||
code: result.code || workerData.code, | ||
code: result.code || data.code, | ||
nameCache: options.nameCache | ||
@@ -52,6 +55,4 @@ }; | ||
parentPort.postMessage(output); | ||
} catch (e) { | ||
process.exit(1); | ||
} | ||
parentPort?.postMessage(output); | ||
}); | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
27950
12
717
4