1 |
|
2 |
|
3 | import {IdentifierRole} from "../parser/tokenizer";
|
4 | import {TokenType as tt} from "../parser/tokenizer/types";
|
5 |
|
6 |
|
7 | import Transformer from "./Transformer";
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | export default class ReactDisplayNameTransformer extends Transformer {
|
14 | constructor(
|
15 | rootTransformer,
|
16 | tokens,
|
17 | importProcessor,
|
18 | options,
|
19 | ) {
|
20 | super();this.rootTransformer = rootTransformer;this.tokens = tokens;this.importProcessor = importProcessor;this.options = options;;
|
21 | }
|
22 |
|
23 | process() {
|
24 | const startIndex = this.tokens.currentIndex();
|
25 | if (this.tokens.identifierName() === "createReactClass") {
|
26 | const newName =
|
27 | this.importProcessor && this.importProcessor.getIdentifierReplacement("createReactClass");
|
28 | if (newName) {
|
29 | this.tokens.replaceToken(`(0, ${newName})`);
|
30 | } else {
|
31 | this.tokens.copyToken();
|
32 | }
|
33 | this.tryProcessCreateClassCall(startIndex);
|
34 | return true;
|
35 | }
|
36 | if (
|
37 | this.tokens.matches3(tt.name, tt.dot, tt.name) &&
|
38 | this.tokens.identifierName() === "React" &&
|
39 | this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 2) === "createClass"
|
40 | ) {
|
41 | const newName = this.importProcessor
|
42 | ? this.importProcessor.getIdentifierReplacement("React") || "React"
|
43 | : "React";
|
44 | if (newName) {
|
45 | this.tokens.replaceToken(newName);
|
46 | this.tokens.copyToken();
|
47 | this.tokens.copyToken();
|
48 | } else {
|
49 | this.tokens.copyToken();
|
50 | this.tokens.copyToken();
|
51 | this.tokens.copyToken();
|
52 | }
|
53 | this.tryProcessCreateClassCall(startIndex);
|
54 | return true;
|
55 | }
|
56 | return false;
|
57 | }
|
58 |
|
59 | |
60 |
|
61 |
|
62 | tryProcessCreateClassCall(startIndex) {
|
63 | const displayName = this.findDisplayName(startIndex);
|
64 | if (!displayName) {
|
65 | return;
|
66 | }
|
67 |
|
68 | if (this.classNeedsDisplayName()) {
|
69 | this.tokens.copyExpectedToken(tt.parenL);
|
70 | this.tokens.copyExpectedToken(tt.braceL);
|
71 | this.tokens.appendCode(`displayName: '${displayName}',`);
|
72 | this.rootTransformer.processBalancedCode();
|
73 | this.tokens.copyExpectedToken(tt.braceR);
|
74 | this.tokens.copyExpectedToken(tt.parenR);
|
75 | }
|
76 | }
|
77 |
|
78 | findDisplayName(startIndex) {
|
79 | if (startIndex < 2) {
|
80 | return null;
|
81 | }
|
82 | if (this.tokens.matches2AtIndex(startIndex - 2, tt.name, tt.eq)) {
|
83 |
|
84 |
|
85 | return this.tokens.identifierNameAtIndex(startIndex - 2);
|
86 | }
|
87 | if (
|
88 | startIndex >= 2 &&
|
89 | this.tokens.tokens[startIndex - 2].identifierRole === IdentifierRole.ObjectKey
|
90 | ) {
|
91 |
|
92 | return this.tokens.identifierNameAtIndex(startIndex - 2);
|
93 | }
|
94 | if (this.tokens.matches2AtIndex(startIndex - 2, tt._export, tt._default)) {
|
95 | return this.getDisplayNameFromFilename();
|
96 | }
|
97 | return null;
|
98 | }
|
99 |
|
100 | getDisplayNameFromFilename() {
|
101 | const filePath = this.options.filePath || "unknown";
|
102 | const pathSegments = filePath.split("/");
|
103 | const filename = pathSegments[pathSegments.length - 1];
|
104 | const dotIndex = filename.lastIndexOf(".");
|
105 | const baseFilename = dotIndex === -1 ? filename : filename.slice(0, dotIndex);
|
106 | if (baseFilename === "index" && pathSegments[pathSegments.length - 2]) {
|
107 | return pathSegments[pathSegments.length - 2];
|
108 | } else {
|
109 | return baseFilename;
|
110 | }
|
111 | }
|
112 |
|
113 | |
114 |
|
115 |
|
116 |
|
117 |
|
118 | classNeedsDisplayName() {
|
119 | let index = this.tokens.currentIndex();
|
120 | if (!this.tokens.matches2(tt.parenL, tt.braceL)) {
|
121 | return false;
|
122 | }
|
123 |
|
124 |
|
125 |
|
126 | const objectStartIndex = index + 1;
|
127 | const objectContextId = this.tokens.tokens[objectStartIndex].contextId;
|
128 | if (objectContextId == null) {
|
129 | throw new Error("Expected non-null context ID on object open-brace.");
|
130 | }
|
131 |
|
132 | for (; index < this.tokens.tokens.length; index++) {
|
133 | const token = this.tokens.tokens[index];
|
134 | if (token.type === tt.braceR && token.contextId === objectContextId) {
|
135 | index++;
|
136 | break;
|
137 | }
|
138 |
|
139 | if (
|
140 | this.tokens.identifierNameAtIndex(index) === "displayName" &&
|
141 | this.tokens.tokens[index].identifierRole === IdentifierRole.ObjectKey &&
|
142 | token.contextId === objectContextId
|
143 | ) {
|
144 |
|
145 | return false;
|
146 | }
|
147 | }
|
148 |
|
149 | if (index === this.tokens.tokens.length) {
|
150 | throw new Error("Unexpected end of input when processing React class.");
|
151 | }
|
152 |
|
153 |
|
154 |
|
155 | return (
|
156 | this.tokens.matches1AtIndex(index, tt.parenR) ||
|
157 | this.tokens.matches2AtIndex(index, tt.comma, tt.parenR)
|
158 | );
|
159 | }
|
160 | }
|