Skip to content

Commit 6ceab04

Browse files
authored
Merge pull request #449 from kylemh/master
Resolve issue with first displayName always being used
2 parents 6803c92 + 54bfeca commit 6ceab04

File tree

6 files changed

+131
-1
lines changed

6 files changed

+131
-1
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Node.js Package
22

33
on:
4+
push:
5+
branches:
6+
- '**'
47
release:
58
types: [created]
69

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as React from 'react';
2+
3+
interface ButtonProps
4+
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {}
5+
6+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
7+
(props, ref) => <button {...props} ref={ref} type="button" />
8+
);
9+
10+
Button.displayName = 'First';
11+
12+
export const SubmitButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
13+
(props, ref) => <button {...props} ref={ref} type="submit" />
14+
);
15+
16+
SubmitButton.displayName = 'Second';
17+
18+
export const ResetButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
19+
(props, ref) => <button {...props} ref={ref} type="reset" />
20+
);
21+
22+
ResetButton.displayName = 'Third';
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as React from 'react';
2+
3+
interface ButtonProps
4+
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {}
5+
6+
export const Button1 = React.forwardRef<HTMLButtonElement, ButtonProps>(
7+
(props, ref) => <button {...props} ref={ref} type="button" />
8+
);
9+
Button1.displayName = 'First';
10+
11+
export const NoExplicitDisplayName1 = (props: any) => <div>Some text</div>;
12+
13+
export const Button2 = (props: ButtonProps) => (
14+
<button {...props} type="button" />
15+
);
16+
Button2.displayName = 'Second';
17+
18+
export const NoExplicitDisplayName2 = (props: any) => <div>Some more text</div>;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as React from 'react';
2+
3+
interface ButtonProps
4+
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {}
5+
6+
export const Button = (props: ButtonProps) => (
7+
<button {...props} type="button" />
8+
);
9+
export const SubmitButton = (props: ButtonProps) => (
10+
<button {...props} type="submit" />
11+
);
12+
export const ResetButton = (props: ButtonProps) => (
13+
<button {...props} type="reset" />
14+
);

src/__tests__/parser.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,48 @@ describe('parser', () => {
10021002
const [parsed] = parse(fixturePath('StatefulDisplayNameFolder/index'));
10031003
assert.equal(parsed.displayName, 'StatefulDisplayNameFolder');
10041004
});
1005+
1006+
describe('multiple components in one', () => {
1007+
it('should parse all `displayName` properties correctly when all are explicitly defined.', () => {
1008+
const result = parse(fixturePath('MultipleAllWithDisplayName'));
1009+
1010+
// Ensure we're not missing any exports.
1011+
assert.equal(result.length, 3);
1012+
1013+
const [parsed1, parsed2, parsed3] = result;
1014+
1015+
assert.equal(parsed1.displayName, 'First');
1016+
assert.equal(parsed2.displayName, 'Second');
1017+
assert.equal(parsed3.displayName, 'Third');
1018+
});
1019+
1020+
it('should parse all `displayName` properties correctly when some are explicitly defined.', () => {
1021+
const result = parse(fixturePath('MultipleSomeWithDisplayName'));
1022+
1023+
// Ensure we're not missing any exports.
1024+
assert.equal(result.length, 4);
1025+
1026+
const [parsed1, parsed2, parsed3, parsed4] = result;
1027+
1028+
assert.equal(parsed1.displayName, 'First');
1029+
assert.equal(parsed2.displayName, 'NoExplicitDisplayName1');
1030+
assert.equal(parsed3.displayName, 'Second');
1031+
assert.equal(parsed4.displayName, 'NoExplicitDisplayName2');
1032+
});
1033+
1034+
it('should parse all `displayName` properties correctly when none are explicitly defined.', () => {
1035+
const result = parse(fixturePath('MultipleWithNoExplicitDisplayName'));
1036+
1037+
// Ensure we're not missing any exports.
1038+
assert.equal(result.length, 3);
1039+
1040+
const [parsed1, parsed2, parsed3] = result;
1041+
1042+
assert.equal(parsed1.displayName, 'Button');
1043+
assert.equal(parsed2.displayName, 'SubmitButton');
1044+
assert.equal(parsed3.displayName, 'ResetButton');
1045+
});
1046+
});
10051047
});
10061048

10071049
describe('Parser options', () => {

src/parser.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1211,11 +1211,42 @@ function getTextValueOfFunctionProperty(
12111211
.filter(statement => {
12121212
const expr = (statement as ts.ExpressionStatement)
12131213
.expression as ts.BinaryExpression;
1214+
1215+
const locals = Array.from(
1216+
(source as any).locals as [string, ts.Symbol][]
1217+
);
1218+
const hasOneLocalExport =
1219+
locals.filter(local => !!local[1].exports).length === 1;
1220+
1221+
if (hasOneLocalExport) {
1222+
return (
1223+
expr.left &&
1224+
(expr.left as ts.PropertyAccessExpression).name &&
1225+
(expr.left as ts.PropertyAccessExpression).name.escapedText ===
1226+
propertyName
1227+
);
1228+
}
1229+
1230+
/**
1231+
* Ensure the .displayName is for the currently processing function.
1232+
*
1233+
* This avoids the following situations:
1234+
*
1235+
* - A file has multiple functions, one has `.displayName`, and all
1236+
* functions ends up with that same `.displayName` value.
1237+
*
1238+
* - A file has multiple functions, each with a different
1239+
* `.displayName`, but the first is applied to all of them.
1240+
*/
1241+
const flowNodeNameEscapedText = (statement as any)?.flowNode?.node?.name
1242+
?.escapedText as false | ts.__String | undefined;
1243+
12141244
return (
12151245
expr.left &&
12161246
(expr.left as ts.PropertyAccessExpression).name &&
12171247
(expr.left as ts.PropertyAccessExpression).name.escapedText ===
1218-
propertyName
1248+
propertyName &&
1249+
flowNodeNameEscapedText === exp.escapedName
12191250
);
12201251
})
12211252
.filter(statement => {

0 commit comments

Comments
 (0)