@samitouri / QOS-React / commits / a58276cbc3

[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,