diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index 3879b06aeee..00000000000
--- a/.babelrc
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "presets": ["react"],
- "ignore": ["third_party"],
- "plugins": [
- "transform-class-properties",
- "syntax-trailing-function-commas",
- ["transform-object-rest-spread", { "useBuiltIns": true }],
- "transform-es2015-template-literals",
- "transform-es2015-literals",
- "transform-es2015-arrow-functions",
- "transform-es2015-block-scoped-functions",
- ["transform-es2015-classes", { "loose": true }],
- "transform-es2015-object-super",
- "transform-es2015-shorthand-properties",
- "transform-es2015-computed-properties",
- "transform-es2015-for-of",
- "check-es2015-constants",
- ["transform-es2015-spread", { "loose": true }],
- "transform-es2015-parameters",
- ["transform-es2015-destructuring", { "loose": true }],
- ["transform-es2015-block-scoping", { "throwIfClosureRequired": true }]
- ]
-}
diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 6c0b662c1f8..00000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-version: 2
-jobs:
- build:
-
- docker:
- - image: circleci/openjdk:8-jdk-node-browsers
-
- environment:
- TZ: /usr/share/zoneinfo/America/Los_Angeles
-
- parallelism: 4
-
- steps:
- - checkout
-
- - run: echo $CIRCLE_COMPARE_URL | cut -d/ -f7
-
- - restore_cache:
- name: Restore node_modules cache
- keys:
- - v1-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
- - v1-node-{{ arch }}-{{ .Branch }}-
- - v1-node-{{ arch }}-
-
- - run:
- name: Nodejs Version
- command: node --version
-
- - run:
- name: Install Packages
- command: yarn install --frozen-lockfile
-
- - run:
- name: Test Packages
- command: ./scripts/circleci/test_entry_point.sh
-
- - save_cache:
- name: Save node_modules cache
- key: v1-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
- paths:
- - node_modules
-
- - store_artifacts:
- path: ./node_modules.tgz
-
- - store_artifacts:
- path: ./build.tgz
-
- - store_artifacts:
- path: ./build/bundle-sizes.json
-
- - store_artifacts:
- path: ./scripts/error-codes/codes.json
diff --git a/.claude/instructions.md b/.claude/instructions.md
new file mode 100644
index 00000000000..39573f6c449
--- /dev/null
+++ b/.claude/instructions.md
@@ -0,0 +1,46 @@
+# React
+
+**Scope**: All code EXCEPT `/compiler/` (compiler has its own instructions).
+
+## Project Structure
+
+| Directory | Purpose |
+|-----------|---------|
+| `/packages/` | Publishable packages (react, react-dom, scheduler, etc.) |
+| `/scripts/` | Build, test, and development scripts |
+| `/fixtures/` | Test applications for manual testing |
+| `/compiler/` | React Compiler (separate sub-project) |
+
+## Key Packages
+
+| Package | Purpose |
+|---------|---------|
+| `react` | Core React library |
+| `react-dom` | DOM renderer |
+| `react-reconciler` | Core reconciliation algorithm |
+| `scheduler` | Cooperative scheduling |
+| `react-server-dom-*` | Server Components |
+| `react-devtools-*` | Developer Tools |
+| `react-refresh` | Fast Refresh runtime |
+
+## Requirements
+
+- **Node**: Must be installed. Stop and prompt user if missing.
+- **Package Manager**: Use `yarn` only.
+
+## Verification
+
+**IMPORTANT**: Use `/verify` to validate all changes before committing.
+
+## Commands
+
+| Command | Purpose |
+|----------|----------------------|
+| `/fix` | Lint and format code |
+| `/test` | Run tests |
+| `/flow` | Type check with Flow |
+| `/flags` | Check feature flags |
+
+## Building
+
+Builds are handled by CI. Do not run locally unless instructed.
diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 00000000000..f12f21abf44
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,44 @@
+{
+ "hooks": {
+ "SessionStart": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "if [[ \"$PWD\" != */compiler* ]]; then cat .claude/instructions.md 2>/dev/null || true; fi"
+ }
+ ]
+ }
+ ]
+ },
+ "permissions": {
+ "allow": [
+ "Skill(extract-errors)",
+ "Skill(feature-flags)",
+ "Skill(fix)",
+ "Skill(flags)",
+ "Skill(flow)",
+ "Skill(test)",
+ "Skill(verify)",
+ "Bash(yarn test:*)",
+ "Bash(yarn test-www:*)",
+ "Bash(yarn test-classic:*)",
+ "Bash(yarn test-stable:*)",
+ "Bash(yarn linc:*)",
+ "Bash(yarn lint:*)",
+ "Bash(yarn flow:*)",
+ "Bash(yarn prettier:*)",
+ "Bash(yarn build:*)",
+ "Bash(yarn extract-errors:*)",
+ "Bash(yarn flags:*)"
+ ],
+ "deny": [
+ "Bash(yarn download-build:*)",
+ "Bash(yarn download-build-for-head:*)",
+ "Bash(npm:*)",
+ "Bash(pnpm:*)",
+ "Bash(bun:*)",
+ "Bash(npx:*)"
+ ]
+ }
+}
diff --git a/.claude/skills/extract-errors/SKILL.md b/.claude/skills/extract-errors/SKILL.md
new file mode 100644
index 00000000000..08bb1830eb4
--- /dev/null
+++ b/.claude/skills/extract-errors/SKILL.md
@@ -0,0 +1,12 @@
+---
+name: extract-errors
+description: Use when adding new error messages to React, or seeing "unknown error code" warnings.
+---
+
+# Extract Error Codes
+
+## Instructions
+
+1. Run `yarn extract-errors`
+2. Report if any new errors need codes assigned
+3. Check if error codes are up to date
diff --git a/.claude/skills/feature-flags/SKILL.md b/.claude/skills/feature-flags/SKILL.md
new file mode 100644
index 00000000000..8e3281a1af2
--- /dev/null
+++ b/.claude/skills/feature-flags/SKILL.md
@@ -0,0 +1,79 @@
+---
+name: feature-flags
+description: Use when feature flag tests fail, flags need updating, understanding @gate pragmas, debugging channel-specific test failures, or adding new flags to React.
+---
+
+# React Feature Flags
+
+## Flag Files
+
+| File | Purpose |
+|------|---------|
+| `packages/shared/ReactFeatureFlags.js` | Default flags (canary), `__EXPERIMENTAL__` overrides |
+| `packages/shared/forks/ReactFeatureFlags.www.js` | www channel, `__VARIANT__` overrides |
+| `packages/shared/forks/ReactFeatureFlags.native-fb.js` | React Native, `__VARIANT__` overrides |
+| `packages/shared/forks/ReactFeatureFlags.test-renderer.js` | Test renderer |
+
+## Gating Tests
+
+### `@gate` pragma (test-level)
+
+Use when the feature is completely unavailable without the flag:
+
+```javascript
+// @gate enableViewTransition
+it('supports view transitions', () => {
+ // This test only runs when enableViewTransition is true
+ // and is SKIPPED (not failed) when false
+});
+```
+
+### `gate()` inline (assertion-level)
+
+Use when the feature exists but behavior differs based on flag:
+
+```javascript
+it('renders component', async () => {
+ await act(() => root.render());
+
+ if (gate(flags => flags.enableNewBehavior)) {
+ expect(container.textContent).toBe('new output');
+ } else {
+ expect(container.textContent).toBe('legacy output');
+ }
+});
+```
+
+## Adding a New Flag
+
+1. Add to `ReactFeatureFlags.js` with default value
+2. Add to each fork file (`*.www.js`, `*.native-fb.js`, etc.)
+3. If it should vary in www/RN, set to `__VARIANT__` in the fork file
+4. Gate tests with `@gate flagName` or inline `gate()`
+
+## Checking Flag States
+
+Use `/flags` to view states across channels. See the `flags` skill for full command options.
+
+## `__VARIANT__` Flags (GKs)
+
+Flags set to `__VARIANT__` simulate gatekeepers - tested twice (true and false):
+
+```bash
+/test www # __VARIANT__ = true
+/test www variant false # __VARIANT__ = false
+```
+
+## Debugging Channel-Specific Failures
+
+1. Run `/flags --diff ` to compare values
+2. Check `@gate` conditions - test may be gated to specific channels
+3. Run `/test ` to isolate the failure
+4. Verify flag exists in all fork files if newly added
+
+## Common Mistakes
+
+- **Forgetting both variants** - Always test `www` AND `www variant false` for `__VARIANT__` flags
+- **Using @gate for behavior differences** - Use inline `gate()` if both paths should run
+- **Missing fork files** - New flags must be added to ALL fork files, not just the main one
+- **Wrong gate syntax** - It's `gate(flags => flags.name)`, not `gate('name')`
diff --git a/.claude/skills/fix/SKILL.md b/.claude/skills/fix/SKILL.md
new file mode 100644
index 00000000000..9335409af1d
--- /dev/null
+++ b/.claude/skills/fix/SKILL.md
@@ -0,0 +1,17 @@
+---
+name: fix
+description: Use when you have lint errors, formatting issues, or before committing code to ensure it passes CI.
+---
+
+# Fix Lint and Formatting
+
+## Instructions
+
+1. Run `yarn prettier` to fix formatting
+2. Run `yarn linc` to check for remaining lint issues
+3. Report any remaining manual fixes needed
+
+## Common Mistakes
+
+- **Running prettier on wrong files** - `yarn prettier` only formats changed files
+- **Ignoring linc errors** - These will fail CI, fix them before committing
diff --git a/.claude/skills/flags/SKILL.md b/.claude/skills/flags/SKILL.md
new file mode 100644
index 00000000000..a9ddc331ea9
--- /dev/null
+++ b/.claude/skills/flags/SKILL.md
@@ -0,0 +1,39 @@
+---
+name: flags
+description: Use when you need to check feature flag states, compare channels, or debug why a feature behaves differently across release channels.
+---
+
+# Feature Flags
+
+Arguments:
+- $ARGUMENTS: Optional flags
+
+## Options
+
+| Option | Purpose |
+|--------|---------|
+| (none) | Show all flags across all channels |
+| `--diff ` | Compare flags between channels |
+| `--cleanup` | Show flags grouped by cleanup status |
+| `--csv` | Output in CSV format |
+
+## Channels
+
+- `www`, `www-modern` - Meta internal
+- `canary`, `next`, `experimental` - OSS channels
+- `rn`, `rn-fb`, `rn-next` - React Native
+
+## Legend
+
+โ
enabled, โ disabled, ๐งช `__VARIANT__`, ๐ profiling-only
+
+## Instructions
+
+1. Run `yarn flags $ARGUMENTS`
+2. Explain the output to the user
+3. For --diff, highlight meaningful differences
+
+## Common Mistakes
+
+- **Forgetting `__VARIANT__` flags** - These are tested both ways in www; check both variants
+- **Comparing wrong channels** - Use `--diff` to see exact differences
diff --git a/.claude/skills/flow/SKILL.md b/.claude/skills/flow/SKILL.md
new file mode 100644
index 00000000000..5ef34a20129
--- /dev/null
+++ b/.claude/skills/flow/SKILL.md
@@ -0,0 +1,30 @@
+---
+name: flow
+description: Use when you need to run Flow type checking, or when seeing Flow type errors in React code.
+---
+
+# Flow Type Checking
+
+Arguments:
+- $ARGUMENTS: Renderer to check (default: dom-node)
+
+## Renderers
+
+| Renderer | When to Use |
+|----------|-------------|
+| `dom-node` | Default, recommended for most changes |
+| `dom-browser` | Browser-specific DOM code |
+| `native` | React Native |
+| `fabric` | React Native Fabric |
+
+## Instructions
+
+1. Run `yarn flow $ARGUMENTS` (use `dom-node` if no argument)
+2. Report type errors with file locations
+3. For comprehensive checking (slow), use `yarn flow-ci`
+
+## Common Mistakes
+
+- **Running without a renderer** - Always specify or use default `dom-node`
+- **Ignoring suppressions** - Check if `$FlowFixMe` comments are masking real issues
+- **Missing type imports** - Ensure types are imported from the correct package
diff --git a/.claude/skills/test/SKILL.md b/.claude/skills/test/SKILL.md
new file mode 100644
index 00000000000..e150ad7e549
--- /dev/null
+++ b/.claude/skills/test/SKILL.md
@@ -0,0 +1,46 @@
+---
+name: test
+description: Use when you need to run tests for React core. Supports source, www, stable, and experimental channels.
+---
+
+Run tests for the React codebase.
+
+Arguments:
+- $ARGUMENTS: Channel, flags, and test pattern
+
+Usage Examples:
+- `/test ReactFiberHooks` - Run with source channel (default)
+- `/test experimental ReactFiberHooks` - Run with experimental channel
+- `/test www ReactFiberHooks` - Run with www-modern channel
+- `/test www variant false ReactFiberHooks` - Test __VARIANT__=false
+- `/test stable ReactFiberHooks` - Run with stable channel
+- `/test classic ReactFiberHooks` - Run with www-classic channel
+- `/test watch ReactFiberHooks` - Run in watch mode (TDD)
+
+Release Channels:
+- `(default)` - Source/canary channel, uses ReactFeatureFlags.js defaults
+- `experimental` - Source/experimental channel with __EXPERIMENTAL__ flags = true
+- `www` - www-modern channel with __VARIANT__ flags = true
+- `www variant false` - www channel with __VARIANT__ flags = false
+- `stable` - What ships to npm
+- `classic` - Legacy www-classic (rarely needed)
+
+Instructions:
+1. Parse channel from arguments (default: source)
+2. Map to yarn command:
+ - (default) โ `yarn test --silent --no-watchman `
+ - experimental โ `yarn test -r=experimental --silent --no-watchman `
+ - stable โ `yarn test-stable --silent --no-watchman `
+ - classic โ `yarn test-classic --silent --no-watchman `
+ - www โ `yarn test-www --silent --no-watchman `
+ - www variant false โ `yarn test-www --variant=false --silent --no-watchman `
+3. Report test results and any failures
+
+Hard Rules:
+1. **Use --silent to see failures** - This limits the test output to only failures.
+2. **Use --no-watchman** - This is a common failure in sandboxing.
+
+Common Mistakes:
+- **Running without a pattern** - Runs ALL tests, very slow. Always specify a pattern.
+- **Forgetting both www variants** - Test `www` AND `www variant false` for `__VARIANT__` flags.
+- **Test skipped unexpectedly** - Check for `@gate` pragma; see `feature-flags` skill.
diff --git a/.claude/skills/verify/SKILL.md b/.claude/skills/verify/SKILL.md
new file mode 100644
index 00000000000..daed015e28e
--- /dev/null
+++ b/.claude/skills/verify/SKILL.md
@@ -0,0 +1,24 @@
+---
+name: verify
+description: Use when you want to validate changes before committing, or when you need to check all React contribution requirements.
+---
+
+# Verification
+
+Run all verification steps.
+
+Arguments:
+- $ARGUMENTS: Test pattern for the test step
+
+## Instructions
+
+Run these first in sequence:
+1. Run `yarn prettier` - format code (stop if fails)
+2. Run `yarn linc` - lint changed files (stop if fails)
+
+Then run these with subagents in parallel:
+1. Use `/flow` to type check (stop if fails)
+2. Use `/test` to test changes in source (stop if fails)
+3. Use `/test www` to test changes in www (stop if fails)
+
+If all pass, show success summary. On failure, stop immediately and report the issue with suggested fixes.
diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json
new file mode 100644
index 00000000000..2164efec0c8
--- /dev/null
+++ b/.codesandbox/ci.json
@@ -0,0 +1,13 @@
+{
+ "packages": ["packages/react", "packages/react-dom", "packages/react-server-dom-webpack", "packages/scheduler"],
+ "buildCommand": "download-build-in-codesandbox-ci",
+ "node": "20",
+ "publishDirectory": {
+ "react": "build/oss-experimental/react",
+ "react-dom": "build/oss-experimental/react-dom",
+ "react-server-dom-webpack": "build/oss-experimental/react-server-dom-webpack",
+ "scheduler": "build/oss-experimental/scheduler"
+ },
+ "sandboxes": ["new"],
+ "silent": true
+}
diff --git a/.editorconfig b/.editorconfig
index d7638731d07..48d2b3d27e8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,4 +1,4 @@
-# http://editorconfig.org
+# https://editorconfig.org
root = true
[*]
@@ -8,11 +8,9 @@ indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 80
-trim_trailing_whitespace = true
[*.md]
max_line_length = 0
-trim_trailing_whitespace = false
[COMMIT_EDITMSG]
max_line_length = 0
diff --git a/.eslintignore b/.eslintignore
index d95f2bf247e..fd9cc6bdca2 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -12,3 +12,22 @@ scripts/bench/benchmarks/**/*.js
# React repository clone
scripts/bench/remote-repo/
+
+# Compiler uses its own lint setup
+compiler/
+
+packages/react-devtools-core/dist
+packages/react-devtools-extensions/chrome/build
+packages/react-devtools-extensions/firefox/build
+packages/react-devtools-extensions/shared/build
+packages/react-devtools-extensions/src/ErrorTesterCompiled.js
+packages/react-devtools-fusebox/dist
+packages/react-devtools-inline/dist
+packages/react-devtools-shared/src/hooks/__tests__/__source__/__compiled__/
+packages/react-devtools-shared/src/hooks/__tests__/__source__/__untransformed__/
+packages/react-devtools-shell/dist
+packages/react-devtools-timeline/dist
+packages/react-devtools-timeline/static
+
+# Imported third-party Flow types
+flow-typed/
diff --git a/.eslintrc.js b/.eslintrc.js
index 1c48ea5330a..952149f30ca 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -5,58 +5,258 @@ const {
esNextPaths,
} = require('./scripts/shared/pathsByLanguageVersion');
+const restrictedGlobals = require('confusing-browser-globals');
+
const OFF = 0;
+const WARNING = 1;
const ERROR = 2;
module.exports = {
- extends: 'fbjs',
+ extends: ['prettier', 'plugin:jest/recommended'],
// Stop ESLint from looking for a configuration file in parent folders
- 'root': true,
+ root: true,
+
+ reportUnusedDisableDirectives: true,
plugins: [
+ 'babel',
+ 'ft-flow',
'jest',
+ 'es',
'no-for-of-loops',
+ 'no-function-declare-after-return',
'react',
'react-internal',
],
- parser: 'espree',
+ parser: 'hermes-eslint',
parserOptions: {
- ecmaVersion: 2017,
+ ecmaVersion: 9,
sourceType: 'script',
- ecmaFeatures: {
- experimentalObjectRestSpread: true,
- },
},
// We're stricter than the default config, mostly. We'll override a few rules
// and then enable some React specific ones.
rules: {
+ 'ft-flow/array-style-complex-type': [OFF, 'verbose'],
+ 'ft-flow/array-style-simple-type': [OFF, 'verbose'], // TODO should be WARNING
+ 'ft-flow/boolean-style': ERROR,
+ 'ft-flow/no-dupe-keys': ERROR,
+ 'ft-flow/no-primitive-constructor-types': ERROR,
+ 'ft-flow/no-types-missing-file-annotation': OFF, // TODO should be ERROR
+ 'ft-flow/no-unused-expressions': ERROR,
+ // 'ft-flow/no-weak-types': WARNING,
+ // 'ft-flow/require-valid-file-annotation': ERROR,
+ 'es/no-optional-chaining': ERROR,
+ 'no-cond-assign': OFF,
+ 'no-constant-condition': OFF,
+ 'no-control-regex': OFF,
+ 'no-debugger': ERROR,
+ 'no-dupe-args': ERROR,
+ 'no-dupe-keys': ERROR,
+ 'no-duplicate-case': WARNING,
+ 'no-empty-character-class': WARNING,
+ 'no-empty': OFF,
+ 'no-ex-assign': WARNING,
+ 'no-extra-boolean-cast': WARNING,
+ 'no-func-assign': ERROR,
+ 'no-invalid-regexp': WARNING,
+ 'no-irregular-whitespace': WARNING,
+ 'no-negated-in-lhs': ERROR,
+ 'no-obj-calls': ERROR,
+ 'no-regex-spaces': WARNING,
+ 'no-sparse-arrays': ERROR,
+ 'no-unreachable': ERROR,
+ 'use-isnan': ERROR,
+ 'valid-jsdoc': OFF,
+ 'block-scoped-var': OFF,
+ complexity: OFF,
+ 'default-case': OFF,
+ 'guard-for-in': OFF,
+ 'no-alert': OFF,
+ 'no-caller': ERROR,
+ 'no-case-declarations': OFF,
+ 'no-div-regex': OFF,
+ 'no-else-return': OFF,
+ 'no-empty-pattern': WARNING,
+ 'no-eq-null': OFF,
+ 'no-eval': ERROR,
+ 'no-extend-native': WARNING,
+ 'no-extra-bind': WARNING,
+ 'no-fallthrough': WARNING,
+ 'no-implicit-coercion': OFF,
+ 'no-implied-eval': ERROR,
+ 'no-invalid-this': OFF,
+ 'no-iterator': OFF,
+ 'no-labels': [ERROR, {allowLoop: true, allowSwitch: true}],
+ 'no-lone-blocks': WARNING,
+ 'no-loop-func': OFF,
+ 'no-magic-numbers': OFF,
+ 'no-multi-str': ERROR,
+ 'no-native-reassign': [ERROR, {exceptions: ['Map', 'Set']}],
+ 'no-new-func': ERROR,
+ 'no-new': WARNING,
+ 'no-new-wrappers': WARNING,
+ 'no-octal-escape': WARNING,
+ 'no-octal': WARNING,
+ 'no-param-reassign': OFF,
+ 'no-process-env': OFF,
+ 'no-proto': ERROR,
+ 'no-redeclare': OFF, // TODO should be WARNING?
+ 'no-return-assign': OFF,
+ 'no-script-url': ERROR,
+ 'no-self-compare': WARNING,
+ 'no-sequences': WARNING,
+ 'no-throw-literal': ERROR,
+ 'no-useless-call': WARNING,
+ 'no-void': OFF,
+ 'no-warning-comments': OFF,
+ 'no-with': OFF,
+ radix: WARNING,
+ 'vars-on-top': OFF,
+ yoda: OFF,
+ 'init-declarations': OFF,
+ 'no-catch-shadow': ERROR,
+ 'no-delete-var': ERROR,
+ 'no-label-var': WARNING,
+ 'no-shadow-restricted-names': WARNING,
+ 'no-undef-init': OFF,
+ 'no-undef': ERROR,
+ 'no-undefined': OFF,
+ 'callback-return': OFF,
+ 'global-require': OFF,
+ 'handle-callback-err': OFF,
+ 'no-mixed-requires': OFF,
+ 'no-new-require': OFF,
+ 'no-path-concat': OFF,
+ 'no-process-exit': OFF,
+ 'no-restricted-modules': OFF,
+ 'no-sync': OFF,
+ camelcase: [OFF, {properties: 'always'}],
+ 'consistent-this': [OFF, 'self'],
+ 'func-names': OFF,
+ 'func-style': [OFF, 'declaration'],
+ 'id-length': OFF,
+ 'id-match': OFF,
+ 'max-depth': OFF,
+ 'max-nested-callbacks': OFF,
+ 'max-params': OFF,
+ 'max-statements': OFF,
+ 'new-cap': OFF,
+ 'newline-after-var': OFF,
+ 'no-array-constructor': ERROR,
+ 'no-continue': OFF,
+ 'no-inline-comments': OFF,
+ 'no-lonely-if': OFF,
+ 'no-negated-condition': OFF,
+ 'no-nested-ternary': OFF,
+ 'no-new-object': WARNING,
+ 'no-plusplus': OFF,
+ 'no-ternary': OFF,
+ 'no-underscore-dangle': OFF,
+ 'no-unneeded-ternary': WARNING,
+ 'one-var': [WARNING, {initialized: 'never'}],
+ 'operator-assignment': [WARNING, 'always'],
+ 'require-jsdoc': OFF,
+ 'sort-vars': OFF,
+ 'spaced-comment': [
+ OFF,
+ 'always',
+ {exceptions: ['jshint', 'jslint', 'eslint', 'global']},
+ ],
+ 'constructor-super': ERROR,
+ 'no-class-assign': WARNING,
+ 'no-const-assign': ERROR,
+ 'no-dupe-class-members': ERROR,
+ 'no-this-before-super': ERROR,
+ 'object-shorthand': OFF,
+ 'prefer-const': OFF,
+ 'prefer-spread': OFF,
+ 'prefer-reflect': OFF,
+ 'prefer-template': OFF,
+ 'require-yield': OFF,
+ 'babel/generator-star-spacing': OFF,
+ 'babel/new-cap': OFF,
+ 'babel/array-bracket-spacing': OFF,
+ 'babel/object-curly-spacing': OFF,
+ 'babel/object-shorthand': OFF,
+ 'babel/arrow-parens': OFF,
+ 'babel/no-await-in-loop': OFF,
+ 'babel/flow-object-type': OFF,
+ 'react/display-name': OFF,
+ 'react/forbid-prop-types': OFF,
+ 'react/jsx-closing-bracket-location': OFF,
+ 'react/jsx-curly-spacing': OFF,
+ 'react/jsx-equals-spacing': WARNING,
+ 'react/jsx-filename-extension': OFF,
+ 'react/jsx-first-prop-new-line': OFF,
+ 'react/jsx-handler-names': OFF,
+ 'react/jsx-indent': OFF,
+ 'react/jsx-indent-props': OFF,
+ 'react/jsx-key': OFF,
+ 'react/jsx-max-props-per-line': OFF,
+ 'react/jsx-no-bind': OFF,
+ 'react/jsx-no-duplicate-props': ERROR,
+ 'react/jsx-no-literals': OFF,
+ 'react/jsx-no-target-blank': OFF,
+ 'react/jsx-pascal-case': OFF,
+ 'react/jsx-sort-props': OFF,
+ 'react/jsx-uses-vars': ERROR,
+ 'react/no-comment-textnodes': OFF,
+ 'react/no-danger': OFF,
+ 'react/no-deprecated': OFF,
+ 'react/no-did-mount-set-state': OFF,
+ 'react/no-did-update-set-state': OFF,
+ 'react/no-direct-mutation-state': OFF,
+ 'react/no-multi-comp': OFF,
+ 'react/no-render-return-value': OFF,
+ 'react/no-set-state': OFF,
+ 'react/no-string-refs': OFF,
+ 'react/no-unknown-property': OFF,
+ 'react/prefer-es6-class': OFF,
+ 'react/prefer-stateless-function': OFF,
+ 'react/prop-types': OFF,
+ 'react/require-extension': OFF,
+ 'react/require-optimization': OFF,
+ 'react/require-render-return': OFF,
+ 'react/sort-comp': OFF,
+ 'react/sort-prop-types': OFF,
+
'accessor-pairs': OFF,
'brace-style': [ERROR, '1tbs'],
- 'comma-dangle': [ERROR, 'always-multiline'],
'consistent-return': OFF,
'dot-location': [ERROR, 'property'],
- 'dot-notation': ERROR,
+ // We use console['error']() as a signal to not transform it:
+ 'dot-notation': [ERROR, {allowPattern: '^(error|warn)$'}],
'eol-last': ERROR,
- 'eqeqeq': [ERROR, 'allow-null'],
- 'indent': OFF,
+ eqeqeq: [ERROR, 'allow-null'],
+ indent: OFF,
'jsx-quotes': [ERROR, 'prefer-double'],
'keyword-spacing': [ERROR, {after: true, before: true}],
'no-bitwise': OFF,
+ 'no-console': OFF,
'no-inner-declarations': [ERROR, 'functions'],
'no-multi-spaces': ERROR,
- 'no-restricted-syntax': [ERROR, 'WithStatement'],
+ 'no-restricted-globals': [ERROR].concat(restrictedGlobals),
+ 'no-restricted-syntax': [
+ ERROR,
+ 'WithStatement',
+ {
+ selector: 'MemberExpression[property.name=/^(?:substring|substr)$/]',
+ message: 'Prefer string.slice() over .substring() and .substr().',
+ },
+ ],
'no-shadow': ERROR,
- 'no-unused-expressions': ERROR,
- 'no-unused-vars': [ERROR, {args: 'none'}],
- 'no-use-before-define': [ERROR, {functions: false, variables: false}],
+ 'no-unused-vars': [ERROR, {args: 'none', ignoreRestSiblings: true}],
+ 'no-use-before-define': OFF,
'no-useless-concat': OFF,
- 'quotes': [ERROR, 'single', {avoidEscape: true, allowTemplateLiterals: true }],
+ quotes: [ERROR, 'single', {avoidEscape: true, allowTemplateLiterals: true}],
'space-before-blocks': ERROR,
'space-before-function-paren': OFF,
'valid-typeof': [ERROR, {requireStringLiterals: true}],
+ // Flow fails with non-string literal keys
+ 'no-useless-computed-key': OFF,
// We apply these settings to files that should run on Node.
// They can't use JSX or ES6 modules, and must be in strict mode.
@@ -65,6 +265,12 @@ module.exports = {
'no-var': ERROR,
strict: ERROR,
+ // Enforced by Prettier
+ // TODO: Prettier doesn't handle long strings or long comments. Not a big
+ // deal. But I turned it off because loading the plugin causes some obscure
+ // syntax error and it didn't seem worth investigating.
+ 'max-len': OFF,
+
// React & JSX
// Our transforms set this automatically
'react/jsx-boolean-value': [ERROR, 'always'],
@@ -78,20 +284,69 @@ module.exports = {
'react/react-in-jsx-scope': ERROR,
'react/self-closing-comp': ERROR,
// We don't care to do this
- 'react/jsx-wrap-multilines': [ERROR, {declaration: false, assignment: false}],
+ 'react/jsx-wrap-multilines': [
+ ERROR,
+ {declaration: false, assignment: false},
+ ],
// Prevent for...of loops because they require a Symbol polyfill.
// You can disable this rule for code that isn't shipped (e.g. build scripts and tests).
'no-for-of-loops/no-for-of-loops': ERROR,
+ // Prevent function declarations after return statements
+ 'no-function-declare-after-return/no-function-declare-after-return': ERROR,
+
// CUSTOM RULES
// the second argument of warning/invariant should be a literal string
'react-internal/no-primitive-constructors': ERROR,
- 'react-internal/no-to-warn-dev-within-to-throw': ERROR,
- 'react-internal/warning-and-invariant-args': ERROR,
+ 'react-internal/safe-string-coercion': [
+ ERROR,
+ {isProductionUserAppCode: true},
+ ],
+ 'react-internal/warning-args': ERROR,
+ 'react-internal/no-production-logging': ERROR,
},
overrides: [
+ {
+ // By default, anything error message that appears the packages directory
+ // must have a corresponding error code. The exceptions are defined
+ // in the next override entry.
+ files: ['packages/**/*.js'],
+ rules: {
+ 'react-internal/prod-error-codes': ERROR,
+ },
+ },
+ {
+ // These are files where it's OK to have unminified error messages. These
+ // are environments where bundle size isn't a concern, like tests
+ // or Node.
+ files: [
+ 'packages/react-dom/src/test-utils/**/*.js',
+ 'packages/react-devtools-shared/**/*.js',
+ 'packages/react-noop-renderer/**/*.js',
+ 'packages/react-refresh/**/*.js',
+ 'packages/react-server-dom-esm/**/*.js',
+ 'packages/react-server-dom-webpack/**/*.js',
+ 'packages/react-server-dom-turbopack/**/*.js',
+ 'packages/react-server-dom-parcel/**/*.js',
+ 'packages/react-server-dom-fb/**/*.js',
+ 'packages/react-server-dom-unbundled/**/*.js',
+ 'packages/react-test-renderer/**/*.js',
+ 'packages/react-debug-tools/**/*.js',
+ 'packages/react-devtools-extensions/**/*.js',
+ 'packages/react-devtools-timeline/**/*.js',
+ 'packages/react-native-renderer/**/*.js',
+ 'packages/eslint-plugin-react-hooks/**/*.js',
+ 'packages/jest-react/**/*.js',
+ 'packages/internal-test-utils/**/*.js',
+ 'packages/**/__tests__/*.js',
+ 'packages/**/npm/*.js',
+ ],
+ rules: {
+ 'react-internal/prod-error-codes': OFF,
+ },
+ },
{
// We apply these settings to files that we ship through npm.
// They must be ES5.
@@ -110,12 +365,14 @@ module.exports = {
// We apply these settings to the source files that get compiled.
// They can use all features including JSX (but shouldn't use `var`).
files: esNextPaths,
- parser: 'babel-eslint',
+ parser: 'hermes-eslint',
parserOptions: {
+ ecmaVersion: 8,
sourceType: 'module',
},
rules: {
'no-var': ERROR,
+ 'prefer-const': ERROR,
strict: OFF,
},
},
@@ -123,22 +380,279 @@ module.exports = {
files: ['**/__tests__/*.js'],
rules: {
// https://github.com/jest-community/eslint-plugin-jest
- 'jest/no-focused-tests': ERROR,
- }
+ // Meh, who cares.
+ 'jest/consistent-test-it': OFF,
+ // Meh, we have a lot of these, who cares.
+ 'jest/no-alias-methods': OFF,
+ // We do conditions based on feature flags.
+ 'jest/no-conditional-expect': OFF,
+ // We have our own assertion helpers.
+ 'jest/expect-expect': OFF,
+ // Lame rule that fires in itRender helpers or in render methods.
+ 'jest/no-standalone-expect': OFF,
+ },
+ },
+ {
+ // Rules specific to test setup helper files.
+ files: [
+ '**/setupTests.js',
+ '**/setupEnv.js',
+ '**/jest/TestFlags.js',
+ '**/dom-event-testing-library/testHelpers.js',
+ '**/utils/ReactDOMServerIntegrationTestUtils.js',
+ '**/babel/transform-react-version-pragma.js',
+ '**/babel/transform-test-gate-pragma.js',
+ ],
+ rules: {
+ // Some helpers intentionally focus tests.
+ 'jest/no-focused-tests': OFF,
+ // Test fn helpers don't use static text names.
+ 'jest/valid-title': OFF,
+ // We have our own assertion helpers.
+ 'jest/expect-expect': OFF,
+ // Some helpers intentionally disable tests.
+ 'jest/no-disabled-tests': OFF,
+ // Helpers export text function helpers.
+ 'jest/no-export': OFF,
+ // The examples in comments trigger false errors.
+ 'jest/no-commented-out-tests': OFF,
+ },
+ },
+ {
+ files: ['**/jest/TestFlags.js'],
+ rules: {
+ // The examples in comments trigger false errors.
+ 'jest/no-commented-out-tests': OFF,
+ },
+ },
+ {
+ files: [
+ '**/__tests__/**/*.js',
+ 'scripts/**/*.js',
+ 'packages/*/npm/**/*.js',
+ 'packages/dom-event-testing-library/**/*.js',
+ 'packages/react-devtools*/**/*.js',
+ 'dangerfile.js',
+ 'fixtures',
+ 'packages/react-dom/src/test-utils/*.js',
+ ],
+ rules: {
+ 'es/no-optional-chaining': OFF,
+ 'react-internal/no-production-logging': OFF,
+ 'react-internal/warning-args': OFF,
+ 'react-internal/safe-string-coercion': [
+ ERROR,
+ {isProductionUserAppCode: false},
+ ],
+ },
+ },
+ {
+ files: ['scripts/eslint-rules/*.js'],
+ plugins: ['eslint-plugin'],
+ rules: {
+ 'eslint-plugin/prefer-object-rule': ERROR,
+ 'eslint-plugin/require-meta-fixable': [
+ ERROR,
+ {catchNoFixerButFixableProperty: true},
+ ],
+ 'eslint-plugin/require-meta-has-suggestions': ERROR,
+ },
},
{
files: ['packages/react-native-renderer/**/*.js'],
globals: {
- nativeFabricUIManager: true,
- }
- }
+ nativeFabricUIManager: 'readonly',
+ RN$enableMicrotasksInReact: 'readonly',
+ },
+ },
+ {
+ files: ['packages/react-server-dom-webpack/**/*.js'],
+ globals: {
+ __webpack_chunk_load__: 'readonly',
+ __webpack_get_script_filename__: 'readonly',
+ __webpack_require__: 'readonly',
+ },
+ },
+ {
+ files: ['packages/react-server-dom-turbopack/**/*.js'],
+ globals: {
+ __turbopack_load_by_url__: 'readonly',
+ __turbopack_require__: 'readonly',
+ },
+ },
+ {
+ files: ['packages/react-server-dom-parcel/**/*.js'],
+ globals: {
+ parcelRequire: 'readonly',
+ },
+ },
+ {
+ files: ['packages/scheduler/**/*.js'],
+ globals: {
+ TaskController: 'readonly',
+ },
+ },
+ {
+ files: [
+ 'packages/react-devtools-extensions/**/*.js',
+ 'packages/react-devtools-shared/src/devtools/views/**/*.js',
+ 'packages/react-devtools-shared/src/hook.js',
+ 'packages/react-devtools-shared/src/backend/console.js',
+ 'packages/react-devtools-shared/src/backend/fiber/renderer.js',
+ 'packages/react-devtools-shared/src/backend/shared/DevToolsComponentStackFrame.js',
+ 'packages/react-devtools-shared/src/frontend/utils/withPermissionsCheck.js',
+ ],
+ globals: {
+ __IS_CHROME__: 'readonly',
+ __IS_FIREFOX__: 'readonly',
+ __IS_EDGE__: 'readonly',
+ __IS_NATIVE__: 'readonly',
+ __IS_INTERNAL_MCP_BUILD__: 'readonly',
+ __IS_INTERNAL_VERSION__: 'readonly',
+ chrome: 'readonly',
+ },
+ },
+ {
+ files: ['packages/react-devtools-shared/**/*.js'],
+ globals: {
+ __IS_INTERNAL_VERSION__: 'readonly',
+ },
+ },
+ {
+ files: ['packages/react-devtools-*/**/*.js'],
+ excludedFiles: '**/__tests__/**/*.js',
+ plugins: ['eslint-plugin-react-hooks-published'],
+ rules: {
+ 'react-hooks-published/rules-of-hooks': ERROR,
+ },
+ },
+ {
+ files: ['packages/eslint-plugin-react-hooks/src/**/*'],
+ extends: ['plugin:@typescript-eslint/recommended'],
+ parser: '@typescript-eslint/parser',
+ plugins: ['@typescript-eslint', 'eslint-plugin'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': OFF,
+ '@typescript-eslint/no-non-null-assertion': OFF,
+ '@typescript-eslint/array-type': [ERROR, {default: 'generic'}],
+
+ 'es/no-optional-chaining': OFF,
+
+ 'eslint-plugin/prefer-object-rule': ERROR,
+ 'eslint-plugin/require-meta-fixable': [
+ ERROR,
+ {catchNoFixerButFixableProperty: true},
+ ],
+ 'eslint-plugin/require-meta-has-suggestions': ERROR,
+ },
+ },
],
+ env: {
+ browser: true,
+ es6: true,
+ node: true,
+ jest: true,
+ },
+
globals: {
- spyOnDev: true,
- spyOnDevAndProd: true,
- spyOnProd: true,
- __PROFILE__: true,
- __UMD__: true,
+ $Flow$ModuleRef: 'readonly',
+ $FlowFixMe: 'readonly',
+ $Keys: 'readonly',
+ $NonMaybeType: 'readonly',
+ $ReadOnly: 'readonly',
+ $ReadOnlyArray: 'readonly',
+ $ArrayBufferView: 'readonly',
+ $Shape: 'readonly',
+ CallSite: 'readonly',
+ ConsoleTask: 'readonly', // TOOD: Figure out what the official name of this will be.
+ ReturnType: 'readonly',
+ AnimationFrameID: 'readonly',
+ WeakRef: 'readonly',
+ // For Flow type annotation. Only `BigInt` is valid at runtime.
+ bigint: 'readonly',
+ BigInt: 'readonly',
+ BigInt64Array: 'readonly',
+ BigUint64Array: 'readonly',
+ CacheType: 'readonly',
+ Class: 'readonly',
+ ClientRect: 'readonly',
+ CopyInspectedElementPath: 'readonly',
+ DOMHighResTimeStamp: 'readonly',
+ EventListener: 'readonly',
+ Iterable: 'readonly',
+ AsyncIterable: 'readonly',
+ $AsyncIterable: 'readonly',
+ $AsyncIterator: 'readonly',
+ Iterator: 'readonly',
+ AsyncIterator: 'readonly',
+ IntervalID: 'readonly',
+ IteratorResult: 'readonly',
+ JSONValue: 'readonly',
+ JSResourceReference: 'readonly',
+ mixin$Animatable: 'readonly',
+ MouseEventHandler: 'readonly',
+ NavigateEvent: 'readonly',
+ Partial: 'readonly',
+ PerformanceMeasureOptions: 'readonly',
+ PropagationPhases: 'readonly',
+ PropertyDescriptor: 'readonly',
+ PropertyDescriptorMap: 'readonly',
+ Proxy$traps: 'readonly',
+ React$Component: 'readonly',
+ React$Config: 'readonly',
+ React$Context: 'readonly',
+ React$Element: 'readonly',
+ React$ElementConfig: 'readonly',
+ React$ElementProps: 'readonly',
+ React$ElementRef: 'readonly',
+ React$ElementType: 'readonly',
+ React$Key: 'readonly',
+ React$Node: 'readonly',
+ React$Portal: 'readonly',
+ React$Ref: 'readonly',
+ React$RefSetter: 'readonly',
+ ReadableStreamController: 'readonly',
+ ReadableStreamReader: 'readonly',
+ RequestInfo: 'readonly',
+ RequestOptions: 'readonly',
+ StoreAsGlobal: 'readonly',
+ symbol: 'readonly',
+ SyntheticEvent: 'readonly',
+ SyntheticMouseEvent: 'readonly',
+ SyntheticPointerEvent: 'readonly',
+ Thenable: 'readonly',
+ TimeoutID: 'readonly',
+ WheelEventHandler: 'readonly',
+ FinalizationRegistry: 'readonly',
+ Exclude: 'readonly',
+ Omit: 'readonly',
+ Keyframe: 'readonly',
+ PropertyIndexedKeyframes: 'readonly',
+ KeyframeAnimationOptions: 'readonly',
+ GetAnimationsOptions: 'readonly',
+ ScrollTimeline: 'readonly',
+ EventListenerOptionsOrUseCapture: 'readonly',
+ FocusOptions: 'readonly',
+ OptionalEffectTiming: 'readonly',
+
+ __REACT_ROOT_PATH_TEST__: 'readonly',
+ spyOnDev: 'readonly',
+ spyOnDevAndProd: 'readonly',
+ spyOnProd: 'readonly',
+ __DEV__: 'readonly',
+ __EXPERIMENTAL__: 'readonly',
+ __EXTENSION__: 'readonly',
+ __PROFILE__: 'readonly',
+ __TEST__: 'readonly',
+ __VARIANT__: 'readonly',
+ __unmockReact: 'readonly',
+ gate: 'readonly',
+ trustedTypes: 'readonly',
+ IS_REACT_ACT_ENVIRONMENT: 'readonly',
+ AsyncLocalStorage: 'readonly',
+ async_hooks: 'readonly',
+ globalThis: 'readonly',
+ navigation: 'readonly',
},
};
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 00000000000..2c19b248459
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+c998bb1ed4b3285398c9c7797135d3f060243c6a
+fd2b3e13d330a4559f5aa21462e1cb2cbbcf144b
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index f2f6a21e173..00000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-**Do you want to request a *feature* or report a *bug*?**
-
-**What is the current behavior?**
-
-**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:**
-
-**What is the expected behavior?**
-
-**Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?**
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000000..e561f9d2615
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,41 @@
+---
+name: "๐ Bug Report"
+about: Report a reproducible bug or regression.
+title: 'Bug: '
+labels: 'Status: Unconfirmed'
+
+---
+
+
+
+React version:
+
+## Steps To Reproduce
+
+1.
+2.
+
+
+
+Link to code example:
+
+
+
+## The current behavior
+
+
+## The expected behavior
diff --git a/.github/ISSUE_TEMPLATE/compiler_bug_report.yml b/.github/ISSUE_TEMPLATE/compiler_bug_report.yml
new file mode 100644
index 00000000000..a2c0e3d7aa3
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/compiler_bug_report.yml
@@ -0,0 +1,64 @@
+name: "โ๏ธ โจ Compiler bug report"
+description: "Report a problem with React Compiler. Please provide enough information that we can reproduce the problem."
+title: "[Compiler Bug]: "
+labels: ["Component: Optimizing Compiler", "Type: Bug", "Status: Unconfirmed"]
+body:
+- type: checkboxes
+ attributes:
+ label: What kind of issue is this?
+ description: |
+ Please indicate if this issue affects the following tools provided by React Compiler.
+ options:
+ - label: React Compiler core (the JS output is incorrect, or your app works incorrectly after optimization)
+ - label: babel-plugin-react-compiler (build issue installing or using the Babel plugin)
+ - label: eslint-plugin-react-hooks (build issue installing or using the eslint plugin)
+ - label: react-compiler-healthcheck (build issue installing or using the healthcheck script)
+- type: input
+ attributes:
+ label: Link to repro
+ description: |
+ Please provide a repro by either sharing a [Playground link](https://playground.react.dev), or a public GitHub repo so the React team can reproduce the error being reported. Please do not share localhost links!
+ placeholder: |
+ e.g. public GitHub repo, or Playground link
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Repro steps
+ description: |
+ What were you doing when the bug happened? Detailed information helps maintainers reproduce and fix bugs.
+
+ Issues filed without repro steps will be closed.
+ placeholder: |
+ Example bug report:
+ 1. Log in with username/password
+ 2. Click "Messages" on the left menu
+ 3. Open any message in the list
+ validations:
+ required: true
+- type: dropdown
+ attributes:
+ label: How often does this bug happen?
+ description: |
+ Following the repro steps above, how easily are you able to reproduce this bug?
+ options:
+ - Every time
+ - Often
+ - Sometimes
+ - Only once
+ validations:
+ required: true
+- type: input
+ attributes:
+ label: What version of React are you using?
+ description: |
+ Please provide your React version in the app where this issue occurred.
+ validations:
+ required: true
+- type: input
+ attributes:
+ label: What version of React Compiler are you using?
+ description: |
+ Please provide the exact React Compiler version in the app where this issue occurred.
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000000..2b1404175e6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: ๐ Documentation Issue
+ url: https://github.com/reactjs/react.dev/issues/new/choose
+ about: This issue tracker is not for documentation issues. Please file documentation issues here.
+ - name: ๐ค Questions and Help
+ url: https://reactjs.org/community/support.html
+ about: This issue tracker is not for support questions. Please refer to the React community's help and discussion forums.
diff --git a/.github/ISSUE_TEMPLATE/devtools_bug_report.yml b/.github/ISSUE_TEMPLATE/devtools_bug_report.yml
new file mode 100644
index 00000000000..f9020976468
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/devtools_bug_report.yml
@@ -0,0 +1,81 @@
+name: "โ๏ธ ๐ DevTools bug report"
+description: "Report a problem with React DevTools. Please provide enough information that we can reproduce the problem."
+title: "[DevTools Bug]: "
+labels: ["Component: Developer Tools", "Type: Bug", "Status: Unconfirmed"]
+body:
+- type: input
+ attributes:
+ label: Website or app
+ description: |
+ Which website or app were you using when the bug happened?
+
+ This should be a public URL, GitHub repo, or Code Sandbox app so the React team can reproduce the error being reported. (Please no localhost URLs.)
+ placeholder: |
+ e.g. website URL, public GitHub repo, or Code Sandbox app
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Repro steps
+ description: |
+ What were you doing on the website or app when the bug happened? Detailed information helps maintainers reproduce and fix bugs.
+
+ Issues filed without repro steps will be closed.
+ placeholder: |
+ Example bug report:
+ 1. Log in with username/password
+ 2. Click "Messages" on the left menu
+ 3. Open any message in the list
+ validations:
+ required: true
+- type: dropdown
+ attributes:
+ label: How often does this bug happen?
+ description: |
+ Following the repro steps above, how easily are you able to reproduce this bug?
+ options:
+ - Every time
+ - Often
+ - Sometimes
+ - Only once
+ validations:
+ required: true
+- type: input
+ id: automated_package
+ attributes:
+ label: DevTools package (automated)
+ description: |
+ Please do not edit this field.
+- type: input
+ id: automated_version
+ attributes:
+ label: DevTools version (automated)
+ description: |
+ Please do not edit this field.
+- type: input
+ id: automated_error_message
+ attributes:
+ label: Error message (automated)
+ description: |
+ Please do not edit this field.
+- type: textarea
+ id: automated_call_stack
+ attributes:
+ label: Error call stack (automated)
+ description: |
+ Please do not edit this field.
+ render: text
+- type: textarea
+ id: automated_component_stack
+ attributes:
+ label: Error component stack (automated)
+ description: |
+ Please do not edit this field.
+ render: text
+- type: textarea
+ id: automated_github_query_string
+ attributes:
+ label: GitHub query string (automated)
+ description: |
+ Please do not edit this field.
+ render: text
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index b8a0139bd96..23fbc65ab57 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,14 +1,33 @@
-**Before submitting a pull request,** please make sure the following is done:
-
-1. Fork [the repository](https://github.com/facebook/react) and create your branch from `master`.
-2. Run `yarn` in the repository root.
-3. If you've fixed a bug or added code that should be tested, add tests!
-4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development.
-5. Run `yarn test-prod` to test in the production environment. It supports the same options as `yarn test`.
-6. If you need a debugger, run `yarn debug-test --watch TestName`, open `chrome://inspect`, and press "Inspect".
-7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`).
-8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files.
-9. Run the [Flow](https://flowtype.org/) typechecks (`yarn flow`).
-10. If you haven't already, complete the CLA.
-
-**Learn more about contributing:** https://reactjs.org/docs/how-to-contribute.html
+
+
+## Summary
+
+
+
+## How did you test this change?
+
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000000..11800ad7575
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+ - package-ecosystem: "npm"
+ directories:
+ - "/fixtures/*"
+ schedule:
+ interval: "monthly"
+ open-pull-requests-limit: 0
+ ignore:
+ - dependency-name: "*"
diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml
new file mode 100644
index 00000000000..5a57cf6a32c
--- /dev/null
+++ b/.github/workflows/compiler_discord_notify.yml
@@ -0,0 +1,49 @@
+name: (Compiler) Discord Notify
+
+on:
+ pull_request_target:
+ types: [opened, ready_for_review]
+ paths:
+ - compiler/**
+ - .github/workflows/compiler_**.yml
+
+permissions: {}
+
+jobs:
+ check_access:
+ if: ${{ github.event.pull_request.draft == false }}
+ runs-on: ubuntu-latest
+ outputs:
+ is_member_or_collaborator: ${{ steps.check_is_member_or_collaborator.outputs.is_member_or_collaborator }}
+ steps:
+ - run: echo ${{ github.event.pull_request.author_association }}
+ - name: Check is member or collaborator
+ id: check_is_member_or_collaborator
+ if: ${{ github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'COLLABORATOR' }}
+ run: echo "is_member_or_collaborator=true" >> "$GITHUB_OUTPUT"
+
+ check_maintainer:
+ if: ${{ needs.check_access.outputs.is_member_or_collaborator == 'true' || needs.check_access.outputs.is_member_or_collaborator == true }}
+ needs: [check_access]
+ uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main
+ permissions:
+ # Used by check_maintainer
+ contents: read
+ with:
+ actor: ${{ github.event.pull_request.user.login }}
+
+ notify:
+ if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }}
+ needs: check_maintainer
+ runs-on: ubuntu-latest
+ steps:
+ - name: Discord Webhook Action
+ uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
+ with:
+ webhook-url: ${{ secrets.COMPILER_DISCORD_WEBHOOK_URL }}
+ embed-author-name: ${{ github.event.pull_request.user.login }}
+ embed-author-url: ${{ github.event.pull_request.user.html_url }}
+ embed-author-icon-url: ${{ github.event.pull_request.user.avatar_url }}
+ embed-title: '#${{ github.event.number }} (+${{github.event.pull_request.additions}} -${{github.event.pull_request.deletions}}): ${{ github.event.pull_request.title }}'
+ embed-description: ${{ github.event.pull_request.body }}
+ embed-url: ${{ github.event.pull_request.html_url }}
diff --git a/.github/workflows/compiler_playground.yml b/.github/workflows/compiler_playground.yml
new file mode 100644
index 00000000000..a19e87e25e7
--- /dev/null
+++ b/.github/workflows/compiler_playground.yml
@@ -0,0 +1,69 @@
+name: (Compiler) Playground
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ paths:
+ - compiler/**
+ - .github/workflows/compiler_playground.yml
+
+permissions: {}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+defaults:
+ run:
+ working-directory: compiler/apps/playground
+
+jobs:
+ playground:
+ name: Test playground
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: compiler/**/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: compiler-and-playground-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/**/yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ working-directory: compiler
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Check Playwright version
+ id: playwright_version
+ run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT"
+ - name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }}
+ id: cache_playwright_browsers
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/ms-playwright
+ key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }}
+ - run: npx playwright install --with-deps chromium
+ if: steps.cache_playwright_browsers.outputs.cache-hit != 'true'
+ - run: CI=true yarn test
+ - run: ls -R test-results
+ if: '!cancelled()'
+ - name: Archive test results
+ if: '!cancelled()'
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-results
+ path: compiler/apps/playground/test-results
+ if-no-files-found: ignore
diff --git a/.github/workflows/compiler_prereleases.yml b/.github/workflows/compiler_prereleases.yml
new file mode 100644
index 00000000000..76cd3310b1a
--- /dev/null
+++ b/.github/workflows/compiler_prereleases.yml
@@ -0,0 +1,70 @@
+name: (Compiler) Publish Prereleases
+
+on:
+ workflow_call:
+ inputs:
+ commit_sha:
+ required: true
+ default: ''
+ type: string
+ release_channel:
+ required: true
+ type: string
+ dist_tag:
+ required: true
+ type: string
+ version_name:
+ required: true
+ type: string
+ tag_version:
+ required: false
+ type: string
+ dry_run:
+ required: false
+ type: boolean
+ secrets:
+ NPM_TOKEN:
+ required: true
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+defaults:
+ run:
+ working-directory: compiler
+
+jobs:
+ publish_prerelease:
+ name: Publish prelease (${{ inputs.release_channel }}) ${{ inputs.commit_sha }} @${{ inputs.dist_tag }}
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - if: inputs.dry_run == true
+ name: Publish packages to npm (dry run)
+ run: |
+ cp ./scripts/release/ci-npmrc ~/.npmrc
+ scripts/release/publish.js --frfr --debug --ci --versionName=${{ inputs.version_name }} --tag=${{ inputs.dist_tag }} ${{ inputs.tag_version && format('--tagVersion={0}', inputs.tag_version) || '' }}
+ - if: inputs.dry_run != true
+ name: Publish packages to npm
+ run: |
+ cp ./scripts/release/ci-npmrc ~/.npmrc
+ scripts/release/publish.js --frfr --ci --versionName=${{ inputs.version_name }} --tag=${{ inputs.dist_tag }} ${{ inputs.tag_version && format('--tagVersion={0}', inputs.tag_version) || '' }}
diff --git a/.github/workflows/compiler_prereleases_manual.yml b/.github/workflows/compiler_prereleases_manual.yml
new file mode 100644
index 00000000000..c4a7a16aca3
--- /dev/null
+++ b/.github/workflows/compiler_prereleases_manual.yml
@@ -0,0 +1,41 @@
+name: (Compiler) Publish Prereleases Manual
+
+on:
+ workflow_dispatch:
+ inputs:
+ prerelease_commit_sha:
+ required: false
+ release_channel:
+ required: true
+ type: string
+ dist_tag:
+ required: true
+ type: string
+ version_name:
+ required: true
+ type: string
+ tag_version:
+ required: false
+ type: string
+ dry_run:
+ required: false
+ type: boolean
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ publish_prerelease_experimental:
+ name: Publish to Experimental channel
+ uses: facebook/react/.github/workflows/compiler_prereleases.yml@main
+ with:
+ commit_sha: ${{ inputs.prerelease_commit_sha || github.sha }}
+ release_channel: ${{ inputs.release_channel }}
+ dist_tag: ${{ inputs.dist_tag }}
+ version_name: ${{ inputs.version_name }}
+ tag_version: ${{ inputs.tag_version }}
+ dry_run: ${{ inputs.dry_run }}
+ secrets:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/compiler_prereleases_nightly.yml b/.github/workflows/compiler_prereleases_nightly.yml
new file mode 100644
index 00000000000..ca2b5589def
--- /dev/null
+++ b/.github/workflows/compiler_prereleases_nightly.yml
@@ -0,0 +1,24 @@
+name: (Compiler) Publish Prereleases Nightly
+
+on:
+ schedule:
+ # At 10 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri
+ - cron: 10 16 * * 1,2,3,4,5
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ publish_prerelease_experimental:
+ name: Publish to Experimental channel
+ uses: facebook/react/.github/workflows/compiler_prereleases.yml@main
+ with:
+ commit_sha: ${{ github.sha }}
+ release_channel: experimental
+ dist_tag: experimental
+ version_name: '0.0.0'
+ dry_run: false
+ secrets:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/compiler_typescript.yml b/.github/workflows/compiler_typescript.yml
new file mode 100644
index 00000000000..6a3b52e21ae
--- /dev/null
+++ b/.github/workflows/compiler_typescript.yml
@@ -0,0 +1,108 @@
+name: (Compiler) TypeScript
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ paths:
+ - compiler/**
+ - .github/workflows/compiler_typescript.yml
+
+permissions: {}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+defaults:
+ run:
+ working-directory: compiler
+
+jobs:
+ discover_yarn_workspaces:
+ name: Discover yarn workspaces
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: set-matrix
+ run: echo "matrix=$(find packages -mindepth 1 -maxdepth 1 -type d | sed 's!packages/!!g' | tr '\n' ',' | sed s/.$// | jq -Rsc '. / "," - [""]')" >> $GITHUB_OUTPUT
+
+ # Hardcoded to improve parallelism
+ lint:
+ name: Lint babel-plugin-react-compiler
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn workspace babel-plugin-react-compiler lint
+
+ # Hardcoded to improve parallelism
+ jest:
+ name: Jest babel-plugin-react-compiler
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn workspace babel-plugin-react-compiler jest
+
+ test:
+ name: Test ${{ matrix.workspace_name }}
+ needs: discover_yarn_workspaces
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ workspace_name: ${{ fromJSON(needs.discover_yarn_workspaces.outputs.matrix) }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: compiler-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('compiler/yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: xvfb-run -a yarn workspace ${{ matrix.workspace_name }} test
+ if: runner.os == 'Linux' && matrix.workspace_name == 'react-forgive'
+ - run: yarn workspace ${{ matrix.workspace_name }} test
+ if: matrix.workspace_name != 'react-forgive'
diff --git a/.github/workflows/devtools_discord_notify.yml b/.github/workflows/devtools_discord_notify.yml
new file mode 100644
index 00000000000..bb498f00371
--- /dev/null
+++ b/.github/workflows/devtools_discord_notify.yml
@@ -0,0 +1,49 @@
+name: (DevTools) Discord Notify
+
+on:
+ pull_request_target:
+ types: [opened, ready_for_review]
+ paths:
+ - packages/react-devtools**
+ - .github/workflows/devtools_**.yml
+
+permissions: {}
+
+jobs:
+ check_access:
+ if: ${{ github.event.pull_request.draft == false }}
+ runs-on: ubuntu-latest
+ outputs:
+ is_member_or_collaborator: ${{ steps.check_is_member_or_collaborator.outputs.is_member_or_collaborator }}
+ steps:
+ - run: echo ${{ github.event.pull_request.author_association }}
+ - name: Check is member or collaborator
+ id: check_is_member_or_collaborator
+ if: ${{ github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'COLLABORATOR' }}
+ run: echo "is_member_or_collaborator=true" >> "$GITHUB_OUTPUT"
+
+ check_maintainer:
+ if: ${{ needs.check_access.outputs.is_member_or_collaborator == 'true' || needs.check_access.outputs.is_member_or_collaborator == true }}
+ needs: [check_access]
+ uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main
+ permissions:
+ # Used by check_maintainer
+ contents: read
+ with:
+ actor: ${{ github.event.pull_request.user.login }}
+
+ notify:
+ if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }}
+ needs: check_maintainer
+ runs-on: ubuntu-latest
+ steps:
+ - name: Discord Webhook Action
+ uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
+ with:
+ webhook-url: ${{ secrets.DEVTOOLS_DISCORD_WEBHOOK_URL }}
+ embed-author-name: ${{ github.event.pull_request.user.login }}
+ embed-author-url: ${{ github.event.pull_request.user.html_url }}
+ embed-author-icon-url: ${{ github.event.pull_request.user.avatar_url }}
+ embed-title: '#${{ github.event.number }} (+${{github.event.pull_request.additions}} -${{github.event.pull_request.deletions}}): ${{ github.event.pull_request.title }}'
+ embed-description: ${{ github.event.pull_request.body }}
+ embed-url: ${{ github.event.pull_request.html_url }}
diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml
new file mode 100644
index 00000000000..9fe0c55e0bd
--- /dev/null
+++ b/.github/workflows/devtools_regression_tests.yml
@@ -0,0 +1,205 @@
+name: (DevTools) Regression Tests
+
+on:
+ schedule:
+ - cron: 0 0 * * *
+ workflow_dispatch:
+ inputs:
+ commit_sha:
+ required: false
+ type: string
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ download_build:
+ name: Download base build
+ runs-on: ubuntu-latest
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd scripts/release install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Download react-devtools artifacts for base revision
+ run: |
+ git fetch origin main
+ GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=${{ inputs.commit_sha || '$(git rev-parse origin/main)' }}
+ - name: Display structure of build
+ run: ls -R build
+ - name: Archive build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build
+ path: build
+ if-no-files-found: error
+
+ build_devtools_and_process_artifacts:
+ name: Build DevTools and process artifacts
+ needs: download_build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ name: build
+ path: build
+ - run: ./scripts/ci/pack_and_store_devtools_artifacts.sh
+ env:
+ RELEASE_CHANNEL: experimental
+ - name: Display structure of build
+ run: ls -R build
+ - name: Archive devtools build
+ uses: actions/upload-artifact@v4
+ with:
+ name: react-devtools
+ path: build/devtools
+ if-no-files-found: error
+ # Simplifies getting the extension for local testing
+ - name: Archive chrome extension
+ uses: actions/upload-artifact@v4
+ with:
+ name: react-devtools-chrome-extension
+ path: build/devtools/chrome-extension.zip
+ if-no-files-found: error
+ - name: Archive firefox extension
+ uses: actions/upload-artifact@v4
+ with:
+ name: react-devtools-firefox-extension
+ path: build/devtools/firefox-extension.zip
+ if-no-files-found: error
+
+ run_devtools_tests_for_versions:
+ name: Run DevTools tests for versions
+ needs: build_devtools_and_process_artifacts
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ version:
+ - "16.0"
+ - "16.5" # schedule package
+ - "16.8" # hooks
+ - "17.0"
+ - "18.0"
+ - "18.2" # compiler polyfill
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore all archived build artifacts
+ uses: actions/download-artifact@v4
+ - name: Display structure of build
+ run: ls -R build
+ - run: ./scripts/ci/download_devtools_regression_build.js ${{ matrix.version }} --replaceBuild
+ - run: node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion ${{ matrix.version }} --ci
+
+ run_devtools_e2e_tests_for_versions:
+ name: Run DevTools e2e tests for versions
+ needs: build_devtools_and_process_artifacts
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ version:
+ - "16.0"
+ - "16.5" # schedule package
+ - "16.8" # hooks
+ - "17.0"
+ - "18.0"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore all archived build artifacts
+ uses: actions/download-artifact@v4
+ - name: Display structure of build
+ run: ls -R build
+ - name: Check Playwright version
+ id: playwright_version
+ run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT"
+ - name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }}
+ id: cache_playwright_browsers
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/ms-playwright
+ key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }}
+ - run: npx playwright install --with-deps
+ if: steps.cache_playwright_browsers.outputs.cache-hit != 'true'
+ - run: npx playwright install-deps
+ if: steps.cache_playwright_browsers.outputs.cache-hit == 'true'
+ - run: ./scripts/ci/download_devtools_regression_build.js ${{ matrix.version }}
+ - run: ls -R build-regression
+ - run: ./scripts/ci/run_devtools_e2e_tests.js ${{ matrix.version }}
+ env:
+ RELEASE_CHANNEL: experimental
+ - name: Cleanup build regression folder
+ run: rm -r ./build-regression
+ - uses: actions/upload-artifact@v4
+ with:
+ name: screenshots
+ path: ./tmp/playwright-artifacts
+ if-no-files-found: warn
diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml
new file mode 100644
index 00000000000..3eec5f90bee
--- /dev/null
+++ b/.github/workflows/runtime_build_and_test.yml
@@ -0,0 +1,933 @@
+name: (Runtime) Build and Test
+
+on:
+ push:
+ branches: [main]
+ tags:
+ # To get CI for backport releases.
+ # This will duplicate CI for releases from main which is acceptable
+ - "v*"
+ pull_request:
+ paths-ignore:
+ - compiler/**
+ workflow_dispatch:
+ inputs:
+ commit_sha:
+ required: false
+ type: string
+ default: ''
+
+permissions: {}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ # ----- NODE_MODULES CACHE -----
+ # Centralize the node_modules cache so it is saved once and each subsequent job only needs to
+ # restore the cache. Prevents race conditions where multiple workflows try to write to the cache.
+ runtime_node_modules_cache:
+ name: Cache Runtime node_modules
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - name: Check cache hit
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ lookup-only: true
+ - uses: actions/setup-node@v4
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Warm with old cache
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Save cache
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+
+ runtime_compiler_node_modules_cache:
+ name: Cache Runtime, Compiler node_modules
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - name: Check cache hit
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+ lookup-only: true
+ - uses: actions/setup-node@v4
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: |
+ yarn.lock
+ compiler/yarn.lock
+ - name: Warm with old cache
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd compiler install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Save cache
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+
+ # ----- FLOW -----
+ discover_flow_inline_configs:
+ name: Discover flow inline configs
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.result }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/github-script@v7
+ id: set-matrix
+ with:
+ script: |
+ const inlinedHostConfigs = require('./scripts/shared/inlinedHostConfigs.js');
+ return inlinedHostConfigs.map(config => config.shortName);
+
+ flow:
+ name: Flow check ${{ matrix.flow_inline_config_shortname }}
+ needs: [discover_flow_inline_configs, runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ flow_inline_config_shortname: ${{ fromJSON(needs.discover_flow_inline_configs.outputs.matrix) }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: node ./scripts/tasks/flow-ci ${{ matrix.flow_inline_config_shortname }}
+
+ # ----- FIZZ -----
+ check_generated_fizz_runtime:
+ name: Confirm generated inline Fizz runtime is up to date
+ needs: [runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: |
+ yarn generate-inline-fizz-runtime
+ git diff --exit-code || (echo "There was a change to the Fizz runtime. Run \`yarn generate-inline-fizz-runtime\` and check in the result." && false)
+
+ # ----- FEATURE FLAGS -----
+ flags:
+ name: Check flags
+ needs: [runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn flags
+
+ # ----- TESTS -----
+ test:
+ name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }})
+ needs: [runtime_compiler_node_modules_cache]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ params:
+ - "-r=stable --env=development"
+ - "-r=stable --env=production"
+ - "-r=experimental --env=development"
+ - "-r=experimental --env=production"
+ - "-r=www-classic --env=development --variant=false"
+ - "-r=www-classic --env=production --variant=false"
+ - "-r=www-classic --env=development --variant=true"
+ - "-r=www-classic --env=production --variant=true"
+ - "-r=www-modern --env=development --variant=false"
+ - "-r=www-modern --env=production --variant=false"
+ - "-r=www-modern --env=development --variant=true"
+ - "-r=www-modern --env=production --variant=true"
+ - "-r=xplat --env=development --variant=false"
+ - "-r=xplat --env=development --variant=true"
+ - "-r=xplat --env=production --variant=false"
+ - "-r=xplat --env=production --variant=true"
+ # TODO: Test more persistent configurations?
+ - "-r=stable --env=development --persistent"
+ - "-r=experimental --env=development --persistent"
+ shard:
+ - 1/5
+ - 2/5
+ - 3/5
+ - 4/5
+ - 5/5
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: |
+ yarn.lock
+ compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd compiler install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: node --version
+ - run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }}
+
+ # Hardcoded to improve parallelism
+ test-linter:
+ name: Test eslint-plugin-react-hooks
+ needs: [runtime_compiler_node_modules_cache]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: |
+ yarn.lock
+ compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+ - name: Install runtime dependencies
+ run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Install compiler dependencies
+ run: yarn install --frozen-lockfile
+ working-directory: compiler
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: ./scripts/react-compiler/build-compiler.sh && ./scripts/react-compiler/link-compiler.sh
+ - run: yarn workspace eslint-plugin-react-hooks test
+
+ # ----- BUILD -----
+ build_and_lint:
+ name: yarn build and lint
+ needs: [runtime_compiler_node_modules_cache]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ # yml is dumb. update the --total arg to yarn build if you change the number of workers
+ worker_id: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
+ release_channel: [stable, experimental]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: |
+ yarn.lock
+ compiler/yarn.lock
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 11.0.22
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd compiler install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn build --index=${{ matrix.worker_id }} --total=25 --r=${{ matrix.release_channel }} --ci
+ env:
+ CI: github
+ RELEASE_CHANNEL: ${{ matrix.release_channel }}
+ NODE_INDEX: ${{ matrix.worker_id }}
+ - name: Lint build
+ run: yarn lint-build
+ - name: Display structure of build
+ run: ls -R build
+ - name: Archive build
+ uses: actions/upload-artifact@v4
+ with:
+ name: _build_${{ matrix.worker_id }}_${{ matrix.release_channel }}
+ path: build
+ if-no-files-found: error
+
+ test_build:
+ name: yarn test-build
+ needs: [build_and_lint, runtime_compiler_node_modules_cache]
+ strategy:
+ fail-fast: false
+ matrix:
+ test_params: [
+ # Intentionally passing these as strings instead of creating a
+ # separate parameter per CLI argument, since it's easier to
+ # control/see which combinations we want to run.
+ -r=stable --env=development,
+ -r=stable --env=production,
+ -r=experimental --env=development,
+ -r=experimental --env=production,
+
+ # TODO: Update test config to support www build tests
+ # - "-r=www-classic --env=development --variant=false"
+ # - "-r=www-classic --env=production --variant=false"
+ # - "-r=www-classic --env=development --variant=true"
+ # - "-r=www-classic --env=production --variant=true"
+ # - "-r=www-modern --env=development --variant=false"
+ # - "-r=www-modern --env=production --variant=false"
+ # - "-r=www-modern --env=development --variant=true"
+ # - "-r=www-modern --env=production --variant=true"
+
+ # TODO: Update test config to support xplat build tests
+ # - "-r=xplat --env=development --variant=false"
+ # - "-r=xplat --env=development --variant=true"
+ # - "-r=xplat --env=production --variant=false"
+ # - "-r=xplat --env=production --variant=true"
+
+ # TODO: Test more persistent configurations?
+ ]
+ shard:
+ - 1/10
+ - 2/10
+ - 3/10
+ - 4/10
+ - 5/10
+ - 6/10
+ - 7/10
+ - 8/10
+ - 9/10
+ - 10/10
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: |
+ yarn.lock
+ compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd compiler install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - run: node --version
+ - run: yarn test --build ${{ matrix.test_params }} --shard=${{ matrix.shard }} --ci
+
+ test_build_devtools:
+ name: yarn test-build (devtools)
+ needs: [build_and_lint, runtime_node_modules_cache]
+ strategy:
+ fail-fast: false
+ matrix:
+ shard:
+ - 1/5
+ - 2/5
+ - 3/5
+ - 4/5
+ - 5/5
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - run: node --version
+ - run: yarn test --build --project=devtools -r=experimental --shard=${{ matrix.shard }} --ci
+
+ process_artifacts_combined:
+ name: Process artifacts combined
+ needs: [build_and_lint, runtime_node_modules_cache]
+ permissions:
+ # https://github.com/actions/attest-build-provenance
+ id-token: write
+ attestations: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - run: echo ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
+ - name: Scrape warning messages
+ run: |
+ mkdir -p ./build/__test_utils__
+ node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js
+ # Compress build directory into a single tarball for easy download
+ - run: tar -zcvf ./build.tgz ./build
+ # TODO: Migrate scripts to use `build` directory instead of `build2`
+ - run: cp ./build.tgz ./build2.tgz
+ - name: Archive build artifacts
+ id: upload_artifacts_combined
+ uses: actions/upload-artifact@v4
+ with:
+ name: artifacts_combined
+ path: |
+ ./build.tgz
+ ./build2.tgz
+ if-no-files-found: error
+ - uses: actions/attest-build-provenance@v2
+ # We don't verify builds generated from pull requests not originating from facebook/react.
+ # However, if the PR lands, the run on `main` will generate the attestation which can then
+ # be used to download a build via scripts/release/download-experimental-build.js.
+ #
+ # Note that this means that scripts/release/download-experimental-build.js must be run with
+ # --no-verify when downloading a build from a fork.
+ if: github.event_name == 'push' && github.ref_name == 'main' || github.event.pull_request.head.repo.full_name == github.repository
+ with:
+ subject-name: artifacts_combined.zip
+ subject-digest: sha256:${{ steps.upload_artifacts_combined.outputs.artifact-digest }}
+
+ check_error_codes:
+ name: Search build artifacts for unminified errors
+ needs: [build_and_lint, runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - name: Search build artifacts for unminified errors
+ run: |
+ yarn extract-errors
+ git diff --exit-code || (echo "Found unminified errors. Either update the error codes map or disable error minification for the affected build, if appropriate." && false)
+
+ check_release_dependencies:
+ name: Check release dependencies
+ needs: [build_and_lint, runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - run: yarn check-release-dependencies
+
+ RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
+ name: Check fixtures DOM (stable)
+ needs: build_and_lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: fixtures_dom-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'fixtures/dom/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn --cwd fixtures/dom install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - name: Run DOM fixture tests
+ run: |
+ yarn predev
+ yarn test
+ working-directory: fixtures/dom
+ env:
+ RELEASE_CHANNEL: stable
+
+ # ----- FLIGHT -----
+ run_fixtures_flight_tests:
+ name: Run fixtures Flight tests
+ needs: build_and_lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ # Fixture copies some built packages from the workroot after install.
+ # That means dependencies of the built packages are not installed.
+ # We need to install dependencies of the workroot to fulfill all dependency constraints
+ - name: Restore cached node_modules
+ uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: fixtures_flight-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'fixtures/flight/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd fixtures/flight install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Check Playwright version
+ id: playwright_version
+ run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT"
+ - name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }}
+ id: cache_playwright_browsers
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/ms-playwright
+ key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }}
+ - name: Playwright install deps
+ if: steps.cache_playwright_browsers.outputs.cache-hit != 'true'
+ working-directory: fixtures/flight
+ run: npx playwright install --with-deps chromium
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Display structure of build
+ run: ls -R build
+ - name: Run tests
+ working-directory: fixtures/flight
+ run: yarn test
+ env:
+ # Otherwise the webserver is a blackbox
+ DEBUG: pw:webserver
+ - name: Archive Flight fixture artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: flight-playwright-report
+ path: fixtures/flight/playwright-report
+ if-no-files-found: warn
+ - name: Archive Flight fixture artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: flight-test-results
+ path: fixtures/flight/test-results
+ if-no-files-found: ignore
+
+ # ----- DEVTOOLS -----
+ build_devtools_and_process_artifacts:
+ name: Build DevTools and process artifacts
+ needs: [build_and_lint, runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ browser: [chrome, firefox, edge]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - run: ./scripts/ci/pack_and_store_devtools_artifacts.sh ${{ matrix.browser }}
+ env:
+ RELEASE_CHANNEL: experimental
+ - name: Display structure of build
+ run: ls -R build
+ # Simplifies getting the extension for local testing
+ - name: Archive ${{ matrix.browser }} extension
+ uses: actions/upload-artifact@v4
+ with:
+ name: react-devtools-${{ matrix.browser }}-extension
+ path: build/devtools/${{ matrix.browser }}-extension.zip
+ if-no-files-found: error
+ - name: Archive ${{ matrix.browser }} metadata
+ uses: actions/upload-artifact@v4
+ with:
+ name: react-devtools-${{ matrix.browser }}-metadata
+ path: build/devtools/webpack-stats.*.json
+
+ merge_devtools_artifacts:
+ name: Merge DevTools artifacts
+ needs: build_devtools_and_process_artifacts
+ runs-on: ubuntu-latest
+ steps:
+ - name: Merge artifacts
+ uses: actions/upload-artifact/merge@v4
+ with:
+ name: react-devtools
+ pattern: react-devtools-*
+
+ run_devtools_e2e_tests:
+ name: Run DevTools e2e tests
+ needs: [build_and_lint, runtime_node_modules_cache]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache/restore@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-node_modules-v7-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+ # Don't use restore-keys here. Otherwise the cache grows indefinitely.
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Restore archived build
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Check Playwright version
+ id: playwright_version
+ run: echo "playwright_version=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//' | head -1)" >> "$GITHUB_OUTPUT"
+ - name: Cache Playwright Browsers for version ${{ steps.playwright_version.outputs.playwright_version }}
+ id: cache_playwright_browsers
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/ms-playwright
+ key: playwright-browsers-v6-${{ runner.arch }}-${{ runner.os }}-${{ steps.playwright_version.outputs.playwright_version }}
+ - name: Playwright install deps
+ if: steps.cache_playwright_browsers.outputs.cache-hit != 'true'
+ run: npx playwright install --with-deps chromium
+ - run: ./scripts/ci/run_devtools_e2e_tests.js
+ env:
+ RELEASE_CHANNEL: experimental
+ - name: Archive Playwright report
+ uses: actions/upload-artifact@v4
+ with:
+ name: devtools-playwright-artifacts
+ path: tmp/playwright-artifacts
+ if-no-files-found: warn
+
+ # ----- SIZEBOT -----
+ sizebot:
+ if: ${{ github.event_name == 'pull_request' && github.ref_name != 'main' && github.event.pull_request.base.ref == 'main' }}
+ name: Run sizebot
+ needs: [build_and_lint]
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4 # note: this does not reuse centralized cache since it has unique cache key
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd scripts/release install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Download artifacts for base revision
+ # The build could have been generated from a fork, so we must download the build without
+ # any verification. This is safe since we only use this for sizebot calculation and the
+ # unverified artifact is not used. Additionally this workflow runs in the pull_request
+ # trigger so only restricted permissions are available.
+ run: |
+ GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=$(git rev-parse ${{ github.event.pull_request.base.sha }}) ${{ (github.event.pull_request.head.repo.full_name != github.repository && '--noVerify') || ''}}
+ mv ./build ./base-build
+ - name: Delete extraneous files
+ # TODO: The `download-experimental-build` script copies the npm
+ # packages into the `node_modules` directory. This is a historical
+ # quirk of how the release script works. Let's pretend they
+ # don't exist.
+ run: rm -rf ./base-build/node_modules
+ - name: Display structure of base-build from origin/main
+ run: ls -R base-build
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - name: Restore archived build for PR
+ uses: actions/download-artifact@v4
+ with:
+ pattern: _build_*
+ path: build
+ merge-multiple: true
+ - name: Scrape warning messages
+ run: |
+ mkdir -p ./build/__test_utils__
+ node ./scripts/print-warnings/print-warnings.js > build/__test_utils__/ReactAllWarnings.js
+ - name: Display structure of build for PR
+ run: ls -R build
+ - run: echo ${{ github.event.inputs.commit_sha != '' && github.event.inputs.commit_sha || github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
+ - run: node ./scripts/tasks/danger
+ - name: Archive sizebot results
+ uses: actions/upload-artifact@v4
+ with:
+ name: sizebot-message
+ path: sizebot-message.md
+ if-no-files-found: ignore
diff --git a/.github/workflows/runtime_commit_artifacts.yml b/.github/workflows/runtime_commit_artifacts.yml
new file mode 100644
index 00000000000..1b98673cd4d
--- /dev/null
+++ b/.github/workflows/runtime_commit_artifacts.yml
@@ -0,0 +1,474 @@
+name: (Runtime) Commit Artifacts for Meta WWW and fbsource V2
+
+on:
+ workflow_run:
+ workflows: ["(Runtime) Build and Test"]
+ types: [completed]
+ branches:
+ - main
+ workflow_dispatch:
+ inputs:
+ commit_sha:
+ required: false
+ type: string
+ force:
+ description: 'Force a commit to the builds/... branches'
+ required: true
+ default: false
+ type: boolean
+ dry_run:
+ description: Perform a dry run (run everything except push)
+ required: true
+ default: false
+ type: boolean
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ download_artifacts:
+ runs-on: ubuntu-latest
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ steps:
+ - uses: actions/checkout@v4
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd scripts/release install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Download artifacts for base revision
+ run: |
+ GH_TOKEN=${{ github.token }} scripts/release/download-experimental-build.js --commit=${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}
+ - name: Display structure of build
+ run: ls -R build
+ - name: Archive build
+ uses: actions/upload-artifact@v4
+ with:
+ name: build
+ path: build/
+ if-no-files-found: error
+
+
+ process_artifacts:
+ runs-on: ubuntu-latest
+ needs: [download_artifacts]
+ outputs:
+ www_branch_count: ${{ steps.check_branches.outputs.www_branch_count }}
+ fbsource_branch_count: ${{ steps.check_branches.outputs.fbsource_branch_count }}
+ last_version_classic: ${{ steps.get_last_version_www.outputs.last_version_classic }}
+ last_version_modern: ${{ steps.get_last_version_www.outputs.last_version_modern }}
+ last_version_rn: ${{ steps.get_last_version_rn.outputs.last_version_rn }}
+ current_version_classic: ${{ steps.get_current_version.outputs.current_version_classic }}
+ current_version_modern: ${{ steps.get_current_version.outputs.current_version_modern }}
+ current_version_rn: ${{ steps.get_current_version.outputs.current_version_rn }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: builds/facebook-www
+ - name: "Get last version string for www"
+ id: get_last_version_www
+ run: |
+ # Empty checks only needed for backwards compatibility,can remove later.
+ VERSION_CLASSIC=$( [ -f ./compiled/facebook-www/VERSION_CLASSIC ] && cat ./compiled/facebook-www/VERSION_CLASSIC || echo '' )
+ VERSION_MODERN=$( [ -f ./compiled/facebook-www/VERSION_MODERN ] && cat ./compiled/facebook-www/VERSION_MODERN || echo '' )
+ echo "Last classic version is $VERSION_CLASSIC"
+ echo "Last modern version is $VERSION_MODERN"
+ echo "last_version_classic=$VERSION_CLASSIC" >> "$GITHUB_OUTPUT"
+ echo "last_version_modern=$VERSION_MODERN" >> "$GITHUB_OUTPUT"
+ - uses: actions/checkout@v4
+ with:
+ ref: builds/facebook-fbsource
+ - name: "Get last version string for rn"
+ id: get_last_version_rn
+ run: |
+ # Empty checks only needed for backwards compatibility,can remove later.
+ VERSION_NATIVE_FB=$( [ -f ./compiled-rn/VERSION_NATIVE_FB ] && cat ./compiled-rn/VERSION_NATIVE_FB || echo '' )
+ echo "Last rn version is $VERSION_NATIVE_FB"
+ echo "last_version_rn=$VERSION_NATIVE_FB" >> "$GITHUB_OUTPUT"
+ - uses: actions/checkout@v4
+ - name: "Check branches"
+ id: check_branches
+ run: |
+ echo "www_branch_count=$(git ls-remote --heads origin "refs/heads/meta-www" | wc -l)" >> "$GITHUB_OUTPUT"
+ echo "fbsource_branch_count=$(git ls-remote --heads origin "refs/heads/meta-fbsource" | wc -l)" >> "$GITHUB_OUTPUT"
+ - name: Restore downloaded build
+ uses: actions/download-artifact@v4
+ with:
+ name: build
+ path: build
+ - name: Display structure of build
+ run: ls -R build
+ - name: Strip @license from eslint plugin and react-refresh
+ run: |
+ sed -i -e 's/ @license React*//' \
+ build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \
+ build/oss-experimental/react-refresh/cjs/react-refresh-babel.development.js
+ - name: Insert @headers into eslint plugin and react-refresh
+ run: |
+ sed -i -e 's/ LICENSE file in the root directory of this source tree./ LICENSE file in the root directory of this source tree.\n *\n * @noformat\n * @nolint\n * @lightSyntaxTransform\n * @preventMunge\n * @oncall react_core/' \
+ build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \
+ build/oss-experimental/react-refresh/cjs/react-refresh-babel.development.js
+ - name: Move relevant files for React in www into compiled
+ run: |
+ # Move the facebook-www folder into compiled
+ mkdir ./compiled
+ mv build/facebook-www ./compiled
+
+ # Move ReactAllWarnings.js to facebook-www
+ mkdir ./compiled/facebook-www/__test_utils__
+ mv build/__test_utils__/ReactAllWarnings.js ./compiled/facebook-www/__test_utils__/ReactAllWarnings.js
+
+ # Copy eslint-plugin-react-hooks
+ mkdir ./compiled/eslint-plugin-react-hooks
+ cp build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \
+ ./compiled/eslint-plugin-react-hooks/index.js
+
+ # Move unstable_server-external-runtime.js into facebook-www
+ mv build/oss-experimental/react-dom/unstable_server-external-runtime.js \
+ ./compiled/facebook-www/unstable_server-external-runtime.js
+
+ # Move react-refresh-babel.development.js into babel-plugin-react-refresh
+ mkdir ./compiled/babel-plugin-react-refresh
+ mv build/oss-experimental/react-refresh/cjs/react-refresh-babel.development.js \
+ ./compiled/babel-plugin-react-refresh/index.js
+
+ ls -R ./compiled
+ - name: Move relevant files for React in fbsource into compiled-rn
+ run: |
+ BASE_FOLDER='compiled-rn/facebook-fbsource/xplat/js'
+ mkdir -p ${BASE_FOLDER}/react-native-github/Libraries/Renderer/
+ mkdir -p ${BASE_FOLDER}/RKJSModules/vendor/react/{scheduler,react,react-dom,react-is,react-test-renderer}/
+
+ # Move React Native renderer
+ mv build/react-native/implementations/ $BASE_FOLDER/react-native-github/Libraries/Renderer/
+ mv build/react-native/shims/ $BASE_FOLDER/react-native-github/Libraries/Renderer/
+ mv build/facebook-react-native/scheduler/cjs/ $BASE_FOLDER/RKJSModules/vendor/react/scheduler/
+ mv build/facebook-react-native/react/cjs/ $BASE_FOLDER/RKJSModules/vendor/react/react/
+ mv build/facebook-react-native/react-dom/cjs/ $BASE_FOLDER/RKJSModules/vendor/react/react-dom/
+ mv build/facebook-react-native/react-is/cjs/ $BASE_FOLDER/RKJSModules/vendor/react/react-is/
+ mv build/facebook-react-native/react-test-renderer/cjs/ $BASE_FOLDER/RKJSModules/vendor/react/react-test-renderer/
+
+ # Delete the OSS renderers, these are sync'd to RN separately.
+ RENDERER_FOLDER=$BASE_FOLDER/react-native-github/Libraries/Renderer/implementations/
+ rm $RENDERER_FOLDER/ReactFabric-{dev,prod,profiling}.js
+
+ # Delete the legacy renderer shim, this is not sync'd and will get deleted in the future.
+ SHIM_FOLDER=$BASE_FOLDER/react-native-github/Libraries/Renderer/shims/
+ rm $SHIM_FOLDER/ReactNative.js
+
+ # Copy eslint-plugin-react-hooks
+ # NOTE: This is different from www, here we include the full package
+ # including package.json to include dependencies in fbsource.
+ mkdir "$BASE_FOLDER/tools"
+ cp -r build/oss-experimental/eslint-plugin-react-hooks "$BASE_FOLDER/tools"
+
+ # Move React Native version file
+ mv build/facebook-react-native/VERSION_NATIVE_FB ./compiled-rn/VERSION_NATIVE_FB
+
+ ls -R ./compiled-rn
+ - name: Add REVISION files
+ run: |
+ echo ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} >> ./compiled/facebook-www/REVISION
+ cp ./compiled/facebook-www/REVISION ./compiled/facebook-www/REVISION_TRANSFORMS
+ echo ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} >> ./compiled-rn/facebook-fbsource/xplat/js/react-native-github/Libraries/Renderer/REVISION
+ - name: "Get current version string"
+ id: get_current_version
+ run: |
+ VERSION_CLASSIC=$(cat ./compiled/facebook-www/VERSION_CLASSIC)
+ VERSION_MODERN=$(cat ./compiled/facebook-www/VERSION_MODERN)
+ VERSION_NATIVE_FB=$(cat ./compiled-rn/VERSION_NATIVE_FB)
+ echo "Current classic version is $VERSION_CLASSIC"
+ echo "Current modern version is $VERSION_MODERN"
+ echo "Current rn version is $VERSION_NATIVE_FB"
+ echo "current_version_classic=$VERSION_CLASSIC" >> "$GITHUB_OUTPUT"
+ echo "current_version_modern=$VERSION_MODERN" >> "$GITHUB_OUTPUT"
+ echo "current_version_rn=$VERSION_NATIVE_FB" >> "$GITHUB_OUTPUT"
+ - uses: actions/upload-artifact@v4
+ with:
+ name: compiled
+ path: compiled/
+ if-no-files-found: error
+ - uses: actions/upload-artifact@v4
+ with:
+ name: compiled-rn
+ path: compiled-rn/
+ if-no-files-found: error
+
+ commit_www_artifacts:
+ needs: [download_artifacts, process_artifacts]
+ if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.process_artifacts.outputs.www_branch_count == '0')
+ runs-on: ubuntu-latest
+ permissions:
+ # Used to push a commit to builds/facebook-www
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: builds/facebook-www
+ - name: Ensure clean directory
+ run: rm -rf compiled
+ - uses: actions/download-artifact@v4
+ with:
+ name: compiled
+ path: compiled/
+ - name: Revert version changes
+ if: needs.process_artifacts.outputs.last_version_classic != '' && needs.process_artifacts.outputs.last_version_modern != ''
+ env:
+ CURRENT_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.current_version_classic }}
+ CURRENT_VERSION_MODERN: ${{ needs.process_artifacts.outputs.current_version_modern }}
+ LAST_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.last_version_classic }}
+ LAST_VERSION_MODERN: ${{ needs.process_artifacts.outputs.last_version_modern }}
+ run: |
+ echo "Reverting $CURRENT_VERSION_CLASSIC to $LAST_VERSION_CLASSIC"
+ grep -rl "$CURRENT_VERSION_CLASSIC" ./compiled || echo "No files found with $CURRENT_VERSION_CLASSIC"
+ grep -rl "$CURRENT_VERSION_CLASSIC" ./compiled | xargs -r sed -i -e "s/$CURRENT_VERSION_CLASSIC/$LAST_VERSION_CLASSIC/g"
+ grep -rl "$CURRENT_VERSION_CLASSIC" ./compiled || echo "Classic version reverted"
+ echo "===================="
+ echo "Reverting $CURRENT_VERSION_MODERN to $LAST_VERSION_MODERN"
+ grep -rl "$CURRENT_VERSION_MODERN" ./compiled || echo "No files found with $CURRENT_VERSION_MODERN"
+ grep -rl "$CURRENT_VERSION_MODERN" ./compiled | xargs -r sed -i -e "s/$CURRENT_VERSION_MODERN/$LAST_VERSION_MODERN/g"
+ grep -rl "$CURRENT_VERSION_MODERN" ./compiled || echo "Modern version reverted"
+ - name: Check for changes
+ if: inputs.force != true
+ id: check_should_commit
+ run: |
+ echo "Full git status"
+ git add .
+ git status
+ echo "===================="
+ if git status --porcelain | grep -qv '/REVISION'; then
+ echo "Changes detected"
+ echo "===== Changes ====="
+ git --no-pager diff -U0 | grep '^[+-]' | head -n 50
+ echo "==================="
+ echo "should_commit=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "No Changes detected"
+ echo "should_commit=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Re-apply version changes
+ if: inputs.force == true || (steps.check_should_commit.outputs.should_commit == 'true' && needs.process_artifacts.outputs.last_version_classic != '' && needs.process_artifacts.outputs.last_version_modern != '')
+ env:
+ CURRENT_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.current_version_classic }}
+ CURRENT_VERSION_MODERN: ${{ needs.process_artifacts.outputs.current_version_modern }}
+ LAST_VERSION_CLASSIC: ${{ needs.process_artifacts.outputs.last_version_classic }}
+ LAST_VERSION_MODERN: ${{ needs.process_artifacts.outputs.last_version_modern }}
+ run: |
+ echo "Re-applying $LAST_VERSION_CLASSIC to $CURRENT_VERSION_CLASSIC"
+ grep -rl "$LAST_VERSION_CLASSIC" ./compiled || echo "No files found with $LAST_VERSION_CLASSIC"
+ grep -rl "$LAST_VERSION_CLASSIC" ./compiled | xargs -r sed -i -e "s/$LAST_VERSION_CLASSIC/$CURRENT_VERSION_CLASSIC/g"
+ grep -rl "$LAST_VERSION_CLASSIC" ./compiled || echo "Classic version re-applied"
+ echo "===================="
+ echo "Re-applying $LAST_VERSION_MODERN to $CURRENT_VERSION_MODERN"
+ grep -rl "$LAST_VERSION_MODERN" ./compiled || echo "No files found with $LAST_VERSION_MODERN"
+ grep -rl "$LAST_VERSION_MODERN" ./compiled | xargs -r sed -i -e "s/$LAST_VERSION_MODERN/$CURRENT_VERSION_MODERN/g"
+ grep -rl "$LAST_VERSION_MODERN" ./compiled || echo "Classic version re-applied"
+ - name: Will commit these changes
+ if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
+ run: |
+ git add .
+ git status
+ - name: Check commit message
+ if: inputs.dry_run
+ run: |
+ git fetch origin --quiet
+ git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:"%B"
+ - name: Commit changes to branch
+ if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
+ run: |
+ git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}"
+ git config --global user.name "${{ github.triggering_actor }}"
+
+ git fetch origin --quiet
+ git commit -m "$(git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit"
+ - name: Push changes to branch
+ if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
+ run: git push
+
+ commit_fbsource_artifacts:
+ needs: [download_artifacts, process_artifacts]
+ permissions:
+ # Used to push a commit to builds/facebook-fbsource
+ contents: write
+ if: inputs.force == true || (github.ref == 'refs/heads/main' && needs.process_artifacts.outputs.fbsource_branch_count == '0')
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: builds/facebook-fbsource
+ - name: Ensure clean directory
+ run: rm -rf compiled-rn
+ - uses: actions/download-artifact@v4
+ with:
+ name: compiled-rn
+ path: compiled-rn/
+ - name: Revert version changes
+ if: needs.process_artifacts.outputs.last_version_rn != ''
+ env:
+ CURRENT_VERSION: ${{ needs.process_artifacts.outputs.current_version_rn }}
+ LAST_VERSION: ${{ needs.process_artifacts.outputs.last_version_rn }}
+ run: |
+ echo "Reverting $CURRENT_VERSION to $LAST_VERSION"
+ grep -rl "$CURRENT_VERSION" ./compiled-rn || echo "No files found with $CURRENT_VERSION"
+ grep -rl "$CURRENT_VERSION" ./compiled-rn | xargs -r sed -i -e "s/$CURRENT_VERSION/$LAST_VERSION/g"
+ grep -rl "$CURRENT_VERSION" ./compiled-rn || echo "Version reverted"
+ - name: Check for changes
+ if: inputs.force != 'true'
+ id: check_should_commit
+ run: |
+ echo "Full git status"
+ git add .
+ git --no-pager diff -U0 --cached | grep '^[+-]' | head -n 100
+ echo "===================="
+ # Ignore REVISION or lines removing @generated headers.
+ if git diff --cached ':(exclude)*REVISION' ':(exclude)*/eslint-plugin-react-hooks/package.json' | grep -vE "^(@@|diff|index|\-\-\-|\+\+\+|\- \* @generated SignedSource)" | grep "^[+-]" > /dev/null; then
+ echo "Changes detected"
+ echo "===== Changes ====="
+ git --no-pager diff --cached ':(exclude)*REVISION' ':(exclude)*/eslint-plugin-react-hooks/package.json' | grep -vE "^(@@|diff|index|\-\-\-|\+\+\+|\- \* @generated SignedSource)" | grep "^[+-]" | head -n 50
+ echo "==================="
+ echo "should_commit=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "No Changes detected"
+ echo "should_commit=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Re-apply version changes
+ if: inputs.force == true || (steps.check_should_commit.outputs.should_commit == 'true' && needs.process_artifacts.outputs.last_version_rn != '')
+ env:
+ CURRENT_VERSION: ${{ needs.process_artifacts.outputs.current_version_rn }}
+ LAST_VERSION: ${{ needs.process_artifacts.outputs.last_version_rn }}
+ run: |
+ echo "Re-applying $LAST_VERSION to $CURRENT_VERSION"
+ grep -rl "$LAST_VERSION" ./compiled-rn || echo "No files found with $LAST_VERSION"
+ grep -rl "$LAST_VERSION" ./compiled-rn | xargs -r sed -i -e "s/$LAST_VERSION/$CURRENT_VERSION/g"
+ grep -rl "$LAST_VERSION" ./compiled-rn || echo "Version re-applied"
+ - name: Add files for signing
+ if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
+ run: |
+ echo ":"
+ git add .
+ - name: Signing files
+ if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ // TODO: Move this to a script file.
+ // We currently can't call scripts from the repo because
+ // at this point in the workflow, we're on the compiled
+ // artifact branch (so the scripts don't exist).
+ // We can fix this with a composite action in the main repo.
+ // This script is duplicated above.
+ const fs = require('fs');
+ const crypto = require('crypto');
+ const {execSync} = require('child_process');
+
+ // TODO: when we move this to a script, we can use this from npm.
+ // Copy of signedsource since we can't install deps on this branch.
+ const GENERATED = '@' + 'generated';
+ const NEWTOKEN = '<>';
+ const PATTERN = new RegExp(`${GENERATED} (?:SignedSource<<([a-f0-9]{32})>>)`);
+
+ const TokenNotFoundError = new Error(
+ `SignedSource.signFile(...): Cannot sign file without token: ${NEWTOKEN}`
+ );
+
+ function hash(data, encoding) {
+ const md5sum = crypto.createHash('md5');
+ md5sum.update(data, encoding);
+ return md5sum.digest('hex');
+ }
+
+ const SignedSource = {
+ getSigningToken() {
+ return `${GENERATED} ${NEWTOKEN}`;
+ },
+ isSigned(data) {
+ return PATTERN.exec(data) != null;
+ },
+ signFile(data) {
+ if (!data.includes(NEWTOKEN)) {
+ if (SignedSource.isSigned(data)) {
+ // Signing a file that was previously signed.
+ data = data.replace(PATTERN, SignedSource.getSigningToken());
+ } else {
+ throw TokenNotFoundError;
+ }
+ }
+ return data.replace(NEWTOKEN, `SignedSource<<${hash(data, 'utf8')}>>`);
+ },
+ };
+
+ const directory = './compiled-rn';
+ console.log('Signing files in directory:', directory);
+ try {
+ const result = execSync(`git status --porcelain ${directory}`, {encoding: 'utf8'});
+ console.log(result);
+
+ // Parse the git status output to get file paths!
+ const files = result.split('\n').filter(file => file.endsWith('.js'));
+
+ if (files.length === 0) {
+ throw new Error(
+ 'git status returned no files to sign. this job should not have run.'
+ );
+ } else {
+ files.forEach(line => {
+ let file = null;
+ if (line.startsWith('D ')) {
+ return;
+ } else if (line.startsWith('R ')) {
+ file = line.slice(line.indexOf('->') + 3);
+ } else {
+ file = line.slice(3).trim();
+ }
+ if (file) {
+ console.log(' Signing file:', file);
+ const originalContents = fs.readFileSync(file, 'utf8');
+ const signedContents = SignedSource.signFile(
+ originalContents
+ // Need to add the header in, since it's not inserted at build time.
+ .replace(' */\n', ` * ${SignedSource.getSigningToken()}\n */\n`)
+ );
+
+ fs.writeFileSync(file, signedContents, 'utf8');
+ }
+ });
+ }
+ } catch (e) {
+ process.exitCode = 1;
+ console.error('Error signing files:', e);
+ }
+ - name: Will commit these changes
+ if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
+ run: |
+ git add .
+ git status
+ - name: Check commit message
+ if: inputs.dry_run
+ run: |
+ git fetch origin --quiet
+ git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:"%B"
+ - name: Commit changes to branch
+ if: inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true'
+ run: |
+ git config --global user.email "${{ format('{0}@users.noreply.github.com', github.triggering_actor) }}"
+ git config --global user.name "${{ github.triggering_actor }}"
+
+ git fetch origin --quiet
+ git commit -m "$(git show ${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }} --no-patch --pretty=format:'%B%n%nDiffTrain build for [${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha }}](https://github.com/facebook/react/commit/${{ inputs.commit_sha || github.event.workflow_run.head_sha || github.sha}})')" || echo "No changes to commit"
+ - name: Push changes to branch
+ if: inputs.dry_run == false && (inputs.force == true || steps.check_should_commit.outputs.should_commit == 'true')
+ run: git push
diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml
new file mode 100644
index 00000000000..ae9930adf11
--- /dev/null
+++ b/.github/workflows/runtime_discord_notify.yml
@@ -0,0 +1,51 @@
+name: (Runtime) Discord Notify
+
+on:
+ pull_request_target:
+ types: [opened, ready_for_review]
+ paths-ignore:
+ - packages/react-devtools**
+ - compiler/**
+ - .github/workflows/compiler_**.yml
+ - .github/workflows/devtools**.yml
+
+permissions: {}
+
+jobs:
+ check_access:
+ if: ${{ github.event.pull_request.draft == false }}
+ runs-on: ubuntu-latest
+ outputs:
+ is_member_or_collaborator: ${{ steps.check_is_member_or_collaborator.outputs.is_member_or_collaborator }}
+ steps:
+ - run: echo ${{ github.event.pull_request.author_association }}
+ - name: Check is member or collaborator
+ id: check_is_member_or_collaborator
+ if: ${{ github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'COLLABORATOR' }}
+ run: echo "is_member_or_collaborator=true" >> "$GITHUB_OUTPUT"
+
+ check_maintainer:
+ if: ${{ needs.check_access.outputs.is_member_or_collaborator == 'true' || needs.check_access.outputs.is_member_or_collaborator == true }}
+ needs: [check_access]
+ uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main
+ permissions:
+ # Used by check_maintainer
+ contents: read
+ with:
+ actor: ${{ github.event.pull_request.user.login }}
+
+ notify:
+ if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }}
+ needs: check_maintainer
+ runs-on: ubuntu-latest
+ steps:
+ - name: Discord Webhook Action
+ uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
+ with:
+ webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ embed-author-name: ${{ github.event.pull_request.user.login }}
+ embed-author-url: ${{ github.event.pull_request.user.html_url }}
+ embed-author-icon-url: ${{ github.event.pull_request.user.avatar_url }}
+ embed-title: '#${{ github.event.number }} (+${{github.event.pull_request.additions}} -${{github.event.pull_request.deletions}}): ${{ github.event.pull_request.title }}'
+ embed-description: ${{ github.event.pull_request.body }}
+ embed-url: ${{ github.event.pull_request.html_url }}
diff --git a/.github/workflows/runtime_eslint_plugin_e2e.yml b/.github/workflows/runtime_eslint_plugin_e2e.yml
new file mode 100644
index 00000000000..92921646c1b
--- /dev/null
+++ b/.github/workflows/runtime_eslint_plugin_e2e.yml
@@ -0,0 +1,65 @@
+name: (Runtime) ESLint Plugin E2E
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ paths-ignore:
+ - compiler/**
+
+permissions: {}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ # ----- TESTS -----
+ test:
+ name: ESLint v${{ matrix.eslint_major }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ eslint_major:
+ - "6"
+ - "7"
+ - "8"
+ - "9"
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: |
+ yarn.lock
+ compiler/yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-and-compiler-eslint_e2e-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'compiler/yarn.lock', 'fixtures/eslint-v*/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd compiler install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Install fixture dependencies
+ working-directory: ./fixtures/eslint-v${{ matrix.eslint_major }}
+ run: yarn --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - name: Build plugin
+ working-directory: fixtures/eslint-v${{ matrix.eslint_major }}
+ run: node build.mjs
+ - name: Run lint test
+ working-directory: ./fixtures/eslint-v${{ matrix.eslint_major }}
+ run: yarn lint
diff --git a/.github/workflows/runtime_fuzz_tests.yml b/.github/workflows/runtime_fuzz_tests.yml
new file mode 100644
index 00000000000..a88ce523a62
--- /dev/null
+++ b/.github/workflows/runtime_fuzz_tests.yml
@@ -0,0 +1,33 @@
+name: (Runtime) Fuzz tests
+
+on:
+ schedule:
+ - cron: 0 * * * *
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ test_fuzz:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4.1.0
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: 'yarn'
+ - name: Install dependencies
+ run: yarn install --frozen-lockfile
+ env:
+ ELECTRON_SKIP_BINARY_DOWNLOAD: "1"
+ shell: bash
+ - name: Run fuzz tests
+ run: |-
+ FUZZ_TEST_SEED=$RANDOM yarn test fuzz --ci
+ FUZZ_TEST_SEED=$RANDOM yarn test --prod fuzz --ci
diff --git a/.github/workflows/runtime_prereleases.yml b/.github/workflows/runtime_prereleases.yml
new file mode 100644
index 00000000000..6559b144997
--- /dev/null
+++ b/.github/workflows/runtime_prereleases.yml
@@ -0,0 +1,110 @@
+name: (Runtime) Publish Prereleases
+
+on:
+ workflow_call:
+ inputs:
+ commit_sha:
+ required: true
+ default: ''
+ type: string
+ release_channel:
+ required: true
+ type: string
+ dist_tag:
+ required: true
+ type: string
+ enableFailureNotification:
+ description: 'Whether to notify the team on Discord when the release fails. Useful if this workflow is called from an automation.'
+ required: false
+ type: boolean
+ only_packages:
+ description: Packages to publish (space separated)
+ type: string
+ skip_packages:
+ description: Packages to NOT publish (space separated)
+ type: string
+ dry:
+ required: true
+ description: Dry run instead of publish?
+ type: boolean
+ default: true
+ secrets:
+ DISCORD_WEBHOOK_URL:
+ description: 'Discord webhook URL to notify on failure. Only required if enableFailureNotification is true.'
+ required: false
+ GH_TOKEN:
+ required: true
+ NPM_TOKEN:
+ required: true
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+jobs:
+ publish_prerelease:
+ name: Publish prelease (${{ inputs.release_channel }}) ${{ inputs.commit_sha }} @${{ inputs.dist_tag }}
+ runs-on: ubuntu-latest
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd scripts/release install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: cp ./scripts/release/ci-npmrc ~/.npmrc
+ - run: |
+ GH_TOKEN=${{ secrets.GH_TOKEN }} scripts/release/prepare-release-from-ci.js --skipTests -r ${{ inputs.release_channel }} --commit=${{ inputs.commit_sha }}
+ - name: Check prepared files
+ run: ls -R build/node_modules
+ - if: '${{ inputs.only_packages }}'
+ name: 'Publish ${{ inputs.only_packages }}'
+ run: |
+ scripts/release/publish.js \
+ --ci \
+ --tags=${{ inputs.dist_tag }} \
+ --onlyPackages=${{ inputs.only_packages }} ${{ (inputs.dry && '') || '\'}}
+ ${{ inputs.dry && '--dry' || '' }}
+ - if: '${{ inputs.skip_packages }}'
+ name: 'Publish all packages EXCEPT ${{ inputs.skip_packages }}'
+ run: |
+ scripts/release/publish.js \
+ --ci \
+ --tags=${{ inputs.dist_tag }} \
+ --skipPackages=${{ inputs.skip_packages }} ${{ (inputs.dry && '') || '\'}}
+ ${{ inputs.dry && '--dry' || '' }}
+ - if: '${{ !inputs.skip_packages && !inputs.only_packages }}'
+ name: 'Publish all packages'
+ run: |
+ scripts/release/publish.js \
+ --ci \
+ --tags=${{ inputs.dist_tag }} ${{ (inputs.dry && '') || '\'}}
+ ${{ inputs.dry && '--dry' || '' }}
+ - name: Notify Discord on failure
+ if: failure() && inputs.enableFailureNotification == true
+ uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
+ with:
+ webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ embed-author-name: "GitHub Actions"
+ embed-title: '[Runtime] Publish of ${{ inputs.release_channel }}@${{ inputs.dist_tag}} release failed'
+ embed-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}
diff --git a/.github/workflows/runtime_prereleases_manual.yml b/.github/workflows/runtime_prereleases_manual.yml
new file mode 100644
index 00000000000..407d931e907
--- /dev/null
+++ b/.github/workflows/runtime_prereleases_manual.yml
@@ -0,0 +1,102 @@
+name: (Runtime) Publish Prereleases Manual
+
+on:
+ workflow_dispatch:
+ inputs:
+ prerelease_commit_sha:
+ required: true
+ only_packages:
+ description: Packages to publish (space separated)
+ type: string
+ skip_packages:
+ description: Packages to NOT publish (space separated)
+ type: string
+ dry:
+ required: true
+ description: Dry run instead of publish?
+ type: boolean
+ default: true
+ experimental_only:
+ type: boolean
+ description: Only publish to the experimental tag
+ default: false
+ force_notify:
+ description: Force a Discord notification?
+ type: boolean
+ default: false
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ notify:
+ if: ${{ inputs.force_notify || inputs.dry == false || inputs.dry == 'false' }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Discord Webhook Action
+ uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
+ with:
+ webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ embed-author-name: ${{ github.event.sender.login }}
+ embed-author-url: ${{ github.event.sender.html_url }}
+ embed-author-icon-url: ${{ github.event.sender.avatar_url }}
+ embed-title: "โ ๏ธ Publishing ${{ inputs.experimental_only && 'EXPERIMENTAL' || 'CANARY & EXPERIMENTAL' }} release ${{ (inputs.dry && ' (dry run)') || '' }}"
+ embed-description: |
+ ```json
+ ${{ toJson(inputs) }}
+ ```
+ embed-url: https://github.com/facebook/react/actions/runs/${{ github.run_id }}
+
+ publish_prerelease_canary:
+ if: ${{ !inputs.experimental_only }}
+ name: Publish to Canary channel
+ uses: facebook/react/.github/workflows/runtime_prereleases.yml@main
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ with:
+ commit_sha: ${{ inputs.prerelease_commit_sha }}
+ release_channel: stable
+ # The tags to use when publishing canaries. The main one we should
+ # always include is "canary" but we can use multiple (e.g. alpha,
+ # beta, rc). To declare multiple, use a comma-separated string, like
+ # this:
+ # dist_tag: "canary,alpha,beta,rc"
+ #
+ # TODO: We currently tag canaries with "next" in addition to "canary"
+ # because this used to be called the "next" channel and some
+ # downstream consumers might still expect that tag. We can remove this
+ # after some time has elapsed and the change has been communicated.
+ dist_tag: canary,next
+ only_packages: ${{ inputs.only_packages }}
+ skip_packages: ${{ inputs.skip_packages }}
+ dry: ${{ inputs.dry }}
+ secrets:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ publish_prerelease_experimental:
+ name: Publish to Experimental channel
+ uses: facebook/react/.github/workflows/runtime_prereleases.yml@main
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ # NOTE: Intentionally running these jobs sequentially because npm
+ # will sometimes fail if you try to concurrently publish two
+ # different versions of the same package, even if they use different
+ # dist tags.
+ needs: publish_prerelease_canary
+ # Ensures the job runs even if canary is skipped
+ if: always()
+ with:
+ commit_sha: ${{ inputs.prerelease_commit_sha }}
+ release_channel: experimental
+ dist_tag: experimental
+ only_packages: ${{ inputs.only_packages }}
+ skip_packages: ${{ inputs.skip_packages }}
+ dry: ${{ inputs.dry }}
+ secrets:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/runtime_prereleases_nightly.yml b/.github/workflows/runtime_prereleases_nightly.yml
new file mode 100644
index 00000000000..f13a92e46f4
--- /dev/null
+++ b/.github/workflows/runtime_prereleases_nightly.yml
@@ -0,0 +1,51 @@
+name: (Runtime) Publish Prereleases Nightly
+
+on:
+ schedule:
+ # At 10 minutes past 16:00 on Mon, Tue, Wed, Thu, and Fri
+ - cron: 10 16 * * 1,2,3,4,5
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ publish_prerelease_canary:
+ name: Publish to Canary channel
+ uses: facebook/react/.github/workflows/runtime_prereleases.yml@main
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ with:
+ commit_sha: ${{ github.sha }}
+ release_channel: stable
+ dist_tag: canary,next
+ enableFailureNotification: true
+ dry: false
+ secrets:
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ publish_prerelease_experimental:
+ name: Publish to Experimental channel
+ uses: facebook/react/.github/workflows/runtime_prereleases.yml@main
+ permissions:
+ # We use github.token to download the build artifact from a previous runtime_build_and_test.yml run
+ actions: read
+ # NOTE: Intentionally running these jobs sequentially because npm
+ # will sometimes fail if you try to concurrently publish two
+ # different versions of the same package, even if they use different
+ # dist tags.
+ needs: publish_prerelease_canary
+ with:
+ commit_sha: ${{ github.sha }}
+ release_channel: experimental
+ dist_tag: experimental
+ enableFailureNotification: true
+ dry: false
+ secrets:
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/runtime_releases_from_npm_manual.yml b/.github/workflows/runtime_releases_from_npm_manual.yml
new file mode 100644
index 00000000000..f164e9f0806
--- /dev/null
+++ b/.github/workflows/runtime_releases_from_npm_manual.yml
@@ -0,0 +1,128 @@
+name: (Runtime) Publish Releases from NPM Manual
+
+on:
+ workflow_dispatch:
+ inputs:
+ version_to_promote:
+ required: true
+ description: Current npm version (non-experimental) to promote
+ type: string
+ version_to_publish:
+ required: true
+ description: Version to publish for the specified packages
+ type: string
+ only_packages:
+ description: Packages to publish (space separated)
+ type: string
+ skip_packages:
+ description: Packages to NOT publish (space separated)
+ type: string
+ tags:
+ description: NPM tags (space separated)
+ type: string
+ default: untagged
+ dry:
+ required: true
+ description: Dry run instead of publish?
+ type: boolean
+ default: true
+ force_notify:
+ description: Force a Discord notification?
+ type: boolean
+ default: false
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+jobs:
+ notify:
+ if: ${{ inputs.force_notify || inputs.dry == false || inputs.dry == 'false' }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Discord Webhook Action
+ uses: tsickert/discord-webhook@86dc739f3f165f16dadc5666051c367efa1692f4
+ with:
+ webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ embed-author-name: ${{ github.event.sender.login }}
+ embed-author-url: ${{ github.event.sender.html_url }}
+ embed-author-icon-url: ${{ github.event.sender.avatar_url }}
+ embed-title: "โ ๏ธ Publishing release from NPM${{ (inputs.dry && ' (dry run)') || '' }}"
+ embed-description: |
+ ```json
+ ${{ toJson(inputs) }}
+ ```
+ embed-url: https://github.com/facebook/react/actions/runs/${{ github.run_id }}
+
+ publish:
+ name: Publish releases
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: runtime-release-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('yarn.lock', 'scripts/release/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn --cwd scripts/release install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: cp ./scripts/release/ci-npmrc ~/.npmrc
+ - if: '${{ inputs.only_packages }}'
+ name: 'Prepare ${{ inputs.only_packages }} from NPM'
+ run: |
+ scripts/release/prepare-release-from-npm.js \
+ --ci \
+ --skipTests \
+ --version=${{ inputs.version_to_promote }} \
+ --publishVersion=${{ inputs.version_to_publish }} \
+ --onlyPackages=${{ inputs.only_packages }}
+ - if: '${{ inputs.skip_packages }}'
+ name: 'Prepare all packages EXCEPT ${{ inputs.skip_packages }} from NPM'
+ run: |
+ scripts/release/prepare-release-from-npm.js \
+ --ci \
+ --skipTests \
+ --version=${{ inputs.version_to_promote }} \
+ --publishVersion=${{ inputs.version_to_publish }} \
+ --skipPackages=${{ inputs.skip_packages }}
+ - name: Check prepared files
+ run: ls -R build/node_modules
+ - if: '${{ inputs.only_packages }}'
+ name: 'Publish ${{ inputs.only_packages }}'
+ run: |
+ scripts/release/publish.js \
+ --ci \
+ --tags=${{ inputs.tags }} \
+ --publishVersion=${{ inputs.version_to_publish }} \
+ --onlyPackages=${{ inputs.only_packages }} ${{ (inputs.dry && '') || '\'}}
+ ${{ inputs.dry && '--dry' || '' }}
+ - if: '${{ inputs.skip_packages }}'
+ name: 'Publish all packages EXCEPT ${{ inputs.skip_packages }}'
+ run: |
+ scripts/release/publish.js \
+ --ci \
+ --tags=${{ inputs.tags }} \
+ --publishVersion=${{ inputs.version_to_publish }} \
+ --skipPackages=${{ inputs.skip_packages }} ${{ (inputs.dry && '') || '\'}}
+ ${{ inputs.dry && '--dry' || '' }}
+ - name: Archive released package for debugging
+ uses: actions/upload-artifact@v4
+ with:
+ name: build
+ path: |
+ ./build/node_modules
diff --git a/.github/workflows/shared_check_maintainer.yml b/.github/workflows/shared_check_maintainer.yml
new file mode 100644
index 00000000000..f6eb9b9a6d1
--- /dev/null
+++ b/.github/workflows/shared_check_maintainer.yml
@@ -0,0 +1,58 @@
+name: (Shared) Check maintainer
+
+on:
+ workflow_call:
+ inputs:
+ actor:
+ required: true
+ type: string
+ outputs:
+ is_core_team:
+ value: ${{ jobs.check_maintainer.outputs.is_core_team }}
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ check_maintainer:
+ runs-on: ubuntu-latest
+ permissions:
+ # We fetch the contents of the MAINTAINERS file
+ contents: read
+ outputs:
+ is_core_team: ${{ steps.check_if_actor_is_maintainer.outputs.result }}
+ steps:
+ - name: Check if actor is maintainer
+ id: check_if_actor_is_maintainer
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const fs = require('fs');
+ const actor = '${{ inputs.actor }}';
+ const res = await github.rest.repos.getContent({
+ owner: 'facebook',
+ repo: 'react',
+ path: 'MAINTAINERS',
+ ref: 'main',
+ headers: { Accept: 'application/vnd.github+json' }
+ });
+ if (res.status !== 200) {
+ console.error(res);
+ throw new Error('Unable to fetch MAINTAINERS file');
+ }
+ content = Buffer.from(res.data.content, 'base64').toString();
+ if (content == null || typeof content !== 'string') {
+ throw new Error('Unable to retrieve MAINTAINERS file');
+ }
+
+ const maintainers = new Set(content.split('\n'));
+ if (maintainers.has(actor)) {
+ console.log(`๐ข ${actor} is a maintainer`);
+ return true;
+ }
+ console.log(`๐ด ${actor} is NOT a maintainer`);
+ return null;
diff --git a/.github/workflows/shared_cleanup_merged_branch_caches.yml b/.github/workflows/shared_cleanup_merged_branch_caches.yml
new file mode 100644
index 00000000000..ed80a505e42
--- /dev/null
+++ b/.github/workflows/shared_cleanup_merged_branch_caches.yml
@@ -0,0 +1,41 @@
+# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy
+
+name: (Shared) Cleanup Merged Branch Caches
+on:
+ pull_request:
+ types:
+ - closed
+ workflow_dispatch:
+ inputs:
+ pr_number:
+ required: true
+ type: string
+
+permissions: {}
+
+jobs:
+ cleanup:
+ runs-on: ubuntu-latest
+ permissions:
+ # `actions:write` permission is required to delete caches
+ # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id
+ actions: write
+ contents: read
+ steps:
+ - name: Cleanup
+ run: |
+ echo "Fetching list of cache key"
+ cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')
+
+ ## Setting this to not fail the workflow while deleting cache keys.
+ set +e
+ for cacheKey in $cacheKeysForPR
+ do
+ gh cache delete $cacheKey
+ echo "Deleting $cacheKey"
+ done
+ echo "Done"
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_REPO: ${{ github.repository }}
+ BRANCH: refs/pull/${{ inputs.pr_number || github.event.pull_request.number }}/merge
diff --git a/.github/workflows/shared_cleanup_stale_branch_caches.yml b/.github/workflows/shared_cleanup_stale_branch_caches.yml
new file mode 100644
index 00000000000..f6c532b485a
--- /dev/null
+++ b/.github/workflows/shared_cleanup_stale_branch_caches.yml
@@ -0,0 +1,36 @@
+# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy
+
+name: (Shared) Cleanup Stale Branch Caches
+on:
+ schedule:
+ # Every 6 hours
+ - cron: 0 */6 * * *
+ workflow_dispatch:
+
+permissions: {}
+
+jobs:
+ cleanup:
+ runs-on: ubuntu-latest
+ permissions:
+ # `actions:write` permission is required to delete caches
+ # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id
+ actions: write
+ contents: read
+ steps:
+ - name: Cleanup
+ run: |
+ echo "Fetching list of cache keys"
+ cacheKeysForPR=$(gh cache list --limit 100 --json id,ref --jq '.[] | select(.ref != "refs/heads/main") | .id')
+
+ ## Setting this to not fail the workflow while deleting cache keys.
+ set +e
+ for cacheKey in $cacheKeysForPR
+ do
+ gh cache delete $cacheKey
+ echo "Deleting $cacheKey"
+ done
+ echo "Done"
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_REPO: ${{ github.repository }}
diff --git a/.github/workflows/shared_close_direct_sync_branch_prs.yml b/.github/workflows/shared_close_direct_sync_branch_prs.yml
new file mode 100644
index 00000000000..caa4da880b5
--- /dev/null
+++ b/.github/workflows/shared_close_direct_sync_branch_prs.yml
@@ -0,0 +1,43 @@
+name: (Shared) Close Direct Sync Branch PRs
+
+on:
+ pull_request:
+ branches:
+ - 'builds/facebook-**'
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ close_pr:
+ runs-on: ubuntu-latest
+ permissions:
+ # Used to create a review and close PRs
+ pull-requests: write
+ contents: write
+ steps:
+ - name: Close PR
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+ const pullNumber = ${{ github.event.number }};
+
+ await github.rest.pulls.createReview({
+ owner,
+ repo,
+ pull_number: pullNumber,
+ body: 'Do not land changes to `${{ github.event.pull_request.base.ref }}`. Please re-open your PR targeting `main` instead.',
+ event: 'REQUEST_CHANGES'
+ });
+ await github.rest.pulls.update({
+ owner,
+ repo,
+ pull_number: pullNumber,
+ state: 'closed'
+ });
diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml
new file mode 100644
index 00000000000..cc10e87dcc2
--- /dev/null
+++ b/.github/workflows/shared_label_core_team_prs.yml
@@ -0,0 +1,55 @@
+name: (Shared) Label Core Team PRs
+
+on:
+ pull_request_target:
+ types: [opened]
+
+permissions: {}
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ check_access:
+ runs-on: ubuntu-latest
+ outputs:
+ is_member_or_collaborator: ${{ steps.check_is_member_or_collaborator.outputs.is_member_or_collaborator }}
+ steps:
+ - run: echo ${{ github.event.pull_request.author_association }}
+ - name: Check is member or collaborator
+ id: check_is_member_or_collaborator
+ if: ${{ github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'COLLABORATOR' }}
+ run: echo "is_member_or_collaborator=true" >> "$GITHUB_OUTPUT"
+
+ check_maintainer:
+ if: ${{ needs.check_access.outputs.is_member_or_collaborator == 'true' || needs.check_access.outputs.is_member_or_collaborator == true }}
+ needs: [check_access]
+ uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main
+ permissions:
+ # Used by check_maintainer
+ contents: read
+ with:
+ actor: ${{ github.event.pull_request.user.login }}
+
+ label:
+ if: ${{ needs.check_maintainer.outputs.is_core_team == 'true' }}
+ runs-on: ubuntu-latest
+ needs: check_maintainer
+ permissions:
+ # Used to add labels on issues
+ issues: write
+ # Used to add labels on PRs
+ pull-requests: write
+ steps:
+ - name: Label PR as React Core Team
+ uses: actions/github-script@v7
+ with:
+ script: |
+ github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: ${{ github.event.number }},
+ labels: ['React Core Team']
+ });
diff --git a/.github/workflows/shared_lint.yml b/.github/workflows/shared_lint.yml
new file mode 100644
index 00000000000..3c359cff228
--- /dev/null
+++ b/.github/workflows/shared_lint.yml
@@ -0,0 +1,110 @@
+name: (Shared) Lint
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+permissions: {}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
+
+jobs:
+ prettier:
+ name: Run prettier
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: yarn prettier-check
+
+ eslint:
+ name: Run eslint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: node ./scripts/tasks/eslint
+
+ check_license:
+ name: Check license
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: ./scripts/ci/check_license.sh
+
+ test_print_warnings:
+ name: Test print warnings
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version-file: '.nvmrc'
+ cache: yarn
+ cache-dependency-path: yarn.lock
+ - name: Restore cached node_modules
+ uses: actions/cache@v4
+ id: node_modules
+ with:
+ path: |
+ **/node_modules
+ key: shared-lint-node_modules-v6-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
+ - name: Ensure clean build directory
+ run: rm -rf build
+ - run: yarn install --frozen-lockfile
+ if: steps.node_modules.outputs.cache-hit != 'true'
+ - run: ./scripts/ci/test_print_warnings.sh
diff --git a/.github/workflows/shared_stale.yml b/.github/workflows/shared_stale.yml
new file mode 100644
index 00000000000..c24895edc5d
--- /dev/null
+++ b/.github/workflows/shared_stale.yml
@@ -0,0 +1,55 @@
+# Configuration for stale action workflow - https://github.com/actions/stale
+name: (Shared) Manage stale issues and PRs
+on:
+ schedule:
+ # Run hourly
+ - cron: '0 * * * *'
+ workflow_dispatch:
+
+permissions:
+ # https://github.com/actions/stale/tree/v9/?tab=readme-ov-file#recommended-permissions
+ issues: write
+ pull-requests: write
+
+env:
+ TZ: /usr/share/zoneinfo/America/Los_Angeles
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v9
+ with:
+ # --- Issues & PRs ---
+ # Number of days of inactivity before an issue or PR becomes stale
+ days-before-stale: 90
+ # Number of days of inactivity before a stale issue or PR is closed
+ days-before-close: 7
+ # API calls per run
+ operations-per-run: 100
+
+ # --- Issues ---
+ stale-issue-label: "Resolution: Stale"
+ # Comment to post when marking an issue as stale
+ stale-issue-message: >
+ This issue has been automatically marked as stale.
+ **If this issue is still affecting you, please leave any comment** (for example, "bump"), and we'll keep it open.
+ We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!
+ # Comment to post when closing a stale issue
+ close-issue-message: >
+ Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!
+ # Issues with these labels will never be considered stale
+ exempt-issue-labels: "Partner,React Core Team,Resolution: Backlog,Type: Bug,Type: Discussion,Type: Needs Investigation,Type: Regression,Type: Feature Request,Type: Enhancement"
+
+ # --- PRs ---
+ stale-pr-label: "Resolution: Stale"
+ # Comment to post when marking a pull request as stale
+ stale-pr-message: >
+ This pull request has been automatically marked as stale.
+ **If this pull request is still relevant, please leave any comment** (for example, "bump"), and we'll keep it open.
+ We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated.
+ # Comment to post when closing a stale pull request
+ close-pr-message: >
+ Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you!
+ # PRs with these labels will never be considered stale
+ exempt-pr-labels: "Partner,React Core Team,Resolution: Backlog,Type: Bug,Type: Discussion,Type: Needs Investigation,Type: Regression,Type: Feature Request,Type: Enhancement"
diff --git a/.gitignore b/.gitignore
index 86f2dbb4f4c..438d125b47a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
.DS_STORE
node_modules
scripts/flow/*/.flowconfig
-scripts/rollup/results.json
+.flowconfig
*~
*.pyc
.grunt
@@ -23,3 +23,21 @@ chrome-user-data
.vscode
*.swp
*.swo
+/tmp
+/.worktrees
+.claude/*.local.*
+
+packages/react-devtools-core/dist
+packages/react-devtools-extensions/chrome/build
+packages/react-devtools-extensions/chrome/*.crx
+packages/react-devtools-extensions/chrome/*.pem
+packages/react-devtools-extensions/firefox/build
+packages/react-devtools-extensions/firefox/*.xpi
+packages/react-devtools-extensions/firefox/*.pem
+packages/react-devtools-extensions/shared/build
+packages/react-devtools-extensions/.tempUserDataDir
+packages/react-devtools-fusebox/dist
+packages/react-devtools-inline/dist
+packages/react-devtools-shell/dist
+packages/react-devtools-timeline/dist
+
diff --git a/.mailmap b/.mailmap
index 278784e0afa..e661c3707d5 100644
--- a/.mailmap
+++ b/.mailmap
@@ -88,6 +88,7 @@ Kevin Coughlin
Krystian Karczewski
Kunal Mehta
Laurence Rowe
+Lea Rosema
Marcin K.
Mark Anderson
Mark Funk
@@ -126,6 +127,8 @@ Rainer Oviir
Ray
Richard Feldman
Richard Livesey
+Rick Hanlon
+Rick Hanlon
Rob Arnold
Robert Binna
Robin Frischmann
@@ -144,6 +147,7 @@ Steven Luscher
Steven Luscher
Steven Luscher
Steven Luscher
+Seth Webster
Stoyan Stefanov
Tengfei Guo
Thomas Aylott
diff --git a/.nvmrc b/.nvmrc
index 33edba2d01f..5f53e875de6 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v8.4.0
+v20.19.0
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000000..7e09af76a3a
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,41 @@
+# react runtime
+build
+
+packages/react-devtools-core/dist
+packages/react-devtools-extensions/chrome/build
+packages/react-devtools-extensions/firefox/build
+packages/react-devtools-extensions/edge/build
+packages/react-devtools-extensions/shared/build
+packages/react-devtools-extensions/src/ErrorTesterCompiled.js
+packages/react-devtools-fusebox/dist
+packages/react-devtools-inline/dist
+packages/react-devtools-shared/src/hooks/__tests__/__source__/__compiled__/
+packages/react-devtools-shared/src/hooks/__tests__/__source__/__untransformed__/
+packages/react-devtools-shell/dist
+packages/react-devtools-timeline/dist
+packages/react-devtools-timeline/static
+
+# react compiler
+compiler/**/dist
+compiler/**/__tests__/fixtures/**/*.expect.md
+compiler/**/.next
+
+# contains invalid graphql`...` which results in a promise rejection error from `yarn prettier-all`.
+compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-kitchensink.js
+
+compiler/crates
+compiler/target
+compiler/apps/playground/public
+
+compiler/**/LICENSE
+compiler/*.md*
+compiler/*.json
+compiler/*.css
+compiler/*.webmanifest
+compiler/*.map
+compiler/*.sh
+compiler/*.txt
+compiler/*.ico
+compiler/*.svg
+compiler/*.lock
+compiler/*.toml
diff --git a/.prettierrc.js b/.prettierrc.js
index df8d863ae4c..aa54cbae1f9 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -5,17 +5,30 @@ const {esNextPaths} = require('./scripts/shared/pathsByLanguageVersion');
module.exports = {
bracketSpacing: false,
singleQuote: true,
- jsxBracketSameLine: true,
+ bracketSameLine: true,
trailingComma: 'es5',
printWidth: 80,
- parser: 'babylon',
-
+ parser: 'flow',
+ arrowParens: 'avoid',
overrides: [
+ {
+ files: ['*.code-workspace'],
+ options: {
+ parser: 'json-stringify',
+ },
+ },
{
files: esNextPaths,
options: {
trailingComma: 'all',
},
},
+ {
+ files: ['*.ts', '*.tsx'],
+ options: {
+ trailingComma: 'all',
+ parser: 'typescript',
+ },
+ },
],
};
diff --git a/.watchmanconfig b/.watchmanconfig
index e69de29bb2d..0967ef424bc 100644
--- a/.watchmanconfig
+++ b/.watchmanconfig
@@ -0,0 +1 @@
+{}
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index f70e33a4226..00000000000
--- a/AUTHORS
+++ /dev/null
@@ -1,1131 +0,0 @@
-839 <8398a7@gmail.com>
-Aaron Ackerman
-Aaron Cannon
-Aaron Franks
-Aaron Gelter
-Abhay Nikam
-Abhishek Soni
-Adam
-Adam Bloomston
-Adam Krebs
-Adam Mark
-Adam Solove
-Adam Stankiewicz
-Adam Timberlake
-Adam Zapletal
-Addy Osmani
-Adrian Sieber
-Aesop Wolf
-Ahmad Wali Sidiqi
-Alan Plum
-Alan Souza
-Alan deLevie
-Alastair Hole
-Alex
-Alex
-Alex Babkov
-Alex Baumgertner
-Alex Boatwright
-Alex Boyd
-Alex Dajani
-Alex Jacobs
-Alex Katopodis
-Alex Lopatin
-Alex Mykyta
-Alex Pien
-Alex Smith
-Alex Zelenskiy
-Alex Zherdev
-Alexander
-Alexander
-Alexander Shtuchkin
-Alexander Solovyov
-Alexander Tseung
-Alexandre Gaudencio
-Alexandre Kirszenberg
-Alexey Raspopov
-Alexey Shamrin
-Ali Taheri Moghaddar
-Ali Ukani
-Alireza Mostafizi
-Almero Steyn
-Amanvir Sangha
-Amjad Masad
-Anastasia A
-Andre Giron
-Andre Z Sanchez
-Andreas Mรถller
-Andreas Savvides
-Andreas Svensson
-Andres Kalle
-Andres Suarez
-Andrew Clark
-Andrew Cobby
-Andrew Davey
-Andrew Henderson
-Andrew Imm
-Andrew Kulakov
-Andrew Lo
-Andrew Poliakov
-Andrew Rasmussen
-Andrew Rota
-Andrew Sokolov
-Andrew Zich
-Andrey Marchenko
-Andrey Okonetchnikov
-Andrey Popp <8mayday@gmail.com>
-Andrey Safronov
-Andy Edwards
-Ankeet Maini
-Anthony van der Hoorn
-Anto Aravinth
-Antonio Ruberto
-Antti Ahti
-Antรณnio Nuno Monteiro
-Anuj Tomar
-Anuja Ware
-AoDev
-April Arcus
-Areeb Malik
-Aria Buckles
-Aria Stewart
-Arian Faurtosh
-Arni Fannar
-Arshabh Kumar Agarwal
-Artem Nezvigin
-Arthur Gunn
-Ashish
-Austin Wright
-Avinash Kondeti
-Ayman Osman
-B.Orlov
-BDav24
-BEAUDRU Manuel
-Baraa Hamodi
-Bartosz Kaszubowski
-Basarat Ali Syed
-Battaile Fauber
-Beau Smith
-Ben Anderson
-Ben Berman
-Ben Brooks
-Ben Foxall
-Ben Halpern
-Ben Jaffe
-Ben Moss
-Ben Newman
-Ben Ripkens
-Benedikt Meurer
-Benjamin Keen
-Benjamin Leiken
-Benjamin Woodruff
-Benjy Cui
-Benoit Girard
-Benton Rochester
-Bernard Lin
-Bill Blanchard
-Bill Fisher
-Billy Shih
-Blaine Hatab
-Blaine Kasten
-Bob Eagan
-Bob Ralian
-Bob Renwick
-Bobby
-Bogdan Chadkin
-Bojan Mihelac
-Boris Yankov
-Brad Vogel
-Bradford
-Bradley Spaulding
-Brandon Bloom
-Brandon Dail
-Brandon Tilley
-Brenard Cubacub
-Brent Vatne
-Brian Cooke
-Brian Emil Hartz
-Brian Holt
-Brian Hsu
-Brian Kim
-Brian Kung
-Brian Reavis
-Brian Rue
-Brian Vaughn
-Bruce Harris
-Bruno Heridet
-Bruno ล kvorc
-Bryan Braun
-CT Wu
-Cam Song
-Cam Spiers
-Cameron Chamberlain
-Cameron Matheson
-Carolina Powers
-Carter Chung
-Cassus Adam Banko
-Cat Chen
-Cedric Sohrauer
-Cesar William Alvarenga
-Chad Fawcett
-Changsoon Bok
-Charles Marsh
-Charlie Garcia
-Chase Adams
-Cheng Lou
-Chitharanjan Das
-Chris
-Chris Bolin
-Chris Grovers
-Chris Ha
-Chris Pearce
-Chris Rebert
-Chris Sciolla
-Christian Alfoni
-Christian Oliff
-Christian Roman
-Christoffer Sawicki
-Christoph Pojer
-Christophe Hurpeau
-Christopher Monsanto
-Claudio Brandolino
-Clay Allsopp
-Clay Miller
-Clement Hoang
-CodinCat
-Cody Reichert
-Colin Wren
-Connor McSheffrey
-Conor Hastings
-Constantin Gavrilete
-Cory House
-Cotton Hou