diff --git a/packages/strip/README.md b/packages/strip/README.md
index ef06a5cb9..43e8dd4e0 100755
--- a/packages/strip/README.md
+++ b/packages/strip/README.md
@@ -51,42 +51,52 @@ Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#comma
### `include`
Type: `String | RegExp | Array[...String|RegExp]`
-Default: `['**/*.js']`
+Default: `['**/*.js']`
+Example: `include: '**/*.(mjs|js)',`
A pattern, or array of patterns, which specify the files in the build the plugin should operate on.
### `exclude`
Type: `String | RegExp | Array[...String|RegExp]`
-Default: `[]`
+Default: `[]`
+Example: `exlude: 'tests/**/*',`
A pattern, or array of patterns, which specify the files in the build the plugin should _ignore_.
### `debugger`
Type: `Boolean`
-Default: `true`
+Default: `true`
+Example: `debugger: false,`
-If `true`, instructs the plugin to remove debugger statements.
+If `true` instructs the plugin to remove debugger statements.
### `functions`
Type: `Array[...String]`
-Default: `[ 'console.*', 'assert.*' ]`
+Default: `[ 'console.*', 'assert.*' ]`
+Example: `functions: [ 'console.log', 'MyClass.Test' ],`
Specifies the functions that the plugin will target and remove.
+_Note: specifying functions that are used at the begining of a chain, such as 'a().b().c()', will result in '(void 0).b().c()' which will generate an error at runtime._
+
### `labels`
Type: `Array[...String]`
-Default: `[]`
+Default: `[]`
+Example: `labels: ['unittest'],`
+
+Specifies the [labeled blocks or statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label) that the plugin will target and remove.
-Specifies the [labeled blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label) that the plugin will target and remove.
+_Note: the '**:**' is implied and should not be specified in the config._
### `sourceMap`
Type: `Boolean`
-Default: `true`
+Default: `true`
+Example: `sourceMap: false,`
If `true`, instructs the plugin to update source maps accordingly after removing configured targets from the bundle.
diff --git a/packages/strip/src/index.js b/packages/strip/src/index.js
index 0e8d01833..0759a7491 100755
--- a/packages/strip/src/index.js
+++ b/packages/strip/src/index.js
@@ -39,20 +39,30 @@ export default function strip(options = {}) {
const removeDebuggerStatements = options.debugger !== false;
const functions = (options.functions || ['console.*', 'assert.*']).map((keypath) =>
- keypath.replace(/\./g, '\\.').replace(/\*/g, '\\w+')
+ keypath.replace(/\*/g, '\\w+').replace(/\./g, '\\s*\\.\\s*')
);
const labels = options.labels || [];
- const firstpass = new RegExp(`\\b(?:${functions.join('|')}|debugger)\\b`);
- const pattern = new RegExp(`^(?:${functions.join('|')})$`);
+ const labelsPatterns = labels.map((l) => `${l}\\s*:`);
+
+ const firstPass = [...functions, ...labelsPatterns];
+ if (removeDebuggerStatements) {
+ firstPass.push('debugger\\b');
+ }
+
+ const reFunctions = new RegExp(`^(?:${functions.join('|')})$`);
+ const reFirstpass = new RegExp(`\\b(?:${firstPass.join('|')})`);
+ const firstPassFilter = firstPass.length > 0 ? (code) => reFirstpass.test(code) : () => false;
+ const UNCHANGED = null;
return {
name: 'strip',
transform(code, id) {
- if (!filter(id)) return null;
- if (functions.length > 0 && !firstpass.test(code)) return null;
+ if (!filter(id) || !firstPassFilter(code)) {
+ return UNCHANGED;
+ }
let ast;
@@ -81,7 +91,7 @@ export default function strip(options = {}) {
if (parent.type === 'ExpressionStatement') {
removeStatement(parent);
} else {
- magicString.overwrite(node.start, node.end, 'void 0');
+ magicString.overwrite(node.start, node.end, '(void 0)');
}
edited = true;
@@ -93,7 +103,7 @@ export default function strip(options = {}) {
if (isBlock(parent)) {
remove(node.start, node.end);
} else {
- magicString.overwrite(node.start, node.end, 'void 0;');
+ magicString.overwrite(node.start, node.end, '(void 0);');
}
edited = true;
@@ -114,13 +124,15 @@ export default function strip(options = {}) {
if (removeDebuggerStatements && node.type === 'DebuggerStatement') {
removeStatement(node);
+ this.skip();
} else if (node.type === 'LabeledStatement') {
if (node.label && labels.includes(node.label.name)) {
removeStatement(node);
+ this.skip();
}
} else if (node.type === 'CallExpression') {
const keypath = flatten(node.callee);
- if (keypath && pattern.test(keypath)) {
+ if (keypath && reFunctions.test(keypath)) {
removeExpression(node);
this.skip();
}
@@ -128,7 +140,9 @@ export default function strip(options = {}) {
}
});
- if (!edited) return null;
+ if (!edited) {
+ return UNCHANGED;
+ }
code = magicString.toString();
const map = sourceMap ? magicString.generateMap() : null;
diff --git a/packages/strip/test/fixtures/excluded-not-changed/input.js b/packages/strip/test/fixtures/excluded-not-changed/input.js
new file mode 100644
index 000000000..bd4e4ddba
--- /dev/null
+++ b/packages/strip/test/fixtures/excluded-not-changed/input.js
@@ -0,0 +1,9 @@
+/* eslint-disable */
+export default function foo() {
+ before();
+ debugger;
+ logging:
+ console.log('a');
+ console.error('b');
+ after();
+}
diff --git a/packages/strip/test/fixtures/functions-direct/input.js b/packages/strip/test/fixtures/functions-direct/input.js
new file mode 100644
index 000000000..d60875bdc
--- /dev/null
+++ b/packages/strip/test/fixtures/functions-direct/input.js
@@ -0,0 +1,4 @@
+/* eslint-disable */
+ before();
+ f().t();
+ after();
diff --git a/packages/strip/test/fixtures/functions-spaced/input.js b/packages/strip/test/fixtures/functions-spaced/input.js
new file mode 100644
index 000000000..d5e4655ba
--- /dev/null
+++ b/packages/strip/test/fixtures/functions-spaced/input.js
@@ -0,0 +1,20 @@
+/* eslint-disable */
+ before();
+ function f() {
+ return {
+ g: function () {
+ return {
+ hello: function () {
+ console.log('hello');
+ }
+ };
+ }
+ };
+ }
+
+ Test
+ .
+ f()
+ . g() .
+ hello();
+ after();
diff --git a/packages/strip/test/fixtures/label-awkward-spacing/input.js b/packages/strip/test/fixtures/label-awkward-spacing/input.js
new file mode 100644
index 000000000..70af38461
--- /dev/null
+++ b/packages/strip/test/fixtures/label-awkward-spacing/input.js
@@ -0,0 +1,10 @@
+/* eslint-disable */
+ before();
+ unittest
+ : {
+ test('some test', (assert) => {
+ });
+ }
+ again();
+ unittest:console.log();
+ after();
diff --git a/packages/strip/test/fixtures/label-expression/input.js b/packages/strip/test/fixtures/label-expression/input.js
new file mode 100644
index 000000000..a8a10d760
--- /dev/null
+++ b/packages/strip/test/fixtures/label-expression/input.js
@@ -0,0 +1,2 @@
+/* eslint-disable */
+ before();unittest:console.log();after();
diff --git a/packages/strip/test/fixtures/label-multiple-times/input.js b/packages/strip/test/fixtures/label-multiple-times/input.js
new file mode 100644
index 000000000..70af38461
--- /dev/null
+++ b/packages/strip/test/fixtures/label-multiple-times/input.js
@@ -0,0 +1,10 @@
+/* eslint-disable */
+ before();
+ unittest
+ : {
+ test('some test', (assert) => {
+ });
+ }
+ again();
+ unittest:console.log();
+ after();
diff --git a/packages/strip/test/fixtures/label-whitespace/input.js b/packages/strip/test/fixtures/label-whitespace/input.js
new file mode 100644
index 000000000..5e95eca7b
--- /dev/null
+++ b/packages/strip/test/fixtures/label-whitespace/input.js
@@ -0,0 +1,7 @@
+/* eslint-disable */
+ before();
+ unittest : {
+ test('some test', (assert) => {
+ });
+ }
+ after();
diff --git a/packages/strip/test/fixtures/lambda-void/input.js b/packages/strip/test/fixtures/lambda-void/input.js
new file mode 100644
index 000000000..f370859ff
--- /dev/null
+++ b/packages/strip/test/fixtures/lambda-void/input.js
@@ -0,0 +1,2 @@
+/* eslint-disable */
+console.log(['h', 'e', 'y'].forEach((letter) => console.warn(letter)))
diff --git a/packages/strip/test/fixtures/no-changes/input.js b/packages/strip/test/fixtures/no-changes/input.js
new file mode 100644
index 000000000..bd4e4ddba
--- /dev/null
+++ b/packages/strip/test/fixtures/no-changes/input.js
@@ -0,0 +1,9 @@
+/* eslint-disable */
+export default function foo() {
+ before();
+ debugger;
+ logging:
+ console.log('a');
+ console.error('b');
+ after();
+}
diff --git a/packages/strip/test/snapshots/test.js.md b/packages/strip/test/snapshots/test.js.md
index 76bed0fd5..0fde251ad 100644
--- a/packages/strip/test/snapshots/test.js.md
+++ b/packages/strip/test/snapshots/test.js.md
@@ -4,6 +4,21 @@ The actual snapshot is saved in `test.js.snap`.
Generated by [AVA](https://ava.li).
+## can be configured to make no changes
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ export default function foo() {␊
+ before();␊
+ debugger;␊
+ logging:␊
+ console.log('a');␊
+ console.error('b');␊
+ after();␊
+ }␊
+ `
+
## does not remove debugger statements with debugger: false
> Snapshot 1
@@ -15,6 +30,57 @@ Generated by [AVA](https://ava.li).
}␊
`
+## empty functions list leaves assert statements
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ function foo(message) {␊
+ assert.equal(arguments.length, 1);␊
+ assert.equal(typeof arguments[0], 'string');␊
+ bar(message);␊
+ }␊
+ `
+
+## empty functions list leaves console statements
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ export default function foo() {␊
+ before();␊
+ logging:␊
+ console.log('a');␊
+ console.error('b');␊
+ after();␊
+ }␊
+ `
+
+## excluded files do not get changed
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ export default function foo() {␊
+ before();␊
+ debugger;␊
+ logging:␊
+ console.log('a');␊
+ console.error('b');␊
+ after();␊
+ }␊
+ `
+
+## function calls without object are replaced with (void 0)
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ before();␊
+ (void 0).t();␊
+ after();␊
+ `
+
## leaves console statements if custom functions are provided
> Snapshot 1
@@ -93,6 +159,32 @@ Generated by [AVA](https://ava.li).
after();␊
`
+## removes labeled blocks when filtered function is present
+
+> Snapshot 1
+
+ `before();␊
+ after();␊
+ `
+
+## removes labeled blocks when functions list is empty
+
+> Snapshot 1
+
+ `before();␊
+ after();␊
+ `
+
+## removes labeled even with awkward spacing
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ before();␊
+ again();␊
+ after();␊
+ `
+
## removes methods of this
> Snapshot 1
@@ -123,42 +215,101 @@ Generated by [AVA](https://ava.li).
}␊
`
+## removing a lable also works for expressions
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ before();after();␊
+ `
+
## replaces case body with void 0
> Snapshot 1
`switch (a) {␊
case 1:␊
- void 0;␊
+ (void 0);␊
}␊
`
+## rewrites expressions as void 0 in lambdas
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ console.log(['h', 'e', 'y'].forEach((letter) => (void 0)))␊
+ `
+
## rewrtestes inline call expressions (not expression statements) as void 0
> Snapshot 1
- `DEBUG && void 0;␊
+ `DEBUG && (void 0);␊
`
## rewrtestes inline if expessions as void 0
> Snapshot 1
- `if (DEBUG) void 0;␊
+ `if (DEBUG) (void 0);␊
`
## rewrtestes inline while expressions as void 0
> Snapshot 1
- `while (test()) void 0;␊
+ `while (test()) (void 0);␊
+ `
+
+## spaces around . in function calls are accepted
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ before();␊
+ function f() {␊
+ return {␊
+ g: function () {␊
+ return {␊
+ hello: function () {␊
+ console.log('hello');␊
+ }␊
+ };␊
+ }␊
+ };␊
+ }␊
+ ␊
+ (void 0)␊
+ . g() .␊
+ hello();␊
+ after();␊
`
## supports object destructuring assignments with default values
> Snapshot 1
- `export function fn({ foo = void 0, bar } = {}) {␊
- const { baz = void 0 } = bar;␊
+ `export function fn({ foo = (void 0), bar } = {}) {␊
+ const { baz = (void 0) } = bar;␊
}␊
`
+
+## the same label can occur multiple times and all are removed
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ before();␊
+ again();␊
+ after();␊
+ `
+
+## whitespace between label and colon is accepted
+
+> Snapshot 1
+
+ `/* eslint-disable */␊
+ before();␊
+ after();␊
+ `
diff --git a/packages/strip/test/snapshots/test.js.snap b/packages/strip/test/snapshots/test.js.snap
index 5c1e641a4..e828bef81 100644
Binary files a/packages/strip/test/snapshots/test.js.snap and b/packages/strip/test/snapshots/test.js.snap differ
diff --git a/packages/strip/test/test.js b/packages/strip/test/test.js
index 27fa54b87..c279a14a7 100755
--- a/packages/strip/test/test.js
+++ b/packages/strip/test/test.js
@@ -25,6 +25,18 @@ const compare = (t, fixture, options) => {
t.snapshot(output ? output.code : input);
};
+test('can be configured to make no changes', (t) => {
+ compare(t, 'no-changes', {
+ labels: [],
+ functions: [],
+ debugger: false
+ });
+});
+
+test('excluded files do not get changed', (t) => {
+ compare(t, 'excluded-not-changed', { exclude: `**/excluded-not-changed/input.js` });
+});
+
test('removes debugger statements', (t) => {
compare(t, 'debugger');
});
@@ -33,6 +45,10 @@ test('does not remove debugger statements with debugger: false', (t) => {
compare(t, 'debugger-false', { debugger: false });
});
+test('empty functions list leaves console statements', (t) => {
+ compare(t, 'no-changes', { functions: [] });
+});
+
test('removes console statements', (t) => {
compare(t, 'console');
});
@@ -41,6 +57,10 @@ test('removes assert statements', (t) => {
compare(t, 'assert');
});
+test('empty functions list leaves assert statements', (t) => {
+ compare(t, 'assert', { functions: [] });
+});
+
test('leaves console statements if custom functions are provided', (t) => {
compare(t, 'console-custom', { functions: ['console.log'] });
});
@@ -57,6 +77,10 @@ test('rewrtestes inline if expessions as void 0', (t) => {
compare(t, 'inline-if');
});
+test('rewrites expressions as void 0 in lambdas', (t) => {
+ compare(t, 'lambda-void', { functions: ['console.warn'] });
+});
+
test('removes expressions in if blocks', (t) => {
compare(t, 'if-block');
});
@@ -92,3 +116,35 @@ test('removes multiple labeled blocks', (t) => {
test('only removes specified blocks', (t) => {
compare(t, 'label-block-discriminate', { labels: ['second'] });
});
+
+test('removes labeled blocks when filtered function is present', (t) => {
+ compare(t, 'label-block', { labels: ['unittest'], functions: ['console.*'] });
+});
+
+test('removes labeled blocks when functions list is empty', (t) => {
+ compare(t, 'label-block', { labels: ['unittest'], functions: [] });
+});
+
+test('whitespace between label and colon is accepted', (t) => {
+ compare(t, 'label-whitespace', { labels: ['unittest'], functions: ['console.*'] });
+});
+
+test('removing a lable also works for expressions', (t) => {
+ compare(t, 'label-expression', { labels: ['unittest'] });
+});
+
+test('the same label can occur multiple times and all are removed', (t) => {
+ compare(t, 'label-multiple-times', { labels: ['unittest'] });
+});
+
+test('removes labeled even with awkward spacing', (t) => {
+ compare(t, 'label-awkward-spacing', { labels: ['unittest'], functions: ['console.*'] });
+});
+
+test('spaces around . in function calls are accepted', (t) => {
+ compare(t, 'functions-spaced', { labels: ['unittest'], functions: ['Test.f'] });
+});
+
+test('function calls without object are replaced with (void 0)', (t) => {
+ compare(t, 'functions-direct', { labels: ['unittest'], functions: ['f'] });
+});