merge new into master

This commit is contained in:
2026-02-05 17:31:20 +00:00
16267 changed files with 2194867 additions and 0 deletions

21
node_modules/hermes-parser/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

11
node_modules/hermes-parser/README.md generated vendored Normal file
View File

@@ -0,0 +1,11 @@
# hermes-parser
A JavaScript parser built from the Hermes engine's parser compiled to WebAssembly. Can parse ES6, Flow, and JSX syntax.
## API
The Hermes parser exposes a single `parse(code, [options])` function, where `code` is the source code to parse as a string, and `options` is an optional object that may contain the following properties:
- **babel**: `boolean`, defaults to `false`. If `true`, output an AST conforming to Babel's AST format. If `false`, output an AST conforming to the ESTree AST format.
- **allowReturnOutsideFunction**: `boolean`, defaults to `false`. If `true`, do not error on return statements found outside functions.
- **flow**: `"all"` or `"detect"`, defaults to `"detect"`. If `"detect"`, only parse syntax as Flow syntax where it is ambiguous whether it is a Flow feature or regular JavaScript when the `@flow` pragma is present in the file. Otherwise if `"all"`, always parse ambiguous syntax as Flow syntax regardless of the presence of an `@flow` pragma. For example `foo<T>(x)` in a file without an `@flow` pragma will be parsed as two comparisons if set to `"detect"`, otherwise if set to `"all"` or the `@flow` pragma is included it will be parsed as a call expression with a type argument.
- **sourceFilename**: `string`, defaults to `null`. The filename corresponding to the code that is to be parsed. If non-null, the filename will be added to all source locations in the output AST.
- **sourceType**: `"module"`, `"script"`, or `"unambiguous"` (default). If `"unambiguous"`, source type will be automatically detected and set to `"module"` if any ES6 imports or exports are present in the code, otherwise source type will be set to `"script"`.
- **tokens**: `boolean`, defaults to `false`. If `true`, add all tokens to a `tokens` property on the root node.

57
node_modules/hermes-parser/dist/HermesAST.js.flow generated vendored Normal file
View File

@@ -0,0 +1,57 @@
/**
* 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.
*
* @flow strict
* @format
*/
// flowlint unclear-type:off
export type HermesPosition = {
/** >= 1 */
+line: number,
/** >= 0 */
+column: number,
};
export type HermesSourceLocation = {
start?: HermesPosition,
end?: HermesPosition,
rangeStart?: number,
rangeEnd?: number,
};
export type HermesNode = {
type: string,
[string]: any,
};
export type HermesToken = {
type:
| 'Boolean'
| 'Identifier'
| 'Keyword'
| 'Null'
| 'Numeric'
| 'BigInt'
| 'Punctuator'
| 'String'
| 'RegularExpression'
| 'Template'
| 'JSXText',
loc: HermesSourceLocation,
value: ?string,
};
export type HermesComment = {
type: 'CommentLine' | 'CommentBlock' | 'InterpreterDirective',
loc: HermesSourceLocation,
value: ?string,
};
export type HermesProgram = {
type: 'Program',
loc: HermesSourceLocation,
body: Array<?HermesNode>,
comments: Array<HermesComment>,
tokens?: Array<HermesToken>,
interpreter?: ?HermesComment,
};

193
node_modules/hermes-parser/dist/HermesASTAdapter.js generated vendored Normal file
View File

