Merge branch 'master' into 7.0
# Conflicts: # src/parser/index.js
This commit is contained in:
242
src/plugins/estree.js
Normal file
242
src/plugins/estree.js
Normal file
@@ -0,0 +1,242 @@
|
||||
import { types as tt } from "../tokenizer/types";
|
||||
import Parser from "../parser";
|
||||
|
||||
const pp = Parser.prototype;
|
||||
|
||||
pp.estreeParseRegExpLiteral = function ({ pattern, flags }) {
|
||||
let regex = null;
|
||||
try {
|
||||
regex = new RegExp(pattern, flags);
|
||||
} catch (e) {
|
||||
// In environments that don't support these flags value will
|
||||
// be null as the regex can't be represented natively.
|
||||
}
|
||||
const node = this.estreeParseLiteral(regex);
|
||||
node.regex = { pattern, flags };
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
pp.estreeParseLiteral = function (value) {
|
||||
const node = this.parseLiteral(value, "Literal");
|
||||
node.raw = node.extra.raw;
|
||||
delete node.extra;
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
pp.directiveToStmt = function (directive) {
|
||||
const directiveLiteral = directive.value;
|
||||
|
||||
const stmt = this.startNodeAt(directive.start, directive.loc.start);
|
||||
const expression = this.startNodeAt(directiveLiteral.start, directiveLiteral.loc.start);
|
||||
|
||||
expression.value = directiveLiteral.value;
|
||||
expression.raw = directiveLiteral.extra.raw;
|
||||
|
||||
stmt.expression = this.finishNodeAt(expression, "Literal", directiveLiteral.end, directiveLiteral.loc.end);
|
||||
stmt.directive = directiveLiteral.extra.raw.slice(1, -1);
|
||||
|
||||
return this.finishNodeAt(stmt, "ExpressionStatement", directive.end, directive.loc.end);
|
||||
};
|
||||
|
||||
function isSimpleProperty(node) {
|
||||
return node &&
|
||||
node.type === "Property" &&
|
||||
node.kind === "init" &&
|
||||
node.method === false;
|
||||
}
|
||||
|
||||
export default function (instance) {
|
||||
instance.extend("checkDeclaration", function(inner) {
|
||||
return function (node) {
|
||||
if (isSimpleProperty(node)) {
|
||||
this.checkDeclaration(node.value);
|
||||
} else {
|
||||
inner.call(this, node);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("checkGetterSetterParamCount", function() {
|
||||
return function (prop) {
|
||||
const paramCount = prop.kind === "get" ? 0 : 1;
|
||||
if (prop.value.params.length !== paramCount) {
|
||||
const start = prop.start;
|
||||
if (prop.kind === "get") {
|
||||
this.raise(start, "getter should have no params");
|
||||
} else {
|
||||
this.raise(start, "setter should have exactly one param");
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("checkLVal", function(inner) {
|
||||
return function (expr, isBinding, checkClashes, ...args) {
|
||||
switch (expr.type) {
|
||||
case "ObjectPattern":
|
||||
expr.properties.forEach((prop) => {
|
||||
this.checkLVal(
|
||||
prop.type === "Property" ? prop.value : prop,
|
||||
isBinding,
|
||||
checkClashes,
|
||||
"object destructuring pattern"
|
||||
);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
inner.call(this, expr, isBinding, checkClashes, ...args);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("checkPropClash", function () {
|
||||
return function (prop, propHash) {
|
||||
if (prop.computed || !isSimpleProperty(prop)) return;
|
||||
|
||||
const key = prop.key;
|
||||
// It is either an Identifier or a String/NumericLiteral
|
||||
const name = key.type === "Identifier" ? key.name : String(key.value);
|
||||
|
||||
if (name === "__proto__") {
|
||||
if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
|
||||
propHash.proto = true;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("isStrictBody", function () {
|
||||
return function (node, isExpression) {
|
||||
if (!isExpression && node.body.body.length > 0) {
|
||||
for (const directive of (node.body.body: Array<Object>)) {
|
||||
if (directive.type === "ExpressionStatement" && directive.expression.type === "Literal") {
|
||||
if (directive.expression.value === "use strict") return true;
|
||||
} else {
|
||||
// Break for the first non literal expression
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("isValidDirective", function () {
|
||||
return function (stmt) {
|
||||
return stmt.type === "ExpressionStatement" &&
|
||||
stmt.expression.type === "Literal" &&
|
||||
typeof stmt.expression.value === "string" &&
|
||||
(!stmt.expression.extra || !stmt.expression.extra.parenthesized);
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseBlockBody", function (inner) {
|
||||
return function (node, ...args) {
|
||||
inner.call(this, node, ...args);
|
||||
|
||||
node.directives.reverse().forEach((directive) => {
|
||||
node.body.unshift(this.directiveToStmt(directive));
|
||||
});
|
||||
delete node.directives;
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseClassMethod", function (inner) {
|
||||
return function (classBody, ...args) {
|
||||
inner.call(this, classBody, ...args);
|
||||
|
||||
const body = classBody.body;
|
||||
body[body.length - 1].type = "MethodDefinition";
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseExprAtom", function(inner) {
|
||||
return function (...args) {
|
||||
switch (this.state.type) {
|
||||
case tt.regexp:
|
||||
return this.estreeParseRegExpLiteral(this.state.value);
|
||||
|
||||
case tt.num:
|
||||
case tt.string:
|
||||
return this.estreeParseLiteral(this.state.value);
|
||||
|
||||
case tt._null:
|
||||
return this.estreeParseLiteral(null);
|
||||
|
||||
case tt._true:
|
||||
return this.estreeParseLiteral(true);
|
||||
|
||||
case tt._false:
|
||||
return this.estreeParseLiteral(false);
|
||||
|
||||
default:
|
||||
return inner.call(this, ...args);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseMethod", function(inner) {
|
||||
return function (node, ...args) {
|
||||
let funcNode = this.startNode();
|
||||
funcNode.kind = node.kind; // provide kind, so inner method correctly sets state
|
||||
funcNode = inner.call(this, funcNode, ...args);
|
||||
delete funcNode.kind;
|
||||
node.value = this.finishNode(funcNode, "FunctionExpression");
|
||||
|
||||
return node;
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseObjectMethod", function(inner) {
|
||||
return function (...args) {
|
||||
const node = inner.call(this, ...args);
|
||||
|
||||
if (node) {
|
||||
if (node.kind === "method") node.kind = "init";
|
||||
node.type = "Property";
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("parseObjectProperty", function(inner) {
|
||||
return function (...args) {
|
||||
const node = inner.call(this, ...args);
|
||||
|
||||
if (node) {
|
||||
node.kind = "init";
|
||||
node.type = "Property";
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
});
|
||||
|
||||
instance.extend("toAssignable", function(inner) {
|
||||
return function (node, isBinding, ...args) {
|
||||
if (isSimpleProperty(node)) {
|
||||
this.toAssignable(node.value, isBinding, ...args);
|
||||
|
||||
return node;
|
||||
} else if (node.type === "ObjectExpression") {
|
||||
node.type = "ObjectPattern";
|
||||
for (const prop of (node.properties: Array<Object>)) {
|
||||
if (prop.kind === "get" || prop.kind === "set") {
|
||||
this.raise(prop.key.start, "Object pattern can't contain getter or setter");
|
||||
} else if (prop.method) {
|
||||
this.raise(prop.key.start, "Object pattern can't contain methods");
|
||||
} else {
|
||||
this.toAssignable(prop, isBinding, "object destructuring pattern");
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return inner.call(this, node, isBinding, ...args);
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -28,6 +28,45 @@ pp.flowParseTypeInitialiser = function (tok) {
|
||||
return type;
|
||||
};
|
||||
|
||||
pp.flowParsePredicate = function() {
|
||||
const node = this.startNode();
|
||||
const moduloLoc = this.state.startLoc;
|
||||
const moduloPos = this.state.start;
|
||||
this.expect(tt.modulo);
|
||||
const checksLoc = this.state.startLoc;
|
||||
this.expectContextual("checks");
|
||||
// Force '%' and 'checks' to be adjacent
|
||||
if (moduloLoc.line !== checksLoc.line || moduloLoc.column !== checksLoc.column - 1) {
|
||||
this.raise(moduloPos, "Spaces between ´%´ and ´checks´ are not allowed here.");
|
||||
}
|
||||
if (this.eat(tt.parenL)) {
|
||||
node.expression = this.parseExpression();
|
||||
this.expect(tt.parenR);
|
||||
return this.finishNode(node, "DeclaredPredicate");
|
||||
} else {
|
||||
return this.finishNode(node, "InferredPredicate");
|
||||
}
|
||||
};
|
||||
|
||||
pp.flowParseTypeAndPredicateInitialiser = function () {
|
||||
const oldInType = this.state.inType;
|
||||
this.state.inType = true;
|
||||
this.expect(tt.colon);
|
||||
let type = null;
|
||||
let predicate = null;
|
||||
if (this.match(tt.modulo)) {
|
||||
this.state.inType = oldInType;
|
||||
predicate = this.flowParsePredicate();
|
||||
} else {
|
||||
type = this.flowParseType();
|
||||
this.state.inType = oldInType;
|
||||
if (this.match(tt.modulo)) {
|
||||
predicate = this.flowParsePredicate();
|
||||
}
|
||||
}
|
||||
return [type, predicate];
|
||||
};
|
||||
|
||||
pp.flowParseDeclareClass = function (node) {
|
||||
this.next();
|
||||
this.flowParseInterfaceish(node, true);
|
||||
@@ -53,9 +92,10 @@ pp.flowParseDeclareFunction = function (node) {
|
||||
typeNode.params = tmp.params;
|
||||
typeNode.rest = tmp.rest;
|
||||
this.expect(tt.parenR);
|
||||
typeNode.returnType = this.flowParseTypeInitialiser();
|
||||
|
||||
let predicate = null;
|
||||
[typeNode.returnType, predicate] = this.flowParseTypeAndPredicateInitialiser();
|
||||
typeContainer.typeAnnotation = this.finishNode(typeNode, "FunctionTypeAnnotation");
|
||||
typeContainer.predicate = predicate;
|
||||
id.typeAnnotation = this.finishNode(typeContainer, "TypeAnnotation");
|
||||
|
||||
this.finishNode(id, id.type);
|
||||
@@ -531,7 +571,7 @@ pp.reinterpretTypeAsFunctionTypeParam = function (type) {
|
||||
|
||||
pp.flowParseFunctionTypeParams = function (params = []) {
|
||||
const ret = { params, rest: null };
|
||||
while (this.match(tt.name)) {
|
||||
while (!this.match(tt.parenR) && !this.match(tt.ellipsis)) {
|
||||
ret.params.push(this.flowParseFunctionTypeParam());
|
||||
if (!this.match(tt.parenR)) {
|
||||
this.expect(tt.comma);
|
||||
@@ -791,6 +831,12 @@ pp.flowParseTypeAnnotation = function () {
|
||||
return this.finishNode(node, "TypeAnnotation");
|
||||
};
|
||||
|
||||
pp.flowParseTypeAndPredicateAnnotation = function () {
|
||||
const node = this.startNode();
|
||||
[node.typeAnnotation, node.predicate] = this.flowParseTypeAndPredicateInitialiser();
|
||||
return this.finishNode(node, "TypeAnnotation");
|
||||
};
|
||||
|
||||
pp.flowParseTypeAnnotatableIdentifier = function () {
|
||||
const ident = this.flowParseRestrictedIdentifier();
|
||||
if (this.match(tt.colon)) {
|
||||
@@ -833,7 +879,7 @@ export default function (instance) {
|
||||
if (this.match(tt.colon) && !allowExpression) {
|
||||
// if allowExpression is true then we're parsing an arrow function and if
|
||||
// there's a return type then it's been handled elsewhere
|
||||
node.returnType = this.flowParseTypeAnnotation();
|
||||
node.returnType = this.flowParseTypeAndPredicateAnnotation();
|
||||
}
|
||||
|
||||
return inner.call(this, node, allowExpression);
|
||||
@@ -1045,9 +1091,9 @@ export default function (instance) {
|
||||
// parse an item inside a expression list eg. `(NODE, NODE)` where NODE represents
|
||||
// the position where this function is called
|
||||
instance.extend("parseExprListItem", function (inner) {
|
||||
return function (allowEmpty, refShorthandDefaultPos) {
|
||||
return function (...args) {
|
||||
const container = this.startNode();
|
||||
const node = inner.call(this, allowEmpty, refShorthandDefaultPos);
|
||||
const node = inner.call(this, ...args);
|
||||
if (this.match(tt.colon)) {
|
||||
container._exprListItem = true;
|
||||
container.expression = node;
|
||||
@@ -1220,9 +1266,10 @@ export default function (instance) {
|
||||
specifierTypeKind = "typeof";
|
||||
}
|
||||
|
||||
let isBinding = false;
|
||||
if (this.isContextual("as")) {
|
||||
const as_ident = this.parseIdentifier(true);
|
||||
if (specifierTypeKind !== null && !this.match(tt.name)) {
|
||||
if (specifierTypeKind !== null && !this.match(tt.name) && !this.state.type.keyword) {
|
||||
// `import {type as ,` or `import {type as }`
|
||||
specifier.imported = as_ident;
|
||||
specifier.importKind = specifierTypeKind;
|
||||
@@ -1231,23 +1278,20 @@ export default function (instance) {
|
||||
// `import {type as foo`
|
||||
specifier.imported = firstIdent;
|
||||
specifier.importKind = null;
|
||||
specifier.local = this.parseIdentifier(false);
|
||||
specifier.local = this.parseIdentifier();
|
||||
}
|
||||
} else if (specifierTypeKind !== null && this.match(tt.name)) {
|
||||
} else if (specifierTypeKind !== null && (this.match(tt.name) || this.state.type.keyword)) {
|
||||
// `import {type foo`
|
||||
specifier.imported = this.parseIdentifier(true);
|
||||
specifier.importKind = specifierTypeKind;
|
||||
specifier.local =
|
||||
this.eatContextual("as")
|
||||
? this.parseIdentifier(false)
|
||||
: specifier.imported.__clone();
|
||||
} else {
|
||||
if (firstIdent.name === "typeof") {
|
||||
this.unexpected(
|
||||
firstIdentLoc,
|
||||
"Cannot import a variable named `typeof`"
|
||||
);
|
||||
if (this.eatContextual("as")) {
|
||||
specifier.local = this.parseIdentifier();
|
||||
} else {
|
||||
isBinding = true;
|
||||
specifier.local = specifier.imported.__clone();
|
||||
}
|
||||
} else {
|
||||
isBinding = true;
|
||||
specifier.imported = firstIdent;
|
||||
specifier.importKind = null;
|
||||
specifier.local = specifier.imported.__clone();
|
||||
@@ -1260,6 +1304,8 @@ export default function (instance) {
|
||||
this.raise(firstIdentLoc, "`The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements`");
|
||||
}
|
||||
|
||||
if (isBinding) this.checkReservedWord(specifier.local.name, specifier.start, true, true);
|
||||
|
||||
this.checkLVal(specifier.local, true, undefined, "import specifier");
|
||||
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
|
||||
};
|
||||
@@ -1377,7 +1423,7 @@ export default function (instance) {
|
||||
try {
|
||||
const oldNoAnonFunctionType = this.state.noAnonFunctionType;
|
||||
this.state.noAnonFunctionType = true;
|
||||
const returnType = this.flowParseTypeAnnotation();
|
||||
const returnType = this.flowParseTypeAndPredicateAnnotation();
|
||||
this.state.noAnonFunctionType = oldNoAnonFunctionType;
|
||||
|
||||
if (this.canInsertSemicolon()) this.unexpected();
|
||||
|
||||
Reference in New Issue
Block a user