hermes-parser
Advanced tools
Comparing version 0.7.0 to 0.8.0
@@ -80,2 +80,5 @@ /** | ||
}, | ||
ChainExpression: { | ||
expression: 'Node' | ||
}, | ||
ClassBody: { | ||
@@ -106,14 +109,2 @@ body: 'NodeList' | ||
}, | ||
ClassPrivateProperty: { | ||
key: 'Node', | ||
value: 'Node', | ||
variance: 'Node', | ||
typeAnnotation: 'Node' | ||
}, | ||
ClassProperty: { | ||
key: 'Node', | ||
value: 'Node', | ||
variance: 'Node', | ||
typeAnnotation: 'Node' | ||
}, | ||
ConditionalExpression: { | ||
@@ -217,2 +208,3 @@ test: 'Node', | ||
ExportAllDeclaration: { | ||
exported: 'Node', | ||
source: 'Node' | ||
@@ -228,5 +220,2 @@ }, | ||
}, | ||
ExportNamespaceSpecifier: { | ||
exported: 'Node' | ||
}, | ||
ExportSpecifier: { | ||
@@ -456,7 +445,2 @@ exported: 'Node', | ||
}, | ||
OptionalCallExpression: { | ||
callee: 'Node', | ||
typeArguments: 'Node', | ||
arguments: 'NodeList' | ||
}, | ||
OptionalIndexedAccessType: { | ||
@@ -466,9 +450,3 @@ objectType: 'Node', | ||
}, | ||
OptionalMemberExpression: { | ||
object: 'Node', | ||
property: 'Node' | ||
}, | ||
PrivateName: { | ||
id: 'Node' | ||
}, | ||
PrivateIdentifier: {}, | ||
Program: { | ||
@@ -481,2 +459,8 @@ body: 'NodeList' | ||
}, | ||
PropertyDefinition: { | ||
key: 'Node', | ||
value: 'Node', | ||
variance: 'Node', | ||
typeAnnotation: 'Node' | ||
}, | ||
QualifiedTypeIdentifier: { | ||
@@ -610,4 +594,28 @@ qualification: 'Node', | ||
}, | ||
Import: {} | ||
Import: {}, | ||
ClassProperty: { | ||
key: 'Node', | ||
value: 'Node', | ||
variance: 'Node', | ||
typeAnnotation: 'Node' | ||
}, | ||
ClassPrivateProperty: { | ||
key: 'Node', | ||
value: 'Node', | ||
variance: 'Node', | ||
typeAnnotation: 'Node' | ||
}, | ||
PrivateName: { | ||
id: 'Node' | ||
}, | ||
OptionalCallExpression: { | ||
callee: 'Node', | ||
typeArguments: 'Node', | ||
arguments: 'NodeList' | ||
}, | ||
OptionalMemberExpression: { | ||
object: 'Node', | ||
property: 'Node' | ||
} | ||
}; | ||
exports.HERMES_AST_VISITOR_KEYS = HERMES_AST_VISITOR_KEYS; |
@@ -176,6 +176,2 @@ "use strict"; | ||
mapPrivateProperty(node) { | ||
throw new SyntaxError(this.formatError(node, 'Private properties are not supported')); | ||
} | ||
formatError(node, message) { | ||
@@ -182,0 +178,0 @@ return `${message} (${node.loc.start.line}:${node.loc.start.column})`; |
@@ -92,2 +92,5 @@ "use strict"; | ||
case 'ExportNamespaceSpecifier': | ||
return this.mapExportNamespaceSpecifier(node); | ||
case 'ExportAllDeclaration': | ||
@@ -106,2 +109,4 @@ return this.mapExportAllDeclaration(node); | ||
case 'PrivateName': | ||
return this.mapPrivateName(node); | ||
case 'ClassPrivateProperty': | ||
@@ -118,2 +123,5 @@ return this.mapPrivateProperty(node); | ||
case 'BigIntLiteral': | ||
return this.mapBigIntLiteral(node); | ||
default: | ||
@@ -284,3 +292,3 @@ return this.mapNodeDefault(node); | ||
} = value; | ||
return { | ||
const newNode = { | ||
type: 'ObjectMethod', | ||
@@ -292,2 +300,3 @@ loc: node.loc, | ||
kind: node.kind === 'init' ? 'method' : node.kind, | ||
method: node.kind === 'init' ? true : false, | ||
computed: node.computed, | ||
@@ -304,2 +313,9 @@ key, | ||
}; | ||
if (node.kind !== 'init') { | ||
// babel emits an empty variance property on accessors for some reason | ||
newNode.variance = null; | ||
} | ||
return newNode; | ||
} else { | ||
@@ -355,3 +371,11 @@ // Non-method property nodes should be renamed to ObjectProperty | ||
restElement.typeAnnotation = annotation; | ||
restElement.argument.typeAnnotation = null; | ||
restElement.argument.typeAnnotation = null; // Unfortunately there's no way for us to recover the end location of | ||
// the argument for the general case | ||
if (restElement.argument.type === 'Identifier') { | ||
restElement.argument.end = restElement.argument.start + restElement.argument.name.length; | ||
restElement.argument.loc.end = { ...restElement.argument.loc.start, | ||
column: restElement.argument.loc.start.column + restElement.argument.name.length | ||
}; | ||
} | ||
} | ||
@@ -372,5 +396,9 @@ | ||
type: 'Import', | ||
loc: node.loc, | ||
loc: { ...node.loc, | ||
end: { ...node.loc.start, | ||
column: node.loc.start.column + 'import'.length | ||
} | ||
}, | ||
start: node.start, | ||
end: node.end | ||
end: node.start + 'import'.length | ||
}, | ||
@@ -418,4 +446,55 @@ arguments: [this.mapNode(node.source)] | ||
mapBigIntLiteral(node) { | ||
const bigint = node.bigint.replace(/n$/, '').replace(/_/, ''); | ||
node.value = typeof BigInt === 'function' ? BigInt(bigint) : null; | ||
return node; | ||
} | ||
mapPrivateProperty(nodeUnprocessed) { | ||
const node = this.mapNodeDefault(nodeUnprocessed); | ||
node.key = { | ||
type: 'PrivateName', | ||
id: { ...node.key, | ||
// babel doesn't include the hash in the identifier | ||
start: node.key.start + 1, | ||
loc: { ...node.key.loc, | ||
start: { ...node.key.loc.start, | ||
column: node.key.loc.start.column + 1 | ||
} | ||
} | ||
}, | ||
start: node.key.start, | ||
end: node.key.end, | ||
loc: node.key.loc | ||
}; | ||
return node; | ||
} | ||
mapPrivateName(node) { | ||
// babel doesn't include the hash in the identifier | ||
node.id.start += 1; | ||
node.id.loc.start.column += 1; | ||
return node; | ||
} | ||
mapExportNamespaceSpecifier(nodeUnprocessed) { | ||
const node = this.mapNodeDefault(nodeUnprocessed); // the hermes AST emits the location as the location of the entire export | ||
// but babel emits the location as *just* the "* as id" bit | ||
// the end will always align with the end of the identifier (ezpz) | ||
// but the start will align with the "*" token - which we can't recover from just the AST | ||
// so we just fudge the start location a bit to get it "good enough" | ||
// it will be wrong if the AST is anything like "export * as x from 'y'"... but oh well | ||
node.start = node.start + 'export '.length; | ||
node.loc.start.column = node.loc.start.column + 'export '.length; | ||
node.end = node.exported.end; | ||
node.loc.end = { | ||
column: node.exported.loc.end.column, | ||
line: node.exported.loc.end.line | ||
}; | ||
return node; | ||
} | ||
} | ||
exports.default = HermesToBabelAdapter; |
@@ -10,2 +10,4 @@ "use strict"; | ||
var _getModuleDocblock = require("./getModuleDocblock"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -53,2 +55,4 @@ | ||
node.range = [loc.rangeStart, loc.rangeEnd]; | ||
delete node.start; | ||
delete node.end; | ||
} | ||
@@ -105,8 +109,22 @@ | ||
case 'Property': | ||
return this.mapProperty(node); | ||
case 'FunctionDeclaration': | ||
case 'FunctionExpression': | ||
case 'ArrowFunctionExpression': | ||
return this.mapFunction(node); | ||
case 'PrivateName': | ||
return this.mapPrivateName(node); | ||
case 'ClassProperty': | ||
case 'ClassPrivateProperty': | ||
return this.mapPrivateProperty(node); | ||
return this.mapClassProperty(node); | ||
case 'Property': | ||
return this.mapProperty(node); | ||
case 'MemberExpression': | ||
case 'OptionalMemberExpression': | ||
case 'CallExpression': | ||
case 'OptionalCallExpression': | ||
return this.mapChainExpression(node); | ||
@@ -121,2 +139,3 @@ default: | ||
node.sourceType = this.getSourceType(); | ||
node.docblock = (0, _getModuleDocblock.getModuleDocblock)(node); | ||
return node; | ||
@@ -253,4 +272,187 @@ } | ||
mapFunction(nodeUnprocessed) { | ||
const node = this.mapNodeDefault(nodeUnprocessed); | ||
switch (node.type) { | ||
case 'FunctionDeclaration': | ||
case 'FunctionExpression': | ||
node.expression = false; | ||
return node; | ||
case 'ArrowFunctionExpression': | ||
node.expression = node.body.type !== 'BlockStatement'; | ||
return node; | ||
} | ||
return node; | ||
} | ||
mapChainExpression(nodeUnprocessed) { | ||
/* | ||
NOTE - In the below comments `MemberExpression` and `CallExpression` | ||
are completely interchangable. For terseness we just reference | ||
one each time. | ||
*/ | ||
/* | ||
Hermes uses the old babel-style AST: | ||
``` | ||
(one?.two).three?.four; | ||
^^^^^^^^^^^^^^^^^^^^^^ OptionalMemberExpression | ||
^^^^^^^^^^^^^^^^ MemberExpression | ||
^^^^^^^^ OptionalMemberExpression | ||
``` | ||
We need to convert it to the ESTree representation: | ||
``` | ||
(one?.two).three?.four; | ||
^^^^^^^^^^^^^^^^^^^^^^ ChainExpression | ||
^^^^^^^^^^^^^^^^^^^^^^ MemberExpression[optional = true] | ||
^^^^^^^^^^^^^^^^ MemberExpression[optional = false] | ||
^^^^^^^^ ChainExpression | ||
^^^^^^^^ MemberExpression[optional = true] | ||
``` | ||
We do this by converting the AST and its children (depth first), and then unwrapping | ||
the resulting AST as appropriate. | ||
Put another way: | ||
1) traverse to the leaf | ||
2) if the current node is an `OptionalMemberExpression`: | ||
a) if the `.object` is a `ChainExpression`: | ||
i) unwrap the child (`node.object = child.expression`) | ||
b) convert this node to a `MemberExpression[optional = true]` | ||
c) wrap this node (`node = ChainExpression[expression = node]`) | ||
3) if the current node is a `MembedExpression`: | ||
a) convert this node to a `MemberExpression[optional = true]` | ||
*/ | ||
const node = this.mapNodeDefault(nodeUnprocessed); | ||
const { | ||
child, | ||
childKey, | ||
isOptional | ||
} = (() => { | ||
const isOptional = node.optional === true; | ||
if (node.type.endsWith('MemberExpression')) { | ||
return { | ||
child: node.object, | ||
childKey: 'object', | ||
isOptional | ||
}; | ||
} else if (node.type.endsWith('CallExpression')) { | ||
return { | ||
child: node.callee, | ||
childKey: 'callee', | ||
isOptional | ||
}; | ||
} else { | ||
return { | ||
child: node.expression, | ||
childKey: 'expression', | ||
isOptional: false | ||
}; | ||
} | ||
})(); | ||
const isChildUnwrappable = child.type === 'ChainExpression' && // (x?.y).z is semantically different to `x?.y.z`. | ||
// In the un-parenthesised case `.z` is only executed if and only if `x?.y` returns a non-nullish value. | ||
// In the parenthesised case, `.z` is **always** executed, regardless of the return of `x?.y`. | ||
// As such the AST is different between the two cases. | ||
// | ||
// In the hermes AST - any member part of a non-short-circuited optional chain is represented with `OptionalMemberExpression` | ||
// so if we see a `MemberExpression`, then we know we've hit a parenthesis boundary. | ||
node.type !== 'MemberExpression' && node.type !== 'CallExpression'; | ||
if (node.type.startsWith('Optional')) { | ||
node.type = node.type.replace('Optional', ''); | ||
node.optional = isOptional; | ||
} else { | ||
node.optional = false; | ||
} | ||
if (!isChildUnwrappable && !isOptional) { | ||
return node; | ||
} | ||
if (isChildUnwrappable) { | ||
const newChild = child.expression; | ||
node[childKey] = newChild; | ||
} | ||
return { | ||
type: 'ChainExpression', | ||
expression: node, | ||
loc: node.loc, | ||
range: node.range | ||
}; | ||
} | ||
mapClassProperty(nodeUnprocessed) { | ||
const node = this.mapNodeDefault(nodeUnprocessed); | ||
const key = (() => { | ||
if (node.type === 'ClassPrivateProperty') { | ||
const key = this.mapNodeDefault(node.key); | ||
return { | ||
type: 'PrivateIdentifier', | ||
name: key.name, | ||
range: key.range, | ||
loc: key.loc | ||
}; | ||
} | ||
return node.key; | ||
})(); | ||
return { ...node, | ||
computed: node.type === 'ClassPrivateProperty' ? false : node.computed, | ||
key, | ||
type: 'PropertyDefinition' | ||
}; | ||
} | ||
mapPrivateName(node) { | ||
return { | ||
type: 'PrivateIdentifier', | ||
name: node.id.name, | ||
// estree the location refers to the entire string including the hash token | ||
range: node.range, | ||
loc: node.loc | ||
}; | ||
} | ||
mapExportNamedDeclaration(nodeUnprocessed) { | ||
const node = super.mapExportNamedDeclaration(nodeUnprocessed); | ||
const namespaceSpecifier = node.specifiers.find(spec => spec.type === 'ExportNamespaceSpecifier'); | ||
if (namespaceSpecifier != null) { | ||
var _node$exportKind; | ||
if (node.specifiers.length !== 1) { | ||
// this should already a hermes parser error - but let's be absolutely sure we're aligned with the spec | ||
throw new Error('Cannot use an export all with any other specifiers'); | ||
} | ||
return { | ||
type: 'ExportAllDeclaration', | ||
source: node.source, | ||
exportKind: (_node$exportKind = node.exportKind) != null ? _node$exportKind : 'value', | ||
exported: namespaceSpecifier.exported, | ||
range: node.range, | ||
loc: node.loc | ||
}; | ||
} | ||
return node; | ||
} | ||
mapExportAllDeclaration(nodeUnprocessed) { | ||
var _node$exported; | ||
const node = super.mapExportAllDeclaration(nodeUnprocessed); | ||
node.exported = (_node$exported = node.exported) != null ? _node$exported : null; | ||
return node; | ||
} | ||
} | ||
exports.default = HermesToESTreeAdapter; |
{ | ||
"name": "hermes-parser", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "A JavaScript parser built from the Hermes engine", | ||
@@ -12,6 +12,8 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"hermes-estree": "0.7.0" | ||
"hermes-estree": "0.8.0" | ||
}, | ||
"devDependencies": { | ||
"hermes-transform": "0.7.0" | ||
"@babel/parser": "7.7.4", | ||
"espree": "9.3.2", | ||
"hermes-transform": "0.8.0" | ||
}, | ||
@@ -18,0 +20,0 @@ "files": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
1115046
27
3738
3
+ Addedhermes-estree@0.8.0(transitive)
- Removedhermes-estree@0.7.0(transitive)
Updatedhermes-estree@0.8.0