@@ -0,0 +1,193 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _ParserVisitorKeys = require("./generated/ParserVisitorKeys");
/**
* 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
*/
/**
* The base class for transforming the Hermes AST to the desired output format.
* Extended by concrete adapters which output an ESTree or Babel AST.
*/
class HermesASTAdapter {
constructor(options) {
this.sourceFilename = void 0;
this.sourceType = void 0;
this.sourceFilename = options.sourceFilename;
this.sourceType = options.sourceType;
}
/**
* Transform the input Hermes AST to the desired output format.
* This modifies the input AST in place instead of constructing a new AST.
*/
transform(program) {
// Comments are not traversed via visitor keys
const comments = program.comments;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
this.fixSourceLocation(comment);
comments[i] = this.mapComment(comment);
} // The first comment may be an interpreter directive and is stored directly on the program node
program.interpreter = comments.length > 0 && comments[0].type === 'InterpreterDirective' ? comments.shift() : null; // Tokens are not traversed via visitor keys
const tokens = program.tokens;
if (tokens) {
for (let i = 0; i < tokens.length; i++) {
this.fixSourceLocation(tokens[i]);
}
}
const resultNode = this.mapNode(program);
if (resultNode.type !== 'Program') {
throw new Error(`HermesToESTreeAdapter: Must return a Program node, instead of "${resultNode.type}". `);
} // $FlowExpectedError[incompatible-return] We know this is a program at this point.
return resultNode;
}
/**
* Transform a Hermes AST node to the output AST format.
*
* This may modify the input node in-place and return that same node, or a completely
* new node may be constructed and returned. Overriden in child classes.
*/
mapNode(_node) {
throw new Error('Implemented in subclasses');
}
mapNodeDefault(node) {
const visitorKeys = _ParserVisitorKeys.HERMES_AST_VISITOR_KEYS[node.type];
for (const key in visitorKeys) {
const childType = visitorKeys[key];
if (childType === _ParserVisitorKeys.NODE_CHILD) {
const child = node[key];
if (child != null) {
node[key] = this.mapNode(child);
}
} else if (childType === _ParserVisitorKeys.NODE_LIST_CHILD) {
const children = node[key];
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child != null) {
children[i] = this.mapNode(child);
}
}
}
}
return node;
}
/**
* Update the source location for this node depending on the output AST format.
* This can modify the input node in-place. Overriden in child classes.
*/
fixSourceLocation(_node) {
throw new Error('Implemented in subclasses');
}
getSourceType() {
var _this$sourceType;
return (_this$sourceType = this.sourceType) != null ? _this$sourceType : 'script';
}
setModuleSourceType() {
if (this.sourceType == null) {
this.sourceType = 'module';
}
}
mapComment(node) {
return node;
}
mapEmpty(_node) {
// $FlowExpectedError
return null;
}
mapImportDeclaration(node) {
if (node.importKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapImportSpecifier(node) {
if (node.importKind === 'value') {
node.importKind = null;
}
return this.mapNodeDefault(node);
}
mapExportDefaultDeclaration(node) {
this.setModuleSourceType();
return this.mapNodeDefault(node);
}
mapExportNamedDeclaration(node) {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapExportAllDeclaration(node) {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
formatError(node, message) {
return `${message} (${node.loc.start.line}:${node.loc.start.column})`;
}
getBigIntLiteralValue(bigintString) {
// TODO - once we update flow we can remove this
const bigint = bigintString // estree spec is to not have a trailing `n` on this property
// https://github.com/estree/estree/blob/db962bb417a97effcfe9892f87fbb93c81a68584/es2020.md#bigintliteral
.replace(/n$/, '') // `BigInt` doesn't accept numeric separator and `bigint` property should not include numeric separator
.replace(/_/, '');
return {
bigint,
// coerce the string to a bigint value if supported by the environment
value: typeof BigInt === 'function' ? BigInt(bigint) : null
};
}
}
exports.default = HermesASTAdapter;

View File

@@ -0,0 +1,192 @@
/**
* 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.
*
* @flow strict
* @format
*/
import type {Program} from 'hermes-estree';
import type {HermesNode} from './HermesAST';
import type {ParserOptions} from './ParserOptions';
import {
HERMES_AST_VISITOR_KEYS,
NODE_CHILD,
NODE_LIST_CHILD,
} from './generated/ParserVisitorKeys';
/**
* The base class for transforming the Hermes AST to the desired output format.
* Extended by concrete adapters which output an ESTree or Babel AST.
*/
export default class HermesASTAdapter {
sourceFilename: ParserOptions['sourceFilename'];
sourceType: ParserOptions['sourceType'];
constructor(options: ParserOptions) {
this.sourceFilename = options.sourceFilename;
this.sourceType = options.sourceType;
}
/**
* Transform the input Hermes AST to the desired output format.
* This modifies the input AST in place instead of constructing a new AST.
*/
transform(program: HermesNode): Program {
// Comments are not traversed via visitor keys
const comments = program.comments;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
this.fixSourceLocation(comment);
comments[i] = this.mapComment(comment);
}
// The first comment may be an interpreter directive and is stored directly on the program node
program.interpreter =
comments.length > 0 && comments[0].type === 'InterpreterDirective'
? comments.shift()
: null;
// Tokens are not traversed via visitor keys
const tokens = program.tokens;
if (tokens) {
for (let i = 0; i < tokens.length; i++) {
this.fixSourceLocation(tokens[i]);
}
}
const resultNode = this.mapNode(program);
if (resultNode.type !== 'Program') {
throw new Error(
`HermesToESTreeAdapter: Must return a Program node, instead of "${resultNode.type}". `,
);
}
// $FlowExpectedError[incompatible-return] We know this is a program at this point.
return resultNode;
}
/**
* Transform a Hermes AST node to the output AST format.
*
* This may modify the input node in-place and return that same node, or a completely
* new node may be constructed and returned. Overriden in child classes.
*/
mapNode(_node: HermesNode): HermesNode {
throw new Error('Implemented in subclasses');
}
mapNodeDefault(node: HermesNode): HermesNode {
const visitorKeys = HERMES_AST_VISITOR_KEYS[node.type];
for (const key in visitorKeys) {
const childType = visitorKeys[key];
if (childType === NODE_CHILD) {
const child = node[key];
if (child != null) {
node[key] = this.mapNode(child);
}
} else if (childType === NODE_LIST_CHILD) {
const children = node[key];
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child != null) {
children[i] = this.mapNode(child);
}
}
}
}
return node;
}
/**
* Update the source location for this node depending on the output AST format.
* This can modify the input node in-place. Overriden in child classes.
*/
fixSourceLocation(_node: HermesNode): void {
throw new Error('Implemented in subclasses');
}
getSourceType(): ParserOptions['sourceType'] {
return this.sourceType ?? 'script';
}
setModuleSourceType(): void {
if (this.sourceType == null) {
this.sourceType = 'module';
}
}
mapComment(node: HermesNode): HermesNode {
return node;
}
mapEmpty(_node: HermesNode): HermesNode {
// $FlowExpectedError
return null;
}
mapImportDeclaration(node: HermesNode): HermesNode {
if (node.importKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapImportSpecifier(node: HermesNode): HermesNode {
if (node.importKind === 'value') {
node.importKind = null;
}
return this.mapNodeDefault(node);
}
mapExportDefaultDeclaration(node: HermesNode): HermesNode {
this.setModuleSourceType();
return this.mapNodeDefault(node);
}
mapExportNamedDeclaration(node: HermesNode): HermesNode {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapExportAllDeclaration(node: HermesNode): HermesNode {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
formatError(node: HermesNode, message: string): string {
return `${message} (${node.loc.start.line}:${node.loc.start.column})`;
}
getBigIntLiteralValue(bigintString: string): {
bigint: string,
value: $FlowFixMe /* bigint */,
} {
// TODO - once we update flow we can remove this
declare var BigInt: ?(value: $FlowFixMe) => mixed;
const bigint = bigintString
// estree spec is to not have a trailing `n` on this property
// https://github.com/estree/estree/blob/db962bb417a97effcfe9892f87fbb93c81a68584/es2020.md#bigintliteral
.replace(/n$/, '')
// `BigInt` doesn't accept numeric separator and `bigint` property should not include numeric separator
.replace(/_/, '');
return {
bigint,
// coerce the string to a bigint value if supported by the environment
value: typeof BigInt === 'function' ? BigInt(bigint) : null,
};
}
}

108
node_modules/hermes-parser/dist/HermesParser.js generated vendored Normal file
View File

@@ -0,0 +1,108 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parse = parse;
var _HermesParserDeserializer = _interopRequireDefault(require("./HermesParserDeserializer"));
var _HermesParserWASM = _interopRequireDefault(require("./HermesParserWASM"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
let HermesParserWASM;
let hermesParse;
let hermesParseResult_free;
let hermesParseResult_getError;
let hermesParseResult_getErrorLine;
let hermesParseResult_getErrorColumn;
let hermesParseResult_getProgramBuffer;
let hermesParseResult_getPositionBuffer;
let hermesParseResult_getPositionBufferSize;
/**
* Init the WASM wrapper code generated by `emscripten` to preparse the
* HermesParser WASM code.
*/
function initHermesParserWASM() {
if (HermesParserWASM != null) {
return;
}
HermesParserWASM = (0, _HermesParserWASM.default)({
/**
* The emscripten version of `quit` unconditionally assigns the `status` to
* `process.exitCode` which overrides any pre-existing code that has been
* set, even if it is non zero. For our use case we never want an
* `exitCode` to be set so this override removes that functionality.
*/
quit(_status, toThrow) {
throw toThrow;
}
});
hermesParse = HermesParserWASM.cwrap('hermesParse', 'number', ['number', 'number', 'number', 'number', 'number', 'number']);
hermesParseResult_free = HermesParserWASM.cwrap('hermesParseResult_free', 'void', ['number']);
hermesParseResult_getError = HermesParserWASM.cwrap('hermesParseResult_getError', 'string', ['number']);
hermesParseResult_getErrorLine = HermesParserWASM.cwrap('hermesParseResult_getErrorLine', 'number', ['number']);
hermesParseResult_getErrorColumn = HermesParserWASM.cwrap('hermesParseResult_getErrorColumn', 'number', ['number']);
hermesParseResult_getProgramBuffer = HermesParserWASM.cwrap('hermesParseResult_getProgramBuffer', 'number', ['number']);
hermesParseResult_getPositionBuffer = HermesParserWASM.cwrap('hermesParseResult_getPositionBuffer', 'number', ['number']);
hermesParseResult_getPositionBufferSize = HermesParserWASM.cwrap('hermesParseResult_getPositionBufferSize', 'number', ['number']);
} // Copy a string into the WASM heap and null-terminate
function copyToHeap(buffer, addr) {
HermesParserWASM.HEAP8.set(buffer, addr);
HermesParserWASM.HEAP8[addr + buffer.length] = 0;
}
function parse(source, options) {
initHermesParserWASM(); // Allocate space on heap for source text
const sourceBuffer = Buffer.from(source, 'utf8');
const sourceAddr = HermesParserWASM._malloc(sourceBuffer.length + 1);
if (!sourceAddr) {
throw new Error('Parser out of memory');
}
try {
// Copy source text onto WASM heap
copyToHeap(sourceBuffer, sourceAddr);
const parseResult = hermesParse(sourceAddr, sourceBuffer.length + 1, options.flow === 'detect', options.enableExperimentalComponentSyntax, options.tokens, options.allowReturnOutsideFunction);
try {
// Extract and throw error from parse result if parsing failed
const err = hermesParseResult_getError(parseResult);
if (err) {
const syntaxError = new SyntaxError(err); // $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: hermesParseResult_getErrorLine(parseResult),
column: hermesParseResult_getErrorColumn(parseResult)
};
throw syntaxError;
}
const deserializer = new _HermesParserDeserializer.default(hermesParseResult_getProgramBuffer(parseResult), hermesParseResult_getPositionBuffer(parseResult), hermesParseResult_getPositionBufferSize(parseResult), HermesParserWASM, options);
return deserializer.deserialize();
} finally {
hermesParseResult_free(parseResult);
}
} finally {
HermesParserWASM._free(sourceAddr);
}
}

159
node_modules/hermes-parser/dist/HermesParser.js.flow generated vendored Normal file
View File

@@ -0,0 +1,159 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {HermesNode} from './HermesAST';
import type {ParserOptions} from './ParserOptions';
import HermesParserDeserializer from './HermesParserDeserializer';
import HermesParserWASMModule from './HermesParserWASM';
let HermesParserWASM;
let hermesParse;
let hermesParseResult_free;
let hermesParseResult_getError;
let hermesParseResult_getErrorLine;
let hermesParseResult_getErrorColumn;
let hermesParseResult_getProgramBuffer;
let hermesParseResult_getPositionBuffer;
let hermesParseResult_getPositionBufferSize;
/**
* Init the WASM wrapper code generated by `emscripten` to preparse the
* HermesParser WASM code.
*/
function initHermesParserWASM() {
if (HermesParserWASM != null) {
return;
}
HermesParserWASM = HermesParserWASMModule({
/**
* The emscripten version of `quit` unconditionally assigns the `status` to
* `process.exitCode` which overrides any pre-existing code that has been
* set, even if it is non zero. For our use case we never want an
* `exitCode` to be set so this override removes that functionality.
*/
quit(_status: number, toThrow: Error) {
throw toThrow;
},
});
hermesParse = HermesParserWASM.cwrap('hermesParse', 'number', [
'number',
'number',
'number',
'number',
'number',
'number',
]);
hermesParseResult_free = HermesParserWASM.cwrap(
'hermesParseResult_free',
'void',
['number'],
);
hermesParseResult_getError = HermesParserWASM.cwrap(
'hermesParseResult_getError',
'string',
['number'],
);
hermesParseResult_getErrorLine = HermesParserWASM.cwrap(
'hermesParseResult_getErrorLine',
'number',
['number'],
);
hermesParseResult_getErrorColumn = HermesParserWASM.cwrap(
'hermesParseResult_getErrorColumn',
'number',
['number'],
);
hermesParseResult_getProgramBuffer = HermesParserWASM.cwrap(
'hermesParseResult_getProgramBuffer',
'number',
['number'],
);
hermesParseResult_getPositionBuffer = HermesParserWASM.cwrap(
'hermesParseResult_getPositionBuffer',
'number',
['number'],
);
hermesParseResult_getPositionBufferSize = HermesParserWASM.cwrap(
'hermesParseResult_getPositionBufferSize',
'number',
['number'],
);
}
// Copy a string into the WASM heap and null-terminate
function copyToHeap(buffer: Buffer, addr: number) {
HermesParserWASM.HEAP8.set(buffer, addr);
HermesParserWASM.HEAP8[addr + buffer.length] = 0;
}
export function parse(source: string, options: ParserOptions): HermesNode {
initHermesParserWASM();
// Allocate space on heap for source text
const sourceBuffer = Buffer.from(source, 'utf8');
const sourceAddr = HermesParserWASM._malloc(sourceBuffer.length + 1);
if (!sourceAddr) {
throw new Error('Parser out of memory');
}
try {
// Copy source text onto WASM heap
copyToHeap(sourceBuffer, sourceAddr);
const parseResult = hermesParse(
sourceAddr,
sourceBuffer.length + 1,
options.flow === 'detect',
options.enableExperimentalComponentSyntax,
options.tokens,
options.allowReturnOutsideFunction,
);
try {
// Extract and throw error from parse result if parsing failed
const err = hermesParseResult_getError(parseResult);
if (err) {
const syntaxError = new SyntaxError(err);
// $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: hermesParseResult_getErrorLine(parseResult),
column: hermesParseResult_getErrorColumn(parseResult),
};
throw syntaxError;
}
const deserializer = new HermesParserDeserializer(
hermesParseResult_getProgramBuffer(parseResult),
hermesParseResult_getPositionBuffer(parseResult),
hermesParseResult_getPositionBufferSize(parseResult),
HermesParserWASM,
options,
);
return deserializer.deserialize();
} finally {
hermesParseResult_free(parseResult);
}
} finally {
HermesParserWASM._free(sourceAddr);
}
}

View File

@@ -0,0 +1,68 @@
/**
* 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
*/
'use strict';
/**
* Decode a UTF-8 encoded string from Hermes with a known length.
* Based on Emscripten's UTF8ToString with the following differences:
* - Always reads all bytes up to the given length, including null bytes. This
* means that we can decode strings that contain null bytes in the middle.
* - Allow UTF-8 encoded code points that are part of a surrogate pair, even though
* this is technically invalid UTF-8 that UTF8ToString would convert to 0xfffd.
*/
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = HermesParserDecodeUTF8String;
function HermesParserDecodeUTF8String(ptrIn, length, heap) {
let ptr = ptrIn;
const endPtr = ptr + length;
let str = '';
while (ptr < endPtr) {
// ASCII characters fit in single byte code point
let u0 = heap[ptr++];
if (!(u0 & 0x80)) {
str += String.fromCharCode(u0);
continue;
} // Two byte code point
const u1 = heap[ptr++] & 0x3f;
if ((u0 & 0xe0) === 0xc0) {
str += String.fromCharCode((u0 & 0x1f) << 6 | u1);
continue;
}
const u2 = heap[ptr++] & 0x3f;
if ((u0 & 0xf0) === 0xe0) {
// Three byte code point
u0 = (u0 & 0x0f) << 12 | u1 << 6 | u2;
} else {
// Four byte code point
u0 = (u0 & 0x07) << 18 | u1 << 12 | u2 << 6 | heap[ptr++] & 0x3f;
}
if (u0 < 0x10000) {
// Code point fits into a single UTF-16 code unit
str += String.fromCharCode(u0);
} else {
// Code point does not fit into single UTF-16 code unit so convert to surrogate pair
u0 -= 0x10000;
str += String.fromCharCode(0xd800 | u0 >> 10, 0xdc00 | u0 & 0x3ff);
}
}
return str;
}

View File

@@ -0,0 +1,65 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
/**
* Decode a UTF-8 encoded string from Hermes with a known length.
* Based on Emscripten's UTF8ToString with the following differences:
* - Always reads all bytes up to the given length, including null bytes. This
* means that we can decode strings that contain null bytes in the middle.
* - Allow UTF-8 encoded code points that are part of a surrogate pair, even though
* this is technically invalid UTF-8 that UTF8ToString would convert to 0xfffd.
*/
export default function HermesParserDecodeUTF8String(
ptrIn: number,
length: number,
heap: Uint8Array,
): string {
let ptr = ptrIn;
const endPtr = ptr + length;
let str = '';
while (ptr < endPtr) {
// ASCII characters fit in single byte code point
let u0 = heap[ptr++];
if (!(u0 & 0x80)) {
str += String.fromCharCode(u0);
continue;
}
// Two byte code point
const u1 = heap[ptr++] & 0x3f;
if ((u0 & 0xe0) === 0xc0) {
str += String.fromCharCode(((u0 & 0x1f) << 6) | u1);
continue;
}
const u2 = heap[ptr++] & 0x3f;
if ((u0 & 0xf0) === 0xe0) {
// Three byte code point
u0 = ((u0 & 0x0f) << 12) | (u1 << 6) | u2;
} else {
// Four byte code point
u0 = ((u0 & 0x07) << 18) | (u1 << 12) | (u2 << 6) | (heap[ptr++] & 0x3f);
}
if (u0 < 0x10000) {
// Code point fits into a single UTF-16 code unit
str += String.fromCharCode(u0);
} else {
// Code point does not fit into single UTF-16 code unit so convert to surrogate pair
u0 -= 0x10000;
str += String.fromCharCode(0xd800 | (u0 >> 10), 0xdc00 | (u0 & 0x3ff));
}
}
return str;
}

View File

@@ -0,0 +1,243 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _HermesParserDecodeUTF8String = _interopRequireDefault(require("./HermesParserDecodeUTF8String"));
var _HermesParserNodeDeserializers = _interopRequireDefault(require("./HermesParserNodeDeserializers"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class HermesParserDeserializer {
// Matches StoredComment::Kind enum in JSLexer.h
// Matches TokenType enum in HermesParserJSSerializer.h
constructor(programBuffer, positionBuffer, positionBufferSize, wasmParser, options) {
this.programBufferIdx = void 0;
this.positionBufferIdx = void 0;
this.positionBufferSize = void 0;
this.locMap = void 0;
this.HEAPU8 = void 0;
this.HEAPU32 = void 0;
this.HEAPF64 = void 0;
this.options = void 0;
this.commentTypes = ['CommentLine', 'CommentBlock', 'InterpreterDirective'];
this.tokenTypes = ['Boolean', 'Identifier', 'Keyword', 'Null', 'Numeric', 'BigInt', 'Punctuator', 'String', 'RegularExpression', 'Template', 'JSXText'];
// Program and position buffer are memory addresses, so we must convert
// into indices into HEAPU32 (an array of 4-byte integers).
this.programBufferIdx = programBuffer / 4;
this.positionBufferIdx = positionBuffer / 4;
this.positionBufferSize = positionBufferSize;
this.locMap = {};
this.HEAPU8 = wasmParser.HEAPU8;
this.HEAPU32 = wasmParser.HEAPU32;
this.HEAPF64 = wasmParser.HEAPF64;
this.options = options;
}
/**
* Consume and return the next 4 bytes in the program buffer.
*/
next() {
const num = this.HEAPU32[this.programBufferIdx++];
return num;
}
deserialize() {
const program = {
type: 'Program',
loc: this.addEmptyLoc(),
body: this.deserializeNodeList(),
comments: this.deserializeComments()
};
if (this.options.tokens === true) {
program.tokens = this.deserializeTokens();
}
this.fillLocs();
return program;
}
/**
* Booleans are serialized as a single 4-byte integer.
*/
deserializeBoolean() {
return Boolean(this.next());
}
/**
* Numbers are serialized directly into program buffer, taking up 8 bytes
* preceded by 4 bytes of alignment padding if necessary.
*/
deserializeNumber() {
let floatIdx; // Numbers are aligned on 8-byte boundaries, so skip padding if we are at
// an odd index into the 4-byte aligned program buffer.
if (this.programBufferIdx % 2 === 0) {
floatIdx = this.programBufferIdx / 2;
this.programBufferIdx += 2;
} else {
floatIdx = (this.programBufferIdx + 1) / 2;
this.programBufferIdx += 3;
}
return this.HEAPF64[floatIdx];
}
/**
* Strings are serialized as a 4-byte pointer into the heap, followed
* by their size as a 4-byte integer. The size is only present if the
* pointer is non-null.
*/
deserializeString() {
const ptr = this.next();
if (ptr === 0) {
return null;
}
const size = this.next();
return (0, _HermesParserDecodeUTF8String.default)(ptr, size, this.HEAPU8);
}
/**
* Nodes are serialized as a 4-byte integer denoting their node kind,
* followed by a 4-byte loc ID, followed by serialized node properties.
*
* If the node kind is 0 the node is null, otherwise the node kind - 1 is an
* index into the array of node deserialization functions.
*/
deserializeNode() {
const nodeType = this.next();
if (nodeType === 0) {
return null;
}
const nodeDeserializer = _HermesParserNodeDeserializers.default[nodeType - 1].bind(this);
return nodeDeserializer();
}
/**
* Node lists are serialized as a 4-byte integer denoting the number of
* elements in the list, followed by the serialized elements.
*/
deserializeNodeList() {
const size = this.next();
const nodeList = [];
for (let i = 0; i < size; i++) {
nodeList.push(this.deserializeNode());
}
return nodeList;
}
/**
* Comments are serialized as a node list, where each comment is serialized
* as a 4-byte integer denoting comment type, followed by a 4-byte value
* denoting the loc ID, followed by a serialized string for the comment value.
*/
deserializeComments() {
const size = this.next();
const comments = [];
for (let i = 0; i < size; i++) {
const commentType = this.commentTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
comments.push({
type: commentType,
loc,
value
});
}
return comments;
}
deserializeTokens() {
const size = this.next();
const tokens = [];
for (let i = 0; i < size; i++) {
const tokenType = this.tokenTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
tokens.push({
type: tokenType,
loc,
value
});
}
return tokens;
}
/**
* While deserializing the AST locations are represented by
* a 4-byte loc ID. This is used to create a map of loc IDs to empty loc
* objects that are filled after the AST has been deserialized.
*/
addEmptyLoc() {
// $FlowExpectedError
const loc = {};
this.locMap[this.next()] = loc;
return loc;
}
/**
* Positions are serialized as a loc ID which denotes which loc it is associated with,
* followed by kind which denotes whether it is a start or end position,
* followed by line, column, and offset (4-bytes each).
*/
fillLocs() {
for (let i = 0; i < this.positionBufferSize; i++) {
const locId = this.HEAPU32[this.positionBufferIdx++];
const kind = this.HEAPU32[this.positionBufferIdx++];
const line = this.HEAPU32[this.positionBufferIdx++];
const column = this.HEAPU32[this.positionBufferIdx++];
const offset = this.HEAPU32[this.positionBufferIdx++];
const loc = this.locMap[locId];
if (kind === 0) {
loc.start = {
line,
column
};
loc.rangeStart = offset;
} else {
loc.end = {
line,
column
};
loc.rangeEnd = offset;
}
}
}
}
exports.default = HermesParserDeserializer;

View File

@@ -0,0 +1,261 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {
HermesSourceLocation,
HermesNode,
HermesToken,
HermesComment,
} from './HermesAST';
import type {HermesParserWASM} from './HermesParserWASM';
import type {ParserOptions} from './ParserOptions';
import HermesParserDecodeUTF8String from './HermesParserDecodeUTF8String';
import NODE_DESERIALIZERS from './HermesParserNodeDeserializers';
export default class HermesParserDeserializer {
programBufferIdx: number;
positionBufferIdx: number;
+positionBufferSize: number;
+locMap: {[number]: HermesSourceLocation};
+HEAPU8: HermesParserWASM['HEAPU8'];
+HEAPU32: HermesParserWASM['HEAPU32'];
+HEAPF64: HermesParserWASM['HEAPF64'];
+options: ParserOptions;
// Matches StoredComment::Kind enum in JSLexer.h
+commentTypes: $ReadOnlyArray<HermesComment['type']> = [
'CommentLine',
'CommentBlock',
'InterpreterDirective',
];
// Matches TokenType enum in HermesParserJSSerializer.h
+tokenTypes: $ReadOnlyArray<HermesToken['type']> = [
'Boolean',
'Identifier',
'Keyword',
'Null',
'Numeric',
'BigInt',
'Punctuator',
'String',
'RegularExpression',
'Template',
'JSXText',
];
constructor(
programBuffer: number,
positionBuffer: number,
positionBufferSize: number,
wasmParser: HermesParserWASM,
options: ParserOptions,
) {
// Program and position buffer are memory addresses, so we must convert
// into indices into HEAPU32 (an array of 4-byte integers).
this.programBufferIdx = programBuffer / 4;
this.positionBufferIdx = positionBuffer / 4;
this.positionBufferSize = positionBufferSize;
this.locMap = {};
this.HEAPU8 = wasmParser.HEAPU8;
this.HEAPU32 = wasmParser.HEAPU32;
this.HEAPF64 = wasmParser.HEAPF64;
this.options = options;
}
/**
* Consume and return the next 4 bytes in the program buffer.
*/
next(): number {
const num = this.HEAPU32[this.programBufferIdx++];
return num;
}
deserialize(): HermesNode {
const program: HermesNode = {
type: 'Program',
loc: this.addEmptyLoc(),
body: this.deserializeNodeList(),
comments: this.deserializeComments(),
};
if (this.options.tokens === true) {
program.tokens = this.deserializeTokens();
}
this.fillLocs();
return program;
}
/**
* Booleans are serialized as a single 4-byte integer.
*/
deserializeBoolean(): boolean {
return Boolean(this.next());
}
/**
* Numbers are serialized directly into program buffer, taking up 8 bytes
* preceded by 4 bytes of alignment padding if necessary.
*/
deserializeNumber(): number {
let floatIdx;
// Numbers are aligned on 8-byte boundaries, so skip padding if we are at
// an odd index into the 4-byte aligned program buffer.
if (this.programBufferIdx % 2 === 0) {
floatIdx = this.programBufferIdx / 2;
this.programBufferIdx += 2;
} else {
floatIdx = (this.programBufferIdx + 1) / 2;
this.programBufferIdx += 3;
}
return this.HEAPF64[floatIdx];
}
/**
* Strings are serialized as a 4-byte pointer into the heap, followed
* by their size as a 4-byte integer. The size is only present if the
* pointer is non-null.
*/
deserializeString(): ?string {
const ptr = this.next();
if (ptr === 0) {
return null;
}
const size = this.next();
return HermesParserDecodeUTF8String(ptr, size, this.HEAPU8);
}
/**
* Nodes are serialized as a 4-byte integer denoting their node kind,
* followed by a 4-byte loc ID, followed by serialized node properties.
*
* If the node kind is 0 the node is null, otherwise the node kind - 1 is an
* index into the array of node deserialization functions.
*/
deserializeNode(): ?HermesNode {
const nodeType = this.next();
if (nodeType === 0) {
return null;
}
const nodeDeserializer = NODE_DESERIALIZERS[nodeType - 1].bind(this);
return nodeDeserializer();
}
/**
* Node lists are serialized as a 4-byte integer denoting the number of
* elements in the list, followed by the serialized elements.
*/
deserializeNodeList(): Array<?HermesNode> {
const size = this.next();
const nodeList = [];
for (let i = 0; i < size; i++) {
nodeList.push(this.deserializeNode());
}
return nodeList;
}
/**
* Comments are serialized as a node list, where each comment is serialized
* as a 4-byte integer denoting comment type, followed by a 4-byte value
* denoting the loc ID, followed by a serialized string for the comment value.
*/
deserializeComments(): Array<HermesComment> {
const size = this.next();
const comments = [];
for (let i = 0; i < size; i++) {
const commentType = this.commentTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
comments.push({
type: commentType,
loc,
value,
});
}
return comments;
}
deserializeTokens(): Array<HermesToken> {
const size = this.next();
const tokens = [];
for (let i = 0; i < size; i++) {
const tokenType = this.tokenTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
tokens.push({
type: tokenType,
loc,
value,
});
}
return tokens;
}
/**
* While deserializing the AST locations are represented by
* a 4-byte loc ID. This is used to create a map of loc IDs to empty loc
* objects that are filled after the AST has been deserialized.
*/
addEmptyLoc(): HermesSourceLocation {
// $FlowExpectedError
const loc: HermesSourceLocation = {};
this.locMap[this.next()] = loc;
return loc;
}
/**
* Positions are serialized as a loc ID which denotes which loc it is associated with,
* followed by kind which denotes whether it is a start or end position,
* followed by line, column, and offset (4-bytes each).
*/
fillLocs(): void {
for (let i = 0; i < this.positionBufferSize; i++) {
const locId = this.HEAPU32[this.positionBufferIdx++];
const kind = this.HEAPU32[this.positionBufferIdx++];
const line = this.HEAPU32[this.positionBufferIdx++];
const column = this.HEAPU32[this.positionBufferIdx++];
const offset = this.HEAPU32[this.positionBufferIdx++];
const loc = this.locMap[locId];
if (kind === 0) {
loc.start = {
line,
column,
};
loc.rangeStart = offset;
} else {
loc.end = {
line,
column,
};
loc.rangeEnd = offset;
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
/**
* 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.
*
* @flow strict
* @format
*/
import type {HermesNode} from './HermesAST';
import type HermesParserDeserializer from './HermesParserDeserializer';
declare module.exports: $ReadOnlyArray<
(this: HermesParserDeserializer) => HermesNode,
>;

6
node_modules/hermes-parser/dist/HermesParserWASM.js generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,79 @@
/**
* 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.
*
* @flow strict
* @format
*/
type INTENTIONAL_ANY = $FlowFixMe;
type JSType = 'number' | 'string' | 'array' | 'boolean';
type CBoolean = ?(number | boolean);
type WASMModuleOverrides = $ReadOnly<{
quit(status: number, toThrow: Error): void,
}>;
export type HermesParserWASM = $ReadOnly<{
HEAP8: Int8Array,
HEAP16: Int16Array,
HEAP32: Int32Array,
HEAPU8: Uint8Array,
HEAPU16: Uint16Array,
HEAPU32: Uint32Array,
HEAPF32: Float32Array,
HEAPF64: Float64Array,
_malloc(size: number): number,
_free(ptr: number): void,
ccall(
ident: string,
returnType: JSType | null,
argTypes: $ReadOnlyArray<JSType>,
args: $ReadOnlyArray<
number | string | $ReadOnlyArray<INTENTIONAL_ANY> | boolean,
>,
opts?: $ReadOnly<{
async?: boolean | void,
}>,
): INTENTIONAL_ANY,
cwrap: {
(
'hermesParse',
'number',
['number', 'number', 'number', 'number', 'number', 'number'],
): (number, number, CBoolean, CBoolean, CBoolean, CBoolean) => number,
('hermesParseResult_free', 'void', ['number']): number => void,
('hermesParseResult_getError', 'string', ['number']): number => string,
('hermesParseResult_getErrorLine', 'number', ['number']): number => number,
(
'hermesParseResult_getErrorColumn',
'number',
['number'],
): number => number,
(
'hermesParseResult_getProgramBuffer',
'number',
['number'],
): number => number,
(
'hermesParseResult_getPositionBuffer',
'number',
['number'],
): number => number,
(
'hermesParseResult_getPositionBufferSize',
'number',
['number'],
): number => number,
},
stackAlloc(size: number): number,
stackSave(): number,
stackRestore(ptr: number): void,
}>;
declare module.exports: WASMModuleOverrides => HermesParserWASM;

View File

@@ -0,0 +1,441 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _HermesASTAdapter = _interopRequireDefault(require("./HermesASTAdapter"));
var _getModuleDocblock = require("./getModuleDocblock");
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
*/
class HermesToESTreeAdapter extends _HermesASTAdapter.default {
constructor(options, code) {
super(options);
this.code = void 0;
this.code = code;
}
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.range = [loc.rangeStart, loc.rangeEnd];
delete node.start;
delete node.end;
}
mapNode(node) {
this.fixSourceLocation(node);
switch (node.type) {
case 'Program':
return this.mapProgram(node);
case 'NullLiteral':
return this.mapNullLiteral(node);
case 'BooleanLiteral':
case 'StringLiteral':
case 'NumericLiteral':
case 'JSXStringLiteral':
return this.mapSimpleLiteral(node);
case 'BigIntLiteral':
return this.mapBigIntLiteral(node);
case 'RegExpLiteral':
return this.mapRegExpLiteral(node);
case 'Empty':
return this.mapEmpty(node);
case 'TemplateElement':
return this.mapTemplateElement(node);
case 'BigIntLiteralTypeAnnotation':
return this.mapBigIntLiteralTypeAnnotation(node);
case 'GenericTypeAnnotation':
return this.mapGenericTypeAnnotation(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 'ExportAllDeclaration':
return this.mapExportAllDeclaration(node);
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.mapClassProperty(node);
case 'MemberExpression':
case 'OptionalMemberExpression':
case 'CallExpression':
case 'OptionalCallExpression':
return this.mapChainExpression(node);
default:
return this.mapNodeDefault(node);
}
}
mapProgram(node) {
const nodeDefault = this.mapNodeDefault(node);
node.sourceType = this.getSourceType();
node.docblock = (0, _getModuleDocblock.getModuleDocblock)(nodeDefault);
return nodeDefault;
}
mapSimpleLiteral(node) {
return {
type: 'Literal',
loc: node.loc,
range: node.range,
value: node.value,
raw: this.code.slice(node.range[0], node.range[1]),
literalType: (() => {
switch (node.type) {
case 'NullLiteral':
return 'null';
case 'BooleanLiteral':
return 'boolean';
case 'StringLiteral':
case 'JSXStringLiteral':
return 'string';
case 'NumericLiteral':
return 'numeric';
case 'BigIntLiteral':
return 'bigint';
case 'RegExpLiteral':
return 'regexp';
}
return null;
})()
};
}
mapBigIntLiteral(node) {
const newNode = this.mapSimpleLiteral(node);
return { ...newNode,
...this.getBigIntLiteralValue(node.bigint)
};
}
mapNullLiteral(node) {
return { ...this.mapSimpleLiteral(node),
value: null
};
}
mapRegExpLiteral(node) {
const {
pattern,
flags
} = node; // Create RegExp value if possible. This can fail when the flags are invalid.
let value;
try {
value = new RegExp(pattern, flags);
} catch (e) {
value = null;
}
return { ...this.mapSimpleLiteral(node),
value,
regex: {
pattern,
flags
}
};
}
mapBigIntLiteralTypeAnnotation(node) {
return { ...node,
...this.getBigIntLiteralValue(node.raw)
};
}
mapTemplateElement(node) {
return {
type: 'TemplateElement',
loc: node.loc,
range: node.range,
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,
range: node.range
};
}
return this.mapNodeDefault(node);
}
mapProperty(nodeUnprocessed) {
const node = this.mapNodeDefault(nodeUnprocessed);
if (node.value.type === 'FunctionExpression' && (node.method || node.kind !== 'init')) {
node.value.loc.start = node.key.loc.end;
node.value.range[0] = node.key.range[1];
}
return node;
}
mapComment(node) {
if (node.type === 'CommentBlock') {
node.type = 'Block';
} else if (node.type === 'CommentLine') {
node.type = 'Line';
}
return node;
}
mapFunction(nodeUnprocessed) {
const node = this.mapNodeDefault(nodeUnprocessed);
switch (node.type) {
// This prop should ideally only live on `ArrowFunctionExpression` but to
// match espree output we place it on all functions types.
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 `MemberExpression`:
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;

View File

@@ -0,0 +1,427 @@
/**
* 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.
*
* @flow strict
* @format
*/
import type {HermesNode} from './HermesAST';
import type {ParserOptions} from './ParserOptions';
import HermesASTAdapter from './HermesASTAdapter';
import {getModuleDocblock} from './getModuleDocblock';
declare var BigInt: ?(value: $FlowFixMe) => mixed;
export default class HermesToESTreeAdapter extends HermesASTAdapter {
+code: string;
constructor(options: ParserOptions, code: string) {
super(options);
this.code = code;
}
fixSourceLocation(node: HermesNode): void {
const loc = node.loc;
if (loc == null) {
return;
}
node.loc = {
source: this.sourceFilename ?? null,
start: loc.start,
end: loc.end,
};
node.range = [loc.rangeStart, loc.rangeEnd];
delete node.start;
delete node.end;
}
mapNode(node: HermesNode): HermesNode {
this.fixSourceLocation(node);
switch (node.type) {
case 'Program':
return this.mapProgram(node);
case 'NullLiteral':
return this.mapNullLiteral(node);
case 'BooleanLiteral':
case 'StringLiteral':
case 'NumericLiteral':
case 'JSXStringLiteral':
return this.mapSimpleLiteral(node);
case 'BigIntLiteral':
return this.mapBigIntLiteral(node);
case 'RegExpLiteral':
return this.mapRegExpLiteral(node);
case 'Empty':
return this.mapEmpty(node);
case 'TemplateElement':
return this.mapTemplateElement(node);
case 'BigIntLiteralTypeAnnotation':
return this.mapBigIntLiteralTypeAnnotation(node);
case 'GenericTypeAnnotation':
return this.mapGenericTypeAnnotation(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 'ExportAllDeclaration':
return this.mapExportAllDeclaration(node);
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.mapClassProperty(node);
case 'MemberExpression':
case 'OptionalMemberExpression':
case 'CallExpression':
case 'OptionalCallExpression':
return this.mapChainExpression(node);
default:
return this.mapNodeDefault(node);
}
}
mapProgram(node: HermesNode): HermesNode {
const nodeDefault = this.mapNodeDefault(node);
node.sourceType = this.getSourceType();
node.docblock = getModuleDocblock(nodeDefault);
return nodeDefault;
}
mapSimpleLiteral(node: HermesNode): HermesNode {
return {
type: 'Literal',
loc: node.loc,
range: node.range,
value: node.value,
raw: this.code.slice(node.range[0], node.range[1]),
literalType: (() => {
switch (node.type) {
case 'NullLiteral':
return 'null';
case 'BooleanLiteral':
return 'boolean';
case 'StringLiteral':
case 'JSXStringLiteral':
return 'string';
case 'NumericLiteral':
return 'numeric';
case 'BigIntLiteral':
return 'bigint';
case 'RegExpLiteral':
return 'regexp';
}
return null;
})(),
};
}
mapBigIntLiteral(node: HermesNode): HermesNode {
const newNode = this.mapSimpleLiteral(node);
return {
...newNode,
...this.getBigIntLiteralValue(node.bigint),
};
}
mapNullLiteral(node: HermesNode): HermesNode {
return {
...this.mapSimpleLiteral(node),
value: null,
};
}
mapRegExpLiteral(node: HermesNode): HermesNode {
const {pattern, flags} = node;
// Create RegExp value if possible. This can fail when the flags are invalid.
let value;
try {
value = new RegExp(pattern, flags);
} catch (e) {
value = null;
}
return {
...this.mapSimpleLiteral(node),
value,
regex: {
pattern,
flags,
},
};
}
mapBigIntLiteralTypeAnnotation(node: HermesNode): HermesNode {
return {
...node,
...this.getBigIntLiteralValue(node.raw),
};
}
mapTemplateElement(node: HermesNode): HermesNode {
return {
type: 'TemplateElement',
loc: node.loc,
range: node.range,
tail: node.tail,
value: {
cooked: node.cooked,
raw: node.raw,
},
};
}
mapGenericTypeAnnotation(node: HermesNode): HermesNode {
// 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,
range: node.range,
};
}
return this.mapNodeDefault(node);
}
mapProperty(nodeUnprocessed: HermesNode): HermesNode {
const node = this.mapNodeDefault(nodeUnprocessed);
if (
node.value.type === 'FunctionExpression' &&
(node.method || node.kind !== 'init')
) {
node.value.loc.start = node.key.loc.end;
node.value.range[0] = node.key.range[1];
}
return node;
}
mapComment(node: HermesNode): HermesNode {
if (node.type === 'CommentBlock') {
node.type = 'Block';
} else if (node.type === 'CommentLine') {
node.type = 'Line';
}
return node;
}
mapFunction(nodeUnprocessed: HermesNode): HermesNode {
const node = this.mapNodeDefault(nodeUnprocessed);
switch (node.type) {
// This prop should ideally only live on `ArrowFunctionExpression` but to
// match espree output we place it on all functions types.
case 'FunctionDeclaration':
case 'FunctionExpression':
node.expression = false;
return node;
case 'ArrowFunctionExpression':
node.expression = node.body.type !== 'BlockStatement';
return node;
}
return node;
}
mapChainExpression(nodeUnprocessed: HermesNode): HermesNode {
/*
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 `MemberExpression`:
a) convert this node to a `MemberExpression[optional = true]`
*/
const node = this.mapNodeDefault(nodeUnprocessed);
const {child, childKey, isOptional} = ((): {
child: HermesNode,
childKey: string,
isOptional: boolean,
} => {
const isOptional: boolean = 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: HermesNode): HermesNode {
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: HermesNode): HermesNode {
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: HermesNode): HermesNode {
const node = super.mapExportNamedDeclaration(nodeUnprocessed);
const namespaceSpecifier = node.specifiers.find(
spec => spec.type === 'ExportNamespaceSpecifier',
);
if (namespaceSpecifier != null) {
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 ?? 'value',
exported: namespaceSpecifier.exported,
range: node.range,
loc: node.loc,
};
}
return node;
}
mapExportAllDeclaration(nodeUnprocessed: HermesNode): HermesNode {
const node = super.mapExportAllDeclaration(nodeUnprocessed);
node.exported = node.exported ?? null;
return node;
}
}

18
node_modules/hermes-parser/dist/ParserOptions.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ParserOptionsKeys = void 0;
/**
* 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
*/
const ParserOptionsKeys = new Set(['allowReturnOutsideFunction', 'babel', 'flow', 'enableExperimentalComponentSyntax', 'reactRuntimeTarget', 'sourceFilename', 'sourceType', 'tokens']);
exports.ParserOptionsKeys = ParserOptionsKeys;

31
node_modules/hermes-parser/dist/ParserOptions.js.flow generated vendored Normal file
View File

@@ -0,0 +1,31 @@
/**
* 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.
*
* @flow strict
* @format
*/
export type ParserOptions = {
allowReturnOutsideFunction?: boolean,
babel?: boolean,
flow?: 'all' | 'detect',
enableExperimentalComponentSyntax?: boolean,
reactRuntimeTarget?: '18' | '19',
sourceFilename?: string,
sourceType?: 'module' | 'script' | 'unambiguous',
tokens?: boolean,
};
export const ParserOptionsKeys: $ReadOnlySet<$Keys<ParserOptions>> = new Set([
'allowReturnOutsideFunction',
'babel',
'flow',
'enableExperimentalComponentSyntax',
'reactRuntimeTarget',
'sourceFilename',
'sourceType',
'tokens',
]);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,788 @@
/**
* 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
*/
/**
* This transforms component syntax (https://flow.org/en/docs/react/component-syntax/)
* and hook syntax (https://flow.org/en/docs/react/hook-syntax/).
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformProgram = transformProgram;
var _SimpleTransform = require("../transform/SimpleTransform");
var _astNodeMutationHelpers = require("../transform/astNodeMutationHelpers");
var _SimpleTraverser = require("../traverse/SimpleTraverser");
var _createSyntaxError = require("../utils/createSyntaxError");
const nodeWith = _SimpleTransform.SimpleTransform.nodeWith; // Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT = null;
function createDefaultPosition() {
return {
line: 1,
column: 0
};
}
function mapDeclareComponent(node) {
return {
type: 'DeclareVariable',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT
},
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT
}
}),
kind: 'const',
loc: node.loc,
range: node.range,
parent: node.parent
};
}
function getComponentParameterName(paramName) {
switch (paramName.type) {
case 'Identifier':
return paramName.name;
case 'Literal':
return paramName.value;
default:
throw (0, _createSyntaxError.createSyntaxError)(paramName, `Unknown Component parameter name type of "${paramName.type}"`);
}
}
function createPropsTypeAnnotation(propTypes, spread, loc, range) {
// Create empty loc for type annotation nodes
const createParamsTypeLoc = () => ({
loc: {
start: (loc == null ? void 0 : loc.start) != null ? loc.start : createDefaultPosition(),
end: (loc == null ? void 0 : loc.end) != null ? loc.end : createDefaultPosition()
},
range: range != null ? range : [0, 0],
parent: EMPTY_PARENT
}); // Optimize `{...Props}` -> `Props`
if (spread != null && propTypes.length === 0) {
return {
type: 'TypeAnnotation',
typeAnnotation: spread.argument,
...createParamsTypeLoc()
};
}
const typeProperties = [...propTypes];
if (spread != null) {
// Spread needs to be the first type, as inline properties take precedence.
typeProperties.unshift(spread);
}
const propTypeObj = {
type: 'ObjectTypeAnnotation',
callProperties: [],
properties: typeProperties,
indexers: [],
internalSlots: [],
exact: false,
inexact: false,
...createParamsTypeLoc()
};
return {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: '$ReadOnly',
optional: false,
typeAnnotation: null,
...createParamsTypeLoc()
},
typeParameters: {
type: 'TypeParameterInstantiation',
params: [propTypeObj],
...createParamsTypeLoc()
},
...createParamsTypeLoc()
},
...createParamsTypeLoc()
};
}
function mapComponentParameters(params, options) {
var _options$reactRuntime;
if (params.length === 0) {
return {
props: null,
ref: null
};
} // Optimize `component Foo(...props: Props) {}` to `function Foo(props: Props) {}
if (params.length === 1 && params[0].type === 'RestElement' && params[0].argument.type === 'Identifier') {
const restElementArgument = params[0].argument;
return {
props: restElementArgument,
ref: null
};
} // Filter out any ref param and capture it's details when targeting React 18.
// React 19+ treats ref as a regular prop for function components.
let refParam = null;
const paramsWithoutRef = ((_options$reactRuntime = options.reactRuntimeTarget) != null ? _options$reactRuntime : '18') === '18' ? params.filter(param => {
if (param.type === 'ComponentParameter' && getComponentParameterName(param.name) === 'ref') {
refParam = param;
return false;
}
return true;
}) : params;
const [propTypes, spread] = paramsWithoutRef.reduce(([propTypes, spread], param) => {
switch (param.type) {
case 'RestElement':
{
if (spread != null) {
throw (0, _createSyntaxError.createSyntaxError)(param, `Invalid state, multiple rest elements found as Component Parameters`);
}
return [propTypes, mapComponentParameterRestElementType(param)];
}
case 'ComponentParameter':
{
propTypes.push(mapComponentParameterType(param));
return [propTypes, spread];
}
}
}, [[], null]);
const propsProperties = paramsWithoutRef.flatMap(mapComponentParameter);
let props = null;
if (propsProperties.length === 0) {
if (refParam == null) {
throw new Error('StripComponentSyntax: Invalid state, ref should always be set at this point if props are empty');
}
const emptyParamsLoc = {
start: refParam.loc.start,
end: refParam.loc.start
};
const emptyParamsRange = [refParam.range[0], refParam.range[0]]; // no properties provided (must have had a single ref)
props = {
type: 'Identifier',
name: '_$$empty_props_placeholder$$',
optional: false,
typeAnnotation: createPropsTypeAnnotation([], null, emptyParamsLoc, emptyParamsRange),
loc: emptyParamsLoc,
range: emptyParamsRange,
parent: EMPTY_PARENT
};
} else {
const lastPropsProperty = propsProperties[propsProperties.length - 1];
props = {
type: 'ObjectPattern',
properties: propsProperties,
typeAnnotation: createPropsTypeAnnotation(propTypes, spread, {
start: lastPropsProperty.loc.end,
end: lastPropsProperty.loc.end
}, [lastPropsProperty.range[1], lastPropsProperty.range[1]]),
loc: {
start: propsProperties[0].loc.start,
end: lastPropsProperty.loc.end
},
range: [propsProperties[0].range[0], lastPropsProperty.range[1]],
parent: EMPTY_PARENT
};
}
let ref = null;
if (refParam != null) {
ref = refParam.local;
}
return {
props,
ref
};
}
function mapComponentParameterType(param) {
var _typeAnnotation$typeA;
const typeAnnotation = param.local.type === 'AssignmentPattern' ? param.local.left.typeAnnotation : param.local.typeAnnotation;
const optional = param.local.type === 'AssignmentPattern' ? true : param.local.type === 'Identifier' ? param.local.optional : false;
return {
type: 'ObjectTypeProperty',
key: (0, _astNodeMutationHelpers.shallowCloneNode)(param.name),
value: (_typeAnnotation$typeA = typeAnnotation == null ? void 0 : typeAnnotation.typeAnnotation) != null ? _typeAnnotation$typeA : {
type: 'AnyTypeAnnotation',
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT
},
kind: 'init',
optional,
method: false,
static: false,
proto: false,
variance: null,
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT
};
}
function mapComponentParameterRestElementType(param) {
var _param$argument$typeA, _param$argument$typeA2;
if (param.argument.type !== 'Identifier' && param.argument.type !== 'ObjectPattern') {
throw (0, _createSyntaxError.createSyntaxError)(param, `Invalid ${param.argument.type} encountered in restParameter`);
}
return {
type: 'ObjectTypeSpreadProperty',
argument: (_param$argument$typeA = (_param$argument$typeA2 = param.argument.typeAnnotation) == null ? void 0 : _param$argument$typeA2.typeAnnotation) != null ? _param$argument$typeA : {
type: 'AnyTypeAnnotation',
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
},
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
};
}
function mapComponentParameter(param) {
switch (param.type) {
case 'RestElement':
{
switch (param.argument.type) {
case 'Identifier':
{
const a = nodeWith(param, {
typeAnnotation: null,
argument: nodeWith(param.argument, {
typeAnnotation: null
})
});
return [a];
}
case 'ObjectPattern':
{
return param.argument.properties.map(property => {
return nodeWith(property, {
typeAnnotation: null
});
});
}
default:
{
throw (0, _createSyntaxError.createSyntaxError)(param, `Unhandled ${param.argument.type} encountered in restParameter`);
}
}
}
case 'ComponentParameter':
{
let value;
if (param.local.type === 'AssignmentPattern') {
value = nodeWith(param.local, {
left: nodeWith(param.local.left, {
typeAnnotation: null,
optional: false
})
});
} else {
value = nodeWith(param.local, {
typeAnnotation: null,
optional: false
});
} // Shorthand params
if (param.name.type === 'Identifier' && param.shorthand && (value.type === 'Identifier' || value.type === 'AssignmentPattern')) {
return [{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: true,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
}];
} // Complex params
return [{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: false,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
}];
}
default:
{
throw (0, _createSyntaxError.createSyntaxError)(param, `Unknown Component parameter type of "${param.type}"`);
}
}
}
function createForwardRefWrapper(originalComponent) {
const internalCompId = {
type: 'Identifier',
name: `${originalComponent.id.name}_withRef`,
optional: false,
typeAnnotation: null,
loc: originalComponent.id.loc,
range: originalComponent.id.range,
parent: EMPTY_PARENT
};
return {
forwardRefStatement: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [{
type: 'VariableDeclarator',
id: (0, _astNodeMutationHelpers.shallowCloneNode)(originalComponent.id),
init: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
property: {
type: 'Identifier',
name: 'forwardRef',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
computed: false,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
arguments: [(0, _astNodeMutationHelpers.shallowCloneNode)(internalCompId)],
typeArguments: null,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
}],
loc: originalComponent.loc,
range: originalComponent.range,
parent: originalComponent.parent
},
internalCompId: internalCompId,
forwardRefCompId: originalComponent.id
};
}
function mapComponentDeclaration(node, options) {
// Create empty loc for return type annotation nodes
const createRendersTypeLoc = () => ({
loc: {
start: node.body.loc.end,
end: node.body.loc.end
},
range: [node.body.range[1], node.body.range[1]],
parent: EMPTY_PARENT
});
const returnType = {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'QualifiedTypeIdentifier',
qualification: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc()
},
id: {
type: 'Identifier',
name: 'Node',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc()
},
...createRendersTypeLoc()
},
typeParameters: null,
...createRendersTypeLoc()
},
...createRendersTypeLoc()
};
const {
props,
ref
} = mapComponentParameters(node.params, options);
let forwardRefDetails = null;
if (ref != null) {
forwardRefDetails = createForwardRefWrapper(node);
}
const comp = {
type: 'FunctionDeclaration',
id: forwardRefDetails != null ? (0, _astNodeMutationHelpers.shallowCloneNode)(forwardRefDetails.internalCompId) : (0, _astNodeMutationHelpers.shallowCloneNode)(node.id),
__componentDeclaration: true,
typeParameters: node.typeParameters,
params: props == null ? [] : ref == null ? [props] : [props, ref],
returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent
};
return {
comp,
forwardRefDetails
};
}
function mapDeclareHook(node) {
return {
type: 'DeclareFunction',
id: {
type: 'Identifier',
name: node.id.name,
optional: node.id.optional,
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'FunctionTypeAnnotation',
this: null,
params: node.id.typeAnnotation.typeAnnotation.params,
typeParameters: node.id.typeAnnotation.typeAnnotation.typeParameters,
rest: node.id.typeAnnotation.typeAnnotation.rest,
returnType: node.id.typeAnnotation.typeAnnotation.returnType,
loc: node.id.typeAnnotation.typeAnnotation.loc,
range: node.id.typeAnnotation.typeAnnotation.range,
parent: node.id.typeAnnotation.typeAnnotation.parent
},
loc: node.id.typeAnnotation.loc,
range: node.id.typeAnnotation.range,
parent: node.id.typeAnnotation.parent
},
loc: node.id.loc,
range: node.id.range,
parent: node.id.parent
},
loc: node.loc,
range: node.range,
parent: node.parent,
predicate: null
};
}
function mapHookDeclaration(node) {
const comp = {
type: 'FunctionDeclaration',
id: node.id && (0, _astNodeMutationHelpers.shallowCloneNode)(node.id),
__hookDeclaration: true,
typeParameters: node.typeParameters,
params: node.params,
returnType: node.returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent
};
return comp;
}
/**
* Scan a list of statements and return the position of the
* first statement that contains a reference to a given component
* or null of no references were found.
*/
function scanForFirstComponentReference(compName, bodyList) {
for (let i = 0; i < bodyList.length; i++) {
const bodyNode = bodyList[i];
let referencePos = null;
_SimpleTraverser.SimpleTraverser.traverse(bodyNode, {
enter(node) {
switch (node.type) {
case 'Identifier':
{
if (node.name === compName) {
// We found a reference, record it and stop.
referencePos = i;
throw _SimpleTraverser.SimpleTraverser.Break;
}
}
}
},
leave(_node) {}
});
if (referencePos != null) {
return referencePos;
}
}
return null;
}
function mapComponentDeclarationIntoList(node, newBody, options, insertExport) {
const {
comp,
forwardRefDetails
} = mapComponentDeclaration(node, options);
if (forwardRefDetails != null) {
// Scan for references to our component.
const referencePos = scanForFirstComponentReference(forwardRefDetails.forwardRefCompId.name, newBody); // If a reference is found insert the forwardRef statement before it (to simulate function hoisting).
if (referencePos != null) {
newBody.splice(referencePos, 0, forwardRefDetails.forwardRefStatement);
} else {
newBody.push(forwardRefDetails.forwardRefStatement);
}
newBody.push(comp);
if (insertExport != null) {
newBody.push(insertExport(forwardRefDetails.forwardRefCompId));
}
return;
}
newBody.push(insertExport != null ? insertExport(comp) : comp);
}
function mapStatementList(stmts, options) {
const newBody = [];
for (const node of stmts) {
switch (node.type) {
case 'ComponentDeclaration':
{
mapComponentDeclarationIntoList(node, newBody, options);
break;
}
case 'HookDeclaration':
{
const decl = mapHookDeclaration(node);
newBody.push(decl);
break;
}
case 'ExportNamedDeclaration':
{
var _node$declaration, _node$declaration2;
if (((_node$declaration = node.declaration) == null ? void 0 : _node$declaration.type) === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(node.declaration, newBody, options, componentOrRef => {
switch (componentOrRef.type) {
case 'FunctionDeclaration':
{
// No ref, so we can export the component directly.
return nodeWith(node, {
declaration: componentOrRef
});
}
case 'Identifier':
{
// If a ref is inserted, we should just export that id.
return {
type: 'ExportNamedDeclaration',
declaration: null,
specifiers: [{
type: 'ExportSpecifier',
exported: (0, _astNodeMutationHelpers.shallowCloneNode)(componentOrRef),
local: (0, _astNodeMutationHelpers.shallowCloneNode)(componentOrRef),
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT
}],
exportKind: 'value',
source: null,
loc: node.loc,
range: node.range,
parent: node.parent
};
}
}
});
break;
}
if (((_node$declaration2 = node.declaration) == null ? void 0 : _node$declaration2.type) === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {
declaration: comp
}));
break;
}
newBody.push(node);
break;
}
case 'ExportDefaultDeclaration':
{
var _node$declaration3, _node$declaration4;
if (((_node$declaration3 = node.declaration) == null ? void 0 : _node$declaration3.type) === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(node.declaration, newBody, options, componentOrRef => nodeWith(node, {
declaration: componentOrRef
}));
break;
}
if (((_node$declaration4 = node.declaration) == null ? void 0 : _node$declaration4.type) === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {
declaration: comp
}));
break;
}
newBody.push(node);
break;
}
default:
{
newBody.push(node);
}
}
}
return newBody;
}
function transformProgram(program, options) {
return _SimpleTransform.SimpleTransform.transformProgram(program, {
transform(node) {
switch (node.type) {
case 'DeclareComponent':
{
return mapDeclareComponent(node);
}
case 'DeclareHook':
{
return mapDeclareHook(node);
}
case 'Program':
case 'BlockStatement':
{
return nodeWith(node, {
body: mapStatementList(node.body, options)
});
}
case 'SwitchCase':
{
const consequent = mapStatementList(node.consequent, options);
return nodeWith(node, {
/* $FlowExpectedError[incompatible-call] We know `mapStatementList` will
not return `ModuleDeclaration` nodes if it is not passed any */
consequent
});
}
case 'ComponentDeclaration':
{
var _node$parent;
throw (0, _createSyntaxError.createSyntaxError)(node, `Components must be defined at the top level of a module or within a ` + `BlockStatement, instead got parent of "${(_node$parent = node.parent) == null ? void 0 : _node$parent.type}".`);
}
case 'HookDeclaration':
{
var _node$parent2;
throw (0, _createSyntaxError.createSyntaxError)(node, `Hooks must be defined at the top level of a module or within a ` + `BlockStatement, instead got parent of "${(_node$parent2 = node.parent) == null ? void 0 : _node$parent2.type}".`);
}
default:
{
return node;
}
}
}
});
}

View File

@@ -0,0 +1,863 @@
/**
* 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.
*
* @flow strict
* @format
*/
/**
* This transforms component syntax (https://flow.org/en/docs/react/component-syntax/)
* and hook syntax (https://flow.org/en/docs/react/hook-syntax/).
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
import type {ParserOptions} from '../ParserOptions';
import type {
Program,
ESNode,
DeclareComponent,
DeclareVariable,
ComponentDeclaration,
FunctionDeclaration,
TypeAnnotation,
ComponentParameter,
SourceLocation,
Position,
ObjectPattern,
Identifier,
Range,
RestElement,
DestructuringObjectProperty,
VariableDeclaration,
ModuleDeclaration,
DeclareHook,
DeclareFunction,
HookDeclaration,
Statement,
AssignmentPattern,
BindingName,
ObjectTypePropertySignature,
ObjectTypeSpreadProperty,
} from 'hermes-estree';
import {SimpleTransform} from '../transform/SimpleTransform';
import {shallowCloneNode} from '../transform/astNodeMutationHelpers';
import {SimpleTraverser} from '../traverse/SimpleTraverser';
import {createSyntaxError} from '../utils/createSyntaxError';
const nodeWith = SimpleTransform.nodeWith;
// Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT: $FlowFixMe = null;
function createDefaultPosition(): Position {
return {
line: 1,
column: 0,
};
}
function mapDeclareComponent(node: DeclareComponent): DeclareVariable {
return {
type: 'DeclareVariable',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT,
},
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT,
},
}),
kind: 'const',
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
function getComponentParameterName(
paramName: ComponentParameter['name'],
): string {
switch (paramName.type) {
case 'Identifier':
return paramName.name;
case 'Literal':
return paramName.value;
default:
throw createSyntaxError(
paramName,
`Unknown Component parameter name type of "${paramName.type}"`,
);
}
}
function createPropsTypeAnnotation(
propTypes: Array<ObjectTypePropertySignature>,
spread: ?ObjectTypeSpreadProperty,
loc: ?SourceLocation,
range: ?Range,
): TypeAnnotation {
// Create empty loc for type annotation nodes
const createParamsTypeLoc = () => ({
loc: {
start: loc?.start != null ? loc.start : createDefaultPosition(),
end: loc?.end != null ? loc.end : createDefaultPosition(),
},
range: range ?? [0, 0],
parent: EMPTY_PARENT,
});
// Optimize `{...Props}` -> `Props`
if (spread != null && propTypes.length === 0) {
return {
type: 'TypeAnnotation',
typeAnnotation: spread.argument,
...createParamsTypeLoc(),
};
}
const typeProperties: Array<
ObjectTypePropertySignature | ObjectTypeSpreadProperty,
> = [...propTypes];
if (spread != null) {
// Spread needs to be the first type, as inline properties take precedence.
typeProperties.unshift(spread);
}
const propTypeObj = {
type: 'ObjectTypeAnnotation',
callProperties: [],
properties: typeProperties,
indexers: [],
internalSlots: [],
exact: false,
inexact: false,
...createParamsTypeLoc(),
};
return {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: '$ReadOnly',
optional: false,
typeAnnotation: null,
...createParamsTypeLoc(),
},
typeParameters: {
type: 'TypeParameterInstantiation',
params: [propTypeObj],
...createParamsTypeLoc(),
},
...createParamsTypeLoc(),
},
...createParamsTypeLoc(),
};
}
function mapComponentParameters(
params: $ReadOnlyArray<ComponentParameter | RestElement>,
options: ParserOptions,
): $ReadOnly<{
props: ?(ObjectPattern | Identifier),
ref: ?(BindingName | AssignmentPattern),
}> {
if (params.length === 0) {
return {props: null, ref: null};
}
// Optimize `component Foo(...props: Props) {}` to `function Foo(props: Props) {}
if (
params.length === 1 &&
params[0].type === 'RestElement' &&
params[0].argument.type === 'Identifier'
) {
const restElementArgument = params[0].argument;
return {
props: restElementArgument,
ref: null,
};
}
// Filter out any ref param and capture it's details when targeting React 18.
// React 19+ treats ref as a regular prop for function components.
let refParam = null;
const paramsWithoutRef =
(options.reactRuntimeTarget ?? '18') === '18'
? params.filter(param => {
if (
param.type === 'ComponentParameter' &&
getComponentParameterName(param.name) === 'ref'
) {
refParam = param;
return false;
}
return true;
})
: params;
const [propTypes, spread] = paramsWithoutRef.reduce<
[Array<ObjectTypePropertySignature>, ?ObjectTypeSpreadProperty],
>(
([propTypes, spread], param) => {
switch (param.type) {
case 'RestElement': {
if (spread != null) {
throw createSyntaxError(
param,
`Invalid state, multiple rest elements found as Component Parameters`,
);
}
return [propTypes, mapComponentParameterRestElementType(param)];
}
case 'ComponentParameter': {
propTypes.push(mapComponentParameterType(param));
return [propTypes, spread];
}
}
},
[[], null],
);
const propsProperties = paramsWithoutRef.flatMap(mapComponentParameter);
let props = null;
if (propsProperties.length === 0) {
if (refParam == null) {
throw new Error(
'StripComponentSyntax: Invalid state, ref should always be set at this point if props are empty',
);
}
const emptyParamsLoc = {
start: refParam.loc.start,
end: refParam.loc.start,
};
const emptyParamsRange = [refParam.range[0], refParam.range[0]];
// no properties provided (must have had a single ref)
props = {
type: 'Identifier',
name: '_$$empty_props_placeholder$$',
optional: false,
typeAnnotation: createPropsTypeAnnotation(
[],
null,
emptyParamsLoc,
emptyParamsRange,
),
loc: emptyParamsLoc,
range: emptyParamsRange,
parent: EMPTY_PARENT,
};
} else {
const lastPropsProperty = propsProperties[propsProperties.length - 1];
props = {
type: 'ObjectPattern',
properties: propsProperties,
typeAnnotation: createPropsTypeAnnotation(
propTypes,
spread,
{
start: lastPropsProperty.loc.end,
end: lastPropsProperty.loc.end,
},
[lastPropsProperty.range[1], lastPropsProperty.range[1]],
),
loc: {
start: propsProperties[0].loc.start,
end: lastPropsProperty.loc.end,
},
range: [propsProperties[0].range[0], lastPropsProperty.range[1]],
parent: EMPTY_PARENT,
};
}
let ref = null;
if (refParam != null) {
ref = refParam.local;
}
return {
props,
ref,
};
}
function mapComponentParameterType(
param: ComponentParameter,
): ObjectTypePropertySignature {
const typeAnnotation =
param.local.type === 'AssignmentPattern'
? param.local.left.typeAnnotation
: param.local.typeAnnotation;
const optional =
param.local.type === 'AssignmentPattern'
? true
: param.local.type === 'Identifier'
? param.local.optional
: false;
return {
type: 'ObjectTypeProperty',
key: shallowCloneNode(param.name),
value: typeAnnotation?.typeAnnotation ?? {
type: 'AnyTypeAnnotation',
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT,
},
kind: 'init',
optional,
method: false,
static: false,
proto: false,
variance: null,
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT,
};
}
function mapComponentParameterRestElementType(
param: RestElement,
): ObjectTypeSpreadProperty {
if (
param.argument.type !== 'Identifier' &&
param.argument.type !== 'ObjectPattern'
) {
throw createSyntaxError(
param,
`Invalid ${param.argument.type} encountered in restParameter`,
);
}
return {
type: 'ObjectTypeSpreadProperty',
argument: param.argument.typeAnnotation?.typeAnnotation ?? {
type: 'AnyTypeAnnotation',
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
},
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
};
}
function mapComponentParameter(
param: ComponentParameter | RestElement,
): Array<DestructuringObjectProperty | RestElement> {
switch (param.type) {
case 'RestElement': {
switch (param.argument.type) {
case 'Identifier': {
const a = nodeWith(param, {
typeAnnotation: null,
argument: nodeWith(param.argument, {typeAnnotation: null}),
});
return [a];
}
case 'ObjectPattern': {
return param.argument.properties.map(property => {
return nodeWith(property, {
typeAnnotation: null,
});
});
}
default: {
throw createSyntaxError(
param,
`Unhandled ${param.argument.type} encountered in restParameter`,
);
}
}
}
case 'ComponentParameter': {
let value;
if (param.local.type === 'AssignmentPattern') {
value = nodeWith(param.local, {
left: nodeWith(param.local.left, {
typeAnnotation: null,
optional: false,
}),
});
} else {
value = nodeWith(param.local, {
typeAnnotation: null,
optional: false,
});
}
// Shorthand params
if (
param.name.type === 'Identifier' &&
param.shorthand &&
(value.type === 'Identifier' || value.type === 'AssignmentPattern')
) {
return [
{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: true,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
},
];
}
// Complex params
return [
{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: false,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
},
];
}
default: {
throw createSyntaxError(
param,
`Unknown Component parameter type of "${param.type}"`,
);
}
}
}
type ForwardRefDetails = {
forwardRefStatement: VariableDeclaration,
internalCompId: Identifier,
forwardRefCompId: Identifier,
};
function createForwardRefWrapper(
originalComponent: ComponentDeclaration,
): ForwardRefDetails {
const internalCompId = {
type: 'Identifier',
name: `${originalComponent.id.name}_withRef`,
optional: false,
typeAnnotation: null,
loc: originalComponent.id.loc,
range: originalComponent.id.range,
parent: EMPTY_PARENT,
};
return {
forwardRefStatement: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: shallowCloneNode(originalComponent.id),
init: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
property: {
type: 'Identifier',
name: 'forwardRef',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
computed: false,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
arguments: [shallowCloneNode(internalCompId)],
typeArguments: null,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
],
loc: originalComponent.loc,
range: originalComponent.range,
parent: originalComponent.parent,
},
internalCompId: internalCompId,
forwardRefCompId: originalComponent.id,
};
}
function mapComponentDeclaration(
node: ComponentDeclaration,
options: ParserOptions,
): {
comp: FunctionDeclaration,
forwardRefDetails: ?ForwardRefDetails,
} {
// Create empty loc for return type annotation nodes
const createRendersTypeLoc = () => ({
loc: {
start: node.body.loc.end,
end: node.body.loc.end,
},
range: [node.body.range[1], node.body.range[1]],
parent: EMPTY_PARENT,
});
const returnType: TypeAnnotation = {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'QualifiedTypeIdentifier',
qualification: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc(),
},
id: {
type: 'Identifier',
name: 'Node',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc(),
},
...createRendersTypeLoc(),
},
typeParameters: null,
...createRendersTypeLoc(),
},
...createRendersTypeLoc(),
};
const {props, ref} = mapComponentParameters(node.params, options);
let forwardRefDetails: ?ForwardRefDetails = null;
if (ref != null) {
forwardRefDetails = createForwardRefWrapper(node);
}
const comp = {
type: 'FunctionDeclaration',
id:
forwardRefDetails != null
? shallowCloneNode(forwardRefDetails.internalCompId)
: shallowCloneNode(node.id),
__componentDeclaration: true,
typeParameters: node.typeParameters,
params: props == null ? [] : ref == null ? [props] : [props, ref],
returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent,
};
return {comp, forwardRefDetails};
}
function mapDeclareHook(node: DeclareHook): DeclareFunction {
return {
type: 'DeclareFunction',
id: {
type: 'Identifier',
name: node.id.name,
optional: node.id.optional,
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'FunctionTypeAnnotation',
this: null,
params: node.id.typeAnnotation.typeAnnotation.params,
typeParameters: node.id.typeAnnotation.typeAnnotation.typeParameters,
rest: node.id.typeAnnotation.typeAnnotation.rest,
returnType: node.id.typeAnnotation.typeAnnotation.returnType,
loc: node.id.typeAnnotation.typeAnnotation.loc,
range: node.id.typeAnnotation.typeAnnotation.range,
parent: node.id.typeAnnotation.typeAnnotation.parent,
},
loc: node.id.typeAnnotation.loc,
range: node.id.typeAnnotation.range,
parent: node.id.typeAnnotation.parent,
},
loc: node.id.loc,
range: node.id.range,
parent: node.id.parent,
},
loc: node.loc,
range: node.range,
parent: node.parent,
predicate: null,
};
}
function mapHookDeclaration(node: HookDeclaration): FunctionDeclaration {
const comp = {
type: 'FunctionDeclaration',
id: node.id && shallowCloneNode(node.id),
__hookDeclaration: true,
typeParameters: node.typeParameters,
params: node.params,
returnType: node.returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent,
};
return comp;
}
/**
* Scan a list of statements and return the position of the
* first statement that contains a reference to a given component
* or null of no references were found.
*/
function scanForFirstComponentReference(
compName: string,
bodyList: Array<Statement | ModuleDeclaration>,
): ?number {
for (let i = 0; i < bodyList.length; i++) {
const bodyNode = bodyList[i];
let referencePos = null;
SimpleTraverser.traverse(bodyNode, {
enter(node: ESNode) {
switch (node.type) {
case 'Identifier': {
if (node.name === compName) {
// We found a reference, record it and stop.
referencePos = i;
throw SimpleTraverser.Break;
}
}
}
},
leave(_node: ESNode) {},
});
if (referencePos != null) {
return referencePos;
}
}
return null;
}
function mapComponentDeclarationIntoList(
node: ComponentDeclaration,
newBody: Array<Statement | ModuleDeclaration>,
options: ParserOptions,
insertExport?: (Identifier | FunctionDeclaration) => ModuleDeclaration,
) {
const {comp, forwardRefDetails} = mapComponentDeclaration(node, options);
if (forwardRefDetails != null) {
// Scan for references to our component.
const referencePos = scanForFirstComponentReference(
forwardRefDetails.forwardRefCompId.name,
newBody,
);
// If a reference is found insert the forwardRef statement before it (to simulate function hoisting).
if (referencePos != null) {
newBody.splice(referencePos, 0, forwardRefDetails.forwardRefStatement);
} else {
newBody.push(forwardRefDetails.forwardRefStatement);
}
newBody.push(comp);
if (insertExport != null) {
newBody.push(insertExport(forwardRefDetails.forwardRefCompId));
}
return;
}
newBody.push(insertExport != null ? insertExport(comp) : comp);
}
function mapStatementList(
stmts: $ReadOnlyArray<Statement | ModuleDeclaration>,
options: ParserOptions,
) {
const newBody: Array<Statement | ModuleDeclaration> = [];
for (const node of stmts) {
switch (node.type) {
case 'ComponentDeclaration': {
mapComponentDeclarationIntoList(node, newBody, options);
break;
}
case 'HookDeclaration': {
const decl = mapHookDeclaration(node);
newBody.push(decl);
break;
}
case 'ExportNamedDeclaration': {
if (node.declaration?.type === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(
node.declaration,
newBody,
options,
componentOrRef => {
switch (componentOrRef.type) {
case 'FunctionDeclaration': {
// No ref, so we can export the component directly.
return nodeWith(node, {declaration: componentOrRef});
}
case 'Identifier': {
// If a ref is inserted, we should just export that id.
return {
type: 'ExportNamedDeclaration',
declaration: null,
specifiers: [
{
type: 'ExportSpecifier',
exported: shallowCloneNode(componentOrRef),
local: shallowCloneNode(componentOrRef),
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT,
},
],
exportKind: 'value',
source: null,
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
}
},
);
break;
}
if (node.declaration?.type === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {declaration: comp}));
break;
}
newBody.push(node);
break;
}
case 'ExportDefaultDeclaration': {
if (node.declaration?.type === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(
node.declaration,
newBody,
options,
componentOrRef => nodeWith(node, {declaration: componentOrRef}),
);
break;
}
if (node.declaration?.type === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {declaration: comp}));
break;
}
newBody.push(node);
break;
}
default: {
newBody.push(node);
}
}
}
return newBody;
}
export function transformProgram(
program: Program,
options: ParserOptions,
): Program {
return SimpleTransform.transformProgram(program, {
transform(node: ESNode) {
switch (node.type) {
case 'DeclareComponent': {
return mapDeclareComponent(node);
}
case 'DeclareHook': {
return mapDeclareHook(node);
}
case 'Program':
case 'BlockStatement': {
return nodeWith(node, {body: mapStatementList(node.body, options)});
}
case 'SwitchCase': {
const consequent = mapStatementList(node.consequent, options);
return nodeWith(node, {
/* $FlowExpectedError[incompatible-call] We know `mapStatementList` will
not return `ModuleDeclaration` nodes if it is not passed any */
consequent,
});
}
case 'ComponentDeclaration': {
throw createSyntaxError(
node,
`Components must be defined at the top level of a module or within a ` +
`BlockStatement, instead got parent of "${node.parent?.type}".`,
);
}
case 'HookDeclaration': {
throw createSyntaxError(
node,
`Hooks must be defined at the top level of a module or within a ` +
`BlockStatement, instead got parent of "${node.parent?.type}".`,
);
}
default: {
return node;
}
}
},
});
}

View File

@@ -0,0 +1,175 @@
/**
* 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
*/
/**
* This transform strips all Flow types.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformProgram = transformProgram;
var _SimpleTransform = require("../transform/SimpleTransform");
const nodeWith = _SimpleTransform.SimpleTransform.nodeWith;
function transformProgram(program, _options) {
return _SimpleTransform.SimpleTransform.transformProgram(program, {
transform(node) {
switch (node.type) {
case 'AsExpression':
case 'AsConstExpression':
case 'TypeCastExpression':
{
return node.expression;
}
case 'CallExpression':
case 'NewExpression':
{
if (node.typeArguments != null) {
return nodeWith(node, {
typeArguments: null
});
}
return node;
}
case 'ObjectPattern':
case 'ArrayPattern':
case 'Identifier':
{
if (node.typeAnnotation != null) {
return nodeWith(node, {
typeAnnotation: null
});
}
return node;
}
case 'DeclareClass':
case 'DeclareFunction':
case 'DeclareInterface':
case 'DeclareModule':
case 'DeclareModuleExports':
case 'DeclareNamespace':
case 'DeclareOpaqueType':
case 'DeclareTypeAlias':
case 'DeclareVariable':
case 'InterfaceDeclaration':
case 'OpaqueType':
case 'TypeAlias':
{
return null;
}
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
case 'FunctionExpression':
{
const newParams = [];
for (let i = 0; i < node.params.length; i++) {
if (i === 0 && node.params[0].type === 'Identifier' && node.params[0].name === 'this') {
continue;
}
let param = node.params[i];
if (param.type === 'AssignmentPattern') {
param = param.left;
}
if (param.optional === true) {
param = nodeWith(param, {
optional: false
});
}
newParams.push(param);
}
return nodeWith(node, {
params: newParams,
returnType: null,
typeParameters: null,
predicate: null
});
}
case 'ClassDeclaration':
case 'ClassExpression':
{
return nodeWith(node, {
typeParameters: null,
superTypeParameters: null,
implements: [],
decorators: []
});
}
case 'PropertyDefinition':
{
return nodeWith(node, {
typeAnnotation: null,
variance: null,
declare: false,
optional: false
});
}
case 'ImportDeclaration':
{
if (node.importKind === 'type' || node.importKind === 'typeof') {
return null;
}
const nonTypeSpecifiers = node.specifiers.filter(s => s.type !== 'ImportSpecifier' || s.importKind !== 'type' && s.importKind !== 'typeof');
if (nonTypeSpecifiers.length === 0) {
return null;
}
if (nonTypeSpecifiers.length === node.specifiers.length) {
return node;
}
return nodeWith(node, {
specifiers: nonTypeSpecifiers
});
}
case 'ExportAllDeclaration':
case 'ExportNamedDeclaration':
{
if (node.exportKind === 'type') {
return null;
}
return node;
}
default:
{
return node;
}
}
}
});
}

View File

@@ -0,0 +1,158 @@
/**
* 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.
*
* @flow strict
* @format
*/
/**
* This transform strips all Flow types.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
import type {ParserOptions} from '../ParserOptions';
import type {Program, ESNode} from 'hermes-estree';
import {SimpleTransform} from '../transform/SimpleTransform';
const nodeWith = SimpleTransform.nodeWith;
export function transformProgram(
program: Program,
_options: ParserOptions,
): Program {
return SimpleTransform.transformProgram(program, {
transform(node: ESNode) {
switch (node.type) {
case 'AsExpression':
case 'AsConstExpression':
case 'TypeCastExpression': {
return node.expression;
}
case 'CallExpression':
case 'NewExpression': {
if (node.typeArguments != null) {
return nodeWith(node, {typeArguments: null});
}
return node;
}
case 'ObjectPattern':
case 'ArrayPattern':
case 'Identifier': {
if (node.typeAnnotation != null) {
return nodeWith(node, {typeAnnotation: null});
}
return node;
}
case 'DeclareClass':
case 'DeclareFunction':
case 'DeclareInterface':
case 'DeclareModule':
case 'DeclareModuleExports':
case 'DeclareNamespace':
case 'DeclareOpaqueType':
case 'DeclareTypeAlias':
case 'DeclareVariable':
case 'InterfaceDeclaration':
case 'OpaqueType':
case 'TypeAlias': {
return null;
}
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
case 'FunctionExpression': {
const newParams = [];
for (let i = 0; i < node.params.length; i++) {
if (
i === 0 &&
node.params[0].type === 'Identifier' &&
node.params[0].name === 'this'
) {
continue;
}
let param = node.params[i];
if (param.type === 'AssignmentPattern') {
param = param.left;
}
if (param.optional === true) {
param = nodeWith(param, {optional: false});
}
newParams.push(param);
}
return nodeWith(node, {
params: newParams,
returnType: null,
typeParameters: null,
predicate: null,
});
}
case 'ClassDeclaration':
case 'ClassExpression': {
return nodeWith(node, {
typeParameters: null,
superTypeParameters: null,
implements: [],
decorators: [],
});
}
case 'PropertyDefinition': {
return nodeWith(node, {
typeAnnotation: null,
variance: null,
declare: false,
optional: false,
});
}
case 'ImportDeclaration': {
if (node.importKind === 'type' || node.importKind === 'typeof') {
return null;
}
const nonTypeSpecifiers = node.specifiers.filter(
s =>
s.type !== 'ImportSpecifier' ||
(s.importKind !== 'type' && s.importKind !== 'typeof'),
);
if (nonTypeSpecifiers.length === 0) {
return null;
}
if (nonTypeSpecifiers.length === node.specifiers.length) {
return node;
}
return nodeWith(node, {
specifiers: nonTypeSpecifiers,
});
}
case 'ExportAllDeclaration':
case 'ExportNamedDeclaration': {
if (node.exportKind === 'type') {
return null;
}
return node;
}
default: {
return node;
}
}
},
});
}

View File

@@ -0,0 +1,215 @@
/**
* 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
*/
/**
* This transform strips Flow types that are not supported past Babel 7.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformProgram = transformProgram;
var _SimpleTransform = require("../transform/SimpleTransform");
var _createSyntaxError = require("../utils/createSyntaxError");
const nodeWith = _SimpleTransform.SimpleTransform.nodeWith; // Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT = null;
function createSimpleGenericTypeAnnotation(name, nodeForLoc) {
return {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name,
optional: false,
typeAnnotation: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: EMPTY_PARENT
},
typeParameters: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: nodeForLoc.parent
};
}
function createAnyTypeAnnotation(node) {
return {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: node.parent
};
}
/**
* Convert DeclareEnum nodes to DeclareVariable
*/
function mapDeclareEnum(node) {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT
}
}),
loc: node.loc,
range: node.range,
parent: node.parent
};
}
/**
* Convert DeclareNamespace nodes to DeclareVariable
*/
function mapDeclareNamespace(node) {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT
}
}),
loc: node.loc,
range: node.range,
parent: node.parent
};
}
/**
* Remove `this` param from functions.
*/
function 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') {
return nodeWith(node, {
params: node.params.slice(1)
});
}
return node;
}
/**
* Convert to QualifiedTypeIdentifier
*/
function mapQualifiedTypeofIdentifier(node) {
return {
type: 'QualifiedTypeIdentifier',
qualification: node.qualification.type === 'QualifiedTypeofIdentifier' ? mapQualifiedTypeofIdentifier(node.qualification) : node.qualification,
id: node.id,
loc: node.loc,
range: node.range,
parent: node.parent
};
}
function transformProgram(program, _options) {
return _SimpleTransform.SimpleTransform.transformProgram(program, {
transform(node) {
switch (node.type) {
case 'SymbolTypeAnnotation':
{
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('symbol', node);
}
case 'BigIntTypeAnnotation':
{
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('bigint', node);
}
case 'ObjectTypeAnnotation':
{
const shouldStrip = node.properties.some(prop => prop.type === 'ObjectTypeMappedTypeProperty');
if (shouldStrip) {
return createAnyTypeAnnotation(node);
}
return node;
}
case 'ObjectTypeMappedTypeProperty':
{
throw (0, _createSyntaxError.createSyntaxError)(node, `Invalid AST structure, ObjectTypeMappedTypeProperty found outside of an ObjectTypeAnnotation`);
}
case 'IndexedAccessType':
case 'OptionalIndexedAccessType':
case 'KeyofTypeAnnotation':
case 'ConditionalTypeAnnotation':
case 'InferTypeAnnotation':
case 'TupleTypeLabeledElement':
case 'TupleTypeSpreadElement':
case 'ComponentTypeAnnotation':
case 'HookTypeAnnotation':
case 'TypeOperator':
case 'TypePredicate':
{
// Babel does not support these generic types, so convert to any
return createAnyTypeAnnotation(node);
}
case 'QualifiedTypeofIdentifier':
{
return mapQualifiedTypeofIdentifier(node);
}
case 'DeclareEnum':
{
return mapDeclareEnum(node);
}
case 'DeclareNamespace':
{
return mapDeclareNamespace(node);
}
case 'FunctionDeclaration':
case 'FunctionExpression':
{
return mapFunction(node);
}
default:
{
return node;
}
}
}
});
}

