[playground] Allow (Arrow)FunctionExpressions
This was a pet peeve where our playground could only compile top level FunctionDeclarations. Just synthesize a fake identifier if it doesn't have one. ghstack-source-id: 882483c79ceebf382b69e37aed1f293efff9c5a7 Pull Request resolved: https://github.com/facebook/react/pull/30729
Lauren Tan committed
Aug 16, 2024 at 17:39 UTC
a58276cbc3a70ba99572eeb9c2f7b4a54ca44b1e
1 file changed
+34
-29
compiler/apps/playground/components/Editor/EditorImpl.tsx
+34
-29
@@ -66,14 +66,14 @@ function parseFunctions(
66
source: string,
67
language: 'flow' | 'typescript',
68
): Array<
69
- NodePath<
70
- t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
71
- >
69
+ | NodePath<t.FunctionDeclaration>
70
+ | NodePath<t.ArrowFunctionExpression>
71
+ | NodePath<t.FunctionExpression>
72
> {
73
const items: Array<
74
- NodePath<
75
- t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
76
- >
74
+ | NodePath<t.FunctionDeclaration>
75
+ | NodePath<t.ArrowFunctionExpression>
76
+ | NodePath<t.FunctionExpression>
77
> = [];
78
try {
79
const ast = parseInput(source, language);
@@ -155,22 +155,33 @@ function isHookName(s: string): boolean {
155
return /^use[A-Z0-9]/.test(s);
156
}
157
158
-function getReactFunctionType(
159
- id: NodePath<t.Identifier | null | undefined>,
160
-): ReactFunctionType {
161
- if (id && id.node && id.isIdentifier()) {
162
- if (isHookName(id.node.name)) {
158
+function getReactFunctionType(id: t.Identifier | null): ReactFunctionType {
159
+ if (id != null) {
160
+ if (isHookName(id.name)) {
161
return 'Hook';
162
}
163
164
const isPascalCaseNameSpace = /^[A-Z].*/;
167
- if (isPascalCaseNameSpace.test(id.node.name)) {
165
+ if (isPascalCaseNameSpace.test(id.name)) {
166
return 'Component';
167
}
168
}
169
return 'Other';
170
}
171
172
+function getFunctionIdentifier(
173
+ fn:
174
+ | NodePath<t.FunctionDeclaration>
175
+ | NodePath<t.ArrowFunctionExpression>
176
+ | NodePath<t.FunctionExpression>,
177
+): t.Identifier | null {
178
+ if (fn.isArrowFunctionExpression()) {
179
+ return null;
180
+ }
181
+ const id = fn.get('id');
182
+ return Array.isArray(id) === false && id.isIdentifier() ? id.node : null;
183
+}
184
+
185
function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] {
186
const results = new Map<string, PrintedCompilerPipelineValue[]>();
187
const error = new CompilerError();
@@ -188,27 +199,21 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] {
199
} else {
200
language = 'typescript';
201
}
202
+ let count = 0;
203
+ const withIdentifier = (id: t.Identifier | null): t.Identifier => {
204
+ if (id != null && id.name != null) {
205
+ return id;
206
+ } else {
207
+ return t.identifier(`anonymous_${count++}`);
208
+ }
209
+ };
210
try {
211
// Extract the first line to quickly check for custom test directives
212
const pragma = source.substring(0, source.indexOf('\n'));
213
const config = parseConfigPragma(pragma);
214
215
for (const fn of parseFunctions(source, language)) {
197
- if (!fn.isFunctionDeclaration()) {
198
- error.pushErrorDetail(
199
- new CompilerErrorDetail({
200
- reason: `Unexpected function type ${fn.node.type}`,
201
- description:
202
- 'Playground only supports parsing function declarations',
203
- severity: ErrorSeverity.Todo,
204
- loc: fn.node.loc ?? null,
205
- suggestions: null,
206
- }),
207
- );
208
- continue;
209
- }
210
-
211
- const id = fn.get('id');
216
+ const id = withIdentifier(getFunctionIdentifier(fn));
217
for (const result of run(
218
fn,
219
{
@@ -221,7 +226,7 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] {
226
null,
227
null,
228
)) {
224
- const fnName = fn.node.id?.name ?? null;
229
+ const fnName = id.name;
230
switch (result.kind) {
231
case 'ast': {
232
upsert({
@@ -230,7 +235,7 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] {
235
name: result.name,
236
value: {
237
type: 'FunctionDeclaration',
233
- id: result.value.id,
238
+ id,
239
async: result.value.async,
240
generator: result.value.generator,
241
body: result.value.body,