mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-21 16:30:32 +00:00
158 lines
4.6 KiB
TypeScript
158 lines
4.6 KiB
TypeScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
|
|
import { namedTypes as t, visit } from 'ast-types';
|
|
const {
|
|
isExportsOrModuleAssignment,
|
|
isReactComponentClass,
|
|
isReactCreateClassCall,
|
|
isReactForwardRefCall,
|
|
isStatelessComponent,
|
|
normalizeClassDefinition,
|
|
resolveExportDeclaration,
|
|
resolveToValue,
|
|
} = require('react-docgen').utils;
|
|
import resolveHOC from './resolveHOC';
|
|
import resolveIIFE from './resolveIIFE';
|
|
import resolveTranspiledClass from './resolveTranspiledClass';
|
|
|
|
const ERROR_MULTIPLE_DEFINITIONS =
|
|
'Multiple exported component definitions found.';
|
|
|
|
function ignore() {
|
|
return false;
|
|
}
|
|
|
|
function isComponentDefinition(path: any) {
|
|
return (
|
|
isReactCreateClassCall(path) ||
|
|
isReactComponentClass(path) ||
|
|
isStatelessComponent(path) ||
|
|
isReactForwardRefCall(path)
|
|
);
|
|
}
|
|
|
|
function resolveDefinition(definition: any) {
|
|
if (isReactCreateClassCall(definition)) {
|
|
// return argument
|
|
const resolvedPath = resolveToValue(definition.get('arguments', 0));
|
|
if (t.ObjectExpression.check(resolvedPath.node)) {
|
|
return resolvedPath;
|
|
}
|
|
} else if (isReactComponentClass(definition)) {
|
|
normalizeClassDefinition(definition);
|
|
return definition;
|
|
} else if (
|
|
isStatelessComponent(definition) ||
|
|
isReactForwardRefCall(definition)
|
|
) {
|
|
return definition;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Given an AST, this function tries to find the exported component definition.
|
|
*
|
|
* The component definition is either the ObjectExpression passed to
|
|
* `React.createClass` or a `class` definition extending `React.Component` or
|
|
* having a `render()` method.
|
|
*
|
|
* If a definition is part of the following statements, it is considered to be
|
|
* exported:
|
|
*
|
|
* modules.exports = Definition;
|
|
* exports.foo = Definition;
|
|
* export default Definition;
|
|
* export var Definition = ...;
|
|
*/
|
|
export default function findExportedComponentDefinition(ast: any) {
|
|
let foundDefinition: any;
|
|
|
|
function exportDeclaration(path: any) {
|
|
const definitions = resolveExportDeclaration(path).reduce(
|
|
(acc: any, definition: any) => {
|
|
if (isComponentDefinition(definition)) {
|
|
acc.push(definition);
|
|
} else {
|
|
definition = resolveToValue(resolveIIFE(definition));
|
|
if (!isComponentDefinition(definition)) {
|
|
definition = resolveTranspiledClass(definition);
|
|
// console.log("path");
|
|
}
|
|
const resolved = resolveToValue(resolveHOC(definition));
|
|
if (isComponentDefinition(resolved)) {
|
|
acc.push(resolved);
|
|
}
|
|
}
|
|
return acc;
|
|
},
|
|
[],
|
|
);
|
|
|
|
if (definitions.length === 0) {
|
|
return false;
|
|
}
|
|
if (definitions.length > 1 || foundDefinition) {
|
|
// If a file exports multiple components, ... complain!
|
|
throw new Error(ERROR_MULTIPLE_DEFINITIONS);
|
|
}
|
|
foundDefinition = resolveDefinition(definitions[0]);
|
|
return false;
|
|
}
|
|
|
|
visit(ast, {
|
|
visitFunctionDeclaration: ignore,
|
|
visitFunctionExpression: ignore,
|
|
visitClassDeclaration: ignore,
|
|
visitClassExpression: ignore,
|
|
visitIfStatement: ignore,
|
|
visitWithStatement: ignore,
|
|
visitSwitchStatement: ignore,
|
|
visitWhileStatement: ignore,
|
|
visitDoWhileStatement: ignore,
|
|
visitForStatement: ignore,
|
|
visitForInStatement: ignore,
|
|
visitForOfStatement: ignore,
|
|
visitImportDeclaration: ignore,
|
|
|
|
visitExportNamedDeclaration: exportDeclaration,
|
|
visitExportDefaultDeclaration: exportDeclaration,
|
|
|
|
visitAssignmentExpression(path: any) {
|
|
// Ignore anything that is not `exports.X = ...;` or
|
|
// `module.exports = ...;`
|
|
if (!isExportsOrModuleAssignment(path)) {
|
|
return false;
|
|
}
|
|
// Resolve the value of the right hand side. It should resolve to a call
|
|
// expression, something like React.createClass
|
|
path = resolveToValue(path.get('right'));
|
|
if (!isComponentDefinition(path)) {
|
|
// path = resolveToValue(resolveHOC(path));
|
|
// if (!isComponentDefinition(path)) {
|
|
path = resolveToValue(resolveIIFE(path));
|
|
if (!isComponentDefinition(path)) {
|
|
path = resolveTranspiledClass(path);
|
|
if (!isComponentDefinition(path)) {
|
|
path = resolveToValue(resolveHOC(path));
|
|
}
|
|
}
|
|
}
|
|
if (foundDefinition) {
|
|
// If a file exports multiple components, ... complain!
|
|
throw new Error(ERROR_MULTIPLE_DEFINITIONS);
|
|
}
|
|
foundDefinition = resolveDefinition(path);
|
|
return false;
|
|
},
|
|
});
|
|
|
|
return foundDefinition;
|
|
}
|