View File

@@ -0,0 +1,216 @@
/**
* 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.
*
* @flow strict
* @format
*/
/**
* This transform strips Flow types that are not supported past Babel 7.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
import type {ParserOptions} from '../ParserOptions';
import type {
Program,
ESNode,
DeclareEnum,
DeclareNamespace,
DeclareVariable,
AnyTypeAnnotation,
GenericTypeAnnotation,
QualifiedTypeIdentifier,
QualifiedTypeofIdentifier,
AFunction,
} from 'hermes-estree';
import {SimpleTransform} from '../transform/SimpleTransform';
import {createSyntaxError} from '../utils/createSyntaxError';
const nodeWith = SimpleTransform.nodeWith;
// Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT: $FlowFixMe = null;
function createSimpleGenericTypeAnnotation(
name: string,
nodeForLoc: ESNode,
): GenericTypeAnnotation {
return {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name,
optional: false,
typeAnnotation: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: EMPTY_PARENT,
},
typeParameters: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: nodeForLoc.parent,
};
}
function createAnyTypeAnnotation(node: ESNode): AnyTypeAnnotation {
return {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
/**
* Convert DeclareEnum nodes to DeclareVariable
*/
function mapDeclareEnum(node: DeclareEnum): DeclareVariable {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT,
},
}),
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
/**
* Convert DeclareNamespace nodes to DeclareVariable
*/
function mapDeclareNamespace(node: DeclareNamespace): DeclareVariable {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT,
},
}),
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
/**
* Remove `this` param from functions.
*/
function mapFunction(node: AFunction): AFunction {
// 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') {
return nodeWith(node, {
params: node.params.slice(1),
});
}
return node;
}
/**
* Convert to QualifiedTypeIdentifier
*/
function mapQualifiedTypeofIdentifier(
node: QualifiedTypeofIdentifier,
): QualifiedTypeIdentifier {
return {
type: 'QualifiedTypeIdentifier',
qualification:
node.qualification.type === 'QualifiedTypeofIdentifier'
? mapQualifiedTypeofIdentifier(node.qualification)
: node.qualification,
id: node.id,
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
export function transformProgram(
program: Program,
_options: ParserOptions,
): Program {
return SimpleTransform.transformProgram(program, {
transform(node: ESNode) {
switch (node.type) {
case 'SymbolTypeAnnotation': {
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('symbol', node);
}
case 'BigIntTypeAnnotation': {
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('bigint', node);
}
case 'ObjectTypeAnnotation': {
const shouldStrip = node.properties.some(
prop => prop.type === 'ObjectTypeMappedTypeProperty',
);
if (shouldStrip) {
return createAnyTypeAnnotation(node);
}
return node;
}
case 'ObjectTypeMappedTypeProperty': {
throw createSyntaxError(
node,
`Invalid AST structure, ObjectTypeMappedTypeProperty found outside of an ObjectTypeAnnotation`,
);
}
case 'IndexedAccessType':
case 'OptionalIndexedAccessType':
case 'KeyofTypeAnnotation':
case 'ConditionalTypeAnnotation':
case 'InferTypeAnnotation':
case 'TupleTypeLabeledElement':
case 'TupleTypeSpreadElement':
case 'ComponentTypeAnnotation':
case 'HookTypeAnnotation':
case 'TypeOperator':
case 'TypePredicate': {
// Babel does not support these generic types, so convert to any
return createAnyTypeAnnotation(node);
}
case 'QualifiedTypeofIdentifier': {
return mapQualifiedTypeofIdentifier(node);
}
case 'DeclareEnum': {
return mapDeclareEnum(node);
}
case 'DeclareNamespace': {
return mapDeclareNamespace(node);
}
case 'FunctionDeclaration':
case 'FunctionExpression': {
return mapFunction(node);
}
default: {
return node;
}
}
},
});
}

View File

@@ -0,0 +1,203 @@
/**
* 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
* @generated
*/
/*
* !!! GENERATED FILE !!!
*
* Any manual changes to this file will be overwritten. To regenerate run `yarn build`.
*/
// lint directives to let us do some basic validation of generated files
/* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */
/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */
'use strict';
module.exports = {
AnyTypeAnnotation: [],
ArrayExpression: ['elements'],
ArrayPattern: ['elements', 'typeAnnotation'],
ArrayTypeAnnotation: ['elementType'],
ArrowFunctionExpression: ['id', 'params', 'body', 'typeParameters', 'returnType', 'predicate'],
AsConstExpression: ['expression'],
AsExpression: ['expression', 'typeAnnotation'],
AssignmentExpression: ['left', 'right'],
AssignmentPattern: ['left', 'right'],
AwaitExpression: ['argument'],
BigIntLiteralTypeAnnotation: [],
BigIntTypeAnnotation: [],
BinaryExpression: ['left', 'right'],
BlockStatement: ['body'],
BooleanLiteralTypeAnnotation: [],
BooleanTypeAnnotation: [],
BreakStatement: ['label'],
CallExpression: ['callee', 'typeArguments', 'arguments'],
CatchClause: ['param', 'body'],
ChainExpression: ['expression'],
ClassBody: ['body'],
ClassDeclaration: ['id', 'typeParameters', 'superClass', 'superTypeParameters', 'implements', 'decorators', 'body'],
ClassExpression: ['id', 'typeParameters', 'superClass', 'superTypeParameters', 'implements', 'decorators', 'body'],
ClassImplements: ['id', 'typeParameters'],
ComponentDeclaration: ['id', 'params', 'body', 'typeParameters', 'rendersType'],
ComponentParameter: ['name', 'local'],
ComponentTypeAnnotation: ['params', 'rest', 'typeParameters', 'rendersType'],
ComponentTypeParameter: ['name', 'typeAnnotation'],
ConditionalExpression: ['test', 'consequent', 'alternate'],
ConditionalTypeAnnotation: ['checkType', 'extendsType', 'trueType', 'falseType'],
ContinueStatement: ['label'],
DebuggerStatement: [],
DeclareClass: ['id', 'typeParameters', 'extends', 'implements', 'mixins', 'body'],
DeclareComponent: ['id', 'params', 'rest', 'typeParameters', 'rendersType'],
DeclaredPredicate: ['value'],
DeclareEnum: ['id', 'body'],
DeclareExportAllDeclaration: ['source'],
DeclareExportDeclaration: ['declaration', 'specifiers', 'source'],
DeclareFunction: ['id', 'predicate'],
DeclareHook: ['id'],
DeclareInterface: ['id', 'typeParameters', 'extends', 'body'],
DeclareModule: ['id', 'body'],
DeclareModuleExports: ['typeAnnotation'],
DeclareNamespace: ['id', 'body'],
DeclareOpaqueType: ['id', 'typeParameters', 'impltype', 'supertype'],
DeclareTypeAlias: ['id', 'typeParameters', 'right'],
DeclareVariable: ['id'],
DoWhileStatement: ['body', 'test'],
EmptyStatement: [],
EmptyTypeAnnotation: [],
EnumBigIntBody: ['members'],
EnumBigIntMember: ['id', 'init'],
EnumBooleanBody: ['members'],
EnumBooleanMember: ['id', 'init'],
EnumDeclaration: ['id', 'body'],
EnumDefaultedMember: ['id'],
EnumNumberBody: ['members'],
EnumNumberMember: ['id', 'init'],
EnumStringBody: ['members'],
EnumStringMember: ['id', 'init'],
EnumSymbolBody: ['members'],
ExistsTypeAnnotation: [],
ExportAllDeclaration: ['exported', 'source'],
ExportDefaultDeclaration: ['declaration'],
ExportNamedDeclaration: ['declaration', 'specifiers', 'source'],
ExportSpecifier: ['exported', 'local'],
ExpressionStatement: ['expression'],
ForInStatement: ['left', 'right', 'body'],
ForOfStatement: ['left', 'right', 'body'],
ForStatement: ['init', 'test', 'update', 'body'],
FunctionDeclaration: ['id', 'params', 'body', 'typeParameters', 'returnType', 'predicate'],
FunctionExpression: ['id', 'params', 'body', 'typeParameters', 'returnType', 'predicate'],
FunctionTypeAnnotation: ['params', 'this', 'returnType', 'rest', 'typeParameters'],
FunctionTypeParam: ['name', 'typeAnnotation'],
GenericTypeAnnotation: ['id', 'typeParameters'],
HookDeclaration: ['id', 'params', 'body', 'typeParameters', 'returnType'],
HookTypeAnnotation: ['params', 'returnType', 'rest', 'typeParameters'],
Identifier: ['typeAnnotation'],
IfStatement: ['test', 'consequent', 'alternate'],
ImportAttribute: ['key', 'value'],
ImportDeclaration: ['specifiers', 'source', 'assertions'],
ImportDefaultSpecifier: ['local'],
ImportExpression: ['source', 'attributes'],
ImportNamespaceSpecifier: ['local'],
ImportSpecifier: ['imported', 'local'],
IndexedAccessType: ['objectType', 'indexType'],
InferredPredicate: [],
InferTypeAnnotation: ['typeParameter'],
InterfaceDeclaration: ['id', 'typeParameters', 'extends', 'body'],
InterfaceExtends: ['id', 'typeParameters'],
InterfaceTypeAnnotation: ['extends', 'body'],
IntersectionTypeAnnotation: ['types'],
JSXAttribute: ['name', 'value'],
JSXClosingElement: ['name'],
JSXClosingFragment: [],
JSXElement: ['openingElement', 'children', 'closingElement'],
JSXEmptyExpression: [],
JSXExpressionContainer: ['expression'],
JSXFragment: ['openingFragment', 'children', 'closingFragment'],
JSXIdentifier: [],
JSXMemberExpression: ['object', 'property'],
JSXNamespacedName: ['namespace', 'name'],
JSXOpeningElement: ['name', 'attributes', 'typeArguments'],
JSXOpeningFragment: [],
JSXSpreadAttribute: ['argument'],
JSXSpreadChild: ['expression'],
JSXText: [],
KeyofTypeAnnotation: ['argument'],
LabeledStatement: ['label', 'body'],
LogicalExpression: ['left', 'right'],
MemberExpression: ['object', 'property'],
MetaProperty: ['meta', 'property'],
MethodDefinition: ['key', 'value'],
MixedTypeAnnotation: [],
NewExpression: ['callee', 'typeArguments', 'arguments'],
NullableTypeAnnotation: ['typeAnnotation'],
NullLiteralTypeAnnotation: [],
NumberLiteralTypeAnnotation: [],
NumberTypeAnnotation: [],
ObjectExpression: ['properties'],
ObjectPattern: ['properties', 'typeAnnotation'],
ObjectTypeAnnotation: ['properties', 'indexers', 'callProperties', 'internalSlots'],
ObjectTypeCallProperty: ['value'],
ObjectTypeIndexer: ['id', 'key', 'value', 'variance'],
ObjectTypeInternalSlot: ['id', 'value'],
ObjectTypeMappedTypeProperty: ['keyTparam', 'propType', 'sourceType', 'variance'],
ObjectTypeProperty: ['key', 'value', 'variance'],
ObjectTypeSpreadProperty: ['argument'],
OpaqueType: ['id', 'typeParameters', 'impltype', 'supertype'],
OptionalIndexedAccessType: ['objectType', 'indexType'],
PrivateIdentifier: [],
Program: ['body'],
Property: ['key', 'value'],
PropertyDefinition: ['key', 'value', 'variance', 'typeAnnotation'],
QualifiedTypeIdentifier: ['qualification', 'id'],
QualifiedTypeofIdentifier: ['qualification', 'id'],
RestElement: ['argument'],
ReturnStatement: ['argument'],
SequenceExpression: ['expressions'],
SpreadElement: ['argument'],
StringLiteralTypeAnnotation: [],
StringTypeAnnotation: [],
Super: [],
SwitchCase: ['test', 'consequent'],
SwitchStatement: ['discriminant', 'cases'],
SymbolTypeAnnotation: [],
TaggedTemplateExpression: ['tag', 'quasi'],
TemplateElement: [],
TemplateLiteral: ['quasis', 'expressions'],
ThisExpression: [],
ThisTypeAnnotation: [],
ThrowStatement: ['argument'],
TryStatement: ['block', 'handler', 'finalizer'],
TupleTypeAnnotation: ['types'],
TupleTypeLabeledElement: ['label', 'elementType', 'variance'],
TupleTypeSpreadElement: ['label', 'typeAnnotation'],
TypeAlias: ['id', 'typeParameters', 'right'],
TypeAnnotation: ['typeAnnotation'],
TypeCastExpression: ['expression', 'typeAnnotation'],
TypeofTypeAnnotation: ['argument', 'typeArguments'],
TypeOperator: ['typeAnnotation'],
TypeParameter: ['bound', 'variance', 'default'],
TypeParameterDeclaration: ['params'],
TypeParameterInstantiation: ['params'],
TypePredicate: ['parameterName', 'typeAnnotation'],
UnaryExpression: ['argument'],
UnionTypeAnnotation: ['types'],
UpdateExpression: ['argument'],
VariableDeclaration: ['declarations'],
VariableDeclarator: ['id', 'init'],
Variance: [],
VoidTypeAnnotation: [],
WhileStatement: ['test', 'body'],
WithStatement: ['object', 'body'],
YieldExpression: ['argument'],
OptionalMemberExpression: ['object', 'property'],
OptionalCallExpression: ['callee', 'typeArguments', 'arguments'],
Literal: []
};

View File

@@ -0,0 +1,15 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
export type VisitorKeys = $ReadOnly<{[string]: $ReadOnlyArray<string>}>;
declare module.exports: VisitorKeys;

View File

@@ -0,0 +1,730 @@
/**
* 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
* @generated
*/
/*
* !!! GENERATED FILE !!!
*
* Any manual changes to this file will be overwritten. To regenerate run `yarn build`.
*/
// lint directives to let us do some basic validation of generated files
/* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */
/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.NODE_LIST_CHILD = exports.NODE_CHILD = exports.HERMES_AST_VISITOR_KEYS = void 0;
const NODE_CHILD = 'Node';
exports.NODE_CHILD = NODE_CHILD;
const NODE_LIST_CHILD = 'NodeList';
exports.NODE_LIST_CHILD = NODE_LIST_CHILD;
const HERMES_AST_VISITOR_KEYS = {
AnyTypeAnnotation: {},
ArrayExpression: {
elements: 'NodeList'
},
ArrayPattern: {
elements: 'NodeList',
typeAnnotation: 'Node'
},
ArrayTypeAnnotation: {
elementType: 'Node'
},
ArrowFunctionExpression: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node',
predicate: 'Node'
},
AsConstExpression: {
expression: 'Node'
},
AsExpression: {
expression: 'Node',
typeAnnotation: 'Node'
},
AssignmentExpression: {
left: 'Node',
right: 'Node'
},
AssignmentPattern: {
left: 'Node',
right: 'Node'
},
AwaitExpression: {
argument: 'Node'
},
BigIntLiteral: {},
BigIntLiteralTypeAnnotation: {},
BigIntTypeAnnotation: {},
BinaryExpression: {
left: 'Node',
right: 'Node'
},
BlockStatement: {
body: 'NodeList'
},
BooleanLiteral: {},
BooleanLiteralTypeAnnotation: {},
BooleanTypeAnnotation: {},
BreakStatement: {
label: 'Node'
},
CallExpression: {
callee: 'Node',
typeArguments: 'Node',
arguments: 'NodeList'
},
CatchClause: {
param: 'Node',
body: 'Node'
},
ChainExpression: {
expression: 'Node'
},
ClassBody: {
body: 'NodeList'
},
ClassDeclaration: {
id: 'Node',
typeParameters: 'Node',
superClass: 'Node',
superTypeParameters: 'Node',
implements: 'NodeList',
decorators: 'NodeList',
body: 'Node'
},
ClassExpression: {
id: 'Node',
typeParameters: 'Node',
superClass: 'Node',
superTypeParameters: 'Node',
implements: 'NodeList',
decorators: 'NodeList',
body: 'Node'
},
ClassImplements: {
id: 'Node',
typeParameters: 'Node'
},
ComponentDeclaration: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
rendersType: 'Node'
},
ComponentParameter: {
name: 'Node',
local: 'Node'
},
ComponentTypeAnnotation: {
params: 'NodeList',
rest: 'Node',
typeParameters: 'Node',
rendersType: 'Node'
},
ComponentTypeParameter: {
name: 'Node',
typeAnnotation: 'Node'
},
ConditionalExpression: {
test: 'Node',
alternate: 'Node',
consequent: 'Node'
},
ConditionalTypeAnnotation: {
checkType: 'Node',
extendsType: 'Node',
trueType: 'Node',
falseType: 'Node'
},
ContinueStatement: {
label: 'Node'
},
DebuggerStatement: {},
DeclareClass: {
id: 'Node',
typeParameters: 'Node',
extends: 'NodeList',
implements: 'NodeList',
mixins: 'NodeList',
body: 'Node'
},
DeclareComponent: {
id: 'Node',
params: 'NodeList',
rest: 'Node',
typeParameters: 'Node',
rendersType: 'Node'
},
DeclaredPredicate: {
value: 'Node'
},
DeclareEnum: {
id: 'Node',
body: 'Node'
},
DeclareExportAllDeclaration: {
source: 'Node'
},
DeclareExportDeclaration: {
declaration: 'Node',
specifiers: 'NodeList',
source: 'Node'
},
DeclareFunction: {
id: 'Node',
predicate: 'Node'
},
DeclareHook: {
id: 'Node'
},
DeclareInterface: {
id: 'Node',
typeParameters: 'Node',
extends: 'NodeList',
body: 'Node'
},
DeclareModule: {
id: 'Node',
body: 'Node'
},
DeclareModuleExports: {
typeAnnotation: 'Node'
},
DeclareNamespace: {
id: 'Node',
body: 'Node'
},
DeclareOpaqueType: {
id: 'Node',
typeParameters: 'Node',
impltype: 'Node',
supertype: 'Node'
},
DeclareTypeAlias: {
id: 'Node',
typeParameters: 'Node',
right: 'Node'
},
DeclareVariable: {
id: 'Node'
},
DoWhileStatement: {
body: 'Node',
test: 'Node'
},
EmptyStatement: {},
EmptyTypeAnnotation: {},
EnumBigIntBody: {
members: 'NodeList'
},
EnumBigIntMember: {
id: 'Node',
init: 'Node'
},
EnumBooleanBody: {
members: 'NodeList'
},
EnumBooleanMember: {
id: 'Node',
init: 'Node'
},
EnumDeclaration: {
id: 'Node',
body: 'Node'
},
EnumDefaultedMember: {
id: 'Node'
},
EnumNumberBody: {
members: 'NodeList'
},
EnumNumberMember: {
id: 'Node',
init: 'Node'
},
EnumStringBody: {
members: 'NodeList'
},
EnumStringMember: {
id: 'Node',
init: 'Node'
},
EnumSymbolBody: {
members: 'NodeList'
},
ExistsTypeAnnotation: {},
ExportAllDeclaration: {
exported: 'Node',
source: 'Node'
},
ExportDefaultDeclaration: {
declaration: 'Node'
},
ExportNamedDeclaration: {
declaration: 'Node',
specifiers: 'NodeList',
source: 'Node'
},
ExportSpecifier: {
exported: 'Node',
local: 'Node'
},
ExpressionStatement: {
expression: 'Node'
},
ForInStatement: {
left: 'Node',
right: 'Node',
body: 'Node'
},
ForOfStatement: {
left: 'Node',
right: 'Node',
body: 'Node'
},
ForStatement: {
init: 'Node',
test: 'Node',
update: 'Node',
body: 'Node'
},
FunctionDeclaration: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node',
predicate: 'Node'
},
FunctionExpression: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node',
predicate: 'Node'
},
FunctionTypeAnnotation: {
params: 'NodeList',
this: 'Node',
returnType: 'Node',
rest: 'Node',
typeParameters: 'Node'
},
FunctionTypeParam: {
name: 'Node',
typeAnnotation: 'Node'
},
GenericTypeAnnotation: {
id: 'Node',
typeParameters: 'Node'
},
HookDeclaration: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node'
},
HookTypeAnnotation: {
params: 'NodeList',
returnType: 'Node',
rest: 'Node',
typeParameters: 'Node'
},
Identifier: {
typeAnnotation: 'Node'
},
IfStatement: {
test: 'Node',
consequent: 'Node',
alternate: 'Node'
},
ImportAttribute: {
key: 'Node',
value: 'Node'
},
ImportDeclaration: {
specifiers: 'NodeList',
source: 'Node',
assertions: 'NodeList'
},
ImportDefaultSpecifier: {
local: 'Node'
},
ImportExpression: {
source: 'Node',
attributes: 'Node'
},
ImportNamespaceSpecifier: {
local: 'Node'
},
ImportSpecifier: {
imported: 'Node',
local: 'Node'
},
IndexedAccessType: {
objectType: 'Node',
indexType: 'Node'
},
InferredPredicate: {},
InferTypeAnnotation: {
typeParameter: 'Node'
},
InterfaceDeclaration: {
id: 'Node',
typeParameters: 'Node',
extends: 'NodeList',
body: 'Node'
},
InterfaceExtends: {
id: 'Node',
typeParameters: 'Node'
},
InterfaceTypeAnnotation: {
extends: 'NodeList',
body: 'Node'
},
IntersectionTypeAnnotation: {
types: 'NodeList'
},
JSXAttribute: {
name: 'Node',
value: 'Node'
},
JSXClosingElement: {
name: 'Node'
},
JSXClosingFragment: {},
JSXElement: {
openingElement: 'Node',
children: 'NodeList',
closingElement: 'Node'
},
JSXEmptyExpression: {},
JSXExpressionContainer: {
expression: 'Node'
},
JSXFragment: {
openingFragment: 'Node',
children: 'NodeList',
closingFragment: 'Node'
},
JSXIdentifier: {},
JSXMemberExpression: {
object: 'Node',
property: 'Node'
},
JSXNamespacedName: {
namespace: 'Node',
name: 'Node'
},
JSXOpeningElement: {
name: 'Node',
attributes: 'NodeList',
typeArguments: 'Node'
},
JSXOpeningFragment: {},
JSXSpreadAttribute: {
argument: 'Node'
},
JSXSpreadChild: {
expression: 'Node'
},
JSXText: {},
KeyofTypeAnnotation: {
argument: 'Node'
},
LabeledStatement: {
label: 'Node',
body: 'Node'
},
LogicalExpression: {
left: 'Node',
right: 'Node'
},
MemberExpression: {
object: 'Node',
property: 'Node'
},
MetaProperty: {
meta: 'Node',
property: 'Node'
},
MethodDefinition: {
key: 'Node',
value: 'Node'
},
MixedTypeAnnotation: {},
NewExpression: {
callee: 'Node',
typeArguments: 'Node',
arguments: 'NodeList'
},
NullableTypeAnnotation: {
typeAnnotation: 'Node'
},
NullLiteral: {},
NullLiteralTypeAnnotation: {},
NumberLiteralTypeAnnotation: {},
NumberTypeAnnotation: {},
NumericLiteral: {},
ObjectExpression: {
properties: 'NodeList'
},
ObjectPattern: {
properties: 'NodeList',
typeAnnotation: 'Node'
},
ObjectTypeAnnotation: {
properties: 'NodeList',
indexers: 'NodeList',
callProperties: 'NodeList',
internalSlots: 'NodeList'
},
ObjectTypeCallProperty: {
value: 'Node'
},
ObjectTypeIndexer: {
id: 'Node',
key: 'Node',
value: 'Node',
variance: 'Node'
},
ObjectTypeInternalSlot: {
id: 'Node',
value: 'Node'
},
ObjectTypeMappedTypeProperty: {
keyTparam: 'Node',
propType: 'Node',
sourceType: 'Node',
variance: 'Node'
},
ObjectTypeProperty: {
key: 'Node',
value: 'Node',
variance: 'Node'
},
ObjectTypeSpreadProperty: {
argument: 'Node'
},
OpaqueType: {
id: 'Node',
typeParameters: 'Node',
impltype: 'Node',
supertype: 'Node'
},
OptionalIndexedAccessType: {
objectType: 'Node',
indexType: 'Node'
},
PrivateIdentifier: {},
Program: {
body: 'NodeList'
},
Property: {
key: 'Node',
value: 'Node'
},
PropertyDefinition: {
key: 'Node',
value: 'Node',
variance: 'Node',
typeAnnotation: 'Node'
},
QualifiedTypeIdentifier: {
qualification: 'Node',
id: 'Node'
},
QualifiedTypeofIdentifier: {
qualification: 'Node',
id: 'Node'
},
RegExpLiteral: {},
RestElement: {
argument: 'Node'
},
ReturnStatement: {
argument: 'Node'
},
SequenceExpression: {
expressions: 'NodeList'
},
SpreadElement: {
argument: 'Node'
},
StringLiteral: {},
StringLiteralTypeAnnotation: {},
StringTypeAnnotation: {},
Super: {},
SwitchCase: {
test: 'Node',
consequent: 'NodeList'
},
SwitchStatement: {
discriminant: 'Node',
cases: 'NodeList'
},
SymbolTypeAnnotation: {},
TaggedTemplateExpression: {
tag: 'Node',
quasi: 'Node'
},
TemplateElement: {},
TemplateLiteral: {
quasis: 'NodeList',
expressions: 'NodeList'
},
ThisExpression: {},
ThisTypeAnnotation: {},
ThrowStatement: {
argument: 'Node'
},
TryStatement: {
block: 'Node',
handler: 'Node',
finalizer: 'Node'
},
TupleTypeAnnotation: {
types: 'NodeList'
},
TupleTypeLabeledElement: {
label: 'Node',
elementType: 'Node',
variance: 'Node'
},
TupleTypeSpreadElement: {
label: 'Node',
typeAnnotation: 'Node'
},
TypeAlias: {
id: 'Node',
typeParameters: 'Node',
right: 'Node'
},
TypeAnnotation: {
typeAnnotation: 'Node'
},
TypeCastExpression: {
expression: 'Node',
typeAnnotation: 'Node'
},
TypeofTypeAnnotation: {
argument: 'Node',
typeArguments: 'Node'
},
TypeOperator: {
typeAnnotation: 'Node'
},
TypeParameter: {
bound: 'Node',
variance: 'Node',
default: 'Node'
},
TypeParameterDeclaration: {
params: 'NodeList'
},
TypeParameterInstantiation: {
params: 'NodeList'
},
TypePredicate: {
parameterName: 'Node',
typeAnnotation: 'Node'
},
UnaryExpression: {
argument: 'Node'
},
UnionTypeAnnotation: {
types: 'NodeList'
},
UpdateExpression: {
argument: 'Node'
},
VariableDeclaration: {
declarations: 'NodeList'
},
VariableDeclarator: {
init: 'Node',
id: 'Node'
},
Variance: {},
VoidTypeAnnotation: {},
WhileStatement: {
body: 'Node',
test: 'Node'
},
WithStatement: {
object: 'Node',
body: 'Node'
},
YieldExpression: {
argument: 'Node'
},
File: {
program: 'Node'
},
ObjectProperty: {
key: 'Node',
value: 'Node'
},
ObjectMethod: {
key: 'Node',
params: 'NodeList',
body: 'Node',
returnType: 'Node',
typeParameters: 'NodeList'
},
ClassMethod: {
key: 'Node',
params: 'NodeList',
body: 'Node',
returnType: 'Node',
typeParameters: 'NodeList'
},
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'
},
ExportNamespaceSpecifier: {
exported: 'Node'
}
};
exports.HERMES_AST_VISITOR_KEYS = HERMES_AST_VISITOR_KEYS;

