@rollup/plugin-terser
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -5,5 +5,125 @@ 'use strict'; | ||
var process = require('process'); | ||
var worker_threads = require('worker_threads'); | ||
var smob = require('smob'); | ||
var terser$1 = require('terser'); | ||
var os = require('os'); | ||
var events = require('events'); | ||
var serializeJavascript = require('serialize-javascript'); | ||
function terser(options) { | ||
/** | ||
* Duck typing worker context. | ||
* | ||
* @param input | ||
*/ | ||
function isWorkerContextSerialized(input) { | ||
return (smob.isObject(input) && | ||
smob.hasOwnProperty(input, 'code') && | ||
typeof input.code === 'string' && | ||
smob.hasOwnProperty(input, 'options') && | ||
typeof input.options === 'string'); | ||
} | ||
async function runWorker() { | ||
if (worker_threads.isMainThread || !worker_threads.parentPort || !isWorkerContextSerialized(worker_threads.workerData)) { | ||
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); | ||
const output = { | ||
code: result.code || worker_threads.workerData.code, | ||
nameCache: options.nameCache | ||
}; | ||
worker_threads.parentPort.postMessage(output); | ||
} | ||
catch (e) { | ||
process.exit(1); | ||
} | ||
} | ||
const symbol = Symbol.for('FreeWoker'); | ||
class WorkerPool extends events.EventEmitter { | ||
constructor(options) { | ||
super(); | ||
this.tasks = []; | ||
this.workers = 0; | ||
this.maxInstances = options.maxWorkers || os.cpus().length; | ||
this.filePath = options.filePath; | ||
this.on(symbol, () => { | ||
if (this.tasks.length > 0) { | ||
this.run(); | ||
} | ||
}); | ||
} | ||
add(context, cb) { | ||
this.tasks.push({ | ||
context, | ||
cb | ||
}); | ||
if (this.workers >= this.maxInstances) { | ||
return; | ||
} | ||
this.run(); | ||
} | ||
async addAsync(context) { | ||
return new Promise((resolve, reject) => { | ||
this.add(context, (err, output) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
if (!output) { | ||
reject(new Error('The output is empty')); | ||
return; | ||
} | ||
resolve(output); | ||
}); | ||
}); | ||
} | ||
run() { | ||
if (this.tasks.length === 0) { | ||
return; | ||
} | ||
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) | ||
} | ||
}); | ||
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}`)); | ||
} | ||
}); | ||
} | ||
} | ||
function terser(options = {}) { | ||
const workerPool = new WorkerPool({ | ||
filePath: __filename, | ||
maxWorkers: options.maxWorkers | ||
}); | ||
return { | ||
@@ -21,3 +141,35 @@ name: 'terser', | ||
} | ||
return terser$1.minify(code, { ...defaultOptions, ...(options || {}) }); | ||
try { | ||
const { code: result, nameCache } = await workerPool.addAsync({ | ||
code, | ||
options: smob.merge({}, options || {}, defaultOptions) | ||
}); | ||
if (options.nameCache && nameCache) { | ||
let vars = { | ||
props: {} | ||
}; | ||
if (smob.hasOwnProperty(options.nameCache, 'vars') && smob.isObject(options.nameCache.vars)) { | ||
vars = smob.merge({}, options.nameCache.vars || {}, vars); | ||
} | ||
if (smob.hasOwnProperty(nameCache, 'vars') && smob.isObject(nameCache.vars)) { | ||
vars = smob.merge({}, nameCache.vars, vars); | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
options.nameCache.vars = vars; | ||
let props = {}; | ||
if (smob.hasOwnProperty(options.nameCache, 'props') && smob.isObject(options.nameCache.props)) { | ||
// eslint-disable-next-line prefer-destructuring | ||
props = options.nameCache.props; | ||
} | ||
if (smob.hasOwnProperty(nameCache, 'props') && smob.isObject(nameCache.props)) { | ||
props = smob.merge({}, nameCache.props, props); | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
options.nameCache.props = props; | ||
} | ||
return result; | ||
} | ||
catch (e) { | ||
return Promise.reject(e); | ||
} | ||
} | ||
@@ -27,4 +179,6 @@ }; | ||
runWorker(); | ||
exports.default = terser; | ||
module.exports = Object.assign(exports.default, exports); | ||
//# sourceMappingURL=index.js.map |
@@ -0,4 +1,124 @@ | ||
import process from 'process'; | ||
import { isMainThread, parentPort, workerData, Worker } from 'worker_threads'; | ||
import { isObject, hasOwnProperty, merge } from 'smob'; | ||
import { minify } from 'terser'; | ||
import { cpus } from 'os'; | ||
import { EventEmitter } from 'events'; | ||
import serializeJavascript from 'serialize-javascript'; | ||
function terser(options) { | ||
/** | ||
* Duck typing worker context. | ||
* | ||
* @param input | ||
*/ | ||
function isWorkerContextSerialized(input) { | ||
return (isObject(input) && | ||
hasOwnProperty(input, 'code') && | ||
typeof input.code === 'string' && | ||
hasOwnProperty(input, 'options') && | ||
typeof input.options === 'string'); | ||
} | ||
async function runWorker() { | ||
if (isMainThread || !parentPort || !isWorkerContextSerialized(workerData)) { | ||
return; | ||
} | ||
try { | ||
// eslint-disable-next-line no-eval | ||
const eval2 = eval; | ||
const options = eval2(`(${workerData.options})`); | ||
const result = await minify(workerData.code, options); | ||
const output = { | ||
code: result.code || workerData.code, | ||
nameCache: options.nameCache | ||
}; | ||
parentPort.postMessage(output); | ||
} | ||
catch (e) { | ||
process.exit(1); | ||
} | ||
} | ||
const symbol = Symbol.for('FreeWoker'); | ||
class WorkerPool extends EventEmitter { | ||
constructor(options) { | ||
super(); | ||
this.tasks = []; | ||
this.workers = 0; | ||
this.maxInstances = options.maxWorkers || cpus().length; | ||
this.filePath = options.filePath; | ||
this.on(symbol, () => { | ||
if (this.tasks.length > 0) { | ||
this.run(); | ||
} | ||
}); | ||
} | ||
add(context, cb) { | ||
this.tasks.push({ | ||
context, | ||
cb | ||
}); | ||
if (this.workers >= this.maxInstances) { | ||
return; | ||
} | ||
this.run(); | ||
} | ||
async addAsync(context) { | ||
return new Promise((resolve, reject) => { | ||
this.add(context, (err, output) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
if (!output) { | ||
reject(new Error('The output is empty')); | ||
return; | ||
} | ||
resolve(output); | ||
}); | ||
}); | ||
} | ||
run() { | ||
if (this.tasks.length === 0) { | ||
return; | ||
} | ||
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) | ||
} | ||
}); | ||
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}`)); | ||
} | ||
}); | ||
} | ||
} | ||
function terser(options = {}) { | ||
const workerPool = new WorkerPool({ | ||
filePath: __filename, | ||
maxWorkers: options.maxWorkers | ||
}); | ||
return { | ||
@@ -16,3 +136,35 @@ name: 'terser', | ||
} | ||
return minify(code, { ...defaultOptions, ...(options || {}) }); | ||
try { | ||
const { code: result, nameCache } = await workerPool.addAsync({ | ||
code, | ||
options: merge({}, options || {}, defaultOptions) | ||
}); | ||
if (options.nameCache && nameCache) { | ||
let vars = { | ||
props: {} | ||
}; | ||
if (hasOwnProperty(options.nameCache, 'vars') && isObject(options.nameCache.vars)) { | ||
vars = merge({}, options.nameCache.vars || {}, vars); | ||
} | ||
if (hasOwnProperty(nameCache, 'vars') && isObject(nameCache.vars)) { | ||
vars = merge({}, nameCache.vars, vars); | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
options.nameCache.vars = vars; | ||
let props = {}; | ||
if (hasOwnProperty(options.nameCache, 'props') && isObject(options.nameCache.props)) { | ||
// eslint-disable-next-line prefer-destructuring | ||
props = options.nameCache.props; | ||
} | ||
if (hasOwnProperty(nameCache, 'props') && isObject(nameCache.props)) { | ||
props = merge({}, nameCache.props, props); | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
options.nameCache.props = props; | ||
} | ||
return result; | ||
} | ||
catch (e) { | ||
return Promise.reject(e); | ||
} | ||
} | ||
@@ -22,3 +174,5 @@ }; | ||
runWorker(); | ||
export { terser as default }; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@rollup/plugin-terser", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"publishConfig": { | ||
@@ -64,5 +64,8 @@ "access": "public" | ||
"dependencies": { | ||
"serialize-javascript": "^6.0.0", | ||
"smob": "^0.0.6", | ||
"terser": "^5.15.1" | ||
}, | ||
"devDependencies": { | ||
"@types/serialize-javascript": "^5.0.2", | ||
"rollup": "^3.0.0-7", | ||
@@ -69,0 +72,0 @@ "typescript": "^4.8.3" |
@@ -12,7 +12,7 @@ [npm]: https://img.shields.io/npm/v/@rollup/plugin-terser | ||
🍣 A Rollup plugin to generate a minified output bundle. | ||
🍣 A Rollup plugin to generate a minified bundle with terser. | ||
## Requirements | ||
This plugin requires an [LTS](https://github.com/nodejs/Release) Node version (v14.0.0+) and Rollup v1.20.0+. | ||
This plugin requires an [LTS](https://github.com/nodejs/Release) Node version (v14.0.0+) and Rollup v2.0+. | ||
@@ -31,3 +31,3 @@ ## Install | ||
```js | ||
```typescript | ||
import terser from '@rollup/plugin-terser'; | ||
@@ -52,2 +52,28 @@ | ||
In addition to the `terser` options, it is also possible to provide the following options: | ||
### `maxWorkers` | ||
Type: `Number`<br> | ||
Default: `undefined` | ||
Instructs the plugin to use a specific amount of cpu threads. | ||
```typescript | ||
import terser from '@rollup/plugin-terser'; | ||
export default { | ||
input: 'src/index.js', | ||
output: { | ||
dir: 'output', | ||
format: 'cjs' | ||
}, | ||
plugins: [ | ||
terser({ | ||
maxWorkers: 4 | ||
}) | ||
] | ||
}; | ||
``` | ||
## Meta | ||
@@ -58,6 +84,1 @@ | ||
[LICENSE (MIT)](/LICENSE) | ||
## Credits | ||
This package was originally developed by [https://github.com/TrySound](TrySound) but is not | ||
maintained anymore. |
@@ -1,24 +0,8 @@ | ||
import { NormalizedOutputOptions, RenderedChunk } from 'rollup'; | ||
import { minify, MinifyOptions } from 'terser'; | ||
import { runWorker } from './worker'; | ||
import terser from './module'; | ||
export default function terser(options?: MinifyOptions) { | ||
return { | ||
name: 'terser', | ||
runWorker(); | ||
async renderChunk(code: string, chunk: RenderedChunk, outputOptions: NormalizedOutputOptions) { | ||
const defaultOptions: MinifyOptions = { | ||
sourceMap: outputOptions.sourcemap === true || typeof outputOptions.sourcemap === 'string' | ||
}; | ||
export * from './type'; | ||
if (outputOptions.format === 'es') { | ||
defaultOptions.module = true; | ||
} | ||
if (outputOptions.format === 'cjs') { | ||
defaultOptions.toplevel = true; | ||
} | ||
return minify(code, { ...defaultOptions, ...(options || {}) }); | ||
} | ||
}; | ||
} | ||
export default terser; |
@@ -1,3 +0,3 @@ | ||
import { Plugin } from 'rollup'; | ||
import { MinifyOptions } from 'terser'; | ||
import type { Plugin } from 'rollup'; | ||
import type { MinifyOptions } from 'terser'; | ||
@@ -4,0 +4,0 @@ /** |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
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
21976
11
568
81
4
3
3
+ Addedserialize-javascript@^6.0.0
+ Addedsmob@^0.0.6
+ Addedrandombytes@2.1.0(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedserialize-javascript@6.0.2(transitive)
+ Addedsmob@0.0.6(transitive)
+ Addedterser@5.34.1(transitive)
- Removedterser@5.33.0(transitive)