@samitouri / QOS-React-2 / commits / 0eebd37041

[playground] Config panel quality fixes (#34611)

Fixed two small issues with the config panel in the compiler playground: 1. Object descriptions were being confined in the config box and most of it would not be visible upon hover 2. Changed it so that "Applied Configs" would only display a valid set of configs, rather than switching between "Invalid Configs" and the set of options. This would be less visually jarring for users as the Output panel already displays errors. Additionally, if users want to see the list of config options but have a currently broken config, they would previously not know how to fix it. Object hover before: <img width="702" height="481" alt="Screenshot 2025-09-26 at 10 41 03 AM" src="https://github.com/user-attachments/assets/b2ddec2f-16ba-41a1-be1f-96211f46764c" /> Hover after: <img width="702" height="481" alt="Screenshot 2025-09-26 at 10 40 37 AM" src="https://github.com/user-attachments/assets/dc713a22-4710-46a8-a5d7-485060cc9074" /> Applied Configs always displays the last valid set of configs: https://github.com/user-attachments/assets/2fb9232f-7388-4488-9b7a-bb48bf09e4ca

Eugene Choi committed Oct 3, 2025 at 10:52 UTC 0eebd37041a5712f841edd5fad558e0516e5af61
4 files changed +349 -338
compiler/apps/playground/components/Editor/ConfigEditor.tsx
+12 -37
@@ -6,7 +6,6 @@
6 */
7
8 import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
9 -import {PluginOptions} from 'babel-plugin-react-compiler';
9 import type {editor} from 'monaco-editor';
10 import * as monaco from 'monaco-editor';
11 import React, {
@@ -18,9 +17,8 @@ import React, {
17 } from 'react';
18 import {Resizable} from 're-resizable';
19 import {useStore, useStoreDispatch} from '../StoreContext';
21 -import {monacoOptions} from './monacoOptions';
20 +import {monacoConfigOptions} from './monacoOptions';
21 import {IconChevron} from '../Icons/IconChevron';
23 -import prettyFormat from 'pretty-format';
22 import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes';
23
24 // @ts-expect-error - webpack asset/source loader handles .d.ts files as strings
@@ -29,9 +27,9 @@ import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts';
27 loader.config({monaco});
28
29 export default function ConfigEditor({
32 - appliedOptions,
30 + formattedAppliedConfig,
31 }: {
34 - appliedOptions: PluginOptions | null;
32 + formattedAppliedConfig: string;
33 }): React.ReactElement {
34 const [isExpanded, setIsExpanded] = useState(false);
35
@@ -49,7 +47,7 @@ export default function ConfigEditor({
47 setIsExpanded(false);
48 });
49 }}
52 - appliedOptions={appliedOptions}
50 + formattedAppliedConfig={formattedAppliedConfig}
51 />
52 </div>
53 <div
@@ -71,10 +69,10 @@ export default function ConfigEditor({
69
70 function ExpandedEditor({
71 onToggle,
74 - appliedOptions,
72 + formattedAppliedConfig,
73 }: {
76 - onToggle: () => void;
77 - appliedOptions: PluginOptions | null;
74 + onToggle: (expanded: boolean) => void;
75 + formattedAppliedConfig: string;
76 }): React.ReactElement {
77 const store = useStore();
78 const dispatchStore = useStoreDispatch();
@@ -122,13 +120,6 @@ function ExpandedEditor({
120 });
121 };
122
125 - const formattedAppliedOptions = appliedOptions
126 - ? prettyFormat(appliedOptions, {
127 - printFunctionName: false,
128 - printBasicPrototype: false,
129 - })
130 - : 'Invalid configs';
131 -
123 return (
124 <ViewTransition
125 update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}>
@@ -158,7 +149,7 @@ function ExpandedEditor({
149 Config Overrides
150 </h2>
151 </div>
161 - <div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
152 + <div className="flex-1 border border-gray-300">
153 <MonacoEditor
154 path={'config.ts'}
155 language={'typescript'}
@@ -167,16 +158,7 @@ function ExpandedEditor({
158 onChange={handleChange}
159 loading={''}
160 className="monaco-editor-config"
170 - options={{
171 - ...monacoOptions,
172 - lineNumbers: 'off',
173 - renderLineHighlight: 'none',
174 - overviewRulerBorder: false,
175 - overviewRulerLanes: 0,
176 - fontSize: 12,
177 - scrollBeyondLastLine: false,
178 - glyphMargin: false,
179 - }}
161 + options={monacoConfigOptions}
162 />
163 </div>
164 </div>
@@ -186,23 +168,16 @@ function ExpandedEditor({
168 Applied Configs
169 </h2>
170 </div>
189 - <div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
171 + <div className="flex-1 border border-gray-300">
172 <MonacoEditor
173 path={'applied-config.js'}
174 language={'javascript'}
193 - value={formattedAppliedOptions}
175 + value={formattedAppliedConfig}
176 loading={''}
177 className="monaco-editor-applied-config"
178 options={{
197 - ...monacoOptions,
198 - lineNumbers: 'off',
199 - renderLineHighlight: 'none',
200 - overviewRulerBorder: false,
201 - overviewRulerLanes: 0,
202 - fontSize: 12,
203 - scrollBeyondLastLine: false,
179 + ...monacoConfigOptions,
180 readOnly: true,
205 - glyphMargin: false,
181 }}
182 />
183 </div>
compiler/apps/playground/components/Editor/EditorImpl.tsx
+18 -301
@@ -5,312 +5,17 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {parse as babelParse, ParseResult} from '@babel/parser';
9 -import * as HermesParser from 'hermes-parser';
10 -import * as t from '@babel/types';
11 -import BabelPluginReactCompiler, {
12 - CompilerError,
8 +import {
9 CompilerErrorDetail,
10 CompilerDiagnostic,
15 - Effect,
16 - ErrorCategory,
17 - parseConfigPragmaForTests,
18 - ValueKind,
19 - type Hook,
20 - PluginOptions,
21 - CompilerPipelineValue,
22 - parsePluginOptions,
23 - printReactiveFunctionWithOutlined,
24 - printFunctionWithOutlined,
25 - type LoggerEvent,
11 } from 'babel-plugin-react-compiler';
27 -import {useDeferredValue, useMemo} from 'react';
12 +import {useDeferredValue, useMemo, useState} from 'react';
13 import {useStore} from '../StoreContext';
14 import ConfigEditor from './ConfigEditor';
15 import Input from './Input';
31 -import {
32 - CompilerOutput,
33 - CompilerTransformOutput,
34 - default as Output,
35 - PrintedCompilerPipelineValue,
36 -} from './Output';
37 -import {transformFromAstSync} from '@babel/core';
38 -
39 -function parseInput(
40 - input: string,
41 - language: 'flow' | 'typescript',
42 -): ParseResult<t.File> {
43 - // Extract the first line to quickly check for custom test directives
44 - if (language === 'flow') {
45 - return HermesParser.parse(input, {
46 - babel: true,
47 - flow: 'all',
48 - sourceType: 'module',
49 - enableExperimentalComponentSyntax: true,
50 - });
51 - } else {
52 - return babelParse(input, {
53 - plugins: ['typescript', 'jsx'],
54 - sourceType: 'module',
55 - }) as ParseResult<t.File>;
56 - }
57 -}
58 -
59 -function invokeCompiler(
60 - source: string,
61 - language: 'flow' | 'typescript',
62 - options: PluginOptions,
63 -): CompilerTransformOutput {
64 - const ast = parseInput(source, language);
65 - let result = transformFromAstSync(ast, source, {
66 - filename: '_playgroundFile.js',
67 - highlightCode: false,
68 - retainLines: true,
69 - plugins: [[BabelPluginReactCompiler, options]],
70 - ast: true,
71 - sourceType: 'module',
72 - configFile: false,
73 - sourceMaps: true,
74 - babelrc: false,
75 - });
76 - if (result?.ast == null || result?.code == null || result?.map == null) {
77 - throw new Error('Expected successful compilation');
78 - }
79 - return {
80 - code: result.code,
81 - sourceMaps: result.map,
82 - language,
83 - };
84 -}
85 -
86 -const COMMON_HOOKS: Array<[string, Hook]> = [
87 - [
88 - 'useFragment',
89 - {
90 - valueKind: ValueKind.Frozen,
91 - effectKind: Effect.Freeze,
92 - noAlias: true,
93 - transitiveMixedData: true,
94 - },
95 - ],
96 - [
97 - 'usePaginationFragment',
98 - {
99 - valueKind: ValueKind.Frozen,
100 - effectKind: Effect.Freeze,
101 - noAlias: true,
102 - transitiveMixedData: true,
103 - },
104 - ],
105 - [
106 - 'useRefetchableFragment',
107 - {
108 - valueKind: ValueKind.Frozen,
109 - effectKind: Effect.Freeze,
110 - noAlias: true,
111 - transitiveMixedData: true,
112 - },
113 - ],
114 - [
115 - 'useLazyLoadQuery',
116 - {
117 - valueKind: ValueKind.Frozen,
118 - effectKind: Effect.Freeze,
119 - noAlias: true,
120 - transitiveMixedData: true,
121 - },
122 - ],
123 - [
124 - 'usePreloadedQuery',
125 - {
126 - valueKind: ValueKind.Frozen,
127 - effectKind: Effect.Freeze,
128 - noAlias: true,
129 - transitiveMixedData: true,
130 - },
131 - ],
132 -];
133 -
134 -function parseOptions(
135 - source: string,
136 - mode: 'compiler' | 'linter',
137 - configOverrides: string,
138 -): PluginOptions {
139 - // Extract the first line to quickly check for custom test directives
140 - const pragma = source.substring(0, source.indexOf('\n'));
141 -
142 - const parsedPragmaOptions = parseConfigPragmaForTests(pragma, {
143 - compilationMode: 'infer',
144 - environment:
145 - mode === 'linter'
146 - ? {
147 - // enabled in compiler
148 - validateRefAccessDuringRender: false,
149 - // enabled in linter
150 - validateNoSetStateInRender: true,
151 - validateNoSetStateInEffects: true,
152 - validateNoJSXInTryStatements: true,
153 - validateNoImpureFunctionsInRender: true,
154 - validateStaticComponents: true,
155 - validateNoFreezingKnownMutableFunctions: true,
156 - validateNoVoidUseMemo: true,
157 - }
158 - : {
159 - /* use defaults for compiler mode */
160 - },
161 - });
162 -
163 - // Parse config overrides from config editor
164 - let configOverrideOptions: any = {};
165 - const configMatch = configOverrides.match(/^\s*import.*?\n\n\((.*)\)/s);
166 - if (configOverrides.trim()) {
167 - if (configMatch && configMatch[1]) {
168 - const configString = configMatch[1].replace(/satisfies.*$/, '').trim();
169 - configOverrideOptions = new Function(`return (${configString})`)();
170 - } else {
171 - throw new Error('Invalid override format');
172 - }
173 - }
174 -
175 - const opts: PluginOptions = parsePluginOptions({
176 - ...parsedPragmaOptions,
177 - ...configOverrideOptions,
178 - environment: {
179 - ...parsedPragmaOptions.environment,
180 - ...configOverrideOptions.environment,
181 - customHooks: new Map([...COMMON_HOOKS]),
182 - },
183 - });
184 -
185 - return opts;
186 -}
187 -
188 -function compile(
189 - source: string,
190 - mode: 'compiler' | 'linter',
191 - configOverrides: string,
192 -): [CompilerOutput, 'flow' | 'typescript', PluginOptions | null] {
193 - const results = new Map<string, Array<PrintedCompilerPipelineValue>>();
194 - const error = new CompilerError();
195 - const otherErrors: Array<CompilerErrorDetail | CompilerDiagnostic> = [];
196 - const upsert: (result: PrintedCompilerPipelineValue) => void = result => {
197 - const entry = results.get(result.name);
198 - if (Array.isArray(entry)) {
199 - entry.push(result);
200 - } else {
201 - results.set(result.name, [result]);
202 - }
203 - };
204 - let language: 'flow' | 'typescript';
205 - if (source.match(/\@flow/)) {
206 - language = 'flow';
207 - } else {
208 - language = 'typescript';
209 - }
210 - let transformOutput;
211 -
212 - let baseOpts: PluginOptions | null = null;
213 - try {
214 - baseOpts = parseOptions(source, mode, configOverrides);
215 - } catch (err) {
216 - error.details.push(
217 - new CompilerErrorDetail({
218 - category: ErrorCategory.Config,
219 - reason: `Unexpected failure when transforming configs! \n${err}`,
220 - loc: null,
221 - suggestions: null,
222 - }),
223 - );
224 - }
225 - if (baseOpts) {
226 - try {
227 - const logIR = (result: CompilerPipelineValue): void => {
228 - switch (result.kind) {
229 - case 'ast': {
230 - break;
231 - }
232 - case 'hir': {
233 - upsert({
234 - kind: 'hir',
235 - fnName: result.value.id,
236 - name: result.name,
237 - value: printFunctionWithOutlined(result.value),
238 - });
239 - break;
240 - }
241 - case 'reactive': {
242 - upsert({
243 - kind: 'reactive',
244 - fnName: result.value.id,
245 - name: result.name,
246 - value: printReactiveFunctionWithOutlined(result.value),
247 - });
248 - break;
249 - }
250 - case 'debug': {
251 - upsert({
252 - kind: 'debug',
253 - fnName: null,
254 - name: result.name,
255 - value: result.value,
256 - });
257 - break;
258 - }
259 - default: {
260 - const _: never = result;
261 - throw new Error(`Unhandled result ${result}`);
262 - }
263 - }
264 - };
265 - // Add logger options to the parsed options
266 - const opts = {
267 - ...baseOpts,
268 - logger: {
269 - debugLogIRs: logIR,
270 - logEvent: (_filename: string | null, event: LoggerEvent): void => {
271 - if (event.kind === 'CompileError') {
272 - otherErrors.push(event.detail);
273 - }
274 - },
275 - },
276 - };
277 - transformOutput = invokeCompiler(source, language, opts);
278 - } catch (err) {
279 - /**
280 - * error might be an invariant violation or other runtime error
281 - * (i.e. object shape that is not CompilerError)
282 - */
283 - if (err instanceof CompilerError && err.details.length > 0) {
284 - error.merge(err);
285 - } else {
286 - /**
287 - * Handle unexpected failures by logging (to get a stack trace)
288 - * and reporting
289 - */
290 - error.details.push(
291 - new CompilerErrorDetail({
292 - category: ErrorCategory.Invariant,
293 - reason: `Unexpected failure when transforming input! \n${err}`,
294 - loc: null,
295 - suggestions: null,
296 - }),
297 - );
298 - }
299 - }
300 - }
301 - // Only include logger errors if there weren't other errors
302 - if (!error.hasErrors() && otherErrors.length !== 0) {
303 - otherErrors.forEach(e => error.details.push(e));
304 - }
305 - if (error.hasErrors()) {
306 - return [{kind: 'err', results, error}, language, baseOpts];
307 - }
308 - return [
309 - {kind: 'ok', results, transformOutput, errors: error.details},
310 - language,
311 - baseOpts,
312 - ];
313 -}
16 +import {CompilerOutput, default as Output} from './Output';
17 +import {compile} from '../../lib/compilation';
18 +import prettyFormat from 'pretty-format';
19
20 export default function Editor(): JSX.Element {
21 const store = useStore();
@@ -323,6 +28,7 @@ export default function Editor(): JSX.Element {
28 () => compile(deferredStore.source, 'linter', deferredStore.config),
29 [deferredStore.source, deferredStore.config],
30 );
31 + const [formattedAppliedConfig, setFormattedAppliedConfig] = useState('');
32
33 let mergedOutput: CompilerOutput;
34 let errors: Array<CompilerErrorDetail | CompilerDiagnostic>;
@@ -336,11 +42,22 @@ export default function Editor(): JSX.Element {
42 mergedOutput = compilerOutput;
43 errors = compilerOutput.error.details;
44 }
45 +
46 + if (appliedOptions) {
47 + const formatted = prettyFormat(appliedOptions, {
48 + printFunctionName: false,
49 + printBasicPrototype: false,
50 + });
51 + if (formatted !== formattedAppliedConfig) {
52 + setFormattedAppliedConfig(formatted);
53 + }
54 + }
55 +
56 return (
57 <>
58 <div className="relative flex top-14">
59 <div className="flex-shrink-0">
343 - <ConfigEditor appliedOptions={appliedOptions} />
60 + <ConfigEditor formattedAppliedConfig={formattedAppliedConfig} />
61 </div>
62 <div className="flex flex-1 min-w-0">
63 <Input language={language} errors={errors} />
compiler/apps/playground/components/Editor/monacoOptions.ts
+11
@@ -32,3 +32,14 @@ export const monacoOptions: Partial<EditorProps['options']> = {
32
33 tabSize: 2,
34 };
35 +
36 +export const monacoConfigOptions: Partial<EditorProps['options']> = {
37 + ...monacoOptions,
38 + lineNumbers: 'off',
39 + renderLineHighlight: 'none',
40 + overviewRulerBorder: false,
41 + overviewRulerLanes: 0,
42 + fontSize: 12,
43 + scrollBeyondLastLine: false,
44 + glyphMargin: false,
45 +};
compiler/apps/playground/lib/compilation.ts new
+308
@@ -0,0 +1,308 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {parse as babelParse, ParseResult} from '@babel/parser';
9 +import * as HermesParser from 'hermes-parser';
10 +import * as t from '@babel/types';
11 +import BabelPluginReactCompiler, {
12 + CompilerError,
13 + CompilerErrorDetail,
14 + CompilerDiagnostic,
15 + Effect,
16 + ErrorCategory,
17 + parseConfigPragmaForTests,
18 + ValueKind,
19 + type Hook,
20 + PluginOptions,
21 + CompilerPipelineValue,
22 + parsePluginOptions,
23 + printReactiveFunctionWithOutlined,
24 + printFunctionWithOutlined,
25 + type LoggerEvent,
26 +} from 'babel-plugin-react-compiler';
27 +import {transformFromAstSync} from '@babel/core';
28 +import type {
29 + CompilerOutput,
30 + CompilerTransformOutput,
31 + PrintedCompilerPipelineValue,
32 +} from '../components/Editor/Output';
33 +
34 +function parseInput(
35 + input: string,
36 + language: 'flow' | 'typescript',
37 +): ParseResult<t.File> {
38 + // Extract the first line to quickly check for custom test directives
39 + if (language === 'flow') {
40 + return HermesParser.parse(input, {
41 + babel: true,
42 + flow: 'all',
43 + sourceType: 'module',
44 + enableExperimentalComponentSyntax: true,
45 + });
46 + } else {
47 + return babelParse(input, {
48 + plugins: ['typescript', 'jsx'],
49 + sourceType: 'module',
50 + }) as ParseResult<t.File>;
51 + }
52 +}
53 +
54 +function invokeCompiler(
55 + source: string,
56 + language: 'flow' | 'typescript',
57 + options: PluginOptions,
58 +): CompilerTransformOutput {
59 + const ast = parseInput(source, language);
60 + let result = transformFromAstSync(ast, source, {
61 + filename: '_playgroundFile.js',
62 + highlightCode: false,
63 + retainLines: true,
64 + plugins: [[BabelPluginReactCompiler, options]],
65 + ast: true,
66 + sourceType: 'module',
67 + configFile: false,
68 + sourceMaps: true,
69 + babelrc: false,
70 + });
71 + if (result?.ast == null || result?.code == null || result?.map == null) {
72 + throw new Error('Expected successful compilation');
73 + }
74 + return {
75 + code: result.code,
76 + sourceMaps: result.map,
77 + language,
78 + };
79 +}
80 +
81 +const COMMON_HOOKS: Array<[string, Hook]> = [
82 + [
83 + 'useFragment',
84 + {
85 + valueKind: ValueKind.Frozen,
86 + effectKind: Effect.Freeze,
87 + noAlias: true,
88 + transitiveMixedData: true,
89 + },
90 + ],
91 + [
92 + 'usePaginationFragment',
93 + {
94 + valueKind: ValueKind.Frozen,
95 + effectKind: Effect.Freeze,
96 + noAlias: true,
97 + transitiveMixedData: true,
98 + },
99 + ],
100 + [
101 + 'useRefetchableFragment',
102 + {
103 + valueKind: ValueKind.Frozen,
104 + effectKind: Effect.Freeze,
105 + noAlias: true,
106 + transitiveMixedData: true,
107 + },
108 + ],
109 + [
110 + 'useLazyLoadQuery',
111 + {
112 + valueKind: ValueKind.Frozen,
113 + effectKind: Effect.Freeze,
114 + noAlias: true,
115 + transitiveMixedData: true,
116 + },
117 + ],
118 + [
119 + 'usePreloadedQuery',
120 + {
121 + valueKind: ValueKind.Frozen,
122 + effectKind: Effect.Freeze,
123 + noAlias: true,
124 + transitiveMixedData: true,
125 + },
126 + ],
127 +];
128 +
129 +function parseOptions(
130 + source: string,
131 + mode: 'compiler' | 'linter',
132 + configOverrides: string,
133 +): PluginOptions {
134 + // Extract the first line to quickly check for custom test directives
135 + const pragma = source.substring(0, source.indexOf('\n'));
136 +
137 + const parsedPragmaOptions = parseConfigPragmaForTests(pragma, {
138 + compilationMode: 'infer',
139 + environment:
140 + mode === 'linter'
141 + ? {
142 + // enabled in compiler
143 + validateRefAccessDuringRender: false,
144 + // enabled in linter
145 + validateNoSetStateInRender: true,
146 + validateNoSetStateInEffects: true,
147 + validateNoJSXInTryStatements: true,
148 + validateNoImpureFunctionsInRender: true,
149 + validateStaticComponents: true,
150 + validateNoFreezingKnownMutableFunctions: true,
151 + validateNoVoidUseMemo: true,
152 + }
153 + : {
154 + /* use defaults for compiler mode */
155 + },
156 + });
157 +
158 + // Parse config overrides from config editor
159 + let configOverrideOptions: any = {};
160 + const configMatch = configOverrides.match(/^\s*import.*?\n\n\((.*)\)/s);
161 + if (configOverrides.trim()) {
162 + if (configMatch && configMatch[1]) {
163 + const configString = configMatch[1].replace(/satisfies.*$/, '').trim();
164 + configOverrideOptions = new Function(`return (${configString})`)();
165 + } else {
166 + throw new Error('Invalid override format');
167 + }
168 + }
169 +
170 + const opts: PluginOptions = parsePluginOptions({
171 + ...parsedPragmaOptions,
172 + ...configOverrideOptions,
173 + environment: {
174 + ...parsedPragmaOptions.environment,
175 + ...configOverrideOptions.environment,
176 + customHooks: new Map([...COMMON_HOOKS]),
177 + },
178 + });
179 +
180 + return opts;
181 +}
182 +
183 +export function compile(
184 + source: string,
185 + mode: 'compiler' | 'linter',
186 + configOverrides: string,
187 +): [CompilerOutput, 'flow' | 'typescript', PluginOptions | null] {
188 + const results = new Map<string, Array<PrintedCompilerPipelineValue>>();
189 + const error = new CompilerError();
190 + const otherErrors: Array<CompilerErrorDetail | CompilerDiagnostic> = [];
191 + const upsert: (result: PrintedCompilerPipelineValue) => void = result => {
192 + const entry = results.get(result.name);
193 + if (Array.isArray(entry)) {
194 + entry.push(result);
195 + } else {
196 + results.set(result.name, [result]);
197 + }
198 + };
199 + let language: 'flow' | 'typescript';
200 + if (source.match(/\@flow/)) {
201 + language = 'flow';
202 + } else {
203 + language = 'typescript';
204 + }
205 + let transformOutput;
206 +
207 + let baseOpts: PluginOptions | null = null;
208 + try {
209 + baseOpts = parseOptions(source, mode, configOverrides);
210 + } catch (err) {
211 + error.details.push(
212 + new CompilerErrorDetail({
213 + category: ErrorCategory.Config,
214 + reason: `Unexpected failure when transforming configs! \n${err}`,
215 + loc: null,
216 + suggestions: null,
217 + }),
218 + );
219 + }
220 + if (baseOpts) {
221 + try {
222 + const logIR = (result: CompilerPipelineValue): void => {
223 + switch (result.kind) {
224 + case 'ast': {
225 + break;
226 + }
227 + case 'hir': {
228 + upsert({
229 + kind: 'hir',
230 + fnName: result.value.id,
231 + name: result.name,
232 + value: printFunctionWithOutlined(result.value),
233 + });
234 + break;
235 + }
236 + case 'reactive': {
237 + upsert({
238 + kind: 'reactive',
239 + fnName: result.value.id,
240 + name: result.name,
241 + value: printReactiveFunctionWithOutlined(result.value),
242 + });
243 + break;
244 + }
245 + case 'debug': {
246 + upsert({
247 + kind: 'debug',
248 + fnName: null,
249 + name: result.name,
250 + value: result.value,
251 + });
252 + break;
253 + }
254 + default: {
255 + const _: never = result;
256 + throw new Error(`Unhandled result ${result}`);
257 + }
258 + }
259 + };
260 + // Add logger options to the parsed options
261 + const opts = {
262 + ...baseOpts,
263 + logger: {
264 + debugLogIRs: logIR,
265 + logEvent: (_filename: string | null, event: LoggerEvent): void => {
266 + if (event.kind === 'CompileError') {
267 + otherErrors.push(event.detail);
268 + }
269 + },
270 + },
271 + };
272 + transformOutput = invokeCompiler(source, language, opts);
273 + } catch (err) {
274 + /**
275 + * error might be an invariant violation or other runtime error
276 + * (i.e. object shape that is not CompilerError)
277 + */
278 + if (err instanceof CompilerError && err.details.length > 0) {
279 + error.merge(err);
280 + } else {
281 + /**
282 + * Handle unexpected failures by logging (to get a stack trace)
283 + * and reporting
284 + */
285 + error.details.push(
286 + new CompilerErrorDetail({
287 + category: ErrorCategory.Invariant,
288 + reason: `Unexpected failure when transforming input! \n${err}`,
289 + loc: null,
290 + suggestions: null,
291 + }),
292 + );
293 + }
294 + }
295 + }
296 + // Only include logger errors if there weren't other errors
297 + if (!error.hasErrors() && otherErrors.length !== 0) {
298 + otherErrors.forEach(e => error.details.push(e));
299 + }
300 + if (error.hasErrors()) {
301 + return [{kind: 'err', results, error}, language, baseOpts];
302 + }
303 + return [
304 + {kind: 'ok', results, transformOutput, errors: error.details},
305 + language,
306 + baseOpts,
307 + ];
308 +}