diff --git a/compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-all-output.txt b/compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-all-output.txt new file mode 100644 index 0000000000000..5633cf0b0f75a --- /dev/null +++ b/compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-all-output.txt @@ -0,0 +1,13 @@ +import { c as _c } from "react/compiler-runtime"; //  +        @compilationMode(all) +function nonReactFn() { +  const $ = _c(1); +  let t0; +  if ($[0] === Symbol.for("react.memo_cache_sentinel")) { +    t0 = {}; +    $[0] = t0; +  } else { +    t0 = $[0]; +  } +  return t0; +} \ No newline at end of file diff --git a/compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-infer-output.txt b/compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-infer-output.txt new file mode 100644 index 0000000000000..b50c37fc4e48f --- /dev/null +++ b/compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-infer-output.txt @@ -0,0 +1,4 @@ +// @compilationMode(infer) +function nonReactFn() { +  return {}; +} \ No newline at end of file diff --git a/compiler/apps/playground/__tests__/e2e/page.spec.ts b/compiler/apps/playground/__tests__/e2e/page.spec.ts index 05fe96d4b9048..3ba082cf62972 100644 --- a/compiler/apps/playground/__tests__/e2e/page.spec.ts +++ b/compiler/apps/playground/__tests__/e2e/page.spec.ts @@ -79,6 +79,24 @@ function Foo() { // @flow function useFoo(propVal: {+baz: number}) { return
{(propVal.baz as number)}
; +} + `, + noFormat: true, + }, + { + name: 'compilationMode-infer', + input: `// @compilationMode(infer) +function nonReactFn() { + return {}; +} + `, + noFormat: true, + }, + { + name: 'compilationMode-all', + input: `// @compilationMode(all) +function nonReactFn() { + return {}; } `, noFormat: true, diff --git a/compiler/apps/playground/components/Editor/EditorImpl.tsx b/compiler/apps/playground/components/Editor/EditorImpl.tsx index 8c782efe54d4e..8c386116865af 100644 --- a/compiler/apps/playground/components/Editor/EditorImpl.tsx +++ b/compiler/apps/playground/components/Editor/EditorImpl.tsx @@ -20,7 +20,6 @@ import BabelPluginReactCompiler, { CompilerPipelineValue, parsePluginOptions, } from 'babel-plugin-react-compiler/src'; -import {type EnvironmentConfig} from 'babel-plugin-react-compiler/src/HIR/Environment'; import clsx from 'clsx'; import invariant from 'invariant'; import {useSnackbar} from 'notistack'; @@ -69,23 +68,14 @@ function parseInput( function invokeCompiler( source: string, language: 'flow' | 'typescript', - environment: EnvironmentConfig, - logIR: (pipelineValue: CompilerPipelineValue) => void, + options: PluginOptions, ): CompilerTransformOutput { - const opts: PluginOptions = parsePluginOptions({ - logger: { - debugLogIRs: logIR, - logEvent: () => {}, - }, - environment, - panicThreshold: 'all_errors', - }); const ast = parseInput(source, language); let result = transformFromAstSync(ast, source, { filename: '_playgroundFile.js', highlightCode: false, retainLines: true, - plugins: [[BabelPluginReactCompiler, opts]], + plugins: [[BabelPluginReactCompiler, options]], ast: true, sourceType: 'module', configFile: false, @@ -171,51 +161,59 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] { try { // Extract the first line to quickly check for custom test directives const pragma = source.substring(0, source.indexOf('\n')); - const config = parseConfigPragmaForTests(pragma); - - transformOutput = invokeCompiler( - source, - language, - {...config, customHooks: new Map([...COMMON_HOOKS])}, - result => { - switch (result.kind) { - case 'ast': { - break; - } - case 'hir': { - upsert({ - kind: 'hir', - fnName: result.value.id, - name: result.name, - value: printFunctionWithOutlined(result.value), - }); - break; - } - case 'reactive': { - upsert({ - kind: 'reactive', - fnName: result.value.id, - name: result.name, - value: printReactiveFunctionWithOutlined(result.value), - }); - break; - } - case 'debug': { - upsert({ - kind: 'debug', - fnName: null, - name: result.name, - value: result.value, - }); - break; - } - default: { - const _: never = result; - throw new Error(`Unhandled result ${result}`); - } + const logIR = (result: CompilerPipelineValue): void => { + switch (result.kind) { + case 'ast': { + break; + } + case 'hir': { + upsert({ + kind: 'hir', + fnName: result.value.id, + name: result.name, + value: printFunctionWithOutlined(result.value), + }); + break; + } + case 'reactive': { + upsert({ + kind: 'reactive', + fnName: result.value.id, + name: result.name, + value: printReactiveFunctionWithOutlined(result.value), + }); + break; } + case 'debug': { + upsert({ + kind: 'debug', + fnName: null, + name: result.name, + value: result.value, + }); + break; + } + default: { + const _: never = result; + throw new Error(`Unhandled result ${result}`); + } + } + }; + const parsedOptions = parseConfigPragmaForTests(pragma, { + compilationMode: 'infer', + }); + const opts: PluginOptions = parsePluginOptions({ + ...parsedOptions, + environment: { + ...parsedOptions.environment, + customHooks: new Map([...COMMON_HOOKS]), }, - ); + logger: { + debugLogIRs: logIR, + logEvent: () => {}, + }, + }); + transformOutput = invokeCompiler(source, language, opts); } catch (err) { /** * error might be an invariant violation or other runtime error diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index f3f426df56e44..f1db567660ec9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -9,7 +9,13 @@ import * as t from '@babel/types'; import {ZodError, z} from 'zod'; import {fromZodError} from 'zod-validation-error'; import {CompilerError} from '../CompilerError'; -import {Logger} from '../Entrypoint'; +import { + CompilationMode, + Logger, + PanicThresholdOptions, + parsePluginOptions, + PluginOptions, +} from '../Entrypoint'; import {Err, Ok, Result} from '../Utils/Result'; import { DEFAULT_GLOBALS, @@ -683,7 +689,9 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = { /** * For snap test fixtures and playground only. */ -export function parseConfigPragmaForTests(pragma: string): EnvironmentConfig { +function parseConfigPragmaEnvironmentForTest( + pragma: string, +): EnvironmentConfig { const maybeConfig: any = {}; // Get the defaults to programmatically check for boolean properties const defaultConfig = EnvironmentConfigSchema.parse({}); @@ -749,6 +757,48 @@ export function parseConfigPragmaForTests(pragma: string): EnvironmentConfig { suggestions: null, }); } +export function parseConfigPragmaForTests( + pragma: string, + defaults: { + compilationMode: CompilationMode; + }, +): PluginOptions { + const environment = parseConfigPragmaEnvironmentForTest(pragma); + let compilationMode: CompilationMode = defaults.compilationMode; + let panicThreshold: PanicThresholdOptions = 'all_errors'; + for (const token of pragma.split(' ')) { + if (!token.startsWith('@')) { + continue; + } + switch (token) { + case '@compilationMode(annotation)': { + compilationMode = 'annotation'; + break; + } + case '@compilationMode(infer)': { + compilationMode = 'infer'; + break; + } + case '@compilationMode(all)': { + compilationMode = 'all'; + break; + } + case '@compilationMode(syntax)': { + compilationMode = 'syntax'; + break; + } + case '@panicThreshold(none)': { + panicThreshold = 'none'; + break; + } + } + } + return parsePluginOptions({ + environment, + compilationMode, + panicThreshold, + }); +} export type PartialEnvironmentConfig = Partial; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts index dc4d5d25a47e2..903afe4c20b9f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts @@ -6,6 +6,7 @@ */ import {parseConfigPragmaForTests, validateEnvironmentConfig} from '..'; +import {defaultOptions} from '../Entrypoint'; describe('parseConfigPragmaForTests()', () => { it('parses flags in various forms', () => { @@ -19,13 +20,18 @@ describe('parseConfigPragmaForTests()', () => { const config = parseConfigPragmaForTests( '@enableUseTypeAnnotations @validateNoSetStateInPassiveEffects:true @validateNoSetStateInRender:false', + {compilationMode: defaultOptions.compilationMode}, ); expect(config).toEqual({ - ...defaultConfig, - enableUseTypeAnnotations: true, - validateNoSetStateInPassiveEffects: true, - validateNoSetStateInRender: false, - enableResetCacheOnSourceFileChanges: false, + ...defaultOptions, + panicThreshold: 'all_errors', + environment: { + ...defaultOptions.environment, + enableUseTypeAnnotations: true, + validateNoSetStateInPassiveEffects: true, + validateNoSetStateInRender: false, + enableResetCacheOnSourceFileChanges: false, + }, }); }); }); diff --git a/compiler/packages/snap/src/compiler.ts b/compiler/packages/snap/src/compiler.ts index a95c61450d840..6e59276c1cbb2 100644 --- a/compiler/packages/snap/src/compiler.ts +++ b/compiler/packages/snap/src/compiler.ts @@ -11,12 +11,9 @@ import {transformFromAstSync} from '@babel/core'; import * as BabelParser from '@babel/parser'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; -import assert from 'assert'; import type { - CompilationMode, Logger, LoggerEvent, - PanicThresholdOptions, PluginOptions, CompilerReactTarget, CompilerPipelineValue, @@ -51,31 +48,13 @@ function makePluginOptions( ValueKindEnum: typeof ValueKind, ): [PluginOptions, Array<{filename: string | null; event: LoggerEvent}>] { let gating = null; - let compilationMode: CompilationMode = 'all'; - let panicThreshold: PanicThresholdOptions = 'all_errors'; let hookPattern: string | null = null; // TODO(@mofeiZ) rewrite snap fixtures to @validatePreserveExistingMemo:false let validatePreserveExistingMemoizationGuarantees = false; let customMacros: null | Array = null; let validateBlocklistedImports = null; - let enableFire = false; let target: CompilerReactTarget = '19'; - if (firstLine.indexOf('@compilationMode(annotation)') !== -1) { - assert( - compilationMode === 'all', - 'Cannot set @compilationMode(..) more than once', - ); - compilationMode = 'annotation'; - } - if (firstLine.indexOf('@compilationMode(infer)') !== -1) { - assert( - compilationMode === 'all', - 'Cannot set @compilationMode(..) more than once', - ); - compilationMode = 'infer'; - } - if (firstLine.includes('@gating')) { gating = { source: 'ReactForgetFeatureFlag', @@ -96,10 +75,6 @@ function makePluginOptions( } } - if (firstLine.includes('@panicThreshold(none)')) { - panicThreshold = 'none'; - } - let eslintSuppressionRules: Array | null = null; const eslintSuppressionMatch = /@eslintSuppressionRules\(([^)]+)\)/.exec( firstLine, @@ -130,10 +105,6 @@ function makePluginOptions( validatePreserveExistingMemoizationGuarantees = true; } - if (firstLine.includes('@enableFire')) { - enableFire = true; - } - const hookPatternMatch = /@hookPattern:"([^"]+)"/.exec(firstLine); if ( hookPatternMatch && @@ -199,10 +170,11 @@ function makePluginOptions( debugLogIRs: debugIRLogger, }; - const config = parseConfigPragmaFn(firstLine); + const config = parseConfigPragmaFn(firstLine, {compilationMode: 'all'}); const options = { + ...config, environment: { - ...config, + ...config.environment, moduleTypeProvider: makeSharedRuntimeTypeProvider({ EffectEnum, ValueKindEnum, @@ -212,12 +184,9 @@ function makePluginOptions( hookPattern, validatePreserveExistingMemoizationGuarantees, validateBlocklistedImports, - enableFire, }, - compilationMode, logger, gating, - panicThreshold, noEmit: false, eslintSuppressionRules, flowSuppressions,