View File

@@ -0,0 +1,17 @@
/**
* 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.
*
* @flow strict
* @format
*/
declare export var NODE_CHILD: 'Node';
declare export var NODE_LIST_CHILD: 'NodeList';
declare export var HERMES_AST_VISITOR_KEYS: $ReadOnly<{
[string]: $ReadOnly<{
[string]: 'Node' | 'NodeList',
}>,
}>;

112
node_modules/hermes-parser/dist/getModuleDocblock.js generated vendored Normal file
View File

@@ -0,0 +1,112 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getModuleDocblock = getModuleDocblock;
exports.parseDocblockString = parseDocblockString;
const DIRECTIVE_REGEX = /^\s*@([a-zA-Z0-9_-]+)( +.+)?$/;
function parseDocblockString(docblock) {
const directiveLines = docblock.split('\n') // remove the leading " *" from each line
.map(line => line.trimStart().replace(/^\* ?/, '').trim()).filter(line => line.startsWith('@'));
const directives = // $FlowExpectedError[incompatible-type] - flow types this return as {...}
Object.create(null);
for (const line of directiveLines) {
var _match$;
const match = DIRECTIVE_REGEX.exec(line);
if (match == null) {
continue;
}
const name = match[1]; // explicitly use an empty string if there's no value
// this way the array length tracks how many instances of the directive there was
const value = ((_match$ = match[2]) != null ? _match$ : '').trim();
if (directives[name]) {
directives[name].push(value);
} else {
directives[name] = [value];
}
}
return directives;
}
function getModuleDocblock(hermesProgram) {
const docblockNode = (() => {
if (hermesProgram.type !== 'Program') {
return null;
} // $FlowExpectedError[incompatible-type] - escape out of the unsafe hermes types
const program = hermesProgram;
if (program.comments.length === 0) {
return null;
}
const firstComment = (() => {
const first = program.comments[0];
if (first.type === 'Block') {
return first;
}
if (program.comments.length === 1) {
return null;
} // ESLint will always strip out the shebang comment from the code before passing it to the parser
// https://github.com/eslint/eslint/blob/21d647904dc30f9484b22acdd9243a6d0ecfba38/lib/linter/linter.js#L779
// this means that we're forced to parse it as a line comment :(
// this hacks around it by selecting the second comment in this case
const second = program.comments[1];
if (first.type === 'Line' && first.range[0] === 0 && second.type === 'Block') {
return second;
}
return null;
})();
if (firstComment == null) {
return null;
}
/*
Handle cases like this where the comment isn't actually the first thing in the code:
```
const x = 1; /* docblock *./
```
*/
if (program.body.length > 0 && program.body[0].range[0] < firstComment.range[0]) {
return null;
}
return firstComment;
})();
if (docblockNode == null) {
return null;
}
return {
directives: parseDocblockString(docblockNode.value),
comment: docblockNode
};
}

