Skip to content

Commit b52333b

Browse files
committed
Try to use function declaration
1 parent b986ac3 commit b52333b

File tree

3 files changed

+84
-53
lines changed

3 files changed

+84
-53
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Unreleased
22

3+
- Fixed
4+
- Use function declaration instead of function expression when possible
5+
36
## 0.1.6
47

58
- Added

src/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ describe("react-declassify", () => {
111111
text: T;
112112
};
113113
114-
const C = function C<T>(props: Props<T>): React.ReactElement | null {
114+
function C<T>(props: Props<T>): React.ReactElement | null {
115115
return <div>Hello, {props.text}!</div>;
116-
};
116+
}
117117
`;
118118
expect(transform(input, { ts: true })).toBe(output);
119119
});

src/index.ts

Lines changed: 79 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ArrowFunctionExpression, ClassMethod, ClassPrivateMethod, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, ObjectMethod, Pattern, RestElement, Statement, TSEntityName, TSType, TSTypeAnnotation } from "@babel/types";
1+
import type { ArrowFunctionExpression, ClassMethod, ClassPrivateMethod, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, ObjectMethod, Pattern, RestElement, Statement, TSEntityName, TSType, TSTypeAnnotation, TSTypeParameterDeclaration, VariableDeclaration } from "@babel/types";
22
import type { NodePath, PluginObj, PluginPass } from "@babel/core";
33
import { assignReturnType, assignTypeAnnotation, assignTypeArguments, assignTypeParameters, importName, isTS, nonNullPath } from "./utils.js";
44
import { AnalysisError, analyzeBody, analyzeHead, ComponentBody, ComponentHead, needsProps, LibRef } from "./analysis.js";
@@ -22,18 +22,15 @@ export default function plugin(babel: typeof import("@babel/core")): PluginObj<P
2222
const body = analyzeBody(path, head);
2323
const { funcNode, typeNode } = transformClass(head, body, { ts }, babel);
2424
if (path.node.id) {
25+
// Necessary to avoid false error regarding duplicate declaration.
26+
path.scope.removeBinding(path.node.id.name);
2527
declPath.replaceWithMultiple([
26-
t.variableDeclaration("const", [
27-
t.variableDeclarator(
28-
typeNode
29-
? assignTypeAnnotation(
30-
t.cloneNode(path.node.id),
31-
t.tsTypeAnnotation(typeNode),
32-
)
33-
: t.cloneNode(path.node.id),
34-
funcNode,
35-
)
36-
]),
28+
constDeclaration(
29+
babel,
30+
t.cloneNode(path.node.id),
31+
funcNode,
32+
typeNode ? t.tsTypeAnnotation(typeNode) : undefined
33+
),
3734
t.exportDefaultDeclaration(
3835
t.cloneNode(path.node.id)
3936
)
@@ -52,17 +49,16 @@ export default function plugin(babel: typeof import("@babel/core")): PluginObj<P
5249
try {
5350
const body = analyzeBody(path, head);
5451
const { funcNode, typeNode } = transformClass(head, body, { ts }, babel);
55-
path.replaceWith(t.variableDeclaration("const", [
56-
t.variableDeclarator(
57-
typeNode
58-
? assignTypeAnnotation(
59-
t.cloneNode(path.node.id),
60-
t.tsTypeAnnotation(typeNode),
61-
)
62-
: t.cloneNode(path.node.id),
52+
// Necessary to avoid false error regarding duplicate declaration.
53+
path.scope.removeBinding(path.node.id.name);
54+
path.replaceWith(
55+
constDeclaration(
56+
babel,
57+
t.cloneNode(path.node.id),
6358
funcNode,
59+
typeNode ? t.tsTypeAnnotation(typeNode) : undefined
6460
)
65-
]));
61+
);
6662
} catch (e) {
6763
if (!(e instanceof AnalysisError)) {
6864
throw e;
@@ -437,19 +433,22 @@ function functionDeclarationFrom(
437433
name?: Identifier | null
438434
) {
439435
const { types: t } = babel;
440-
return assignReturnType(
441-
t.functionDeclaration(
442-
name ?? functionName(node),
443-
node.params as (Identifier | RestElement | Pattern)[],
444-
node.body.type === "BlockStatement"
445-
? node.body
446-
: t.blockStatement([
447-
t.returnStatement(node.body)
448-
]),
449-
node.generator,
450-
node.async,
436+
return assignTypeParameters(
437+
assignReturnType(
438+
t.functionDeclaration(
439+
name ?? functionName(node),
440+
node.params as (Identifier | RestElement | Pattern)[],
441+
node.body.type === "BlockStatement"
442+
? node.body
443+
: t.blockStatement([
444+
t.returnStatement(node.body)
445+
]),
446+
node.generator,
447+
node.async,
448+
),
449+
node.returnType
451450
),
452-
node.returnType
451+
node.typeParameters as TSTypeParameterDeclaration | null | undefined
453452
);
454453
}
455454

@@ -459,19 +458,22 @@ function functionExpressionFrom(
459458
name?: Identifier | null
460459
) {
461460
const { types: t } = babel;
462-
return assignReturnType(
463-
t.functionExpression(
464-
name ?? functionName(node),
465-
node.params as (Identifier | RestElement | Pattern)[],
466-
node.body.type === "BlockStatement"
467-
? node.body
468-
: t.blockStatement([
469-
t.returnStatement(node.body)
470-
]),
471-
node.generator,
472-
node.async,
461+
return assignTypeParameters(
462+
assignReturnType(
463+
t.functionExpression(
464+
name ?? functionName(node),
465+
node.params as (Identifier | RestElement | Pattern)[],
466+
node.body.type === "BlockStatement"
467+
? node.body
468+
: t.blockStatement([
469+
t.returnStatement(node.body)
470+
]),
471+
node.generator,
472+
node.async,
473+
),
474+
node.returnType
473475
),
474-
node.returnType
476+
node.typeParameters as TSTypeParameterDeclaration | null | undefined
475477
);
476478
}
477479

@@ -480,13 +482,39 @@ function arrowFunctionExpressionFrom(
480482
node: FunctionLike
481483
) {
482484
const { types: t } = babel;
483-
return assignReturnType(
484-
t.arrowFunctionExpression(
485-
node.params as (Identifier | RestElement | Pattern)[],
486-
node.body,
487-
node.async,
485+
return assignTypeParameters(
486+
assignReturnType(
487+
t.arrowFunctionExpression(
488+
node.params as (Identifier | RestElement | Pattern)[],
489+
node.body,
490+
node.async,
491+
),
492+
node.returnType
488493
),
489-
node.returnType
494+
node.typeParameters as TSTypeParameterDeclaration | null | undefined
495+
);
496+
}
497+
498+
function constDeclaration(
499+
babel: typeof import("@babel/core"),
500+
id: Identifier,
501+
init: Expression,
502+
typeAnnotation?: TSTypeAnnotation,
503+
): VariableDeclaration | FunctionDeclaration {
504+
const { types: t } = babel;
505+
if (
506+
init.type === "FunctionExpression"
507+
&& (!init.id || init.id.name === id.name)
508+
&& !typeAnnotation
509+
) {
510+
return functionDeclarationFrom(babel, init, id);
511+
}
512+
return t.variableDeclaration(
513+
"const",
514+
[t.variableDeclarator(
515+
assignTypeAnnotation(id, typeAnnotation),
516+
init
517+
)]
490518
);
491519
}
492520

0 commit comments

Comments
 (0)