UNPKG

3.82 kBJavaScriptView Raw
1'use strict';
2
3function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
4
5var estreeWalker = require('estree-walker');
6var MagicString = _interopDefault(require('magic-string'));
7var rollupPluginutils = require('rollup-pluginutils');
8
9/* eslint-disable no-param-reassign */
10
11const whitespace = /\s/;
12
13function getName(node) {
14 if (node.type === 'Identifier') return node.name;
15 if (node.type === 'ThisExpression') return 'this';
16 if (node.type === 'Super') return 'super';
17
18 return null;
19}
20
21function flatten(node) {
22 const parts = [];
23
24 while (node.type === 'MemberExpression') {
25 if (node.computed) return null;
26
27 parts.unshift(node.property.name);
28 node = node.object;
29 }
30
31 const name = getName(node);
32
33 if (!name) return null;
34
35 parts.unshift(name);
36 return parts.join('.');
37}
38
39function strip(options = {}) {
40 const include = options.include || '**/*.js';
41 const { exclude } = options;
42 const filter = rollupPluginutils.createFilter(include, exclude);
43 const sourceMap = options.sourceMap !== false;
44
45 const removeDebuggerStatements = options.debugger !== false;
46 const functions = (options.functions || ['console.*', 'assert.*']).map((keypath) =>
47 keypath.replace(/\./g, '\\.').replace(/\*/g, '\\w+')
48 );
49
50 const labels = options.labels || [];
51
52 const firstpass = new RegExp(`\\b(?:${functions.join('|')}|debugger)\\b`);
53 const pattern = new RegExp(`^(?:${functions.join('|')})$`);
54
55 return {
56 name: 'strip',
57
58 transform(code, id) {
59 if (!filter(id)) return null;
60 if (functions.length > 0 && !firstpass.test(code)) return null;
61
62 let ast;
63
64 try {
65 ast = this.parse(code);
66 } catch (err) {
67 err.message += ` in ${id}`;
68 throw err;
69 }
70
71 const magicString = new MagicString(code);
72 let edited = false;
73
74 function remove(start, end) {
75 while (whitespace.test(code[start - 1])) start -= 1;
76 magicString.remove(start, end);
77 }
78
79 function isBlock(node) {
80 return node && (node.type === 'BlockStatement' || node.type === 'Program');
81 }
82
83 function removeExpression(node) {
84 const { parent } = node;
85
86 if (parent.type === 'ExpressionStatement') {
87 removeStatement(parent);
88 } else {
89 magicString.overwrite(node.start, node.end, 'void 0');
90 }
91
92 edited = true;
93 }
94
95 function removeStatement(node) {
96 const { parent } = node;
97
98 if (isBlock(parent)) {
99 remove(node.start, node.end);
100 } else {
101 magicString.overwrite(node.start, node.end, 'void 0;');
102 }
103
104 edited = true;
105 }
106
107 estreeWalker.walk(ast, {
108 enter(node, parent) {
109 Object.defineProperty(node, 'parent', {
110 value: parent,
111 enumerable: false,
112 configurable: true
113 });
114
115 if (sourceMap) {
116 magicString.addSourcemapLocation(node.start);
117 magicString.addSourcemapLocation(node.end);
118 }
119
120 if (removeDebuggerStatements && node.type === 'DebuggerStatement') {
121 removeStatement(node);
122 } else if (node.type === 'LabeledStatement') {
123 if (node.label && labels.includes(node.label.name)) {
124 removeStatement(node);
125 }
126 } else if (node.type === 'CallExpression') {
127 const keypath = flatten(node.callee);
128 if (keypath && pattern.test(keypath)) {
129 removeExpression(node);
130 this.skip();
131 }
132 }
133 }
134 });
135
136 if (!edited) return null;
137
138 code = magicString.toString();
139 const map = sourceMap ? magicString.generateMap() : null;
140
141 return { code, map };
142 }
143 };
144}
145
146module.exports = strip;