View File

@@ -0,0 +1,118 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {HermesNode} from './HermesAST';
import type {DocblockDirectives, Program} from 'hermes-estree';
const DIRECTIVE_REGEX = /^\s*@([a-zA-Z0-9_-]+)( +.+)?$/;
export function parseDocblockString(docblock: string): DocblockDirectives {
const directiveLines = docblock
.split('\n')
// remove the leading " *" from each line
.map(line => line.trimStart().replace(/^\* ?/, '').trim())
.filter(line => line.startsWith('@'));
const directives: {
[string]: Array<string>,
} =
// $FlowExpectedError[incompatible-type] - flow types this return as {...}
Object.create(null);
for (const line of directiveLines) {
const match = DIRECTIVE_REGEX.exec(line);
if (match == null) {
continue;
}
const name = match[1];
// explicitly use an empty string if there's no value
// this way the array length tracks how many instances of the directive there was
const value = (match[2] ?? '').trim();
if (directives[name]) {
directives[name].push(value);
} else {
directives[name] = [value];
}
}
return directives;
}
export function getModuleDocblock(
hermesProgram: HermesNode,
): Program['docblock'] {
const docblockNode = (() => {
if (hermesProgram.type !== 'Program') {
return null;
}
// $FlowExpectedError[incompatible-type] - escape out of the unsafe hermes types
const program: Program = hermesProgram;
if (program.comments.length === 0) {
return null;
}
const firstComment = (() => {
const first = program.comments[0];
if (first.type === 'Block') {
return first;
}
if (program.comments.length === 1) {
return null;
}
// ESLint will always strip out the shebang comment from the code before passing it to the parser
// https://github.com/eslint/eslint/blob/21d647904dc30f9484b22acdd9243a6d0ecfba38/lib/linter/linter.js#L779
// this means that we're forced to parse it as a line comment :(
// this hacks around it by selecting the second comment in this case
const second = program.comments[1];
if (
first.type === 'Line' &&
first.range[0] === 0 &&
second.type === 'Block'
) {
return second;
}
return null;
})();
if (firstComment == null) {
return null;
}
/*
Handle cases like this where the comment isn't actually the first thing in the code:
```
const x = 1; /* docblock *./
```
*/
if (
program.body.length > 0 &&
program.body[0].range[0] < firstComment.range[0]
) {
return null;
}
return firstComment;
})();
if (docblockNode == null) {
return null;
}
return {
directives: parseDocblockString(docblockNode.value),
comment: docblockNode,
};
}

