From c5dfad35246435d797c49ed712e1715f2777e61f Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Wed, 13 May 2026 17:30:30 +0200 Subject: [PATCH 1/2] ci: validate PR identity against workflow_run to prevent artifact poisoning (#36203) --- .github/scripts/validate-pr-number.js | 64 ++++++++++++++----- .github/workflows/bundle-size-comment.yml | 2 +- .github/workflows/pr-vrt-comment.yml | 11 +--- .../workflows/pr-website-deploy-comment.yml | 2 +- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/.github/scripts/validate-pr-number.js b/.github/scripts/validate-pr-number.js index d65dfd7e525270..e66ae042c9b243 100644 --- a/.github/scripts/validate-pr-number.js +++ b/.github/scripts/validate-pr-number.js @@ -6,11 +6,16 @@ module.exports = main; /** * - * @param {{filePath:string}} options - * @returns {number} + * @param {Pick & {filePath:string}} options + * @returns {Promise} */ -function main(options) { - return validatePrNumber(options.filePath); +async function main(options) { + const { filePath, github, context } = options; + const prNumber = validatePrNumber(filePath); + + await validatePrIdentity({ prNumber, github, context }); + + return prNumber; } /** @@ -19,18 +24,43 @@ function main(options) { * @returns {number} */ function validatePrNumber(filePath) { - try { - const content = readFileSync(filePath, 'utf-8').trim(); - const prNumber = Number(content); - - if (isNaN(prNumber) || !Number.isInteger(prNumber)) { - throw new Error('The ID in pr.txt is not a valid PR number.'); - } - - console.info('✅ PR ID valid'); - return prNumber; - } catch (err) { - console.error(`Error: ${err.message}`); - process.exit(1); + const content = readFileSync(filePath, 'utf-8').trim(); + const prNumber = Number(content); + + if (isNaN(prNumber) || !Number.isInteger(prNumber)) { + throw new Error('The ID in pr.txt is not a valid PR number.'); } + + console.info('✅ PR ID valid'); + return prNumber; +} + +/** + * Validates that the PR number from the artifact matches the workflow run that produced it. + * This prevents a malicious PR from injecting a different PR number in the artifact. + * + * NOTE: This function is designed to run exclusively within `workflow_run` triggered workflows. + * It uses `context.payload.workflow_run.repository` (not `context.repo`) because in a `workflow_run` + * context we need to reference the repository that originated the triggering workflow run. + * + * @param {{prNumber: number} & Pick} options + */ +async function validatePrIdentity({ prNumber, github, context }) { + const owner = context.payload.workflow_run.repository.owner.login; + const repo = context.payload.workflow_run.repository.name; + const expectedSha = context.payload.workflow_run.head_sha; + + const { data: pr } = await github.rest.pulls.get({ + owner, + repo, + pull_number: prNumber, + }); + + if (pr.head.sha !== expectedSha) { + throw new Error( + `❌ PR #${prNumber} head SHA (${pr.head.sha}) does not match workflow run head SHA (${expectedSha}).`, + ); + } + + console.info('✅ PR identity verified against workflow run'); } diff --git a/.github/workflows/bundle-size-comment.yml b/.github/workflows/bundle-size-comment.yml index 6750b27b6b40e4..9efffc8fb4b7cf 100644 --- a/.github/workflows/bundle-size-comment.yml +++ b/.github/workflows/bundle-size-comment.yml @@ -32,7 +32,7 @@ jobs: result-encoding: string script: | const run = require('./.github/scripts/validate-pr-number'); - return run({ filePath: 'results/pr.txt' }); + return await run({ filePath: 'results/pr.txt', github, context }); - name: 'Comment on PR' uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 diff --git a/.github/workflows/pr-vrt-comment.yml b/.github/workflows/pr-vrt-comment.yml index 76461a47d91a83..4198336a102504 100644 --- a/.github/workflows/pr-vrt-comment.yml +++ b/.github/workflows/pr-vrt-comment.yml @@ -74,7 +74,7 @@ jobs: with: script: | const run = require('./.github/scripts/validate-pr-number'); - const pull_number = run({filePath:'results/pr.txt'}); + const pull_number = await run({filePath:'results/pr.txt', github, context}); const result = await github.rest.pulls.get({ owner: context.payload.workflow_run.repository.owner.login, @@ -84,15 +84,6 @@ jobs: const pr = result.data; - if (pr.head.sha !== context.payload.workflow_run.head_commit.id) { - console.log( - "PR head sha does not match the workflow run head commit id. " + - "There has probably been a push to the PR, so we will not run the VR Diff for the last iteration. " + - "Exiting the script." - ); - process.exit(0); - } - const headOwner = pr.head.repo.owner.login; const baseOwner = pr.base.repo.owner.login; diff --git a/.github/workflows/pr-website-deploy-comment.yml b/.github/workflows/pr-website-deploy-comment.yml index 67a8fa2fa24bf9..42f808fe0dcd9f 100644 --- a/.github/workflows/pr-website-deploy-comment.yml +++ b/.github/workflows/pr-website-deploy-comment.yml @@ -50,7 +50,7 @@ jobs: with: script: | const run = require('./.github/scripts/validate-pr-number'); - const result = run({filePath:'results/pr.txt'}); + const result = await run({filePath:'results/pr.txt', github, context}); return result; result-encoding: string From f395ff9d7e54605b4200ebc71151e81dec87a6bb Mon Sep 17 00:00:00 2001 From: Martin Hochel Date: Wed, 13 May 2026 17:41:36 +0200 Subject: [PATCH 2/2] feat(workspace-plugin): add react-compiler-analyzer to library targets, support opt in react-compiler build executor flag (#36195) --- package.json | 2 + tools/workspace-plugin/eslint.config.js | 1 + .../executor/libs/react-compiler-proj/.swcrc | 30 +++ .../libs/react-compiler-proj/package.json | 3 + .../libs/react-compiler-proj/src/Counter.tsx | 11 ++ .../libs/react-compiler-proj/src/index.ts | 1 + .../libs/react-compiler-proj/tsconfig.json | 16 ++ .../react-compiler-proj/tsconfig.lib.json | 10 + .../src/executors/build/executor.spec.ts | 116 ++++++++++++ .../src/executors/build/executor.ts | 6 +- .../src/executors/build/lib/babel.ts | 167 +++++++++++++++-- .../src/executors/build/lib/shared.ts | 2 + .../src/executors/build/schema.d.ts | 5 + .../src/executors/build/schema.json | 5 + .../src/plugins/workspace-plugin.spec.ts | 171 ++++++++++++++++++ .../src/plugins/workspace-plugin.ts | 95 +++++++++- tools/workspace-plugin/tsconfig.lib.json | 2 +- tools/workspace-plugin/tsconfig.spec.json | 2 +- yarn.lock | 145 ++++++++++----- 19 files changed, 721 insertions(+), 69 deletions(-) create mode 100644 tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/.swcrc create mode 100644 tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/package.json create mode 100644 tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/Counter.tsx create mode 100644 tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/index.ts create mode 100644 tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.json create mode 100644 tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.lib.json diff --git a/package.json b/package.json index 9dc6138cef295c..76f465919e1891 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@eslint/compat": "1.3.0", "@eslint/js": "9.26.0", "@floating-ui/dom": "1.6.12", + "@fluentui/react-compiler-analyzer": "0.0.0-experimental.rc-analyzer.20260505-0d531266b1.0", "@fluentui/react-icons": "^2.0.306", "@griffel/babel-preset": "1.5.8", "@griffel/eslint-plugin": "^2.0.0", @@ -168,6 +169,7 @@ "babel-plugin-iife-wrap-react-components": "1.0.0-5", "babel-plugin-lodash": "3.3.4", "babel-plugin-module-resolver": "5.0.0", + "babel-plugin-react-compiler": "1.0.0", "babel-plugin-tester": "10.1.0", "beachball": "2.31.0", "chalk": "4.1.0", diff --git a/tools/workspace-plugin/eslint.config.js b/tools/workspace-plugin/eslint.config.js index 2a823e88956ffc..8f298c1aea9998 100644 --- a/tools/workspace-plugin/eslint.config.js +++ b/tools/workspace-plugin/eslint.config.js @@ -3,6 +3,7 @@ const fluentPlugin = require('@fluentui/eslint-plugin'); /** @type {import("eslint").Linter.Config[]} */ module.exports = [ + { ignores: ['src/**/__fixtures__/**'] }, ...fluentPlugin.configs['flat/node'], { rules: { diff --git a/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/.swcrc b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/.swcrc new file mode 100644 index 00000000000000..b4ffa86dee3067 --- /dev/null +++ b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/.swcrc @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "exclude": [ + "/testing", + "/**/*.cy.ts", + "/**/*.cy.tsx", + "/**/*.spec.ts", + "/**/*.spec.tsx", + "/**/*.test.ts", + "/**/*.test.tsx" + ], + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true, + "decorators": false, + "dynamicImport": false + }, + "externalHelpers": true, + "transform": { + "react": { + "runtime": "classic", + "useSpread": true + } + }, + "target": "es2019" + }, + "minify": false, + "sourceMaps": true +} diff --git a/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/package.json b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/package.json new file mode 100644 index 00000000000000..2833d5845cf606 --- /dev/null +++ b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/package.json @@ -0,0 +1,3 @@ +{ + "name": "react-compiler-proj" +} diff --git a/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/Counter.tsx b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/Counter.tsx new file mode 100644 index 00000000000000..454c18d2a5f002 --- /dev/null +++ b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/Counter.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +export function Counter({ initialCount = 0 }: { initialCount?: number }) { + const [count, setCount] = React.useState(initialCount); + return ( +
+

Count: {count}

+ +
+ ); +} diff --git a/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/index.ts b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/index.ts new file mode 100644 index 00000000000000..b592b6417b5258 --- /dev/null +++ b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/src/index.ts @@ -0,0 +1 @@ +export { Counter } from './Counter'; diff --git a/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.json b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.json new file mode 100644 index 00000000000000..623d6188701451 --- /dev/null +++ b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "moduleResolution": "Node", + "target": "ES2019", + "jsx": "react", + "skipLibCheck": true, + "pretty": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.lib.json b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.lib.json new file mode 100644 index 00000000000000..e7a7f01e96b009 --- /dev/null +++ b/tools/workspace-plugin/src/executors/build/__fixtures__/executor/libs/react-compiler-proj/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/out-tsc", + "declaration": true, + "types": [] + }, + "include": ["src/*.ts", "src/*.tsx"], + "exclude": ["src/*.spec.ts", "src/*.spec.tsx"] +} diff --git a/tools/workspace-plugin/src/executors/build/executor.spec.ts b/tools/workspace-plugin/src/executors/build/executor.spec.ts index 73cd97294c5e8f..e43d815289d8aa 100644 --- a/tools/workspace-plugin/src/executors/build/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/build/executor.spec.ts @@ -389,4 +389,120 @@ describe('Build Executor', () => { expect(existsSync(join(workspaceRoot, 'libs/proj/lib-commonjs/greeter.styles.raw.js.map'))).toBe(false); }, 60000); }); + + describe(`#reactCompiler`, () => { + const reactCompilerContext: ExecutorContext = { + root: workspaceRoot, + cwd: process.cwd(), + isVerbose: false, + projectName: 'react-compiler-proj', + projectsConfigurations: { + version: 2, + projects: { + 'react-compiler-proj': { + root: 'libs/react-compiler-proj', + name: 'react-compiler-proj', + }, + }, + }, + nxJsonConfiguration: {}, + projectGraph: { nodes: {}, dependencies: {} }, + }; + + it('applies react-compiler transforms to ESM output (without styles)', async () => { + jest.spyOn(logger, 'log').mockImplementation(() => { + return; + }); + jest.spyOn(logger, 'verbose').mockImplementation(() => { + return; + }); + + const reactCompilerOptions: BuildExecutorSchema = { + sourceRoot: 'src', + outputPathRoot: 'libs/react-compiler-proj/dist', + moduleOutput: [ + { module: 'es6', outputPath: 'lib' }, + { module: 'commonjs', outputPath: 'lib-commonjs' }, + ], + assets: [], + generateApi: false, + clean: true, + reactCompiler: true, + }; + + const output = await executor(reactCompilerOptions, reactCompilerContext); + expect(output.success).toBe(true); + + // assert react-compiler memoization cache is present in ESM output + expect(readFileSync(join(workspaceRoot, 'libs/react-compiler-proj/lib/Counter.js'), 'utf-8')) + .toMatchInlineSnapshot(` + "import { c as _c } from \\"react/compiler-runtime\\"; + import * as React from 'react'; + export function Counter(t0) { + const $ = _c(2); + const { initialCount: t1 } = t0; + const initialCount = t1 === undefined ? 0 : t1; + const [count, setCount] = React.useState(initialCount); + let t2; + if ($[0] !== count) { + t2 = /*#__PURE__*/ React.createElement(\\"div\\", null, /*#__PURE__*/ React.createElement(\\"p\\", null, \\"Count: \\", count), /*#__PURE__*/ React.createElement(\\"button\\", { + onClick: ()=>setCount(count + 1) + }, \\"Increment\\")); + $[0] = count; + $[1] = t2; + } else { + t2 = $[1]; + } + return t2; + } + " + `); + + // assert intermediate directory is cleaned up + expect(existsSync(join(workspaceRoot, 'libs/react-compiler-proj/temp/react-compiler-intermediate'))).toBe(false); + }, 60000); + + it('applies react-compiler before griffel AOT (with styles)', async () => { + jest.spyOn(logger, 'log').mockImplementation(() => { + return; + }); + jest.spyOn(logger, 'verbose').mockImplementation(() => { + return; + }); + + const optionsWithReactCompiler: BuildExecutorSchema = { + ...options, + reactCompiler: true, + }; + + const output = await executor(optionsWithReactCompiler, context); + expect(output.success).toBe(true); + + // assert greeter.js ESM output is valid (react-compiler ran on TS source, SWC compiled from intermediate) + expect(readFileSync(join(workspaceRoot, 'libs/proj/lib/greeter.js'), 'utf-8')).toMatchInlineSnapshot(` + "import { useStyles } from './greeter.styles'; + export function greeter(greeting, user) { + var _user_hometown; + const styles = useStyles(); + return \`

\${greeting} \${user.name} from \${(_user_hometown = user.hometown) === null || _user_hometown === void 0 ? void 0 : _user_hometown.name}

\`; + } + " + `); + + // assert griffel AOT still applied correctly on styles files + expect(readFileSync(join(workspaceRoot, 'libs/proj/lib/greeter.styles.js'), 'utf-8')).toMatchInlineSnapshot(` + "import { __styles } from '@griffel/react'; + export const useStyles = /*#__PURE__*/__styles({ + root: { + sj55zd: \\"fe3e8s9\\" + } + }, { + d: [\\".fe3e8s9{color:red;}\\"] + });" + `); + + // assert intermediate directory is cleaned up + expect(existsSync(join(workspaceRoot, 'libs/proj/temp/react-compiler-intermediate'))).toBe(false); + }, 60000); + }); }); diff --git a/tools/workspace-plugin/src/executors/build/executor.ts b/tools/workspace-plugin/src/executors/build/executor.ts index 8c14640b6a3f0a..fddb58be2f1467 100644 --- a/tools/workspace-plugin/src/executors/build/executor.ts +++ b/tools/workspace-plugin/src/executors/build/executor.ts @@ -1,7 +1,7 @@ import { type ExecutorContext, type PromiseExecutor } from '@nx/devkit'; import { compileSwc } from './lib/swc'; -import { compileWithGriffelStylesAOT, hasStylesFilesToProcess } from './lib/babel'; +import { compileWithGriffelStylesAOT, compileWithReactCompiler, hasStylesFilesToProcess } from './lib/babel'; import { assetGlobsToFiles, copyAssets } from './lib/assets'; import { cleanOutput } from './lib/clean'; import { NormalizedOptions, normalizeOptions, processAsyncQueue, runInParallel, runSerially } from './lib/shared'; @@ -49,6 +49,10 @@ async function runBuild(options: NormalizedOptions, _context: ExecutorContext): return compileWithGriffelStylesAOT(options); } + if (options.reactCompiler) { + return compileWithReactCompiler(options); + } + const compilationQueue = options.moduleOutput.map(outputConfig => { return compileSwc(outputConfig, options); }); diff --git a/tools/workspace-plugin/src/executors/build/lib/babel.ts b/tools/workspace-plugin/src/executors/build/lib/babel.ts index 1660890894c854..ed61bbf8234eb1 100644 --- a/tools/workspace-plugin/src/executors/build/lib/babel.ts +++ b/tools/workspace-plugin/src/executors/build/lib/babel.ts @@ -3,12 +3,14 @@ * TODO: remove this module and its usage once we will be able to remove griffel AOT from our build output -> https://github.com/microsoft/fluentui/blob/master/docs/react-v9/contributing/rfcs/shared/build-system/stop-styles-transforms.md */ -import { writeFile, readFile, copyFile } from 'node:fs/promises'; -import { basename, join } from 'node:path'; +import { writeFile, readFile, copyFile, mkdir, rm } from 'node:fs/promises'; +import { basename, dirname, join } from 'node:path'; import { type BabelFileResult, transformAsync } from '@babel/core'; import { globSync } from 'fast-glob'; -import { logger } from '@nx/devkit'; +import { logger, readJsonFile } from '@nx/devkit'; +import { isMatch } from 'micromatch'; +import { type Config } from '@swc/core'; import { processAsyncQueue, type NormalizedOptions } from './shared'; import { compileSwc } from './swc'; @@ -26,21 +28,7 @@ export function hasStylesFilesToProcess(normalizedOptions: NormalizedOptions) { } export async function compileWithGriffelStylesAOT(options: NormalizedOptions) { - const { esmConfig, restOfConfigs } = options.moduleOutput.reduce<{ - esmConfig: NormalizedOptions['moduleOutput'][number] | null; - restOfConfigs: NormalizedOptions['moduleOutput']; - }>( - (acc, outputConfig) => { - if (outputConfig.module === 'es6') { - acc.esmConfig = outputConfig; - return acc; - } - - acc.restOfConfigs.push(outputConfig); - return acc; - }, - { esmConfig: null, restOfConfigs: [] }, - ); + const { esmConfig, restOfConfigs } = splitEsmConfig(options.moduleOutput); if (!esmConfig) { logger.warn('es6 module output not specified. Skipping griffel AOT...'); @@ -55,7 +43,21 @@ export async function compileWithGriffelStylesAOT(options: NormalizedOptions) { logger.log('💅 Griffel RAW styles output enabled'); transforms.push(createStyleRawOutput); } - await compileSwc(esmConfig, options, transforms); + + // When react-compiler is enabled, run it on source TS/TSX BEFORE SWC (compiler needs JSX intact). + // Output goes to an intermediate dir (types stripped, JSX preserved), then SWC compiles from there. + let swcSourceOptions = options; + if (options.reactCompiler) { + const intermediateDir = await babelReactCompiler(options); + swcSourceOptions = { ...options, absoluteSourceRoot: intermediateDir }; + } + + await compileSwc(esmConfig, swcSourceOptions, transforms); + + if (options.reactCompiler) { + await rm(swcSourceOptions.absoluteSourceRoot, { recursive: true, force: true }); + } + await babel(esmConfig, options); const compilationQueue = restOfConfigs.map(outputConfig => { @@ -140,6 +142,133 @@ async function createStyleRawOutput(filePath: string): Promise { logger.verbose(`raw-style: created ${rawFilePath}`); } +function splitEsmConfig(moduleOutput: NormalizedOptions['moduleOutput']) { + return moduleOutput.reduce<{ + esmConfig: NormalizedOptions['moduleOutput'][number] | null; + restOfConfigs: NormalizedOptions['moduleOutput']; + }>( + (acc, outputConfig) => { + if (outputConfig.module === 'es6') { + acc.esmConfig = outputConfig; + return acc; + } + + acc.restOfConfigs.push(outputConfig); + return acc; + }, + { esmConfig: null, restOfConfigs: [] }, + ); +} + +/** + * Runs babel-plugin-react-compiler on source TS/TSX files. + * + * The compiler needs to see JSX (not React.createElement), so it must run BEFORE SWC transforms JSX. + * Uses @babel/preset-typescript to strip types while preserving JSX syntax. + * Outputs JS files (with JSX intact) to a temporary intermediate directory. + * SWC then compiles from the intermediate directory (handling JSX + module format). + * + * @returns The absolute path to the intermediate directory containing compiled files. + */ +async function babelReactCompiler(normalizedOptions: NormalizedOptions): Promise { + const sourceRoot = normalizedOptions.absoluteSourceRoot; + const intermediateDir = join(normalizedOptions.absoluteProjectRoot, 'temp/react-compiler-intermediate'); + + const files = globSync('**/*.{ts,tsx}', { cwd: sourceRoot }); + + if (files.length === 0) { + return intermediateDir; + } + + // Use same exclude patterns as SWC to skip test files + const swcConfigPath = join(normalizedOptions.absoluteProjectRoot, '.swcrc'); + const swcConfig = readJsonFile(swcConfigPath); + + const tsFileExtensionRegex = /\.(tsx|ts)$/; + let processedCount = 0; + + for (const filename of files) { + const srcFilePath = join(sourceRoot, filename); + const isFileExcluded = swcConfig.exclude ? isMatch(srcFilePath, swcConfig.exclude, { contains: true }) : false; + + if (isFileExcluded) { + continue; + } + + const codeBuffer = await readFile(srcFilePath); + const sourceCode = codeBuffer.toString().replace(EOL_REGEX, '\n'); + + const result = (await transformAsync(sourceCode, { + ast: false, + sourceMaps: true, + + // Isolated from project babel configs — only run react-compiler + babelrc: false, + configFile: false, + + // Strip TypeScript types but preserve JSX (no @babel/preset-react) + // Only enable isTSX for .tsx files — .ts files use angle-bracket type assertions that conflict with JSX parsing + presets: [['@babel/preset-typescript', { isTSX: srcFilePath.endsWith('.tsx'), allExtensions: true }]], + plugins: ['babel-plugin-react-compiler'], + + caller: { name: '@fluentui/workspace-plugin:build:react-compiler' }, + filename: srcFilePath, + + sourceFileName: basename(filename), + })) as NonNullableRecord; + + const jsFileName = filename.replace(tsFileExtensionRegex, '.js'); + const outputPath = join(intermediateDir, jsFileName); + + await mkdir(dirname(outputPath), { recursive: true }); + await writeFile(outputPath, result.code); + + if (result.map) { + await writeFile(outputPath + '.map', JSON.stringify(result.map)); + } + + processedCount++; + } + + logger.log(`Processing react-compiler with babel: ${processedCount} files`); + + return intermediateDir; +} + +export async function compileWithReactCompiler(options: NormalizedOptions) { + const { esmConfig, restOfConfigs } = splitEsmConfig(options.moduleOutput); + + if (!esmConfig) { + logger.warn('es6 module output not specified. Skipping react-compiler...'); + const compilationQueue = restOfConfigs.map(outputConfig => { + return compileSwc(outputConfig, options); + }); + return processAsyncQueue(compilationQueue); + } + + // Run react-compiler on source TS/TSX, output to intermediate dir (JS with JSX preserved) + const intermediateDir = await babelReactCompiler(options); + + // SWC compiles from intermediate dir (handles JSX transform + module format) + await compileSwc(esmConfig, { ...options, absoluteSourceRoot: intermediateDir }); + + // Clean up intermediate dir + await rm(intermediateDir, { recursive: true, force: true }); + + const compilationQueue = restOfConfigs.map(outputConfig => { + const overriddenSourceRoot = join(options.workspaceRoot, options.project.root); + // Transpile from ESM+react-compiler output → CJS (same pattern as Griffel AOT) + const overriddenAbsoluteSourceRoot = join(overriddenSourceRoot, esmConfig.outputPath); + + return compileSwc(outputConfig, { + ...options, + absoluteSourceRoot: overriddenAbsoluteSourceRoot, + }); + }); + + return processAsyncQueue(compilationQueue); +} + type NonNullableRecord = { [P in keyof T]-?: NonNullable; }; diff --git a/tools/workspace-plugin/src/executors/build/lib/shared.ts b/tools/workspace-plugin/src/executors/build/lib/shared.ts index 5b45ee530a087c..90723f19c49432 100644 --- a/tools/workspace-plugin/src/executors/build/lib/shared.ts +++ b/tools/workspace-plugin/src/executors/build/lib/shared.ts @@ -52,6 +52,7 @@ export function normalizeOptions(schema: BuildExecutorSchema, context: ExecutorC const absoluteSourceRoot = join(context.root, resolvedSourceRoot); const absoluteOutputPathRoot = join(context.root, schema.outputPathRoot); const enableGriffelRawStyles = schema.enableGriffelRawStyles ?? false; + const reactCompiler = schema.reactCompiler ?? false; return { ...defaults, @@ -62,6 +63,7 @@ export function normalizeOptions(schema: BuildExecutorSchema, context: ExecutorC absoluteProjectRoot, absoluteOutputPathRoot, enableGriffelRawStyles, + reactCompiler, workspaceRoot: context.root, }; diff --git a/tools/workspace-plugin/src/executors/build/schema.d.ts b/tools/workspace-plugin/src/executors/build/schema.d.ts index 5316c9a4054f76..7bafb80325775c 100644 --- a/tools/workspace-plugin/src/executors/build/schema.d.ts +++ b/tools/workspace-plugin/src/executors/build/schema.d.ts @@ -39,6 +39,11 @@ export interface BuildExecutorSchema { * This will generate additional files with '.styles.raw.js' extension that contain Griffel raw styles */ enableGriffelRawStyles?: boolean; + /** + * Enable babel-plugin-react-compiler processing. + * When enabled, source is processed with React Compiler as a separate Babel pass before Griffel AOT. + */ + reactCompiler?: boolean; /** * List of static assets. */ diff --git a/tools/workspace-plugin/src/executors/build/schema.json b/tools/workspace-plugin/src/executors/build/schema.json index b327a9df840088..a416eda7dbd3f4 100644 --- a/tools/workspace-plugin/src/executors/build/schema.json +++ b/tools/workspace-plugin/src/executors/build/schema.json @@ -62,6 +62,11 @@ "description": "Enable Griffel raw styles output. This will generate additional files with '.styles.raw.js' extension that contain Griffel raw styles", "default": false }, + "reactCompiler": { + "type": "boolean", + "description": "Enable babel-plugin-react-compiler processing. When enabled, source is processed with React Compiler as a separate Babel pass before Griffel AOT.", + "default": false + }, "assets": { "type": "array", "description": "List of static assets.", diff --git a/tools/workspace-plugin/src/plugins/workspace-plugin.spec.ts b/tools/workspace-plugin/src/plugins/workspace-plugin.spec.ts index b1e8cc100dd547..f4598066828725 100644 --- a/tools/workspace-plugin/src/plugins/workspace-plugin.spec.ts +++ b/tools/workspace-plugin/src/plugins/workspace-plugin.spec.ts @@ -558,6 +558,7 @@ describe(`workspace-plugin`, () => { "@swc/core", "@microsoft/api-extractor", "typescript", + "babel-plugin-react-compiler", ], }, ], @@ -906,12 +907,182 @@ describe(`workspace-plugin`, () => { } `); }); + + describe('react library project', () => { + describe('with react in peerDependencies', () => { + let targets: ReturnType; + let metadata: ReturnType; + + beforeEach(async () => { + await tempFs.createFiles({ + 'proj/library/project.json': serializeJson({ + root: 'proj/library', + name: 'proj', + projectType: 'library', + tags: ['vNext'], + } satisfies ProjectConfiguration), + 'proj/library/package.json': serializeJson({ + name: '@proj/proj', + peerDependencies: { react: '>=16' }, + } satisfies Partial), + }); + const results = await createNodesFunction(['proj/library/project.json'], options, context); + targets = getTargets(results, 'proj/library'); + metadata = getMetadata(results, 'proj/library'); + }); + + it('should add react-compiler-analyzer target group', async () => { + expect(targets?.['react-compiler-analyzer--lint']).toMatchInlineSnapshot(` + Object { + "cache": true, + "command": "yarn react-compiler-analyzer lint ./src", + "inputs": Array [ + "default", + Object { + "externalDependencies": Array [ + "babel-plugin-react-compiler", + ], + }, + ], + "metadata": Object { + "description": "Lint redundant 'use no memo' directives", + "help": Object { + "command": "yarn react-compiler-analyzer lint --help", + "example": Object { + "options": Object { + "fix": true, + }, + }, + }, + "technologies": Array [ + "react-compiler", + ], + }, + "options": Object { + "cwd": "proj/library", + }, + } + `); + + expect(targets?.['react-compiler-analyzer--analyze']).toMatchInlineSnapshot(` + Object { + "cache": true, + "command": "yarn react-compiler-analyzer analyze ./src", + "inputs": Array [ + "default", + Object { + "externalDependencies": Array [ + "babel-plugin-react-compiler", + ], + }, + ], + "metadata": Object { + "description": "Analyze React Compiler coverage and migration status", + "help": Object { + "command": "yarn react-compiler-analyzer analyze --help", + "example": Object { + "options": Object { + "annotate": true, + }, + }, + }, + "technologies": Array [ + "react-compiler", + ], + }, + "options": Object { + "cwd": "proj/library", + }, + } + `); + + expect(targets?.['react-compiler-analyzer']).toMatchInlineSnapshot(` + Object { + "cache": true, + "dependsOn": Array [ + "react-compiler-analyzer--lint", + ], + "executor": "nx:noop", + "inputs": Array [ + "default", + Object { + "externalDependencies": Array [ + "babel-plugin-react-compiler", + ], + }, + ], + "metadata": Object { + "description": "React Compiler analysis (runs lint on CI)", + "help": Object { + "command": "yarn react-compiler-analyzer --help", + "example": Object {}, + }, + "technologies": Array [ + "react-compiler", + ], + }, + } + `); + + expect(metadata?.targetGroups).toMatchInlineSnapshot(` + Object { + "React Compiler Analyzer": Array [ + "react-compiler-analyzer--lint", + "react-compiler-analyzer--analyze", + "react-compiler-analyzer", + ], + "React Integration Tester": Array [ + "react-integration-testing", + ], + } + `); + }); + + it('should not set reactCompiler build option by default', async () => { + expect(targets?.build.options.reactCompiler).toBeUndefined(); + }); + }); + + describe('without react in peerDependencies', () => { + let targets: ReturnType; + + beforeEach(async () => { + await tempFs.createFiles({ + 'proj/library/project.json': serializeJson({ + root: 'proj/library', + name: 'proj', + projectType: 'library', + tags: ['vNext'], + } satisfies ProjectConfiguration), + 'proj/library/package.json': serializeJson({ + name: '@proj/proj', + private: true, + } satisfies Partial), + }); + const results = await createNodesFunction(['proj/library/project.json'], options, context); + targets = getTargets(results, 'proj/library'); + }); + + it('should not add react-compiler-analyzer targets', async () => { + expect(targets?.['react-compiler-analyzer']).toBeUndefined(); + expect(targets?.['react-compiler-analyzer--lint']).toBeUndefined(); + expect(targets?.['react-compiler-analyzer--analyze']).toBeUndefined(); + }); + + it('should not set reactCompiler build option', async () => { + expect(targets?.build.options.reactCompiler).toBeUndefined(); + }); + }); + }); }); }); function getTargets(results: CreateNodesResultV2, projRoot = 'proj') { return results[0][1].projects?.[projRoot].targets; } +function getMetadata(results: CreateNodesResultV2, projRoot = 'proj') { + return results[0][1].projects?.[projRoot].metadata; +} function getTargetsNames(results: CreateNodesResultV2, projRoot = 'proj'): string[] { return Object.keys(getTargets(results, projRoot) ?? {}); } diff --git a/tools/workspace-plugin/src/plugins/workspace-plugin.ts b/tools/workspace-plugin/src/plugins/workspace-plugin.ts index 6e1b444fd4ba0a..7af58ba2f027ef 100644 --- a/tools/workspace-plugin/src/plugins/workspace-plugin.ts +++ b/tools/workspace-plugin/src/plugins/workspace-plugin.ts @@ -127,7 +127,14 @@ function createNodesInternal( projects: { [projectRoot]: { targets: { ...workspaceConfig.targets, ...ritConfig.targets }, - metadata: { ...workspaceConfig.metadata, ...ritConfig.metadata }, + metadata: { + ...workspaceConfig.metadata, + ...ritConfig.metadata, + targetGroups: { + ...workspaceConfig.metadata?.targetGroups, + ...ritConfig.metadata?.targetGroups, + }, + }, }, }, }; @@ -205,6 +212,8 @@ function buildWorkspaceProjectConfiguration( const { value: userExportSubpaths, enabled: userEnabledExportSubpaths } = resolveExportSubpathsOption(config); + const isReactProject = Boolean(config.packageJSON.peerDependencies?.react); + targets.build = { cache: true, executor: '@fluentui/workspace-plugin:build', @@ -229,7 +238,9 @@ function buildWorkspaceProjectConfiguration( '{projectRoot}/package.json', '{projectRoot}/.swcrc', ...targets['generate-api'].inputs!, - { externalDependencies: ['@swc/core', '@microsoft/api-extractor', 'typescript'] }, + { + externalDependencies: ['@swc/core', '@microsoft/api-extractor', 'typescript', 'babel-plugin-react-compiler'], + }, ], outputs: [ `{projectRoot}/lib`, @@ -265,12 +276,90 @@ function buildWorkspaceProjectConfiguration( targets[options.verifyPackaging.targetName] = verifyPackagingTarget; } - return { targets }; + let metadata: WorkspaceTargets['metadata']; + + if (isReactProject) { + const rcaConfig = buildReactCompilerAnalyzerTargets(projectRoot, options, context, config); + Object.assign(targets, rcaConfig.targets); + metadata = rcaConfig.metadata; + } + + return { targets, metadata }; } return { targets }; } +function buildReactCompilerAnalyzerTargets( + projectRoot: string, + options: Required, + context: CreateNodesContextV2, + config: TaskBuilderConfig, +): WorkspaceTargets { + const targets: Record = {}; + const groupName = 'React Compiler Analyzer'; + const metadata = { targetGroups: { [groupName]: [] as string[] } }; + + const inputs = ['default', { externalDependencies: ['babel-plugin-react-compiler'] }]; + + targets['react-compiler-analyzer--lint'] = { + command: `${config.pmc.exec} react-compiler-analyzer lint ./src`, + options: { cwd: projectRoot }, + cache: true, + inputs, + metadata: { + technologies: ['react-compiler'], + description: "Lint redundant 'use no memo' directives", + help: { + command: `${config.pmc.exec} react-compiler-analyzer lint --help`, + example: { + options: { + fix: true, + }, + }, + }, + }, + }; + + targets['react-compiler-analyzer--analyze'] = { + command: `${config.pmc.exec} react-compiler-analyzer analyze ./src`, + options: { cwd: projectRoot }, + cache: true, + inputs, + metadata: { + technologies: ['react-compiler'], + description: 'Analyze React Compiler coverage and migration status', + help: { + command: `${config.pmc.exec} react-compiler-analyzer analyze --help`, + example: { + options: { + annotate: true, + }, + }, + }, + }, + }; + + targets['react-compiler-analyzer'] = { + executor: 'nx:noop', + cache: true, + dependsOn: ['react-compiler-analyzer--lint'], + inputs, + metadata: { + technologies: ['react-compiler'], + description: 'React Compiler analysis (runs lint on CI)', + help: { + command: `${config.pmc.exec} react-compiler-analyzer --help`, + example: {}, + }, + }, + }; + + metadata.targetGroups[groupName].push(...Object.keys(targets)); + + return { targets, metadata }; +} + function resolveExportSubpathsOption(config: TaskBuilderConfig): { value: boolean | { apiReport?: boolean }; enabled: boolean; diff --git a/tools/workspace-plugin/tsconfig.lib.json b/tools/workspace-plugin/tsconfig.lib.json index 3c6e3cfef8a8a2..dcc610a15706e2 100644 --- a/tools/workspace-plugin/tsconfig.lib.json +++ b/tools/workspace-plugin/tsconfig.lib.json @@ -6,5 +6,5 @@ "types": ["node"] }, "include": ["src/**/*.ts"], - "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "__fixtures__/**/*"] + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "src/**/__fixtures__/**/*"] } diff --git a/tools/workspace-plugin/tsconfig.spec.json b/tools/workspace-plugin/tsconfig.spec.json index f5d8e32cddc3ef..698ba4e031c989 100644 --- a/tools/workspace-plugin/tsconfig.spec.json +++ b/tools/workspace-plugin/tsconfig.spec.json @@ -6,5 +6,5 @@ "types": ["jest", "node"] }, "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"], - "exclude": ["__fixtures__/**/*"] + "exclude": ["src/**/__fixtures__/**/*"] } diff --git a/yarn.lock b/yarn.lock index 39923c4738df9f..55af5ef5dbe1ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -189,21 +189,21 @@ jsonwebtoken "^9.0.0" uuid "^8.3.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.5.5": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0", "@babel/code-frame@^7.5.5": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" - integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5", "@babel/compat-data@^7.28.6": + version "7.29.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.3.tgz#e3f5347f0589596c91d227ccb6a541d37fb1307b" + integrity sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg== -"@babel/core@7.28.5", "@babel/core@^7.10.4", "@babel/core@^7.13.16", "@babel/core@^7.18.2", "@babel/core@^7.18.9", "@babel/core@^7.20.0", "@babel/core@^7.23.2", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.27.4": +"@babel/core@7.28.5", "@babel/core@^7.10.4", "@babel/core@^7.13.16", "@babel/core@^7.18.2", "@babel/core@^7.18.9", "@babel/core@^7.20.0", "@babel/core@^7.23.2", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.27.4", "@babel/core@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== @@ -224,7 +224,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@7.28.5", "@babel/generator@>=7", "@babel/generator@^7.10.3", "@babel/generator@^7.12.13", "@babel/generator@^7.23.0", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5", "@babel/generator@^7.4.4": +"@babel/generator@7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== @@ -235,6 +235,17 @@ "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" +"@babel/generator@>=7", "@babel/generator@^7.10.3", "@babel/generator@^7.12.13", "@babel/generator@^7.23.0", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5", "@babel/generator@^7.29.0", "@babel/generator@^7.4.4": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== + dependencies: + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": version "7.27.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" @@ -243,11 +254,11 @@ "@babel/types" "^7.27.3" "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: - "@babel/compat-data" "^7.27.2" + "@babel/compat-data" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -306,22 +317,22 @@ "@babel/traverse" "^7.28.5" "@babel/types" "^7.28.5" -"@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.27.1", "@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" "@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" - integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" @@ -366,7 +377,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": +"@babel/helper-validator-identifier@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== @@ -386,20 +397,27 @@ "@babel/types" "^7.28.2" "@babel/helpers@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" - integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.2.tgz#9cfbccb02b8e229892c0b07038052cc1a8709c49" + integrity sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.4" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" -"@babel/parser@7.28.5", "@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.13.16", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5", "@babel/parser@^7.4.5": +"@babel/parser@7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: "@babel/types" "^7.28.5" +"@babel/parser@^7.1.0", "@babel/parser@^7.10.3", "@babel/parser@^7.13.16", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.28.5", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0", "@babel/parser@^7.4.5": + version "7.29.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.3.tgz#116f70a77958307fceac27747573032f8a62f88e" + integrity sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA== + dependencies: + "@babel/types" "^7.29.0" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz#fbde57974707bbfa0376d34d425ff4fa6c732421" @@ -1289,7 +1307,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.27.1" "@babel/plugin-transform-react-pure-annotations" "^7.27.1" -"@babel/preset-typescript@7.28.5", "@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.22.5": +"@babel/preset-typescript@7.28.5", "@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.22.5", "@babel/preset-typescript@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz#540359efa3028236958466342967522fd8f2a60c" integrity sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g== @@ -1323,16 +1341,16 @@ resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.28.5.tgz#4fced2b23f9670a04b30cc4942c3e4b87bce4eff" integrity sha512-1DViPYJpRU50irpGMfLBQ9B4kyfQuL6X7SS7pwTeWeZX0mNkjzPi0XFqxCjSdddZXUQy4AhnQnnesA/ZHnvAdw== -"@babel/template@>=7", "@babel/template@^7.0.0", "@babel/template@^7.12.13", "@babel/template@^7.22.15", "@babel/template@^7.27.1", "@babel/template@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== +"@babel/template@>=7", "@babel/template@^7.0.0", "@babel/template@^7.12.13", "@babel/template@^7.22.15", "@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/traverse@7.28.5", "@babel/traverse@>=7", "@babel/traverse@^7.10.3", "@babel/traverse@^7.12.13", "@babel/traverse@^7.16.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.23.2", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5", "@babel/traverse@^7.4.5": +"@babel/traverse@7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== @@ -1345,7 +1363,20 @@ "@babel/types" "^7.28.5" debug "^4.3.1" -"@babel/types@7.28.5", "@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.10.4", "@babel/types@^7.18.9", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.4.4": +"@babel/traverse@>=7", "@babel/traverse@^7.10.3", "@babel/traverse@^7.12.13", "@babel/traverse@^7.16.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.23.2", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.4.5": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + debug "^4.3.1" + +"@babel/types@7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== @@ -1353,6 +1384,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" +"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.10.4", "@babel/types@^7.18.9", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.26.0", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0", "@babel/types@^7.4.4": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1840,6 +1879,17 @@ prop-types "^15.7.2" react-is "^17.0.2" +"@fluentui/react-compiler-analyzer@0.0.0-experimental.rc-analyzer.20260505-0d531266b1.0": + version "0.0.0-experimental.rc-analyzer.20260505-0d531266b1.0" + resolved "https://registry.yarnpkg.com/@fluentui/react-compiler-analyzer/-/react-compiler-analyzer-0.0.0-experimental.rc-analyzer.20260505-0d531266b1.0.tgz#1d4640114fe797c40379c8e2f63d6181c83c652f" + integrity sha512-PdjcIsqGk3BurY07nN1oviRAlQvUoN+sdSzTRmVuAkmm8WOBflFR4RxJSCAC+0UCkAUkIZK//SkVKkAAN3oQ8g== + dependencies: + "@babel/core" "^7.28.5" + "@babel/preset-typescript" "^7.28.5" + "@swc/helpers" "~0.5.1" + babel-plugin-react-compiler "^1.0.0" + yargs "^17.7.2" + "@fluentui/react-component-event-listener@^0.66.5": version "0.66.5" resolved "https://registry.yarnpkg.com/@fluentui/react-component-event-listener/-/react-component-event-listener-0.66.5.tgz#2d8821f77d9485eac937fdf4dbb8eb87ac287145" @@ -6477,6 +6527,13 @@ babel-plugin-polyfill-regenerator@^0.6.5: dependencies: "@babel/helper-define-polyfill-provider" "^0.6.5" +babel-plugin-react-compiler@1.0.0, babel-plugin-react-compiler@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz#bdf7360a23a4d5ebfca090255da3893efd07425f" + integrity sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw== + dependencies: + "@babel/types" "^7.26.0" + babel-plugin-tester@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-tester/-/babel-plugin-tester-10.1.0.tgz#e099ee1d8dec538439c427a7d12aad132885757b"