amis-rpc-design/node_modules/hermes-parser/dist/HermesToBabelAdapter.js

768 lines
21 KiB
JavaScript
Raw Normal View History

2023-10-07 19:42:30 +08:00
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _HermesASTAdapter = _interopRequireDefault(require("./HermesASTAdapter"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
function createSyntaxError(node, err) {
const syntaxError = new SyntaxError(err); // $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: node.loc.start.line,
column: node.loc.start.column
};
return syntaxError;
}
class HermesToBabelAdapter extends _HermesASTAdapter.default {
fixSourceLocation(node) {
var _this$sourceFilename;
const loc = node.loc;
if (loc == null) {
return;
}
node.loc = {
source: (_this$sourceFilename = this.sourceFilename) != null ? _this$sourceFilename : null,
start: loc.start,
end: loc.end
};
node.start = loc.rangeStart;
node.end = loc.rangeEnd;
}
mapNode(node) {
this.fixSourceLocation(node);
switch (node.type) {
case 'Program':
return this.mapProgram(node);
case 'BlockStatement':
return this.mapNodeWithDirectives(node);
case 'Empty':
return this.mapEmpty(node);
case 'Identifier':
return this.mapIdentifier(node);
case 'TemplateElement':
return this.mapTemplateElement(node);
case 'GenericTypeAnnotation':
return this.mapGenericTypeAnnotation(node);
case 'SymbolTypeAnnotation':
return this.mapSymbolTypeAnnotation(node);
case 'Property':
return this.mapProperty(node);
case 'MethodDefinition':
return this.mapMethodDefinition(node);
case 'ImportDeclaration':
return this.mapImportDeclaration(node);
case 'ImportSpecifier':
return this.mapImportSpecifier(node);
case 'ExportDefaultDeclaration':
return this.mapExportDefaultDeclaration(node);
case 'ExportNamedDeclaration':
return this.mapExportNamedDeclaration(node);
case 'ExportNamespaceSpecifier':
return this.mapExportNamespaceSpecifier(node);
case 'ExportAllDeclaration':
return this.mapExportAllDeclaration(node);
case 'RestElement':
return this.mapRestElement(node);
case 'ImportExpression':
return this.mapImportExpression(node);
case 'JSXStringLiteral':
return this.mapJSXStringLiteral(node);
case 'PrivateName':
return this.mapPrivateName(node);
case 'ClassPrivateProperty':
return this.mapPrivateProperty(node);
case 'FunctionDeclaration':
case 'FunctionExpression':
return this.mapFunction(node);
case 'IndexedAccessType':
case 'OptionalIndexedAccessType':
case 'KeyofTypeAnnotation':
case 'ConditionalType':
case 'InferType':
case 'TupleTypeLabeledElement':
case 'TupleTypeSpreadElement':
case 'ObjectTypeMappedTypeProperty':
case 'ComponentTypeAnnotation':
return this.mapUnsupportedTypeAnnotation(node);
case 'BigIntLiteral':
return this.mapBigIntLiteral(node);
case 'BigIntLiteralTypeAnnotation':
return this.mapBigIntLiteralTypeAnnotation(node);
case 'BigIntTypeAnnotation':
return this.mapBigIntTypeAnnotation(node);
case 'TypeofTypeAnnotation':
return this.mapTypeofTypeAnnotation(node);
case 'QualifiedTypeofIdentifier':
return this.mapQualifiedTypeofIdentifier(node);
case 'DeclareVariable':
return this.mapDeclareVariable(node);
case 'DeclareEnum':
return this.mapDeclareEnum(node);
case 'DeclareComponent':
return this.mapDeclareComponent(node);
case 'JSXElement':
return this.mapJSXElement(node);
case 'ComponentDeclaration':
return this.mapComponentDeclaration(node);
default:
return this.mapNodeDefault(node);
}
}
mapProgram(node) {
// Visit child nodes and convert to directives
const {
comments,
...program
} = this.mapNodeWithDirectives(node);
program.sourceType = this.getSourceType(); // Adjust start loc to beginning of file
program.loc.start = {
line: 1,
column: 0
};
program.start = 0; // Adjust end loc to include last comment if program ends with a comment
if (comments.length > 0) {
const lastComment = comments[comments.length - 1];
if (lastComment.end > program.end) {
program.loc.end = lastComment.loc.end;
program.end = lastComment.end;
}
} // Rename root node to File node and move Program node under program property
return {
type: 'File',
loc: program.loc,
start: program.start,
end: program.end,
program,
comments
};
}
mapNodeWithDirectives(node) {
const directives = [];
for (const child of node.body) {
if (child.type === 'ExpressionStatement' && child.directive != null) {
// Visit directive children
const directiveChild = this.mapNode(child); // Modify string literal node to be DirectiveLiteral node
directiveChild.expression.type = 'DirectiveLiteral'; // Construct Directive node with DirectiveLiteral value
directives.push({
type: 'Directive',
loc: directiveChild.loc,
start: directiveChild.start,
end: directiveChild.end,
value: directiveChild.expression
});
} else {
// Once we have found the first non-directive node we know there cannot be any more directives
break;
}
} // Move directives from body to new directives array
node.directives = directives;
if (directives.length !== 0) {
node.body = node.body.slice(directives.length);
} // Visit expression statement children
const body = node.body;
for (let i = 0; i < body.length; i++) {
const child = body[i];
if (child != null) {
body[i] = this.mapNode(child);
}
}
return node;
}
mapIdentifier(node) {
node.loc.identifierName = node.name;
return this.mapNodeDefault(node);
}
mapTemplateElement(node) {
// Adjust start loc to exclude "`" at beginning of template literal if this is the first quasi,
// otherwise exclude "}" from previous expression.
const startCharsToExclude = 1; // Adjust end loc to exclude "`" at end of template literal if this is the last quasi,
// otherwise exclude "${" from next expression.
const endCharsToExclude = node.tail ? 1 : 2;
return {
type: 'TemplateElement',
loc: {
start: {
line: node.loc.start.line,
column: node.loc.start.column + startCharsToExclude
},
end: {
line: node.loc.end.line,
column: node.loc.end.column - endCharsToExclude
}
},
start: node.start + startCharsToExclude,
end: node.end - endCharsToExclude,
tail: node.tail,
value: {
cooked: node.cooked,
raw: node.raw
}
};
}
mapGenericTypeAnnotation(node) {
// Convert simple `this` generic type to ThisTypeAnnotation
if (node.typeParameters == null && node.id.type === 'Identifier' && node.id.name === 'this') {
return {
type: 'ThisTypeAnnotation',
loc: node.loc,
start: node.start,
end: node.end
};
}
return this.mapNodeDefault(node);
}
mapSymbolTypeAnnotation(node) {
return {
type: 'GenericTypeAnnotation',
loc: node.loc,
start: node.start,
end: node.end,
id: {
type: 'Identifier',
loc: node.loc,
start: node.start,
end: node.end,
name: 'symbol'
},
typeParameters: null
};
}
mapProperty(node) {
const key = this.mapNode(node.key);
const value = this.mapNode(node.value); // Convert methods, getters, and setters to ObjectMethod nodes
if (node.method || node.kind !== 'init') {
// Properties under the FunctionExpression value that should be moved
// to the ObjectMethod node itself.
const {
id,
params,
body,
async,
generator,
returnType,
typeParameters,
predicate
} = value;
const newNode = {
type: 'ObjectMethod',
loc: node.loc,
start: node.start,
end: node.end,
// Non getter or setter methods have `kind = method`
kind: node.kind === 'init' ? 'method' : node.kind,
method: node.kind === 'init' ? true : false,
computed: node.computed,
key,
id,
params,
body,
async,
generator,
returnType,
typeParameters,
predicate
};
if (node.kind !== 'init') {
// babel emits an empty variance property on accessors for some reason
newNode.variance = null;
}
return newNode;
} else {
// Non-method property nodes should be renamed to ObjectProperty
node.type = 'ObjectProperty';
return node;
}
}
mapMethodDefinition(node) {
const key = this.mapNode(node.key);
const value = this.mapNode(node.value); // Properties under the FunctionExpression value that should be moved
// to the ClassMethod node itself.
const {
id,
params,
body,
async,
generator,
returnType,
typeParameters,
predicate
} = value;
return {
type: 'ClassMethod',
loc: node.loc,
start: node.start,
end: node.end,
kind: node.kind,
computed: node.computed,
static: node.static,
key,
id,
params,
body,
async,
generator,
returnType,
typeParameters,
predicate
};
}
mapRestElement(node) {
const restElement = this.mapNodeDefault(node); // Hermes puts type annotations on rest elements on the argument node,
// but Babel expects type annotations on the rest element node itself.
const annotation = restElement.argument.typeAnnotation;
if (annotation != null) {
restElement.typeAnnotation = annotation;
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
};
}
}
return restElement;
}
mapImportExpression(node) {
// Babel expects ImportExpression to be structued as a regular
// CallExpression where the callee is an Import node.
return {
type: 'CallExpression',
loc: node.loc,
start: node.start,
end: node.end,
callee: {
type: 'Import',
loc: { ...node.loc,
end: { ...node.loc.start,
column: node.loc.start.column + 'import'.length
}
},
start: node.start,
end: node.start + 'import'.length
},
arguments: [this.mapNode(node.source)]
};
}
mapJSXStringLiteral(node) {
// Babel expects StringLiterals in JSX,
// but Hermes uses JSXStringLiteral to attach the raw value without
// having to internally attach it to every single string literal.
return {
type: 'StringLiteral',
loc: node.loc,
start: node.start,
end: node.end,
value: node.value
};
}
mapFunction(node) {
// Remove the first parameter if it is a this-type annotation,
// which is not recognized by Babel.
if (node.params.length !== 0 && node.params[0].name === 'this') {
node.params.shift();
}
return this.mapNodeDefault(node);
}
/**
* If Babel (the version we target) does not support a type annotation we
* parse, we need to return some other valid type annotation in its place.
*/
mapUnsupportedTypeAnnotation(node) {
return {
type: 'AnyTypeAnnotation',
loc: node.loc,
start: node.start,
end: node.end
};
}
mapBigIntLiteral(node) {
node.value = this.getBigIntLiteralValue(node.bigint).value;
return node;
}
mapBigIntLiteralTypeAnnotation(node) {
node.value = this.getBigIntLiteralValue(node.raw).value;
return node;
}
/**
* Babel does not parse the bigint keyword type as the keyword node.
* So we need to down-level the AST to a plain GenericTypeAnnotation
*/
mapBigIntTypeAnnotation(node) {
return {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: 'bigint',
loc: node.loc,
start: node.start,
end: node.end
},
typeParameters: null,
loc: node.loc,
start: node.start,
end: node.end
};
}
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;
}
mapTypeofTypeAnnotation(nodeUnprocessed) {
nodeUnprocessed.argument = {
type: 'GenericTypeAnnotation',
id: nodeUnprocessed.argument,
typeParameters: null,
loc: nodeUnprocessed.argument.loc
};
return this.mapNodeDefault(nodeUnprocessed);
}
mapQualifiedTypeofIdentifier(nodeUnprocessed) {
nodeUnprocessed.type = 'QualifiedTypeIdentifier';
return this.mapNodeDefault(nodeUnprocessed);
}
mapDeclareVariable(nodeUnprocessed) {
delete nodeUnprocessed.kind;
return this.mapNodeDefault(nodeUnprocessed);
}
mapDeclareEnum(nodeUnprocessed) {
nodeUnprocessed.id.typeAnnotation = this.mapUnsupportedTypeAnnotation(nodeUnprocessed.body);
delete nodeUnprocessed.body;
nodeUnprocessed.type = 'DeclareVariable';
return this.mapDeclareVariable(nodeUnprocessed);
}
mapDeclareComponent(nodeUnprocessed) {
nodeUnprocessed.id.typeAnnotation = this.mapUnsupportedTypeAnnotation(nodeUnprocessed);
delete nodeUnprocessed.params;
delete nodeUnprocessed.rest;
delete nodeUnprocessed.typeParameters;
delete nodeUnprocessed.rendersType;
nodeUnprocessed.type = 'DeclareVariable';
return this.mapDeclareVariable(nodeUnprocessed);
}
mapJSXElement(nodeUnprocessed) {
delete nodeUnprocessed.openingElement.typeArguments;
return this.mapNodeDefault(nodeUnprocessed);
}
mapComponentDeclaration(nodeUnprocessed) {
let rendersType = nodeUnprocessed.rendersType;
if (rendersType == null) {
// Create empty loc for return type annotation nodes
const createRendersTypeLoc = () => ({
loc: {
start: { ...nodeUnprocessed.body.loc.end
},
end: { ...nodeUnprocessed.body.loc.end
},
rangeStart: nodeUnprocessed.body.loc.rangeStart,
rangeEnd: nodeUnprocessed.body.loc.rangeEnd
}
});
rendersType = {
type: 'GenericTypeAnnotation',
id: {
type: 'QualifiedTypeIdentifier',
qualification: {
type: 'Identifier',
name: 'React',
...createRendersTypeLoc()
},
id: {
type: 'Identifier',
name: 'Node',
...createRendersTypeLoc()
}
},
typeParameters: null,
...createRendersTypeLoc()
};
}
function getParamName(paramName) {
switch (paramName.type) {
case 'Identifier':
return paramName.name;
case 'StringLiteral':
return paramName.value;
default:
throw createSyntaxError(paramName, `Unknown Component parameter name type of "${paramName.type}"`);
}
}
const properties = nodeUnprocessed.params.map(param => {
switch (param.type) {
case 'RestElement':
{
delete param.typeAnnotation;
return param;
}
case 'ComponentParameter':
{
if (getParamName(param.name) === 'ref') {
throw createSyntaxError(param, 'Component parameters named "ref" are currently not supported');
}
if (param.name.type === 'Identifier') {
delete param.name.typeAnnotation;
}
if (param.local.type === 'AssignmentPattern') {
delete param.local.left.typeAnnotation;
delete param.local.left.optional;
} else {
delete param.local.typeAnnotation;
delete param.local.optional;
}
return {
type: 'ObjectProperty',
key: param.name,
value: param.local,
method: false,
shorthand: param.shorthand,
computed: false,
loc: param.loc,
start: param.start,
end: param.end
};
}
default:
{
throw createSyntaxError(param, `Unknown Component parameter type of "${param.type}"`);
}
}
});
const paramsLoc = (() => {
if (properties.length === 0) {
// No props, approximate range via existing nodes.
const startLoc = nodeUnprocessed.typeParameters != null ? nodeUnprocessed.typeParameters.loc : nodeUnprocessed.id.loc;
return {
start: startLoc.end,
end: rendersType.loc.start,
startRange: startLoc.endRange,
endRange: rendersType.loc.startRange
};
}
return {
start: properties[0].loc.start,
end: properties[properties.length - 1].loc.end,
startRange: properties[0].loc.startRange,
endRange: properties[properties.length - 1].loc.endRange
};
})(); // Create empty loc for type annotation nodes
const createParamsTypeLoc = () => ({
loc: {
start: { ...paramsLoc.end
},
end: { ...paramsLoc.end
},
startRange: paramsLoc.endRange,
endRange: paramsLoc.endRange
}
});
const params = [{
type: 'ObjectPattern',
properties,
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: '$ReadOnly',
...createParamsTypeLoc()
},
typeParameters: {
type: 'TypeParameterInstantiation',
params: [{
type: 'ObjectTypeAnnotation',
callProperties: [],
properties: [],
indexers: [],
internalSlots: [],
exact: false,
inexact: true,
...createParamsTypeLoc()
}],
...createParamsTypeLoc()
},
...createParamsTypeLoc()
},
...createParamsTypeLoc()
},
loc: paramsLoc
}];
const functionComponent = {
type: 'FunctionDeclaration',
id: nodeUnprocessed.id,
typeParameters: nodeUnprocessed.typeParameters,
params,
returnType: rendersType,
body: nodeUnprocessed.body,
async: false,
generator: false,
predicate: null,
loc: nodeUnprocessed.loc
};
return this.mapNodeDefault(functionComponent);
}
}
exports.default = HermesToBabelAdapter;