146
node_modules/hermes-parser/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,146 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
parse: true,
mutateESTreeASTForPrettier: true,
Transforms: true,
FlowVisitorKeys: true,
astArrayMutationHelpers: true,
astNodeMutationHelpers: true
};
exports.mutateESTreeASTForPrettier = exports.astNodeMutationHelpers = exports.astArrayMutationHelpers = exports.Transforms = void 0;
exports.parse = parse;
var HermesParser = _interopRequireWildcard(require("./HermesParser"));
var _HermesToESTreeAdapter = _interopRequireDefault(require("./HermesToESTreeAdapter"));
var _ESTreeVisitorKeys = _interopRequireDefault(require("./generated/ESTreeVisitorKeys"));
exports.FlowVisitorKeys = _ESTreeVisitorKeys.default;
var StripComponentSyntax = _interopRequireWildcard(require("./estree/StripComponentSyntax"));
var StripFlowTypesForBabel = _interopRequireWildcard(require("./estree/StripFlowTypesForBabel"));
var TransformESTreeToBabel = _interopRequireWildcard(require("./babel/TransformESTreeToBabel"));
var StripFlowTypes = _interopRequireWildcard(require("./estree/StripFlowTypes"));
var _ParserOptions = require("./ParserOptions");
Object.keys(_ParserOptions).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _ParserOptions[key]) return;
exports[key] = _ParserOptions[key];
});
var _SimpleTraverser = require("./traverse/SimpleTraverser");
Object.keys(_SimpleTraverser).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _SimpleTraverser[key]) return;
exports[key] = _SimpleTraverser[key];
});
var _SimpleTransform = require("./transform/SimpleTransform");
Object.keys(_SimpleTransform).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _SimpleTransform[key]) return;
exports[key] = _SimpleTransform[key];
});
var _getVisitorKeys = require("./traverse/getVisitorKeys");
Object.keys(_getVisitorKeys).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _getVisitorKeys[key]) return;
exports[key] = _getVisitorKeys[key];
});
var _astArrayMutationHelpers = _interopRequireWildcard(require("./transform/astArrayMutationHelpers"));
exports.astArrayMutationHelpers = _astArrayMutationHelpers;
var _astNodeMutationHelpers = _interopRequireWildcard(require("./transform/astNodeMutationHelpers"));
exports.astNodeMutationHelpers = _astNodeMutationHelpers;
var _mutateESTreeASTForPrettier = _interopRequireDefault(require("./utils/mutateESTreeASTForPrettier"));
exports.mutateESTreeASTForPrettier = _mutateESTreeASTForPrettier.default;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const DEFAULTS = {
flow: 'detect'
};
function getOptions(options = { ...DEFAULTS
}) {
// Default to detecting whether to parse Flow syntax by the presence
// of an pragma.
if (options.flow == null) {
options.flow = DEFAULTS.flow;
} else if (options.flow !== 'all' && options.flow !== 'detect') {
throw new Error('flow option must be "all" or "detect"');
}
if (options.sourceType === 'unambiguous') {
// Clear source type so that it will be detected from the contents of the file
delete options.sourceType;
} else if (options.sourceType != null && options.sourceType !== 'script' && options.sourceType !== 'module') {
throw new Error('sourceType option must be "script", "module", or "unambiguous" if set');
}
if (options.enableExperimentalComponentSyntax == null) {
options.enableExperimentalComponentSyntax = true; // Enable by default
}
options.tokens = options.tokens === true;
options.allowReturnOutsideFunction = options.allowReturnOutsideFunction === true;
return options;
}
// eslint-disable-next-line no-redeclare
function parse(code, opts) {
const options = getOptions(opts);
const ast = HermesParser.parse(code, options);
const estreeAdapter = new _HermesToESTreeAdapter.default(options, code);
const estreeAST = estreeAdapter.transform(ast);
if (options.babel !== true) {
return estreeAST;
}
const loweredESTreeAST = [StripComponentSyntax.transformProgram, StripFlowTypesForBabel.transformProgram].reduce((ast, transform) => transform(ast, options), estreeAST);
return TransformESTreeToBabel.transformProgram(loweredESTreeAST, options);
}
const Transforms = {
stripComponentSyntax: StripComponentSyntax.transformProgram,
stripFlowTypesForBabel: StripFlowTypesForBabel.transformProgram,
stripFlowTypes: StripFlowTypes.transformProgram
};
exports.Transforms = Transforms;

