mirror of
https://github.com/hex248/ob248.com.git
synced 2026-02-08 10:43:38 +00:00
merge new into master
This commit is contained in:
863
node_modules/hermes-parser/dist/estree/StripComponentSyntax.js.flow
generated
vendored
Normal file
863
node_modules/hermes-parser/dist/estree/StripComponentSyntax.js.flow
generated
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user