123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- const { default: reduceUsing, CloneReducer } = require('shift-reducer');
- const { default: analyzeScope, ScopeLookup } = require('shift-scope');
- const { check: isReservedWord } = require('reserved-words');
- class RegexReducer extends CloneReducer {
- reduceLiteralRegExpExpression(node) {
- return {
- ...node,
- unicode: false,
- };
- }
- }
- class NoReservedKeywordsReducer extends CloneReducer {
- isSafeName(name) {
- return !isReservedWord(name, 3)
- && !isReservedWord(name, 6, true)
- && name !== 'arguments'
- && name !== 'eval';
- }
- reduceLabeledStatement(node, rest) {
- if (this.isSafeName(node.label)) {
- return super.reduceLabeledStatement(node, rest);
- }
- return {
- ...node,
- ...rest,
- label: node.label + '_avoid_reserved_word',
- };
- }
- reduceBindingIdentifier(node) {
- if (this.isSafeName(node.name)) {
- return node;
- }
- return {
- ...node,
- name: node.name + '_avoid_reserved_word',
- };
- }
- reduceAssignmentTargetIdentifier(...args) {
- return this.reduceBindingIdentifier(...args);
- }
- reduceIdentifierExpression(...args) {
- return this.reduceBindingIdentifier(...args);
- }
- }
- class NoIdentifierDuplicatingAnImportReducer extends CloneReducer {
- reduceImportNamespace(node, { namespaceBinding, ...rest }) {
- return {
- ...node,
- ...rest,
- namespaceBinding: namespaceBinding && {
- ...namespaceBinding,
- name: namespaceBinding.name + '_avoid_capture',
- },
- };
- }
- }
- class ScopeLookupCloneReducer extends CloneReducer {
- constructor(scopeLookup) {
- super();
- this.scopeLookup = scopeLookup;
- }
- }
- class NoUndeclaredExportReducer extends ScopeLookupCloneReducer {
- reduceExportLocals(node, { namedExports, ...rest }) {
- return {
- ...node,
- ...rest,
- namedExports: namedExports.filter(export_ => !export_._isUndeclaredVariable),
- };
- }
- reduceExportLocalSpecifier(node, { name: { _isUndeclaredVariable, ...name }, ...rest }) {
- return {
- ...node,
- ...rest,
- name,
- _isUndeclaredVariable,
- };
- }
- reduceIdentifierExpression(node) {
- const [ variable ] = this.scopeLookup.lookup(node);
- return {
- ...node,
- _isUndeclaredVariable: variable && variable.declarations.length === 0,
- };
- }
- }
- const removingHOR = Reducer => class extends Reducer {
- constructor(...args) {
- super(...args);
- this.nodesToRemove = new Set();
- this.shouldKeep = statement => !this.nodesToRemove.has(statement);
- }
- markForRemoval(node) {
- this.nodesToRemove.add(node);
- }
- propagateRemoval(from, to) {
- if (this.nodesToRemove.has(from)) {
- this.nodesToRemove.add(to);
- }
- }
- reduceSwitchCase(node, { consequent, ...rest }) {
- return {
- ...node,
- ...rest,
- consequent: consequent.filter(this.shouldKeep),
- };
- }
- reduceSwitchDefault(node, { consequent, ...rest }) {
- return {
- ...node,
- ...rest,
- consequent: consequent.filter(this.shouldKeep),
- };
- }
- reduceScript(node, { statements, ...rest }) {
- return {
- ...node,
- ...rest,
- statements: statements.filter(this.shouldKeep),
- };
- }
- reduceFunctionBody(node, { statements, ...rest }) {
- return {
- ...node,
- ...rest,
- statements: statements.filter(this.shouldKeep),
- };
- }
- reduceBlock(node, { statements, ...rest }) {
- return {
- ...node,
- ...rest,
- statements: statements.filter(this.shouldKeep),
- };
- }
- reduceVariableDeclaration(node, { declarators, ...rest }) {
- declarators = declarators.filter(this.shouldKeep);
- node = {
- ...node,
- ...rest,
- declarators,
- };
- if (declarators.length === 0) {
- this.markForRemoval(node);
- }
- return node;
- }
- reduceVariableDeclarationStatement(node, { declaration }) {
- this.propagateRemoval(declaration, node);
- return node;
- }
- reduceForStatement(node, { body }) {
- this.propagateRemoval(body, node);
- return node;
- }
- reduceWhileStatement(node, { body }) {
- this.propagateRemoval(body, node);
- return node;
- }
- reduceLabeledStatement(node, { body }) {
- this.propagateRemoval(body, node);
- return node;
- }
- reduceForInStatement(node, { body }) {
- this.propagateRemoval(body, node);
- return node;
- }
- reduceIfStatement(node, { consequent, alternate }) {
- this.propagateRemoval(consequent, node);
- this.propagateRemoval(alternate, node);
- return node;
- }
- reduceDoWhileStatement(node, { body }) {
- this.propagateRemoval(body, node);
- return node;
- }
- };
- class NoNonStrictFeaturesReducer extends removingHOR(CloneReducer) {
- reduceWithStatement(node) {
- this.markForRemoval(node);
- return node;
- }
- reduceUnaryExpression(node) {
- if (node.operator === 'delete' && node.operand.type === 'IdentifierExpression') {
- return node.operand;
- }
- return node;
- }
- }
- class NoLabeledFunctionDeclarationReducer extends CloneReducer {
- reduceLabeledStatement(node, { body }) {
- if (body.type === 'FunctionDeclaration') {
- return body;
- }
- return node;
- }
- }
- class NoLabeledBreakContinueReducer extends CloneReducer {
- reduceBreakStatement(node) {
- return {
- ...node,
- label: null,
- };
- }
- }
- class UniqueBindingIdentifiersReducer extends CloneReducer {
- constructor() {
- super();
- this.boundNames = new Set();
- }
- reduceBindingIdentifier(node) {
- if (!this.boundNames.has(node.name)) {
- this.boundNames.add(node.name);
- return node;
- }
- return {
- ...node,
- name: node.name + Math.random().toString(16).slice(2),
- };
- }
- }
- module.exports = shiftAST => {
- [
- NoNonStrictFeaturesReducer,
- RegexReducer,
- NoReservedKeywordsReducer,
- NoIdentifierDuplicatingAnImportReducer,
- NoUndeclaredExportReducer,
- NoLabeledFunctionDeclarationReducer,
- UniqueBindingIdentifiersReducer,
- NoLabeledBreakContinueReducer,
- ].forEach(Reducer => {
- const scope = analyzeScope(shiftAST);
- const scopeLookup = new ScopeLookup(scope);
- shiftAST = reduceUsing(new Reducer(scopeLookup), shiftAST);
- });
- return shiftAST;
- };
|