112
node_modules/hermes-parser/dist/index.js.flow generated vendored Normal file
View File

@@ -0,0 +1,112 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {Program as ESTreeProgram} from 'hermes-estree';
import type {ParserOptions} from './ParserOptions';
import type {BabelFile} from './babel/TransformESTreeToBabel';
import * as HermesParser from './HermesParser';
import HermesToESTreeAdapter from './HermesToESTreeAdapter';
import FlowVisitorKeys from './generated/ESTreeVisitorKeys';
import * as StripComponentSyntax from './estree/StripComponentSyntax';
import * as StripFlowTypesForBabel from './estree/StripFlowTypesForBabel';
import * as TransformESTreeToBabel from './babel/TransformESTreeToBabel';
import * as StripFlowTypes from './estree/StripFlowTypes';
const DEFAULTS = {
flow: 'detect',
};
function getOptions(options?: ParserOptions = {...DEFAULTS}) {
// Default to detecting whether to parse Flow syntax by the presence
// of an @flow pragma.
if (options.flow == null) {
options.flow = DEFAULTS.flow;
} else if (options.flow !== 'all' && options.flow !== 'detect') {
throw new Error('flow option must be "all" or "detect"');
}
if (options.sourceType === 'unambiguous') {
// Clear source type so that it will be detected from the contents of the file
delete options.sourceType;
} else if (
options.sourceType != null &&
options.sourceType !== 'script' &&
options.sourceType !== 'module'
) {
throw new Error(
'sourceType option must be "script", "module", or "unambiguous" if set',
);
}
if (options.enableExperimentalComponentSyntax == null) {
options.enableExperimentalComponentSyntax = true; // Enable by default
}
options.tokens = options.tokens === true;
options.allowReturnOutsideFunction =
options.allowReturnOutsideFunction === true;
return options;
}
declare function parse(
code: string,
opts: {...ParserOptions, babel: true},
): BabelFile;
// eslint-disable-next-line no-redeclare
declare function parse(
code: string,
opts?:
| {...ParserOptions, babel?: false | void}
| {...ParserOptions, babel: false},
): ESTreeProgram;
// eslint-disable-next-line no-redeclare
export function parse(
code: string,
opts?: ParserOptions,
): BabelFile | ESTreeProgram {
const options = getOptions(opts);
const ast = HermesParser.parse(code, options);
const estreeAdapter = new HermesToESTreeAdapter(options, code);
const estreeAST = estreeAdapter.transform(ast);
if (options.babel !== true) {
return estreeAST;
}
const loweredESTreeAST = [
StripComponentSyntax.transformProgram,
StripFlowTypesForBabel.transformProgram,
].reduce((ast, transform) => transform(ast, options), estreeAST);
return TransformESTreeToBabel.transformProgram(loweredESTreeAST, options);
}
export type {ParserOptions} from './ParserOptions';
export * from './ParserOptions';
export * from './traverse/SimpleTraverser';
export * from './transform/SimpleTransform';
export * from './traverse/getVisitorKeys';
export {FlowVisitorKeys};
export * as astArrayMutationHelpers from './transform/astArrayMutationHelpers';
export * as astNodeMutationHelpers from './transform/astNodeMutationHelpers';
export {default as mutateESTreeASTForPrettier} from './utils/mutateESTreeASTForPrettier';
const Transforms = {
stripComponentSyntax: StripComponentSyntax.transformProgram,
stripFlowTypesForBabel: StripFlowTypesForBabel.transformProgram,
stripFlowTypes: StripFlowTypes.transformProgram,
};
export {Transforms};

View File

@@ -0,0 +1,120 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SimpleTransform = void 0;
var _SimpleTraverser = require("../traverse/SimpleTraverser");
var _astNodeMutationHelpers = require("./astNodeMutationHelpers");
function setParentPointer(node, parent) {
if (parent != null) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
}
}
/**
* A simple class to recursively tranform AST trees.
*/
class SimpleTransform {
/**
* Transform the given AST tree.
* @param rootNode The root node to traverse.
* @param options The option object.
* @return The modified rootNode or a new node if the rootNode was transformed directly.
*/
transform(rootNode, options) {
let resultRootNode = rootNode;
_SimpleTraverser.SimpleTraverser.traverse(rootNode, {
enter: (node, parent) => {
// Ensure the parent pointers are correctly set before entering the node.
setParentPointer(node, parent);
const resultNode = options.transform(node);
if (resultNode !== node) {
let traversedResultNode = null;
if (resultNode != null) {
// Ensure the new node has the correct parent pointers before recursing again.
setParentPointer(resultNode, parent);
traversedResultNode = this.transform(resultNode, options);
}
if (parent == null) {
if (node !== rootNode) {
throw new Error('SimpleTransform infra error: Parent not set on non root node, this should not be possible');
}
resultRootNode = traversedResultNode;
} else if (traversedResultNode == null) {
(0, _astNodeMutationHelpers.removeNodeOnParent)(node, parent, options.visitorKeys);
} else {
(0, _astNodeMutationHelpers.replaceNodeOnParent)(node, parent, traversedResultNode, options.visitorKeys);
setParentPointer(traversedResultNode, parent);
}
throw _SimpleTraverser.SimpleTraverser.Skip;
}
},
leave(_node) {},
visitorKeys: options.visitorKeys
});
return resultRootNode;
}
/**
* Transform the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static transform(node, options) {
return new SimpleTransform().transform(node, options);
}
static transformProgram(program, options) {
const result = SimpleTransform.transform(program, options);
if ((result == null ? void 0 : result.type) === 'Program') {
return result;
}
throw new Error('SimpleTransform.transformProgram: Expected program node.');
}
/**
* Return a new AST node with the given properties overrided if needed.
*
* This function takes care to only create new nodes when needed. Referential equality of nodes
* is important as its used to know if a node should be re-traversed.
*
* @param node The base AST node.
* @param overrideProps New properties to apply to the node.
* @return Either the orginal node if the properties matched the existing node or a new node with
* the new properties.
*/
static nodeWith(node, overrideProps, visitorKeys) {
return (0, _astNodeMutationHelpers.nodeWith)(node, overrideProps, visitorKeys);
}
}
exports.SimpleTransform = SimpleTransform;

View File

@@ -0,0 +1,143 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {VisitorKeysType} from '../traverse/getVisitorKeys';
import type {ESNode, Program} from 'hermes-estree';
import {SimpleTraverser} from '../traverse/SimpleTraverser';
import {
nodeWith,
removeNodeOnParent,
replaceNodeOnParent,
} from './astNodeMutationHelpers';
/**
* Transform callback
* @param node The node we are visiting
* @returns
* - return input node, signals no changes were made will continue to the next node.
* - return new node, the old node will be replaced in the AST. The new node and its
* children are then traversed.
* - return null, signals the node should be deleted from the AST.
*/
export type TransformCallback = (node: ESNode) => ESNode | null;
export type TransformOptions = $ReadOnly<{
/** The callback function which is called on entering each node. */
transform: TransformCallback,
/** The set of visitor keys to use for traversal. Defaults to the Flow ESTree visitor keys */
visitorKeys?: ?VisitorKeysType,
}>;
function setParentPointer(node: ESNode, parent: ?ESNode): void {
if (parent != null) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
}
}
/**
* A simple class to recursively tranform AST trees.
*/
export class SimpleTransform {
/**
* Transform the given AST tree.
* @param rootNode The root node to traverse.
* @param options The option object.
* @return The modified rootNode or a new node if the rootNode was transformed directly.
*/
transform(rootNode: ESNode, options: TransformOptions): ESNode | null {
let resultRootNode: ESNode | null = rootNode;
SimpleTraverser.traverse(rootNode, {
enter: (node: ESNode, parent: ?ESNode) => {
// Ensure the parent pointers are correctly set before entering the node.
setParentPointer(node, parent);
const resultNode = options.transform(node);
if (resultNode !== node) {
let traversedResultNode = null;
if (resultNode != null) {
// Ensure the new node has the correct parent pointers before recursing again.
setParentPointer(resultNode, parent);
traversedResultNode = this.transform(resultNode, options);
}
if (parent == null) {
if (node !== rootNode) {
throw new Error(
'SimpleTransform infra error: Parent not set on non root node, this should not be possible',
);
}
resultRootNode = traversedResultNode;
} else if (traversedResultNode == null) {
removeNodeOnParent(node, parent, options.visitorKeys);
} else {
replaceNodeOnParent(
node,
parent,
traversedResultNode,
options.visitorKeys,
);
setParentPointer(traversedResultNode, parent);
}
throw SimpleTraverser.Skip;
}
},
leave(_node: ESNode) {},
visitorKeys: options.visitorKeys,
});
return resultRootNode;
}
/**
* Transform the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static transform(node: ESNode, options: TransformOptions): ESNode | null {
return new SimpleTransform().transform(node, options);
}
static transformProgram(
program: Program,
options: TransformOptions,
): Program {
const result = SimpleTransform.transform(program, options);
if (result?.type === 'Program') {
return result;
}
throw new Error('SimpleTransform.transformProgram: Expected program node.');
}
/**
* Return a new AST node with the given properties overrided if needed.
*
* This function takes care to only create new nodes when needed. Referential equality of nodes
* is important as its used to know if a node should be re-traversed.
*
* @param node The base AST node.
* @param overrideProps New properties to apply to the node.
* @return Either the orginal node if the properties matched the existing node or a new node with
* the new properties.
*/
static nodeWith<T: ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: VisitorKeysType,
): T {
return nodeWith<T>(node, overrideProps, visitorKeys);
}
}

View File

