Import Upstream version 7.0.2+~5.0.1

This commit is contained in:
Lu zhiping 2022-07-16 11:47:24 +08:00
commit 2a6dbef7ee
37 changed files with 5142 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

5
.travis.yml Normal file
View File

@ -0,0 +1,5 @@
language: node_js
node_js:
- "14"
- "12"
- "10"

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2018 Bogdan Chadkin <trysound@yandex.ru>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

106
README.md Normal file
View File

@ -0,0 +1,106 @@
# rollup-plugin-terser [![Travis Build Status][travis-img]][travis]
[travis-img]: https://travis-ci.org/TrySound/rollup-plugin-terser.svg
[travis]: https://travis-ci.org/TrySound/rollup-plugin-terser
[Rollup](https://github.com/rollup/rollup) plugin to minify generated es bundle. Uses [terser](https://github.com/fabiosantoscode/terser) under the hood.
## Install
```sh
yarn add rollup-plugin-terser --dev
# Or with npm:
npm i rollup-plugin-terser --save-dev
```
_Note: this package requires rollup@0.66 and higher (including rollup@2.0.0)_
## Usage
```js
import { rollup } from "rollup";
import { terser } from "rollup-plugin-terser";
rollup({
input: "main.js",
plugins: [terser()],
});
```
## Why named export?
1. Module is a namespace. Default export often leads to function/component per file dogma and makes code less maintainable.
2. Interop with commonjs is broken in many cases or hard to maintain.
3. Show me any good language with default exports. It's historical javascriptism.
## Options
> ⚠️ **Caveat:** any function used in options object cannot rely on its surrounding scope, since it is executed in an isolated context.
```js
terser(options);
```
`options` - [terser API options](https://github.com/fabiosantoscode/terser#minify-options)
Note: some terser options are set by the plugin automatically:
- `module: true` is set when `format` is `esm` or `es`
- `toplevel: true` is set when `format` is `cjs`
`options.numWorkers: number`
Amount of workers to spawn. Defaults to the number of CPUs minus 1.
## Examples
### Using as output plugin
```js
// rollup.config.js
import { terser } from "rollup-plugin-terser";
export default {
input: "index.js",
output: [
{ file: "lib.js", format: "cjs" },
{ file: "lib.min.js", format: "cjs", plugins: [terser()] },
{ file: "lib.esm.js", format: "esm" },
],
};
```
### Comments
If you'd like to preserve comments (for licensing for example), then you can specify a function to do this like so:
```js
terser({
output: {
comments: function (node, comment) {
var text = comment.value;
var type = comment.type;
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
},
},
});
```
Alternatively, you can also choose to keep all comments (e.g. if a licensing header has already been prepended by a previous rollup plugin):
```js
terser({
output: {
comments: "all",
},
});
```
See [Terser documentation](https://github.com/fabiosantoscode/terser#terser) for further reference.
# License
MIT © [Bogdan Chadkin](mailto:trysound@yandex.ru)

5
Xascjs/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.DS_Store
.nyc_output/
coverage/
node_modules/
package-lock.json

8
Xascjs/.npmignore Normal file
View File

@ -0,0 +1,8 @@
.nyc_output/*
coverage/*
node_modules/*
test/*
.gitignore
.travis.yml
test.js
package-lock.json

11
Xascjs/.travis.yml Normal file
View File

@ -0,0 +1,11 @@
language: node_js
node_js:
- stable
git:
depth: 1
branches:
only:
- master
- /^greenkeeper/.*$/
after_success:
- "npm run coveralls"

15
Xascjs/LICENSE.txt Normal file
View File

@ -0,0 +1,15 @@
ISC License
Copyright (c) 2017, Andrea Giammarchi, @WebReflection
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

107
Xascjs/README.md Normal file
View File

@ -0,0 +1,107 @@
# ascjs
[![License: ISC](https://img.shields.io/badge/License-ISC-yellow.svg)](https://opensource.org/licenses/ISC) [![Build Status](https://travis-ci.org/WebReflection/ascjs.svg?branch=master)](https://travis-ci.org/WebReflection/ascjs) [![Coverage Status](https://coveralls.io/repos/github/WebReflection/ascjs/badge.svg?branch=master)](https://coveralls.io/github/WebReflection/ascjs?branch=master) [![donate](https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat)](https://github.com/WebReflection/donate) [![Greenkeeper badge](https://badges.greenkeeper.io/WebReflection/ascjs.svg)](https://greenkeeper.io/)
ES2015 to CommonJS import/export transformer
- - -
### Looking for a CommonJS minimalistic bundler ?
Fully based on _ascjs_, **[asbundle](https://github.com/WebReflection/asbundle)** is a no-brainer to create out of the box browser compatible bundles. Don't miss it out!
- - -
This module does one thing only:
it loosely transpiles **ES2015** [import](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/import)/[export](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) statements **into** valid **CommonJS** in order to fix the only part of Node that's incompatible with modern JS.
## How to
You can use _ascjs_ as binary utility or as module.
```sh
npm install -g ascjs
# to see what you can do
ascjs --help
```
As executable, you can use _ascjs_ to output, or save, some code content.
```sh
ascjs code
ascjs --ignore=a.js,b.js sourceFile
ascjs --no-default
ascjs sourceFile
ascjs sourceFile destFile
# folders are recursively parsed
# destFolder is mandatory
ascjs sourceFolder destFolder
```
You can also use it via pipe operator.
```sh
echo code | ascjs
cat source.js | ascjs | uglifyjs -o dest.js
```
As module, you can require it and use it to convert ESM to CJS.
```js
const ascjs = require('ascjs');
ascjs('import "test";');
// require("test");
```
### Features
* extremely lightweight, based on [babylon](https://github.com/babel/babylon) for performance and reliability, it transforms only imports/exports ignoring everything else
* produces modern JavaScript, you are in charge of extra transformations if needed
* indentation, spaces, semi or no-semi are preserved: beautiful source code remains beautiful
* uses same [Babel](http://babeljs.io) convention, resolving `export default ...` intent as `exports.default`
* you can finally write `.js` code and transform it for Node only before publishing on _npm_
* you could write `.mjs` modules and transform them into CommonJS for [Browserify](http://browserify.org) or other bundlers as target
### Constrains
* live bindings for exported values are not preserved. You need to delegate in scope eventual changes
* dynamic `import(...)` is untouched. If you write that, let [Webpack](https://webpack.js.org) handle it for you later on
* there is no magic whatsoever in module names resolution, what you write in ESM is what you get as CJS
### Flags
* `--ignore=...` a comma separated paths to ignore parsing
* `--no-default` remove the `__esModule` flag and export default via `module.exports = `
### Example
This module can transform the following ES2015+ code
```js
import func, {a, b} from './module.js';
import * as tmp from 'other';
const val = 123;
export default function test() {
console.log('ascjs');
};
export {func, val};
```
into the following one:
```js
'use strict';
const func = (m => m.__esModule ? m.default : m)(require('./module.js'));
const {a, b} = require('./module.js');
const tmp = require('other');
const val = 123;
function test() {
console.log('ascjs');
}
Object.defineProperty(exports, '__esModule', {value: true}).default = test;
exports.func = func;
exports.val = val;
```

127
Xascjs/bin.js Executable file
View File

@ -0,0 +1,127 @@
#!/usr/bin/env node
const path = require('path');
const $ascjs = require('./index.js');
const ascjs = input => {
const output = $ascjs(input);
return noDefault ?
output.replace(`${$ascjs.EXPORT}.default`, 'module.exports') :
output;
};
const argv = process.argv.slice(2);
const files = argv.filter(arg => /^[^-]/.test(arg));
const options = argv.filter(arg => /^-/.test(arg));
let noDefault = false;
const ignore = [];
options.forEach(arg => {
if (/^--no-default$/.test(arg))
noDefault = true;
else if (/^--ignore=/.test(arg))
ignore.push.apply(
ignore,
arg.slice(9).replace(/^('|")|('|")$/g, '').split(',')
.map(file => path.resolve(__dirname, file))
);
});
const source = files[0];
if (files.length < 1 && options.length) {
const info = require('./package.json');
console.log(`
\x1B[1mascjs\x1B[0m v${info.version}
${'-'.repeat(info.description.length)}
${info.description}
${'-'.repeat(info.description.length)}
# as executable
ascjs code
ascjs --ignore=a.js,b.js sourceFile
ascjs --no-default
ascjs sourceFile destFile
ascjs sourceFolder destFolder # dest is required
# as pipe
echo code | ascjs
cat sourceFile | ascjs
${'-'.repeat(info.description.length)}
${' '.repeat(info.description.length)
.slice(0, -(3 + info.author.length))}by ${info.author}
`);
} else if (files.length) {
const fs = require('fs');
const dest = files[1];
fs.stat(source, (err, stat) => {
if (err) {
process.stdout.write(ascjs(source));
} else if (stat.isFile()) {
fs.readFile(source, (err, source) => {
if (err) throw err;
if (dest) fs.writeFileSync(dest, ascjs(source));
else process.stdout.write(ascjs(source));
});
} else if (stat.isDirectory() && dest && fs.statSync(dest).isDirectory()) {
const cjsDest = path.resolve(process.cwd(), dest);
process.on('exit', () => {
const cjsPackage = path.join(cjsDest, 'package.json');
if (!fs.existsSync(cjsPackage))
fs.writeFileSync(cjsPackage, JSON.stringify({type: 'commonjs'}));
});
const mkdir = dir => {
try{ fs.mkdirSync(dir); }
catch(e){
if(e.errno === 34){
mkdir(path.dirname(dir));
mkdir(dir);
}
}
};
(function walkThrough(source, dest) {
fs.readdir(source, (err, files) => {
if (err) throw err;
files.forEach(file => {
if (ignore.includes(path.join(source, file))) return;
fs.stat(path.join(source, file), (err, stat) => {
if (err) throw err;
switch (true) {
case stat.isDirectory():
walkThrough(path.join(source, file), path.join(dest, file));
break;
case stat.isFile():
if (/\.(?:m\.?)?js$/.test(file)) {
fs.readFile(path.join(source, file), (err, content) => {
if (err) throw err;
mkdir(dest);
fs.writeFile(
path.join(dest, file),
ascjs(content),
(err) => {
if (err) throw err;
}
);
});
}
break;
}
});
});
});
}(
path.resolve(process.cwd(), source),
cjsDest
));
} else {
throw new Error('not sure what to do, try ascjs --help\n');
}
});
} else {
const chunks = [];
process.stdin.on('data', data => {
chunks.push(data);
});
process.stdin.on('end', () => {
process.stdout.write(ascjs(chunks.join('')));
});
}

199
Xascjs/index.js Normal file
View File

@ -0,0 +1,199 @@
'use strict';
const parser = require('@babel/parser');
const defaultOptions = {
allowAwaitOutsideFunction: true,
sourceType: 'module',
plugins: [
// 'estree',
'jsx',
'typescript',
'exportExtensions',
'exportDefaultFrom',
'exportNamespaceFrom',
'dynamicImport',
'importMeta',
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
['decorators', {decoratorsBeforeExport: true}],
'doExpressions',
'functionBind',
'functionSent',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'partialApplication',
['pipelineOperator', {proposal: 'minimal'}],
'throwExpressions',
'topLevelAwait'
]
};
const EXPORT = `Object.defineProperty(exports, '__esModule', {value: true})`;
const IMPORT = `(m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)`;
const asDefault = name => name === 'default' ? `${parse.info.EXPORT}.default` : `exports.${name}`;
const fromDefault = defaultImport => `${parse.info.IMPORT}(${defaultImport})`;
const slice = (code, info) => code.slice(info.start, info.end);
const chunk = (info, esm, cjs) => ({
start: info.start,
end: info.end,
esm, cjs
});
const replace = {
ImportDeclaration(code, item) {
const source = item.source;
const name = withoutCDN(slice(code, source));
const esm = slice(code, item);
const SEPS = /\{(\s+)/.test(esm) ? RegExp.$1 : '';
const SEPE = /(\s+)\}/.test(esm) ? RegExp.$1 : '';
const SEP = /(,\s+)[^{]/.test(esm) ? RegExp.$1 : ', ';
const EOL = /;$/.test(esm) ? ';' : '';
const imported = [];
const specifiers = [];
let defaultImport = `require(${name})`;
if (item.specifiers.length) {
item.specifiers.forEach(specifier => {
switch(specifier.type) {
case 'ImportDefaultSpecifier':
imported.push(
`const ${specifier.local.name} = ${fromDefault(defaultImport)}${EOL}`
);
break;
case 'ImportNamespaceSpecifier':
imported.push(
`const ${specifier.local.name} = ${defaultImport}${EOL}`
);
break;
case 'ImportSpecifier':
specifiers.push(
specifier.local.name === specifier.imported.name ?
specifier.local.name :
`${specifier.imported.name}: ${specifier.local.name}`
);
break;
}
});
if (specifiers.length) {
imported.push(
`const {${SEPS}${specifiers.join(SEP)}${SEPE}} = ${defaultImport}${EOL}`
);
}
} else {
imported.push(`${defaultImport}${EOL}`);
}
return chunk(item, esm, imported.join('\n'));
},
ExportAllDeclaration(code, item) {
const source = item.source;
const esm = slice(code, item);
const cjs = `(m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k])))\n(require(${
withoutCDN(slice(code, source))
}));`;
return chunk(item, esm, cjs);
},
ExportDefaultDeclaration(code, item) {
const declaration = item.declaration;
const esm = slice(code, item);
let cjs;
switch (declaration.type) {
case 'AssignmentExpression':
case 'FunctionDeclaration':
if (declaration.id) {
cjs = `${esm.replace(/^export\s+default\s+/, '')}\n${parse.info.EXPORT}.default = ${declaration.id.name}`;
} else {
cjs = esm.replace(/^export\s+default\s+/, `${parse.info.EXPORT}.default = `);
}
break;
case 'Identifier':
case 'ObjectExpression':
default:
cjs = esm.replace(/^export\s+default\s+/, `${parse.info.EXPORT}.default = `);
break;
}
return chunk(item, esm, cjs);
},
ExportNamedDeclaration(code, item) {
const declaration = item.declaration;
const source = item.source;
const esm = slice(code, item);
const EOL = /;$/.test(esm) ? ';\n' : '\n';
let cjs = source ? '(m => {\n' : '';
item.specifiers.forEach(specifier => {
cjs += `${
source ? ' ' : ''
}${asDefault(specifier.exported.name)} = ${
source ? 'm.' : ''
}${specifier.local.name}${EOL}`;
});
if (declaration) {
cjs += esm.replace(/^export\s+/, '') + '\n';
(declaration.declarations || [declaration]).forEach(specifier => {
cjs += `${asDefault(specifier.id.name)} = ${specifier.id.name}${EOL}`;
});
}
if (source) cjs += `})(require(${
withoutCDN(slice(code, source))
}));\n`;
return chunk(item, esm, cjs.trim());
}
};
const parse = (code, options) => {
if (!options) options = {};
const parserOptions = Object.assign(
{},
defaultOptions,
options
);
parse.info = {
EXPORT: options.EXPORT || EXPORT,
IMPORT: options.IMPORT || IMPORT
};
delete options.EXPORT;
delete options.IMPORT;
code = code.toString();
const out = [];
const chunks = [];
const parsed = parser.parse(code, parserOptions);
parsed.program.body.forEach(item => {
if (replace.hasOwnProperty(item.type)) {
chunks.push(replace[item.type](code, item));
}
});
const length = chunks.length;
let c = 0;
for (let i = 0; i < length; i++) {
out.push(
code.slice(c, chunks[i].start),
chunks[i].cjs
);
c = chunks[i].end;
}
out.push(length ? code.slice(c) : code);
let result = out.join('').replace(
/\bimport\.meta\b/g,
"({url: require('url').pathToFileURL(__filename).href})"
);
return /^(?:#!|['"]use strict['"])/.test(result.trim()) ?
result :
("'use strict';\n" + result);
};
const withoutCDN = name =>
/^(['"`])https?:\/\/(?:unpkg\.com)\/([^@/]+)\S*?\1$/.test(name) ?
`${RegExp.$1}${RegExp.$2}${RegExp.$1}` : name;
parse.EXPORT = EXPORT;
parse.IMPORT = IMPORT;
module.exports = parse;

36
Xascjs/package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "ascjs",
"version": "5.0.1",
"description": "ES2015 to CommonJS import/export transformer",
"bin": "bin.js",
"main": "index.js",
"scripts": {
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"test": "nyc node test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/WebReflection/ascjs.git"
},
"keywords": [
"cjs",
"esm",
"transpile",
"convert",
"safe",
"module"
],
"author": "Andrea Giammarchi",
"license": "ISC",
"bugs": {
"url": "https://github.com/WebReflection/ascjs/issues"
},
"homepage": "https://github.com/WebReflection/ascjs#readme",
"dependencies": {
"@babel/parser": "^7.12.5"
},
"devDependencies": {
"coveralls": "^3.1.0",
"nyc": "^15.1.0"
}
}

159
Xascjs/test.js Normal file
View File

@ -0,0 +1,159 @@
const ascjs = require('./index.js');
const bold = text => `\x1B[1m${text}\x1B[0m`;
const tests = [
{
esm: 'console.log(import.meta.url);',
cjs: `console.log(({url: require('url').pathToFileURL(__filename).href}).url);`
},
// nothing to do
{
esm: '',
cjs: ''
},
// exports
{
esm: `const name1 = 1, name2 = 2, nameN = 3; export { name1, name2, nameN }`,
cjs: `const name1 = 1, name2 = 2, nameN = 3; exports.name1 = name1\nexports.name2 = name2\nexports.nameN = nameN`
},
{
esm: `export { name1, name2, nameN } from "module"`,
cjs: `(m => {\n exports.name1 = m.name1\n exports.name2 = m.name2\n exports.nameN = m.nameN\n})(require("module"));`
},
{
esm: `const name0 = 0, name1 = 1; export { name0, name1 as default };`,
cjs: `const name0 = 0, name1 = 1; exports.name0 = name0;\nObject.defineProperty(exports, '__esModule', {value: true}).default = name1;`
},
{
esm: `export { name0, name1 as default } from "shenanigans"`,
cjs: `(m => {\n exports.name0 = m.name0\n Object.defineProperty(exports, '__esModule', {value: true}).default = m.name1\n})(require("shenanigans"));`
},
{
esm: `const variable1 = 1, variable2 = 2, nameN = 3; export { variable1 as name1, variable2 as name2, nameN };`,
cjs: `const variable1 = 1, variable2 = 2, nameN = 3; exports.name1 = variable1;\nexports.name2 = variable2;\nexports.nameN = nameN;`
},
{
esm: `export let name1 = 1, name2 = function () {}, nameN = 'N';`,
cjs: `let name1 = 1, name2 = function () {}, nameN = 'N';\nexports.name1 = name1;\nexports.name2 = name2;\nexports.nameN = nameN;`
},
{
esm: `export default function () {};`,
cjs: `Object.defineProperty(exports, '__esModule', {value: true}).default = function () {};`
},
{
esm: `export default function named() {};`,
cjs: `function named() {}\nObject.defineProperty(exports, '__esModule', {value: true}).default = named;`
},
{
esm: `export default sideEffect = true;`,
cjs: `Object.defineProperty(exports, '__esModule', {value: true}).default = sideEffect = true;`
},
{
esm: `export const identifier = 123;`,
cjs: `const identifier = 123;\nexports.identifier = identifier;`
},
{
esm: `const identifier = 123;\nexport {identifier};`,
cjs: `const identifier = 123;\nexports.identifier = identifier;`
},
{
esm: `const identifier = 123;\nexport default identifier;`,
cjs: `const identifier = 123;\nObject.defineProperty(exports, '__esModule', {value: true}).default = identifier;`
},
{
esm: `export * from "foo";`,
cjs: `(m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k])))\n(require("foo"));`
},
// imports
{
esm: `import * as name from "module-name";`,
cjs: `const name = require("module-name");`
},
{
esm: `import { name } from "module-name";`,
cjs: `const { name } = require("module-name");`
},
{
esm: `import {name} from "module-name"`,
cjs: `const {name} = require("module-name")`
},
{
esm: `import { name as alias } from "module-name";`,
cjs: `const { name: alias } = require("module-name");`
},
{
esm: `import { export1 , export2 } from "module-name";`,
cjs: `const { export1, export2 } = require("module-name");`
},
{
esm: `import { export1 , export2 as alias2 } from "module-name";`,
cjs: `const { export1, export2: alias2 } = require("module-name");`
},
{
esm: `import defaultExport, { otherExport } from "module-name"`,
cjs: `const defaultExport = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require("module-name"))\nconst { otherExport } = require("module-name")`
},
{
esm: `import defaultExport, * as name from "module-name";`,
cjs: `const defaultExport = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require("module-name"));\nconst name = require("module-name");`
},
{
esm: `import "foo";`,
cjs: `require("foo");`
},
{
esm: `import 'foo';`,
cjs: `require('foo');`
},
{
esm: `import "foo"`,
cjs: `require("foo")`
},
{
esm: `export function test() {}`,
cjs: `function test() {}\nexports.test = test`
},
{
esm: `export default function () {}`,
cjs: `Object.defineProperty(exports, '__esModule', {value: true}).default = function () {}`
},
{
esm: `export default function test() {};`,
cjs: `function test() {}\nObject.defineProperty(exports, '__esModule', {value: true}).default = test;`
},
{
esm: `import "https://unpkg.com/hyperhtml@latest/min.js"`,
cjs: `require("hyperhtml")`
},
{
esm: `export default {a: 1, b: 2};`,
cjs: `Object.defineProperty(exports, '__esModule', {value: true}).default = {a: 1, b: 2};`
},
{
esm: `export default [1, 2, 3]`,
cjs: `Object.defineProperty(exports, '__esModule', {value: true}).default = [1, 2, 3]`
},
{
esm: `'use strict';\nmodule.exports = 123;`,
cjs: `module.exports = 123;`
}
];
console.log(`${bold('ascjs')} v${require('./package.json').version} - ${tests.length} tests\n`);
tests.forEach(code => {
console.assert(
("'use strict';\n" + code.cjs) === ascjs(code.esm),
`\n${bold('Source')}\n${code.esm}\n${bold('Expected')}\n${code.cjs}\n${bold('Received')}\n${ascjs(code.esm)}\n`
);
});
console.log(
ascjs(
'import I from "test";\nexport default function E() {}',
{IMPORT: 'require.I', EXPORT: 'require.E(exports)'}
) ===
`'use strict';\nconst I = require.I(require("test"));\nfunction E() {}\nrequire.E(exports).default = E`,
'both import and export can be overwritten'
);

2
Xascjs/test/default.cjs Normal file
View File

@ -0,0 +1,2 @@
'use strict';
module.exports = 123;

1
Xascjs/test/default.mjs Normal file
View File

@ -0,0 +1 @@
export default 123;

View File

@ -0,0 +1 @@
{"type":"commonjs"}

16
Xascjs/test/dest/test.js Normal file
View File

@ -0,0 +1,16 @@
'use strict';
const func = (m => m.__esModule ? m.default : m)(require('./module.js'));
const {a, b} = require('./module.js');
const tmp = require('other');
console.log(({url: require('url').pathToFileURL(__filename).href}).url);
const val = 123;
function test() {
console.log('ascjs');
}
Object.defineProperty(exports, '__esModule', {value: true}).default = test;
exports.func = func;
exports.val = val;

View File

@ -0,0 +1,12 @@
import func, {a, b} from './module.js';
import * as tmp from 'other';
console.log(import.meta.url);
const val = 123;
export default function test() {
console.log('ascjs');
};
export {func, val};

49
package.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "rollup-plugin-terser",
"version": "7.0.2",
"description": "Rollup plugin to minify generated es bundle",
"type": "commonjs",
"main": "rollup-plugin-terser.js",
"types": "rollup-plugin-terser.d.ts",
"exports": {
"require": "./rollup-plugin-terser.js",
"import": "./rollup-plugin-terser.mjs"
},
"files": [
"rollup-plugin-terser.js",
"rollup-plugin-terser.mjs",
"rollup-plugin-terser.d.ts",
"transform.js"
],
"scripts": {
"test": "jest",
"prepublish": "yarn test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/TrySound/rollup-plugin-terser.git"
},
"keywords": [
"rollup",
"rollup-plugin",
"terser",
"minify"
],
"author": "Bogdan Chadkin <trysound@yandex.ru>",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"jest-worker": "^26.2.1",
"serialize-javascript": "^4.0.0",
"terser": "^5.0.0"
},
"peerDependencies": {
"rollup": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.11.1",
"jest": "^26.2.2",
"prettier": "^2.0.5",
"rollup": "^2.23.1"
}
}

11
rollup-plugin-terser.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
import { Plugin } from "rollup";
import { MinifyOptions } from "terser";
export interface Options extends Omit<MinifyOptions, "sourceMap"> {
/**
* Amount of workers to spawn. Defaults to the number of CPUs minus 1.
*/
numWorkers?: number;
}
export declare function terser(options?: Options): Plugin;

102
rollup-plugin-terser.js Normal file
View File

@ -0,0 +1,102 @@
const { codeFrameColumns } = require("@babel/code-frame");
const Worker = require("jest-worker").default;
const serialize = require("serialize-javascript");
function terser(userOptions = {}) {
if (userOptions.sourceMap != null) {
throw Error(
"sourceMap option is removed. Now it is inferred from rollup options."
);
}
if (userOptions.sourcemap != null) {
throw Error(
"sourcemap option is removed. Now it is inferred from rollup options."
);
}
return {
name: "terser",
async renderChunk(code, chunk, outputOptions) {
if (!this.worker) {
this.worker = new Worker(require.resolve("./transform.js"), {
numWorkers: userOptions.numWorkers,
});
this.numOfBundles = 0;
}
this.numOfBundles++;
const defaultOptions = {
sourceMap:
outputOptions.sourcemap === true ||
typeof outputOptions.sourcemap === "string",
};
if (outputOptions.format === "es" || outputOptions.format === "esm") {
defaultOptions.module = true;
}
if (outputOptions.format === "cjs") {
defaultOptions.toplevel = true;
}
const normalizedOptions = { ...defaultOptions, ...userOptions };
// remove plugin specific options
for (let key of ["numWorkers"]) {
if (normalizedOptions.hasOwnProperty(key)) {
delete normalizedOptions[key];
}
}
const serializedOptions = serialize(normalizedOptions);
try {
const result = await this.worker.transform(code, serializedOptions);
if (result.nameCache) {
let { vars, props } = userOptions.nameCache;
// only assign nameCache.vars if it was provided, and if terser produced values:
if (vars) {
const newVars =
result.nameCache.vars && result.nameCache.vars.props;
if (newVars) {
vars.props = vars.props || {};
Object.assign(vars.props, newVars);
}
}
// support populating an empty nameCache object:
if (!props) {
props = userOptions.nameCache.props = {};
}
// merge updated props into original nameCache object:
const newProps =
result.nameCache.props && result.nameCache.props.props;
if (newProps) {
props.props = props.props || {};
Object.assign(props.props, newProps);
}
}
return result.result;
} catch (error) {
const { message, line, col: column } = error;
console.error(
codeFrameColumns(code, { start: { line, column } }, { message })
);
throw error;
} finally {
this.numOfBundles--;
if (this.numOfBundles === 0) {
this.worker.end();
this.worker = 0;
}
}
},
};
}
exports.terser = terser;

3
rollup-plugin-terser.mjs Normal file
View File

@ -0,0 +1,3 @@
import terserModule from "./rollup-plugin-terser.js";
export const terser = terserModule.terser;

View File

@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`works with code splitting 1`] = `
Object {
"chunk-1.js": Object {
"code": "console.log(\\"chunk-1\\");
",
"dynamicImports": Array [],
"exports": Array [],
"fileName": "chunk-1.js",
"implicitlyLoadedBefore": Array [],
"imports": Array [],
"isDynamicEntry": false,
"isEntry": true,
"isImplicitEntry": false,
"map": null,
"name": "chunk-1",
"referencedFiles": Array [],
"type": "chunk",
},
"chunk-2.js": Object {
"code": "console.log(\\"chunk-2\\");
",
"dynamicImports": Array [],
"exports": Array [],
"fileName": "chunk-2.js",
"implicitlyLoadedBefore": Array [],
"imports": Array [],
"isDynamicEntry": false,
"isEntry": true,
"isImplicitEntry": false,
"map": null,
"name": "chunk-2",
"referencedFiles": Array [],
"type": "chunk",
},
}
`;

2
test/fixtures/chunk-1.js vendored Normal file
View File

@ -0,0 +1,2 @@
var chunk1 = 'chunk-1';
console.log(chunk1)

2
test/fixtures/chunk-2.js vendored Normal file
View File

@ -0,0 +1,2 @@
var chunk2 = 'chunk-2';
console.log(chunk2)

12
test/fixtures/commented.js vendored Normal file
View File

@ -0,0 +1,12 @@
"use strict";
window.a = 5;
/* @preserve this comment */
/* delete this comment */
// and this one too
if (window.a < 3) {
console.log(4);
}

0
test/fixtures/empty.js vendored Normal file
View File

1
test/fixtures/export-number.js vendored Normal file
View File

@ -0,0 +1 @@
export default 5;

1
test/fixtures/failed.js vendored Normal file
View File

@ -0,0 +1 @@
var a = 1;

4
test/fixtures/plain-file.js vendored Normal file
View File

@ -0,0 +1,4 @@
"use strict";
const foo = 'bar';
console.log(foo);

View File

@ -0,0 +1,9 @@
function recurse(count) {
if (count > 0) return recurse(count - 1);
return count;
}
const obj = {
foo: 1,
_priv: 2
};
console.log(obj, recurse(10));

5
test/fixtures/properties.js vendored Normal file
View File

@ -0,0 +1,5 @@
const obj = {
foo: 1,
_priv: 2
};
console.log(obj);

3
test/fixtures/sourcemap.js vendored Normal file
View File

@ -0,0 +1,3 @@
import result from './export-number.js';
console.log(result);

6
test/fixtures/unminified.js vendored Normal file
View File

@ -0,0 +1,6 @@
"use strict";
window.a = 5;
if (window.a < 3) {
console.log(4);
}

473
test/test.js Normal file
View File

@ -0,0 +1,473 @@
const { rollup } = require("rollup");
const { terser } = require("../");
test("minify", async () => {
const bundle = await rollup({
input: "test/fixtures/unminified.js",
plugins: [terser()],
});
const result = await bundle.generate({ format: "cjs" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual(
'"use strict";window.a=5,window.a<3&&console.log(4);\n'
);
expect(output.map).toBeFalsy();
});
test("minify via terser options", async () => {
const bundle = await rollup({
input: "test/fixtures/empty.js",
plugins: [terser({ output: { comments: "all" } })],
});
const result = await bundle.generate({
banner: "/* package name */",
format: "cjs",
});
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual('/* package name */\n"use strict";\n');
expect(output.map).toBeFalsy();
});
test("minify multiple outputs", async () => {
const bundle = await rollup({
input: "test/fixtures/unminified.js",
plugins: [terser()],
});
const [bundle1, bundle2] = await Promise.all([
bundle.generate({ format: "cjs" }),
bundle.generate({ format: "es" }),
]);
const [output1] = bundle1.output;
const [output2] = bundle2.output;
expect(output1.code).toEqual(
'"use strict";window.a=5,window.a<3&&console.log(4);\n'
);
expect(output2.code).toEqual("window.a=5,window.a<3&&console.log(4);\n");
});
test("minify esm module", async () => {
const bundle = await rollup({
input: "test/fixtures/plain-file.js",
plugins: [terser()],
});
const result = await bundle.generate({ format: "esm" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual('console.log("bar");\n');
});
test("minify esm module with disabled module option", async () => {
const bundle = await rollup({
input: "test/fixtures/plain-file.js",
plugins: [terser({ module: false })],
});
const result = await bundle.generate({ format: "esm" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual('const foo="bar";console.log(foo);\n');
});
test("minify cjs module", async () => {
const bundle = await rollup({
input: "test/fixtures/plain-file.js",
plugins: [terser()],
});
const result = await bundle.generate({ format: "cjs" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual('"use strict";console.log("bar");\n');
});
test("minify cjs module with disabled toplevel option", async () => {
const bundle = await rollup({
input: "test/fixtures/plain-file.js",
plugins: [terser({ toplevel: false })],
});
const result = await bundle.generate({ format: "cjs" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual(
'"use strict";const foo="bar";console.log(foo);\n'
);
});
test("minify with sourcemaps", async () => {
const bundle = await rollup({
input: "test/fixtures/sourcemap.js",
plugins: [terser()],
});
const result = await bundle.generate({ format: "cjs", sourcemap: true });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.map).toMatchInlineSnapshot(`
SourceMap {
"file": "sourcemap.js",
"mappings": "aAEAA,QAAQC,ICFO",
"names": Array [
"console",
"log",
],
"sources": Array [
"test/fixtures/sourcemap.js",
"test/fixtures/export-number.js",
],
"sourcesContent": Array [
"import result from './export-number.js';
console.log(result);
",
"export default 5;
",
],
"version": 3,
}
`);
});
test('work with sourcemap: "inline"', async () => {
const bundle = await rollup({
input: "test/fixtures/sourcemap.js",
plugins: [terser()],
});
const result = await bundle.generate({ format: "cjs", sourcemap: "inline" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.map).toMatchInlineSnapshot(`
SourceMap {
"file": "sourcemap.js",
"mappings": "aAEAA,QAAQC,ICFO",
"names": Array [
"console",
"log",
],
"sources": Array [
"test/fixtures/sourcemap.js",
"test/fixtures/export-number.js",
],
"sourcesContent": Array [
"import result from './export-number.js';
console.log(result);
",
"export default 5;
",
],
"version": 3,
}
`);
});
test("does not allow to pass sourcemap option", async () => {
try {
const bundle = await rollup({
input: "test/fixtures/sourcemap.js",
plugins: [terser({ sourcemap: false })],
});
expect(true).toBeFalsy();
} catch (error) {
expect(error.toString()).toMatch(/sourcemap option is removed/);
}
});
test("does not allow to pass sourceMap option", async () => {
try {
const bundle = await rollup({
input: "test/fixtures/sourcemap.js",
plugins: [terser({ sourceMap: false })],
});
expect(true).toBeFalsy();
} catch (error) {
expect(error.toString()).toMatch(/sourceMap option is removed/);
}
});
test("throw error on terser fail", async () => {
try {
const bundle = await rollup({
input: "test/fixtures/failed.js",
plugins: [
{
renderChunk: () => ({ code: "var = 1" }),
},
terser(),
],
});
await bundle.generate({ format: "esm" });
expect(true).toBeFalsy();
} catch (error) {
expect(error.toString()).toMatch(/Name expected/);
}
});
test("throw error on terser fail with multiple outputs", async () => {
try {
const bundle = await rollup({
input: "test/fixtures/failed.js",
plugins: [
{
renderChunk: () => ({ code: "var = 1" }),
},
terser(),
],
});
await Promise.all([
bundle.generate({ format: "cjs" }),
bundle.generate({ format: "esm" }),
]);
expect(true).toBeFalsy();
} catch (error) {
expect(error.toString()).toMatch(/Name expected/);
}
});
test("works with code splitting", async () => {
const bundle = await rollup({
input: ["test/fixtures/chunk-1.js", "test/fixtures/chunk-2.js"],
plugins: [terser()],
});
const { output } = await bundle.generate({ format: "esm" });
const newOutput = {};
output.forEach((out) => {
// TODO rewrite with object rest after node 6 dropping
const value = Object.assign({}, out);
delete value.modules;
delete value.facadeModuleId;
newOutput[out.fileName] = value;
});
expect(newOutput).toMatchSnapshot();
});
test("allow to pass not string values to worker", async () => {
const bundle = await rollup({
input: "test/fixtures/unminified.js",
plugins: [terser({ mangle: { properties: { regex: /^_/ } } })],
});
const result = await bundle.generate({ format: "cjs" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual(
'"use strict";window.a=5,window.a<3&&console.log(4);\n'
);
});
test("allow classic function definitions passing to worker", async () => {
const bundle = await rollup({
input: "test/fixtures/commented.js",
plugins: [
terser({
mangle: { properties: { regex: /^_/ } },
output: {
comments: function (node, comment) {
if (comment.type === "comment2") {
// multiline comment
return /@preserve|@license|@cc_on|^!/i.test(comment.value);
}
return false;
},
},
}),
],
});
const result = await bundle.generate({ format: "cjs", compact: true });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual(
'"use strict";window.a=5,\n/* @preserve this comment */\nwindow.a<3&&console.log(4);'
);
});
test("allow method shorthand definitions passing to worker", async () => {
const bundle = await rollup({
input: "test/fixtures/commented.js",
plugins: [
terser({
mangle: { properties: { regex: /^_/ } },
output: {
comments(node, comment) {
if (comment.type === "comment2") {
// multiline comment
return /@preserve|@license|@cc_on|^!/i.test(comment.value);
}
return false;
},
},
}),
],
});
const result = await bundle.generate({ format: "cjs", compact: true });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual(
'"use strict";window.a=5,\n/* @preserve this comment */\nwindow.a<3&&console.log(4);'
);
});
test("allow arrow function definitions passing to worker", async () => {
const bundle = await rollup({
input: "test/fixtures/unminified.js",
plugins: [
terser({
mangle: { properties: { regex: /^_/ } },
output: {
comments: (node, comment) => {
if (comment.type === "comment2") {
// multiline comment
return /@preserve|@license|@cc_on|^!/i.test(comment.value);
}
return false;
},
},
}),
],
});
const result = await bundle.generate({ format: "cjs" });
expect(result.output).toHaveLength(1);
const [output] = result.output;
expect(output.code).toEqual(
'"use strict";window.a=5,window.a<3&&console.log(4);\n'
);
});
test("allow to pass not string values to worker", async () => {
const bundle = await rollup({
input: "test/fixtures/unminified.js",
plugins: [terser({ mangle: { properties: { regex: /^_/ } } })],
});
const result = await bundle.generate({ format: "cjs" });
expect(result.output[0].code).toEqual(
'"use strict";window.a=5,window.a<3&&console.log(4);\n'
);
});
test("terser accepts the nameCache option", async () => {
const nameCache = {
props: {
props: {
$_priv: "custom",
},
},
};
const bundle = await rollup({
input: "test/fixtures/properties.js",
plugins: [
terser({
mangle: {
properties: {
regex: /^_/,
},
},
nameCache,
}),
],
});
const result = await bundle.generate({ format: "es" });
expect(result.output[0].code.trim()).toEqual(
`console.log({foo:1,custom:2});`
);
});
test("terser updates the nameCache object", async () => {
const nameCache = {
props: {
props: {
$_priv: "f",
},
},
};
const props = nameCache.props;
const bundle = await rollup({
input: "test/fixtures/properties.js",
plugins: [
terser({
mangle: {
properties: {
regex: /./,
},
},
nameCache,
}),
],
});
const result = await bundle.generate({ format: "es" });
expect(result.output[0].code.trim()).toEqual(`console.log({o:1,f:2});`);
expect(nameCache.props).toBe(props);
expect(nameCache).toEqual({
props: {
props: {
$_priv: "f",
$foo: "o",
},
},
});
});
test("omits populates an empty nameCache object", async () => {
const nameCache = {};
const bundle = await rollup({
input: "test/fixtures/properties-and-locals.js",
plugins: [
terser({
mangle: {
properties: {
regex: /./,
},
},
nameCache,
}),
],
});
const result = await bundle.generate({ format: "es" });
expect(result.output[0].code.trim()).toEqual(
`console.log({o:1,i:2},function o(n){return n>0?o(n-1):n}(10));`
);
expect(nameCache).toEqual({
props: {
props: {
$_priv: "i",
$foo: "o",
},
},
});
});
// Note: nameCache.vars never gets populated, but this is a Terser issue.
// Here we're just testing that an empty vars object doesn't get added to nameCache if it wasn't there previously.
test("terser preserve vars in nameCache when provided", async () => {
const nameCache = {
vars: {
props: {},
},
};
const bundle = await rollup({
input: "test/fixtures/properties-and-locals.js",
plugins: [
terser({
mangle: {
properties: {
regex: /./,
},
},
nameCache,
}),
],
});
const result = await bundle.generate({ format: "es" });
expect(result.output[0].code.trim()).toEqual(
`console.log({o:1,i:2},function o(n){return n>0?o(n-1):n}(10));`
);
expect(nameCache).toEqual({
props: {
props: {
$_priv: "i",
$foo: "o",
},
},
vars: {
props: {},
},
});
});

8
transform.js Normal file
View File

@ -0,0 +1,8 @@
const { minify } = require("terser");
const transform = (code, optionsString) => {
const options = eval(`(${optionsString})`);
return minify(code, options).then(result => ({ result, nameCache: options.nameCache }));
};
exports.transform = transform;

3582
yarn.lock Normal file

File diff suppressed because it is too large Load Diff