5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import { parse, ParserPlugin } from "@babel/parser";
8
+import { parse as babelParse, ParserPlugin } from "@babel/parser";
9
+import * as HermesParser from "hermes-parser";
10
import traverse, { NodePath } from "@babel/traverse";
11
import * as t from "@babel/types";
12
import {
43
PrintedCompilerPipelineValue,
44
} from "./Output";
45
46
+function parseInput(input: string, language: "flow" | "typescript") {
47
+ // Extract the first line to quickly check for custom test directives
48
+ if (language === "flow") {
49
+ return HermesParser.parse(input, {
50
+ babel: true,
51
+ flow: "all",
52
+ sourceType: "module",
53
+ enableExperimentalComponentSyntax: true,
54
+ });
55
+ } else {
56
+ return babelParse(input, {
57
+ plugins: ["typescript", "jsx"],
58
+ sourceType: "module",
59
+ });
60
+ }
61
+}
62
+
63
function parseFunctions(
46
- source: string
64
+ source: string,
65
+ language: "flow" | "typescript"
66
): Array<
67
NodePath<
68
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
74
>
75
> = [];
76
try {
58
- const isFlow = source
59
- .trim()
60
- .split("\n", 1)[0]
61
- .match(/\s*\/\/\s*\@flow\s*/);
62
- let type_transform: ParserPlugin;
63
- if (isFlow) {
64
- type_transform = "flow";
65
- } else {
66
- type_transform = "typescript";
67
- }
68
- const ast = parse(source, {
69
- plugins: [type_transform, "jsx"],
70
- sourceType: "module",
71
- });
77
+ const ast = parseInput(source, language);
78
traverse(ast, {
79
FunctionDeclaration(nodePath) {
80
items.push(nodePath);
169
return "Other";
170
}
171
166
-function compile(source: string): CompilerOutput {
172
+function compile(source: string): [CompilerOutput, "flow" | "typescript"] {
173
const results = new Map<string, PrintedCompilerPipelineValue[]>();
174
const error = new CompilerError();
175
const upsert = (result: PrintedCompilerPipelineValue) => {
180
results.set(result.name, [result]);
181
}
182
};
183
+ let language: "flow" | "typescript";
184
+ if (source.match(/\@flow/)) {
185
+ language = "flow";
186
+ } else {
187
+ language = "typescript";
188
+ }
189
try {
190
// Extract the first line to quickly check for custom test directives
191
const pragma = source.substring(0, source.indexOf("\n"));
192
const config = parseConfigPragma(pragma);
193
182
- for (const fn of parseFunctions(source)) {
194
+ for (const fn of parseFunctions(source, language)) {
195
if (!fn.isFunctionDeclaration()) {
196
error.pushErrorDetail(
197
new CompilerErrorDetail({
291
}
292
}
293
if (error.hasErrors()) {
282
- return { kind: "err", results, error: error };
294
+ return [{ kind: "err", results, error: error }, language];
295
}
284
- return { kind: "ok", results };
296
+ return [{ kind: "ok", results }, language];
297
}
298
299
export default function Editor() {
301
const deferredStore = useDeferredValue(store);
302
const dispatchStore = useStoreDispatch();
303
const { enqueueSnackbar } = useSnackbar();
292
- const compilerOutput = useMemo(
304
+ const [compilerOutput, language] = useMemo(
305
() => compile(deferredStore.source),
306
[deferredStore.source]
307
);
333
<div className="relative flex basis top-14">
334
<div className={clsx("relative sm:basis-1/4")}>
335
<Input
336
+ language={language}
337
errors={
338
compilerOutput.kind === "err" ? compilerOutput.error.details : []
339
}