Comparing version 1.9.2 to 1.10.0
{ | ||
"name": "esnext", | ||
"version": "1.9.2", | ||
"version": "1.10.0", | ||
"description": "Update your project to the latest ECMAScript syntax.", | ||
@@ -5,0 +5,0 @@ "main": "dist/esnext.umd.js", |
@@ -31,2 +31,7 @@ import MagicString from 'magic-string'; | ||
type Range = { | ||
start: number, | ||
end: number | ||
}; | ||
export default class Module { | ||
@@ -64,4 +69,20 @@ constructor(id: ?string, source: string) { | ||
tokensInRange(start: number, end: number): Array<Token> { | ||
const result = []; | ||
const tokenRange = this._tokenIndexRangeForSourceRange(start, end); | ||
if (!tokenRange) { | ||
return []; | ||
} | ||
return this.tokens.slice(tokenRange.start, tokenRange.end); | ||
} | ||
tokenRangeForNode(node: Object): ?Range { | ||
return this._tokenIndexRangeForSourceRange(node.range[0], node.range[1]); | ||
} | ||
_tokenIndexRangeForSourceRange(start: number, end: number): ?Range { | ||
let location = null; | ||
let length = 0; | ||
const tokens = this.tokens; | ||
for (let i = 0; i < tokens.length; i++) { | ||
@@ -72,6 +93,12 @@ const { range } = tokens[i]; | ||
} else if (range[0] >= start) { | ||
result.push(tokens[i]); | ||
if (location === null) { location = i; } | ||
length++; | ||
} | ||
} | ||
return result; | ||
if (location === null) { | ||
return null; | ||
} | ||
return { start: location, end: location + length }; | ||
} | ||
@@ -78,0 +105,0 @@ |
import BaseContext from '../context'; | ||
import clone from '../utils/clone'; | ||
import estraverse from 'estraverse'; | ||
import hasParens from '../utils/hasParens'; | ||
import needsParens from '../utils/needsParens'; | ||
import replace from '../utils/replace'; | ||
import type Module from '../module'; | ||
@@ -19,107 +22,217 @@ import type { ScopeManager } from 'escope'; | ||
} | ||
} | ||
export function begin(module: Module): Context { | ||
return new Context(module); | ||
} | ||
export function enter(node: Object, parent: Object, module: Module, context: Context): ?VisitorOption { | ||
if (node.type !== Syntax.FunctionExpression) { | ||
return null; | ||
rewrite(node: Object, parent: Object): boolean { | ||
return ( | ||
this.rewriteFunctionExpression(node, parent) || | ||
this.rewriteCallExpression(node) | ||
); | ||
} | ||
if (node.generator) { | ||
return null; | ||
} | ||
rewriteFunctionExpression(node: Object, parent: Object): boolean { | ||
if (node.type !== Syntax.FunctionExpression) { | ||
return false; | ||
} | ||
if (node.id) { | ||
return null; | ||
} | ||
if (node.generator) { | ||
return false; | ||
} | ||
if (node.body.body.length !== 1) { | ||
return null; | ||
} | ||
if (node.id) { | ||
return false; | ||
} | ||
if (parent.type === Syntax.Property && parent.method) { | ||
return null; | ||
} | ||
if (node.body.body.length !== 1) { | ||
return false; | ||
} | ||
const [ statement ] = node.body.body; | ||
if (parent.type === Syntax.Property && parent.method) { | ||
return false; | ||
} | ||
if (statement.type !== Syntax.ReturnStatement) { | ||
return null; | ||
const [ statement ] = node.body.body; | ||
if (statement.type !== Syntax.ReturnStatement) { | ||
return false; | ||
} | ||
if (referencesThisOrArguments(node, this.module.scopeManager)) { | ||
return false; | ||
} | ||
this._rewriteBlocklessArrowFunction(node, parent); | ||
return true; | ||
} | ||
if (referencesThisOrArguments(node, module.scopeManager)) { | ||
return null; | ||
rewriteCallExpression(node: Object): boolean { | ||
if (node.type !== Syntax.CallExpression) { | ||
return false; | ||
} | ||
const { callee } = node; | ||
if (callee.type !== Syntax.MemberExpression) { | ||
return false; | ||
} | ||
const { object, property } = callee; | ||
if (object.type !== Syntax.FunctionExpression || object.id) { | ||
return false; | ||
} | ||
if (property.type !== Syntax.Identifier || property.name !== 'bind') { | ||
return false; | ||
} | ||
if (node.arguments.length !== 1 || node.arguments[0].type !== Syntax.ThisExpression) { | ||
return false; | ||
} | ||
this._rewriteBlockArrowFunction(object); | ||
// `() => {}.bind(this)` -> `() => {}bind(this)` | ||
// ^ | ||
this.module.tokensBetweenNodes(object, property).forEach(token => { | ||
if (token.type === 'Punctuator' && token.value === '.') { | ||
this.remove(...token.range); | ||
} | ||
}); | ||
// `() => {}bind(this)` -> `() => {}` | ||
// ^^^^^^^^^^ | ||
this.remove(property.range[0], node.range[1]); | ||
replace(node, object); | ||
return true; | ||
} | ||
context.metadata.functions.push(clone(node)); | ||
_rewriteBlocklessArrowFunction(node: Object, parent: Object) { | ||
const [ statement ] = node.body.body; | ||
const tokens = module.tokensForNode(node); | ||
let tokenIndex = 0; | ||
const functionToken = tokens[tokenIndex++]; | ||
const paramStartParenToken = tokens[tokenIndex++]; | ||
let paramEndParenToken; | ||
this.metadata.functions.push(clone(node)); | ||
if (node.params.length === 0) { | ||
paramEndParenToken = tokens[tokenIndex++]; | ||
} else { | ||
const lastParam = node.params[node.params.length - 1]; | ||
while (tokenIndex < tokens.length) { | ||
const token = tokens[tokenIndex++]; | ||
if (token.range[0] >= lastParam.range[1] && token.value === ')') { | ||
paramEndParenToken = token; | ||
break; | ||
const tokens = this.module.tokensForNode(node); | ||
let tokenIndex = 0; | ||
const functionToken = tokens[tokenIndex++]; | ||
const paramStartParenToken = tokens[tokenIndex++]; | ||
let paramEndParenToken; | ||
if (node.params.length === 0) { | ||
paramEndParenToken = tokens[tokenIndex++]; | ||
} else { | ||
const lastParam = node.params[node.params.length - 1]; | ||
while (tokenIndex < tokens.length) { | ||
const token = tokens[tokenIndex++]; | ||
if (token.range[0] >= lastParam.range[1] && token.value === ')') { | ||
paramEndParenToken = token; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
const paramsNeedsParens = node.params.length !== 1 || node.params[0].type !== 'Identifier'; | ||
const paramsNeedsParens = node.params.length !== 1 || node.params[0].type !== Syntax.Identifier; | ||
if (!paramsNeedsParens) { | ||
// `(a)` -> `a` | ||
// ^ ^ | ||
context.remove(...paramStartParenToken.range); | ||
context.remove(...paramEndParenToken.range); | ||
} | ||
if (!paramsNeedsParens) { | ||
// `(a)` -> `a` | ||
// ^ ^ | ||
this.remove(...paramStartParenToken.range); | ||
this.remove(...paramEndParenToken.range); | ||
} | ||
const blockStartBraceToken = tokens[tokenIndex++]; | ||
const blockEndBraceToken = tokens[tokens.length - 1]; | ||
const blockStartBraceToken = tokens[tokenIndex++]; | ||
const blockEndBraceToken = tokens[tokens.length - 1]; | ||
// `function() {` -> `() =>` | ||
// ^^^^^^^^ ^ ^^ | ||
context.remove(functionToken.range[0], paramStartParenToken.range[0]); | ||
context.overwrite(...blockStartBraceToken.range, '=>'); | ||
// `function() {` -> `() =>` | ||
// ^^^^^^^^ ^ ^^ | ||
this.remove(functionToken.range[0], paramStartParenToken.range[0]); | ||
this.overwrite(...blockStartBraceToken.range, '=>'); | ||
const contentBetweenBlockStartBraceAndReturn = context.slice( | ||
blockStartBraceToken.range[1], | ||
statement.range[0] | ||
); | ||
const contentBetweenBlockStartBraceAndReturn = this.slice( | ||
blockStartBraceToken.range[1], | ||
statement.range[0] | ||
); | ||
const shouldCollapseToOneLine = /^\s*$/.test(contentBetweenBlockStartBraceAndReturn); | ||
const shouldCollapseToOneLine = /^\s*$/.test(contentBetweenBlockStartBraceAndReturn); | ||
if (shouldCollapseToOneLine) { | ||
// Removes whitespace between `{` and `return` and `foo;` and `}`. | ||
// | ||
// function() { | ||
// return foo; | ||
// } | ||
// | ||
context.overwrite(blockStartBraceToken.range[1], statement.range[0], ' '); | ||
context.remove(statement.range[1], blockEndBraceToken.range[0]); | ||
if (shouldCollapseToOneLine) { | ||
// Removes whitespace between `{` and `return` and `foo;` and `}`. | ||
// | ||
// function() { | ||
// return foo; | ||
// } | ||
// | ||
this.overwrite(blockStartBraceToken.range[1], statement.range[0], ' '); | ||
this.remove(statement.range[1], blockEndBraceToken.range[0]); | ||
} | ||
// `return foo;` -> `foo` | ||
// ^^^^^^^ ^ | ||
this.remove(statement.range[0], statement.argument.range[0]); | ||
this.remove(statement.argument.range[1], statement.range[1]); | ||
// `…}` -> `…` | ||
this.remove(...blockEndBraceToken.range); | ||
node.type = Syntax.ArrowFunctionExpression; | ||
node.body = statement.argument; | ||
if (needsParens(node, parent) && !hasParens(node, this.module)) { | ||
this.insert(node.range[0], '('); | ||
this.insert(node.range[1], ')'); | ||
} | ||
} | ||
// `return foo;` -> `foo` | ||
// ^^^^^^^ ^ | ||
context.remove(statement.range[0], statement.argument.range[0]); | ||
context.remove(statement.argument.range[1], statement.range[1]); | ||
_rewriteBlockArrowFunction(node: Object) { | ||
this.metadata.functions.push(clone(node)); | ||
// `…}` -> `…` | ||
context.remove(...blockEndBraceToken.range); | ||
const tokens = this.module.tokensForNode(node); | ||
let tokenIndex = 0; | ||
const functionToken = tokens[tokenIndex++]; | ||
const paramStartParenToken = tokens[tokenIndex++]; | ||
let paramEndParenToken; | ||
node.type = Syntax.ArrowFunctionExpression; | ||
node.body = statement.argument; | ||
if (node.params.length === 0) { | ||
paramEndParenToken = tokens[tokenIndex++]; | ||
} else { | ||
const lastParam = node.params[node.params.length - 1]; | ||
while (tokenIndex < tokens.length) { | ||
const token = tokens[tokenIndex++]; | ||
if (token.range[0] >= lastParam.range[1] && token.value === ')') { | ||
paramEndParenToken = token; | ||
break; | ||
} | ||
} | ||
} | ||
const paramsNeedsParens = node.params.length !== 1 || node.params[0].type !== Syntax.Identifier; | ||
if (!paramsNeedsParens) { | ||
// `(a)` -> `a` | ||
// ^ ^ | ||
this.remove(...paramStartParenToken.range); | ||
this.remove(...paramEndParenToken.range); | ||
} | ||
const blockStartBraceToken = tokens[tokenIndex++]; | ||
// `function() {` -> `() =>` | ||
// ^^^^^^^^ ^ ^^ | ||
this.remove(functionToken.range[0], paramStartParenToken.range[0]); | ||
this.insert(blockStartBraceToken.range[0], '=> '); | ||
node.type = Syntax.ArrowFunctionExpression; | ||
} | ||
} | ||
export function begin(module: Module): Context { | ||
return new Context(module); | ||
} | ||
export function enter(node: Object, parent: Object, module: Module, context: Context): ?VisitorOption { | ||
context.rewrite(node, parent); | ||
return null; | ||
} | ||
function referencesThisOrArguments(node: Object, scopeManager: ScopeManager): boolean { | ||
@@ -126,0 +239,0 @@ const scope = scopeManager.acquire(node); |
@@ -151,67 +151,2 @@ import BaseContext from '../context'; | ||
// TODO: test nested template strings | ||
//parts.forEach(part => this.escape('`', part.node.range[0] + 1, part.node.range[1] - 1)); | ||
// | ||
//for (let i = 0; i < parts.length - 1; i++) { | ||
// const thisPart = parts[i]; | ||
// const thisNode = thisPart.node; | ||
// const nextPart = parts[i + 1]; | ||
// const nextNode = nextPart.node; | ||
// if (isString(nextNode)) { | ||
// cooked += nextNode.value; | ||
// raw += nextNode.raw.slice(1, -1); | ||
// } else { | ||
// expressions.push(nextNode); | ||
// quasis.push({ | ||
// type: Syntax.TemplateElement, | ||
// tail: false, | ||
// value: { cooked, raw: raw.replace(/`/g, '\\`') } | ||
// }); | ||
// cooked = ''; | ||
// raw = ''; | ||
// } | ||
// if (isString(thisNode)) { | ||
// if (isString(nextNode)) { | ||
// this.remove( | ||
// thisNode.range[1] - 1, | ||
// nextNode.range[0] + 1 | ||
// ); | ||
// } else { | ||
// this.overwrite( | ||
// thisNode.range[1] - 1, | ||
// nextNode.range[0], | ||
// `\${` | ||
// ); | ||
// } | ||
// } else { | ||
// if (isString(nextNode)) { | ||
// this.overwrite( | ||
// thisNode.range[1], | ||
// nextNode.range[0] + 1, | ||
// `}` | ||
// ); | ||
// } else { | ||
// this.overwrite( | ||
// thisNode.range[1], | ||
// nextNode.range[0], | ||
// `}\${` | ||
// ); | ||
// } | ||
// } | ||
//} | ||
// | ||
//quasis.push({ | ||
// type: Syntax.TemplateElement, | ||
// tail: true, | ||
// value: { cooked, raw } | ||
//}); | ||
// | ||
//const lastPart = parts[parts.length - 1]; | ||
//const lastNode = lastPart.node; | ||
//if (isString(lastNode)) { | ||
// this.overwrite(lastNode.range[1] - 1, node.range[1], '`'); | ||
//} else { | ||
// this.overwrite(lastNode.range[1], node.range[1], '}`'); | ||
//} | ||
return { type: Syntax.TemplateLiteral, expressions, quasis }; | ||
@@ -218,0 +153,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
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
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
212094
26
6269