@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.arrayIsEqual = arrayIsEqual;
exports.insertInArray = insertInArray;
exports.removeFromArray = removeFromArray;
exports.replaceInArray = replaceInArray;
/**
* 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 assertArrayBounds(array, index) {
if (index < 0 || index >= array.length) {
throw new Error(`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`);
}
}
function arrayIsEqual(a1, a2) {
if (a1 === a2) {
return true;
}
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
function insertInArray(array, index, elements) {
if (index === array.length) {
// Support the insert at end of array case.
return array.concat(elements);
}
assertArrayBounds(array, index);
return array.slice(0, index).concat(elements).concat(array.slice(index));
}
function removeFromArray(array, index) {
assertArrayBounds(array, index);
return [...array.slice(0, index), ...array.slice(index + 1)];
}
function replaceInArray(array, index, elements) {
assertArrayBounds(array, index);
return array.slice(0, index).concat(elements).concat(array.slice(index + 1));
}

View File

@@ -0,0 +1,71 @@
/**
* 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.
*
* @flow strict
* @format
*/
function assertArrayBounds<T>(array: $ReadOnlyArray<T>, index: number): void {
if (index < 0 || index >= array.length) {
throw new Error(
`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`,
);
}
}
export function arrayIsEqual(
a1: $ReadOnlyArray<mixed>,
a2: $ReadOnlyArray<mixed>,
): boolean {
if (a1 === a2) {
return true;
}
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
export function insertInArray<T>(
array: $ReadOnlyArray<T>,
index: number,
elements: $ReadOnlyArray<T>,
): Array<T> {
if (index === array.length) {
// Support the insert at end of array case.
return array.concat(elements);
}
assertArrayBounds(array, index);
return array.slice(0, index).concat(elements).concat(array.slice(index));
}
export function removeFromArray<T>(
array: $ReadOnlyArray<T>,
index: number,
): Array<T> {
assertArrayBounds(array, index);
return [...array.slice(0, index), ...array.slice(index + 1)];
}
export function replaceInArray<T>(
array: $ReadOnlyArray<T>,
index: number,
elements: $ReadOnlyArray<T>,
): Array<T> {
assertArrayBounds(array, index);
return array
.slice(0, index)
.concat(elements)
.concat(array.slice(index + 1));
}

View File

@@ -0,0 +1,195 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.deepCloneNode = deepCloneNode;
exports.nodeWith = nodeWith;
exports.removeNodeOnParent = removeNodeOnParent;
exports.replaceNodeOnParent = replaceNodeOnParent;
exports.setParentPointersInDirectChildren = setParentPointersInDirectChildren;
exports.shallowCloneNode = shallowCloneNode;
exports.updateAllParentPointers = updateAllParentPointers;
var _astArrayMutationHelpers = require("./astArrayMutationHelpers");
var _getVisitorKeys = require("../traverse/getVisitorKeys");
var _SimpleTraverser = require("../traverse/SimpleTraverser");
function getParentKey(target, parent, visitorKeys) {
if (parent == null) {
throw new Error(`Expected parent node to be set on "${target.type}"`);
}
for (const key of (0, _getVisitorKeys.getVisitorKeys)(parent, visitorKeys)) {
if ((0, _getVisitorKeys.isNode)( // $FlowExpectedError[prop-missing]
parent[key])) {
if (parent[key] === target) {
return {
type: 'single',
node: parent,
key
};
}
} else if (Array.isArray(parent[key])) {
for (let i = 0; i < parent[key].length; i += 1) {
// $FlowExpectedError[invalid-tuple-index]
const current = parent[key][i];
if (current === target) {
return {
type: 'array',
node: parent,
key,
targetIndex: i
};
}
}
}
} // this shouldn't happen ever
throw new Error(`Expected to find the ${target.type} as a direct child of the ${parent.type}.`);
}
/**
* Replace a node with a new node within an AST (via the parent pointer).
*/
function replaceNodeOnParent(originalNode, originalNodeParent, nodeToReplaceWith, visitorKeys) {
const replacementParent = getParentKey(originalNode, originalNodeParent, visitorKeys);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = (0, _astArrayMutationHelpers.replaceInArray)( // $FlowExpectedError[prop-missing]
parent[replacementParent.key], replacementParent.targetIndex, [nodeToReplaceWith]);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = nodeToReplaceWith;
}
}
/**
* Remove a node from the AST its connected to (via the parent pointer).
*/
function removeNodeOnParent(originalNode, originalNodeParent, visitorKeys) {
const replacementParent = getParentKey(originalNode, originalNodeParent, visitorKeys);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = (0, _astArrayMutationHelpers.removeFromArray)( // $FlowExpectedError[prop-missing]
parent[replacementParent.key], replacementParent.targetIndex);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = null;
}
}
/**
* Corrects the parent pointers in direct children of the given node.
*/
function setParentPointersInDirectChildren(node, visitorKeys) {
for (const key of (0, _getVisitorKeys.getVisitorKeys)(node, visitorKeys)) {
if ((0, _getVisitorKeys.isNode)(node[key])) {
// $FlowExpectedError[cannot-write]
node[key].parent = node;
} else if (Array.isArray(node[key])) {
for (const child of node[key]) {
child.parent = node;
}
}
}
}
/**
* Traverses the entire subtree to ensure the parent pointers are set correctly.
*/
function updateAllParentPointers(node, visitorKeys) {
_SimpleTraverser.SimpleTraverser.traverse(node, {
enter(node, parent) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
},
leave() {},
visitorKeys
});
}
/**
* Clone node and add new props.
*
* This will only create a new object if the overrides actually result in a change.
*/
function nodeWith(node, overrideProps, visitorKeys) {
// Check if this will actually result in a change, maintaining referential equality is important.
const willBeUnchanged = Object.entries(overrideProps).every(([key, value]) => {
const node_ = node;
if (Array.isArray(value)) {
return Array.isArray(node_[key]) ? (0, _astArrayMutationHelpers.arrayIsEqual)(node_[key], value) : false;
}
return node_[key] === value;
});
if (willBeUnchanged) {
return node;
} // Create new node.
// $FlowExpectedError[cannot-spread-interface]
const newNode = { ...node,
...overrideProps
}; // Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Shallow clones node, providing a new reference for an existing node.
*/
function shallowCloneNode(node, visitorKeys) {
// $FlowExpectedError[cannot-spread-interface]
const newNode = { ...node
}; // Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Deeply clones node and its entire tree.
*/
function deepCloneNode(node, visitorKeys) {
const clone = JSON.parse(JSON.stringify(node, (key, value) => {
// null out parent pointers
if (key === 'parent') {
return undefined;
}
return value;
}));
updateAllParentPointers(clone, visitorKeys);
return clone;
}

View File

@@ -0,0 +1,238 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode} from 'hermes-estree';
import type {VisitorKeysType} from '../traverse/getVisitorKeys';
import {
arrayIsEqual,
removeFromArray,
replaceInArray,
} from './astArrayMutationHelpers';
import {getVisitorKeys, isNode} from '../traverse/getVisitorKeys';
import {SimpleTraverser} from '../traverse/SimpleTraverser';
function getParentKey(
target: ESNode,
parent: ESNode,
visitorKeys?: ?VisitorKeysType,
): $ReadOnly<
| {
type: 'single',
node: ESNode,
key: string,
}
| {
type: 'array',
node: ESNode,
key: string,
targetIndex: number,
},
> {
if (parent == null) {
throw new Error(`Expected parent node to be set on "${target.type}"`);
}
for (const key of getVisitorKeys(parent, visitorKeys)) {
if (
isNode(
// $FlowExpectedError[prop-missing]
parent[key],
)
) {
if (parent[key] === target) {
return {type: 'single', node: parent, key};
}
} else if (Array.isArray(parent[key])) {
for (let i = 0; i < parent[key].length; i += 1) {
// $FlowExpectedError[invalid-tuple-index]
const current = parent[key][i];
if (current === target) {
return {type: 'array', node: parent, key, targetIndex: i};
}
}
}
}
// this shouldn't happen ever
throw new Error(
`Expected to find the ${target.type} as a direct child of the ${parent.type}.`,
);
}
/**
* Replace a node with a new node within an AST (via the parent pointer).
*/
export function replaceNodeOnParent(
originalNode: ESNode,
originalNodeParent: ESNode,
nodeToReplaceWith: ESNode,
visitorKeys?: ?VisitorKeysType,
): void {
const replacementParent = getParentKey(
originalNode,
originalNodeParent,
visitorKeys,
);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = replaceInArray(
// $FlowExpectedError[prop-missing]
parent[replacementParent.key],
replacementParent.targetIndex,
[nodeToReplaceWith],
);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = nodeToReplaceWith;
}
}
/**
* Remove a node from the AST its connected to (via the parent pointer).
*/
export function removeNodeOnParent(
originalNode: ESNode,
originalNodeParent: ESNode,
visitorKeys?: ?VisitorKeysType,
): void {
const replacementParent = getParentKey(
originalNode,
originalNodeParent,
visitorKeys,
);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = removeFromArray(
// $FlowExpectedError[prop-missing]
parent[replacementParent.key],
replacementParent.targetIndex,
);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = null;
}
}
/**
* Corrects the parent pointers in direct children of the given node.
*/
export function setParentPointersInDirectChildren(
node: ESNode,
visitorKeys?: ?VisitorKeysType,
): void {
for (const key: $FlowFixMe of getVisitorKeys(node, visitorKeys)) {
if (isNode(node[key])) {
// $FlowExpectedError[cannot-write]
node[key].parent = node;
} else if (Array.isArray(node[key])) {
for (const child of node[key]) {
child.parent = node;
}
}
}
}
/**
* Traverses the entire subtree to ensure the parent pointers are set correctly.
*/
export function updateAllParentPointers(
node: ESNode,
visitorKeys?: ?VisitorKeysType,
) {
SimpleTraverser.traverse(node, {
enter(node, parent) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
},
leave() {},
visitorKeys,
});
}
/**
* Clone node and add new props.
*
* This will only create a new object if the overrides actually result in a change.
*/
export function nodeWith<T: ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: ?VisitorKeysType,
): T {
// Check if this will actually result in a change, maintaining referential equality is important.
const willBeUnchanged = Object.entries(overrideProps).every(
([key, value]) => {
const node_: $FlowFixMe = node;
if (Array.isArray(value)) {
return Array.isArray(node_[key])
? arrayIsEqual(node_[key], value)
: false;
}
return node_[key] === value;
},
);
if (willBeUnchanged) {
return node;
}
// Create new node.
// $FlowExpectedError[cannot-spread-interface]
const newNode: T = {
...node,
...overrideProps,
};
// Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Shallow clones node, providing a new reference for an existing node.
*/
export function shallowCloneNode<T: ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
): T {
// $FlowExpectedError[cannot-spread-interface]
const newNode: T = {...node};
// Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Deeply clones node and its entire tree.
*/
export function deepCloneNode<T: ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
): T {
const clone: T = JSON.parse(
JSON.stringify(node, (key, value) => {
// null out parent pointers
if (key === 'parent') {
return undefined;
}
return value;
}),
);
updateAllParentPointers(clone, visitorKeys);
return clone;
}

View File

@@ -0,0 +1,137 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SimpleTraverserSkip = exports.SimpleTraverserBreak = exports.SimpleTraverser = void 0;
var _getVisitorKeys = require("./getVisitorKeys");
/**
* Can be thrown within the traversal "enter" function to prevent the traverser
* from traversing the node any further, essentially culling the remainder of the
* AST branch
*/
const SimpleTraverserSkip = new Error();
/**
* Can be thrown at any point during the traversal to immediately stop traversal
* entirely.
*/
exports.SimpleTraverserSkip = SimpleTraverserSkip;
const SimpleTraverserBreak = new Error();
/**
* A very simple traverser class to traverse AST trees.
*/
exports.SimpleTraverserBreak = SimpleTraverserBreak;
class SimpleTraverser {
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
traverse(node, options) {
try {
this._traverse(node, null, options);
} catch (ex) {
if (ex === SimpleTraverserBreak) {
return;
}
throw ex;
}
}
/**
* Traverse the given AST tree recursively.
* @param node The current node.
* @param parent The parent node.
* @private
*/
_traverse(node, parent, options) {
if (!(0, _getVisitorKeys.isNode)(node)) {
return;
}
try {
options.enter(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
const keys = (0, _getVisitorKeys.getVisitorKeys)(node, options.visitorKeys);
for (const key of keys) {
const child = node[key];
if (Array.isArray(child)) {
for (let j = 0; j < child.length; ++j) {
this._traverse(child[j], node, options);
}
} else {
this._traverse(child, node, options);
}
}
try {
options.leave(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
}
/**
* Set useful contextual information onto the error object.
* @param ex The error object.
* @param node The current node.
* @private
*/
_setErrorContext(ex, node) {
// $FlowFixMe[prop-missing]
ex.currentNode = {
type: node.type,
range: node.range,
loc: node.loc
};
}
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static traverse(node, options) {
new SimpleTraverser().traverse(node, options);
}
}
exports.SimpleTraverser = SimpleTraverser;
SimpleTraverser.Break = SimpleTraverserBreak;
SimpleTraverser.Skip = SimpleTraverserSkip;

View File

@@ -0,0 +1,133 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {VisitorKeysType} from './getVisitorKeys';
import type {ESNode} from 'hermes-estree';
import {getVisitorKeys, isNode} from './getVisitorKeys';
export type TraverserCallback = (node: ESNode, parent: ?ESNode) => void;
export type TraverserOptions = $ReadOnly<{
/** The callback function which is called on entering each node. */
enter: TraverserCallback,
/** The callback function which is called on leaving each node. */
leave: TraverserCallback,
/** The set of visitor keys to use for traversal. Defaults to the Flow ESTree visitor keys */
visitorKeys?: ?VisitorKeysType,
}>;
/**
* Can be thrown within the traversal "enter" function to prevent the traverser
* from traversing the node any further, essentially culling the remainder of the
* AST branch
*/
export const SimpleTraverserSkip: Error = new Error();
/**
* Can be thrown at any point during the traversal to immediately stop traversal
* entirely.
*/
export const SimpleTraverserBreak: Error = new Error();
/**
* A very simple traverser class to traverse AST trees.
*/
export class SimpleTraverser {
static Break: Error = SimpleTraverserBreak;
static Skip: Error = SimpleTraverserSkip;
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
traverse(node: ESNode, options: TraverserOptions): void {
try {
this._traverse(node, null, options);
} catch (ex) {
if (ex === SimpleTraverserBreak) {
return;
}
throw ex;
}
}
/**
* Traverse the given AST tree recursively.
* @param node The current node.
* @param parent The parent node.
* @private
*/
_traverse(node: ESNode, parent: ?ESNode, options: TraverserOptions): void {
if (!isNode(node)) {
return;
}
try {
options.enter(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
const keys = getVisitorKeys(node, options.visitorKeys);
for (const key of keys) {
const child: ESNode | $ReadOnlyArray<ESNode> = (node[
(key: $FlowFixMe)
]: $FlowFixMe);
if (Array.isArray(child)) {
for (let j = 0; j < child.length; ++j) {
this._traverse(child[j], node, options);
}
} else {
this._traverse(child, node, options);
}
}
try {
options.leave(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
}
/**
* Set useful contextual information onto the error object.
* @param ex The error object.
* @param node The current node.
* @private
*/
_setErrorContext(ex: Error, node: ESNode): void {
// $FlowFixMe[prop-missing]
ex.currentNode = {
type: node.type,
range: node.range,
loc: node.loc,
};
}
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static traverse(node: ESNode, options: TraverserOptions) {
new SimpleTraverser().traverse(node, options);
}
}

View File

@@ -0,0 +1,37 @@
/**
* 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.
*
*
* @noformat
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getVisitorKeys = getVisitorKeys;
exports.isNode = isNode;
var _ESTreeVisitorKeys = _interopRequireDefault(require("../generated/ESTreeVisitorKeys"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function isNode(thing)
/*: implies thing is {+[string]: mixed} */
{
return typeof thing === 'object' && thing != null && typeof thing.type === 'string';
}
function getVisitorKeys(node, visitorKeys) {
const keys = (visitorKeys != null ? visitorKeys : _ESTreeVisitorKeys.default)[node.type];
if (keys == null) {
throw new Error(`No visitor keys found for node type "${node.type}".`);
} // $FlowExpectedError[prop-missing]
return keys;
}

View File

@@ -0,0 +1,36 @@
/**
* 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.
*
* @flow strict
* @noformat
*/
'use strict';
import type {ESNode} from 'hermes-estree';
import type {VisitorKeys as VisitorKeysType} from '../generated/ESTreeVisitorKeys';
import FlowVisitorKeys from '../generated/ESTreeVisitorKeys';
export function isNode(thing: mixed) /*: implies thing is {+[string]: mixed} */ {
return (
typeof thing === 'object' && thing != null && typeof thing.type === 'string'
);
}
export type {VisitorKeysType};
export function getVisitorKeys<T: ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
): $ReadOnlyArray<$Keys<T>> {
const keys = (visitorKeys ?? FlowVisitorKeys)[node.type];
if (keys == null) {
throw new Error(`No visitor keys found for node type "${node.type}".`);
}
// $FlowExpectedError[prop-missing]
return keys;
}

View File

@@ -0,0 +1,25 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createSyntaxError = createSyntaxError;
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;
}

View File

@@ -0,0 +1,24 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode} from 'hermes-estree';
export function createSyntaxError(node: ESNode, err: string): SyntaxError {
const syntaxError = new SyntaxError(err);
// $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: node.loc.start.line,
column: node.loc.start.column,
};
return syntaxError;
}

View File

@@ -0,0 +1,127 @@
/**
* 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
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = mutate;
var _SimpleTransform = require("../transform/SimpleTransform");
// https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
function transformChainExpression(node, comments) {
if (comments != null) {
var _node$comments;
// $FlowExpectedError[prop-missing]
const joinedComments = comments.concat((_node$comments = node.comments) != null ? _node$comments : []); // $FlowExpectedError[prop-missing]
// $FlowFixMe[cannot-write]
node.comments = joinedComments;
}
switch (node.type) {
case 'CallExpression':
// $FlowExpectedError[cannot-spread-interface]
return { ...node,
type: 'OptionalCallExpression',
callee: transformChainExpression(node.callee)
};
case 'MemberExpression':
// $FlowExpectedError[cannot-spread-interface]
return { ...node,
type: 'OptionalMemberExpression',
object: transformChainExpression(node.object)
};
// No default
}
return node;
}
function mutate(rootNode, visitorKeys) {
// Since we don't return the result of `transform` we need to be careful not to replace the Program root node.
_SimpleTransform.SimpleTransform.transform(rootNode, {
transform(node) {
// prettier fully expects the parent pointers are NOT set and
// certain cases can crash due to prettier infinite-looping
// whilst naively traversing the parent property
// https://github.com/prettier/prettier/issues/11793
// Note: Only needed for prettier V2, this is supported in V3
if (node.parent) {
// $FlowExpectedError[cannot-write]
delete node.parent;
} // prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
// so we have to apply their transform to our AST so it can actually format it.
// Note: Only needed for prettier V2, this is supported in V3
if (node.type === 'ChainExpression') {
// $FlowFixMe[prop-missing]
return transformChainExpression(node.expression, node == null ? void 0 : node.comments);
} // Prettier currently relies on comparing the `node` vs `node.value` start positions to know if an
// `ObjectTypeProperty` is a method or not (instead of using the `node.method` boolean). To correctly print
// the node when its not a method we need the start position to be different from the `node.value`s start
// position.
if (node.type === 'ObjectTypeProperty') {
if (node.method === false && node.kind === 'init' && node.range[0] === 1 && node.value.range[0] === 1) {
// $FlowExpectedError[cannot-write]
// $FlowExpectedError[cannot-spread-interface]
node.value = { ...node.value,
range: [2, node.value.range[1]]
};
}
return node;
} // Prettier currently relies on comparing the the start positions to know if the import/export specifier should have a
// rename (eg `Name` vs `Name as Name`) when the name is exactly the same
// So we need to ensure that the range is always the same to avoid the useless code printing
if (node.type === 'ImportSpecifier') {
if (node.local.name === node.imported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
} // $FlowExpectedError[cannot-write]
node.imported.range = [...node.local.range];
}
return node;
}
if (node.type === 'ExportSpecifier') {
if (node.local.name === node.exported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
} // $FlowExpectedError[cannot-write]
node.exported.range = [...node.local.range];
}
return node;
}
return node;
},
visitorKeys
});
}

View File

@@ -0,0 +1,130 @@
/**
* 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.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode, Program, Comment} from 'hermes-estree';
import type {VisitorKeysType} from '../traverse/getVisitorKeys';
import {SimpleTransform} from '../transform/SimpleTransform';
// https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
function transformChainExpression(
node: ESNode,
comments: ?$ReadOnlyArray<Comment>,
): ESNode {
if (comments != null) {
// $FlowExpectedError[prop-missing]
const joinedComments = comments.concat(node.comments ?? []);
// $FlowExpectedError[prop-missing]
// $FlowFixMe[cannot-write]
node.comments = joinedComments;
}
switch (node.type) {
case 'CallExpression':
// $FlowExpectedError[cannot-spread-interface]
return {
...node,
type: 'OptionalCallExpression',
callee: transformChainExpression(node.callee),
};
case 'MemberExpression':
// $FlowExpectedError[cannot-spread-interface]
return {
...node,
type: 'OptionalMemberExpression',
object: transformChainExpression(node.object),
};
// No default
}
return node;
}
export default function mutate(
rootNode: Program,
visitorKeys: ?VisitorKeysType,
): void {
// Since we don't return the result of `transform` we need to be careful not to replace the Program root node.
SimpleTransform.transform(rootNode, {
transform(node): ESNode | null {
// prettier fully expects the parent pointers are NOT set and
// certain cases can crash due to prettier infinite-looping
// whilst naively traversing the parent property
// https://github.com/prettier/prettier/issues/11793
// Note: Only needed for prettier V2, this is supported in V3
if (node.parent) {
// $FlowExpectedError[cannot-write]
delete node.parent;
}
// prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
// so we have to apply their transform to our AST so it can actually format it.
// Note: Only needed for prettier V2, this is supported in V3
if (node.type === 'ChainExpression') {
// $FlowFixMe[prop-missing]
return transformChainExpression(node.expression, node?.comments);
}
// Prettier currently relies on comparing the `node` vs `node.value` start positions to know if an
// `ObjectTypeProperty` is a method or not (instead of using the `node.method` boolean). To correctly print
// the node when its not a method we need the start position to be different from the `node.value`s start
// position.
if (node.type === 'ObjectTypeProperty') {
if (
node.method === false &&
node.kind === 'init' &&
node.range[0] === 1 &&
node.value.range[0] === 1
) {
// $FlowExpectedError[cannot-write]
// $FlowExpectedError[cannot-spread-interface]
node.value = {
...node.value,
range: [2, node.value.range[1]],
};
}
return node;
}
// Prettier currently relies on comparing the the start positions to know if the import/export specifier should have a
// rename (eg `Name` vs `Name as Name`) when the name is exactly the same
// So we need to ensure that the range is always the same to avoid the useless code printing
if (node.type === 'ImportSpecifier') {
if (node.local.name === node.imported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
}
// $FlowExpectedError[cannot-write]
node.imported.range = [...node.local.range];
}
return node;
}
if (node.type === 'ExportSpecifier') {
if (node.local.name === node.exported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
}
// $FlowExpectedError[cannot-write]
node.exported.range = [...node.local.range];
}
return node;
}
return node;
},
visitorKeys,
});
}

25
node_modules/hermes-parser/package.json generated vendored Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "hermes-parser",
"version": "0.25.1",
"description": "A JavaScript parser built from the Hermes engine",
"main": "dist/index.js",
"license": "MIT",
"repository": {
"type": "git",
"url": "git@github.com:facebook/hermes.git"
},
"dependencies": {
"hermes-estree": "0.25.1"
},
"devDependencies": {
"@babel/parser": "7.7.4",
"@babel/generator": "7.7.4",
"@babel/types": "7.7.4",
"espree": "9.3.2"
},
"files": [
"dist",
"LICENCE",
"README.md"
]
}