Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Abramov
5880d7bd66 Remove TestUtils dependency on event registry 2020-07-01 22:36:45 +01:00
6632 changed files with 216828 additions and 776329 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,10 @@
{
"packages": ["packages/react", "packages/react-dom", "packages/scheduler"],
"buildCommand": "download-build-in-codesandbox-ci",
"node": "18",
"buildCommand": "build --type=NODE react/index,react-dom/index,react-dom/server,scheduler/index,scheduler/tracing",
"publishDirectory": {
"react": "build/oss-experimental/react",
"react-dom": "build/oss-experimental/react-dom",
"scheduler": "build/oss-experimental/scheduler"
"react": "build/node_modules/react",
"react-dom": "build/node_modules/react-dom",
"scheduler": "build/node_modules/scheduler"
},
"sandboxes": ["new"],
"silent": true
"sandboxes": ["new"]
}

View File

@@ -13,18 +13,9 @@ 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
packages/react-devtools-shell/dist

View File

@@ -8,218 +8,28 @@ const {
const restrictedGlobals = require('confusing-browser-globals');
const OFF = 0;
const WARNING = 1;
const ERROR = 2;
module.exports = {
extends: ['prettier'],
extends: ['fbjs', 'prettier'],
// Stop ESLint from looking for a configuration file in parent folders
root: true,
plugins: [
'babel',
'ft-flow',
'jest',
'no-for-of-loops',
'no-function-declare-after-return',
'react',
'react-internal',
],
plugins: ['jest', 'no-for-of-loops', 'react', 'react-internal'],
parser: 'hermes-eslint',
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 9,
ecmaVersion: 8,
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,
'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'],
'consistent-return': OFF,
@@ -232,19 +42,12 @@ module.exports = {
'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-globals': [ERROR].concat(restrictedGlobals),
'no-restricted-syntax': [
ERROR,
'WithStatement',
{
selector: 'MemberExpression[property.name=/^(?:substring|substr)$/]',
message: 'Prefer string.slice() over .substring() and .substr().',
},
],
'no-restricted-syntax': [ERROR, 'WithStatement'],
'no-shadow': ERROR,
'no-unused-expressions': ERROR,
'no-unused-vars': [ERROR, {args: 'none'}],
'no-use-before-define': OFF,
'no-useless-concat': OFF,
@@ -252,8 +55,6 @@ module.exports = {
'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.
@@ -290,59 +91,17 @@ module.exports = {
// 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/safe-string-coercion': [
ERROR,
{isProductionUserAppCode: true},
],
'react-internal/no-to-warn-dev-within-to-throw': ERROR,
'react-internal/invariant-args': ERROR,
'react-internal/warning-args': ERROR,
'react-internal/no-production-logging': ERROR,
'react-internal/no-cross-fork-imports': 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-fb/**/*.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.
@@ -361,7 +120,7 @@ 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: 'hermes-eslint',
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 8,
sourceType: 'module',
@@ -381,13 +140,6 @@ module.exports = {
'jest/valid-expect-in-promise': ERROR,
},
},
{
// disable no focused tests for test setup helper files even if they are inside __tests__ directory
files: ['**/setupTests.js'],
rules: {
'jest/no-focused-tests': OFF,
},
},
{
files: [
'**/__tests__/**/*.js',
@@ -395,165 +147,38 @@ module.exports = {
'packages/*/npm/**/*.js',
'packages/dom-event-testing-library/**/*.js',
'packages/react-devtools*/**/*.js',
'dangerfile.js',
'fixtures',
'packages/react-dom/src/test-utils/*.js',
],
rules: {
'react-internal/no-production-logging': OFF,
'react-internal/warning-args': OFF,
'react-internal/safe-string-coercion': [
ERROR,
{isProductionUserAppCode: false},
],
},
},
{
files: [
'scripts/eslint-rules/*.js',
'packages/eslint-plugin-react-hooks/src/*.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: 'readonly',
RN$enableMicrotasksInReact: 'readonly',
nativeFabricUIManager: true,
},
},
{
files: ['packages/react-server-dom-webpack/**/*.js'],
files: ['packages/react-transport-dom-webpack/**/*.js'],
globals: {
__webpack_chunk_load__: 'readonly',
__webpack_require__: 'readonly',
},
},
{
files: ['packages/react-server-dom-turbopack/**/*.js'],
globals: {
__turbopack_load__: 'readonly',
__turbopack_require__: 'readonly',
},
},
{
files: ['packages/scheduler/**/*.js'],
globals: {
TaskController: 'readonly',
},
},
{
files: ['packages/react-devtools-extensions/**/*.js'],
globals: {
__IS_CHROME__: 'readonly',
__IS_FIREFOX__: 'readonly',
__IS_EDGE__: 'readonly',
__IS_INTERNAL_VERSION__: 'readonly',
},
},
{
files: ['packages/react-devtools-shared/**/*.js'],
globals: {
__IS_INTERNAL_VERSION__: 'readonly',
__webpack_chunk_load__: true,
__webpack_require__: true,
},
},
],
env: {
browser: true,
es6: true,
node: true,
jest: true,
},
globals: {
$Call: 'readonly',
$ElementType: 'readonly',
$Flow$ModuleRef: 'readonly',
$FlowFixMe: 'readonly',
$Keys: 'readonly',
$NonMaybeType: 'readonly',
$PropertyType: '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',
// For Flow type annotation. Only `BigInt` is valid at runtime.
bigint: 'readonly',
BigInt: 'readonly',
BigInt64Array: 'readonly',
BigUint64Array: 'readonly',
Class: 'readonly',
ClientRect: 'readonly',
CopyInspectedElementPath: 'readonly',
DOMHighResTimeStamp: 'readonly',
EventListener: 'readonly',
Iterable: 'readonly',
AsyncIterable: 'readonly',
$AsyncIterable: 'readonly',
$AsyncIterator: 'readonly',
Iterator: 'readonly',
AsyncIterator: 'readonly',
IteratorResult: 'readonly',
JSONValue: 'readonly',
JSResourceReference: 'readonly',
MouseEventHandler: 'readonly',
PropagationPhases: 'readonly',
PropertyDescriptor: 'readonly',
React$AbstractComponent: 'readonly',
React$Component: 'readonly',
React$ComponentType: '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',
ReadableStreamController: 'readonly',
ReadableStreamReader: 'readonly',
RequestInfo: 'readonly',
RequestOptions: 'readonly',
StoreAsGlobal: 'readonly',
symbol: 'readonly',
SyntheticEvent: 'readonly',
SyntheticMouseEvent: 'readonly',
Thenable: 'readonly',
TimeoutID: 'readonly',
WheelEventHandler: 'readonly',
FinalizationRegistry: 'readonly',
SharedArrayBuffer: true,
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',
spyOnDev: true,
spyOnDevAndProd: true,
spyOnProd: true,
__PROFILE__: true,
__UMD__: true,
__EXPERIMENTAL__: true,
__VARIANT__: true,
gate: true,
trustedTypes: true,
},
};

View File

@@ -1 +0,0 @@
c998bb1ed4b3285398c9c7797135d3f060243c6a

View File

@@ -1,18 +0,0 @@
---
name: "⚛React 19 beta issue"
about: Report a issue with React 19 beta.
title: '[React 19]'
labels: 'React 19'
---
## Summary
<!--
Please provide a CodeSandbox (https://codesandbox.io/s/new), a link to a
repository on GitHub, or provide a minimal code example that reproduces the
problem. You may provide a screenshot of the application if you think it is
relevant to your bug report. Here are some tips for providing a minimal
example: https://stackoverflow.com/help/mcve.
-->

View File

@@ -1,57 +0,0 @@
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-compiler (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

View File

@@ -1,7 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: 📃 Documentation Issue
url: https://github.com/reactjs/react.dev/issues/new/choose
url: https://github.com/reactjs/reactjs.org/issues/new
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

View File

@@ -1,81 +0,0 @@
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

View File

@@ -4,12 +4,12 @@
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 `main`.
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 test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect".
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/) type checks (`yarn flow`).
@@ -20,14 +20,8 @@
## Summary
<!--
Explain the **motivation** for making this change. What existing problem does the pull request solve?
-->
<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->
## How did you test this change?
## Test Plan
<!--
Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface.
How exactly did you verify that your PR solves the issue you wanted to solve?
If you leave this empty, your PR will very likely be closed.
-->
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->

107
.github/dependabot.yml vendored
View File

@@ -1,107 +0,0 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/fixtures/art"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/attribute-behavior"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/concurrent"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/devtools"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/dom"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/eslint"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/expiration"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/fiber-debugger"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/fiber-triangle"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/fizz"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/fizz-ssr-browser"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/flight"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/flight-browser"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/flight-esm"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/legacy-jsx-runtimes"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/nesting"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/packaging"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/scheduler"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/ssr"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/ssr-2"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
- package-ecosystem: "npm"
directory: "/fixtures/stacks"
schedule:
interval: "weekly"
open-pull-requests-limit: 0

34
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "Partner"
- "React Core Team"
- "Resolution: Backlog"
- "Type: Bug"
- "Type: Discussion"
- "Type: Needs Investigation"
- "Type: Regression"
# Label to use when marking an issue as stale
staleLabel: "Resolution: Stale"
issues:
# Comment to post when marking an issue as stale.
markComment: >
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.
closeComment: >
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!
pulls:
# Comment to post when marking a pull request as stale.
markComment: >
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.
closeComment: >
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!

View File

@@ -1,462 +0,0 @@
name: Commit Artifacts for Meta WWW and fbsource
on:
push:
branches: [main, meta-www, meta-fbsource]
jobs:
download_artifacts:
runs-on: ubuntu-latest
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: Download and unzip artifacts
uses: actions/github-script@v6
env:
CIRCLECI_TOKEN: ${{secrets.CIRCLECI_TOKEN_DIFFTRAIN}}
with:
script: |
// TODO: Move this to a script file.
const cp = require('child_process');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function execHelper(command, options, streamStdout = false) {
return new Promise((resolve, reject) => {
const proc = cp.exec(
command,
options,
(error, stdout) => (error ? reject(error) : resolve(stdout.trim())),
);
if (streamStdout) {
proc.stdout.pipe(process.stdout);
}
});
}
let artifactsUrl = null;
// This is a temporary, dirty hack to avoid needing a GitHub auth token in the circleci
// workflow to notify this GitHub action. Sorry!
let iter = 0;
spinloop: while (iter < 15) {
const res = await github.rest.repos.listCommitStatusesForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha
});
for (const status of res.data) {
if (/process_artifacts_combined/.test(status.context)) {
switch (status.state) {
case 'pending': {
console.log(`${status.context} is still pending`);
break;
}
case 'failure':
case 'error': {
throw new Error(`${status.context} has failed or errored`);
}
case 'success': {
// The status does not include a build ID, but we can extract it
// from the URL. I couldn't find a better way to do this.
const ciBuildId = /\/facebook\/react\/([0-9]+)/.exec(
status.target_url,
)[1];
if (Number.parseInt(ciBuildId, 10) + '' === ciBuildId) {
artifactsUrl =
`https://circleci.com/api/v1.1/project/github/facebook/react/${ciBuildId}/artifacts`;
console.log(`Found artifactsUrl: ${artifactsUrl}`);
break spinloop;
} else {
throw new Error(`${ciBuildId} isn't a number`);
}
break;
}
default: {
throw new Error(`Unhandled status state: ${status.state}`);
break;
}
}
}
}
iter++;
console.log("Sleeping for 60s...");
await sleep(60_000);
}
if (artifactsUrl != null) {
const {CIRCLECI_TOKEN} = process.env;
const res = await fetch(artifactsUrl, {
headers: {
'Circle-Token': CIRCLECI_TOKEN
}
});
const data = await res.json();
if (!Array.isArray(data) && data.message != null) {
throw `CircleCI returned: ${data.message}`;
}
for (const artifact of data) {
if (artifact.path === 'build.tgz') {
console.log(`Downloading and unzipping ${artifact.url}`);
await execHelper(
`curl -L ${artifact.url} -H "Circle-Token: ${CIRCLECI_TOKEN}" | tar -xvz`
);
}
}
} else {
process.exitCode = 1;
}
- 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: 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
# Move eslint-plugin-react-hooks into facebook-www
mv build/oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js \
./compiled/facebook-www/eslint-plugin-react-hooks.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-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-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 OSS renderer. OSS renderer is synced through internal script.
RENDERER_FOLDER=$BASE_FOLDER/react-native-github/Libraries/Renderer/implementations/
rm $RENDERER_FOLDER/ReactFabric-{dev,prod,profiling}.js
rm $RENDERER_FOLDER/ReactNativeRenderer-{dev,prod,profiling}.js
# 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 ${{ github.sha }} >> ./compiled/facebook-www/REVISION
cp ./compiled/facebook-www/REVISION ./compiled/facebook-www/REVISION_TRANSFORMS
echo ${{ 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@v3
with:
name: compiled
path: compiled/
- uses: actions/upload-artifact@v3
with:
name: compiled-rn
path: compiled-rn/
commit_www_artifacts:
needs: download_artifacts
if: ${{ (github.ref == 'refs/heads/main' && needs.download_artifacts.outputs.www_branch_count == '0') || github.ref == 'refs/heads/meta-www' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: builds/facebook-www
- name: Ensure clean directory
run: rm -rf compiled
- uses: actions/download-artifact@v3
with:
name: compiled
path: compiled/
- name: Revert version changes
if: needs.download_artifacts.outputs.last_version_classic != '' && needs.download_artifacts.outputs.last_version_modern != ''
env:
CURRENT_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.current_version_classic }}
CURRENT_VERSION_MODERN: ${{ needs.download_artifacts.outputs.current_version_modern }}
LAST_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.last_version_classic }}
LAST_VERSION_MODERN: ${{ needs.download_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
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: steps.check_should_commit.outputs.should_commit == 'true' && needs.download_artifacts.outputs.last_version_classic != '' && needs.download_artifacts.outputs.last_version_modern != ''
env:
CURRENT_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.current_version_classic }}
CURRENT_VERSION_MODERN: ${{ needs.download_artifacts.outputs.current_version_modern }}
LAST_VERSION_CLASSIC: ${{ needs.download_artifacts.outputs.last_version_classic }}
LAST_VERSION_MODERN: ${{ needs.download_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: steps.check_should_commit.outputs.should_commit == 'true'
run: |
echo ":"
git status -u
- name: Commit changes to branch
if: steps.check_should_commit.outputs.should_commit == 'true'
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: |
${{ github.event.head_commit.message }}
DiffTrain build for [${{ github.sha }}](https://github.com/facebook/react/commit/${{ github.sha }})
branch: builds/facebook-www
commit_user_name: ${{ github.actor }}
commit_user_email: ${{ github.actor }}@users.noreply.github.com
create_branch: true
commit_fbsource_artifacts:
needs: download_artifacts
if: ${{ (github.ref == 'refs/heads/main' && needs.download_artifacts.outputs.fbsource_branch_count == '0') || github.ref == 'refs/heads/meta-fbsource' }}
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@v3
with:
name: compiled-rn
path: compiled-rn/
- name: Revert version changes
if: needs.download_artifacts.outputs.last_version_rn != ''
env:
CURRENT_VERSION: ${{ needs.download_artifacts.outputs.current_version_rn }}
LAST_VERSION: ${{ needs.download_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
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' | grep -vE "^(@@|diff|index|\-\-\-|\+\+\+|\- \* @generated SignedSource)" | grep "^[+-]" > /dev/null; then
echo "Changes detected"
echo "===== Changes ====="
git --no-pager diff --cached ':(exclude)*REVISION' | 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: steps.check_should_commit.outputs.should_commit == 'true' && needs.download_artifacts.outputs.last_version_rn != ''
env:
CURRENT_VERSION: ${{ needs.download_artifacts.outputs.current_version_rn }}
LAST_VERSION: ${{ needs.download_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: steps.check_should_commit.outputs.should_commit == 'true'
run: |
echo ":"
git add .
- name: Signing files
if: steps.check_should_commit.outputs.should_commit == 'true'
uses: actions/github-script@v6
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 = '<<SignedSource::*O*zOeWoEQle#+L!plEphiEmie@IsG>>';
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'});
// 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 => {
const 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: steps.check_should_commit.outputs.should_commit == 'true'
run: |
git add .
git status
- name: Commit changes to branch
if: steps.check_should_commit.outputs.should_commit == 'true'
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: |
${{ github.event.head_commit.message }}
DiffTrain build for commit https://github.com/facebook/react/commit/${{ github.sha }}.
branch: builds/facebook-fbsource
commit_user_name: ${{ github.actor }}
commit_user_email: ${{ github.actor }}@users.noreply.github.com
create_branch: true

View File

@@ -1,34 +0,0 @@
name: Compiler Playground
on:
push:
branches: [main]
pull_request:
paths:
- "compiler/**"
- .github/workflows/compiler-playground.yml
defaults:
run:
working-directory: compiler
jobs:
playground:
name: Test playground
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.x
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: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
- run: yarn install --frozen-lockfile
- run: npx playwright install --with-deps chromium
- run: yarn workspace playground test

View File

@@ -1,72 +0,0 @@
name: React Compiler (Rust)
on:
push:
branches: ["main"]
paths:
- .github/workflows/**
- compiler/crates/**
- compiler/Cargo.*
- compiler/*.toml
pull_request:
paths:
- .github/workflows/**
- compiler/crates/**
- compiler/Cargo.*
- compiler/*.toml
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -Dwarnings
defaults:
run:
working-directory: compiler
jobs:
test:
name: Rust Test (${{ matrix.target.os }})
strategy:
matrix:
target:
- target: ubuntu-latest
os: ubuntu-latest
# TODO: run on more platforms
# - target: macos-latest
# os: macos-latest
# - target: windows-latest
# os: windows-latest
runs-on: ${{ matrix.target.os }}
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: cargo test
run: cargo test --manifest-path=Cargo.toml --locked ${{ matrix.target.features && '--features' }} ${{ matrix.target.features }}
lint:
name: Rust Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
# NOTE: use `rustup run <toolchain> <command>` in commands below
# with this exact same toolchain value
toolchain: nightly-2023-08-01
override: true
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: rustfmt
run: grep -r --include "*.rs" --files-without-match "@generated" crates | xargs rustup run nightly-2023-08-01 rustfmt --check --config="skip_children=true"
# - name: cargo clippy
# run: rustup run nightly-2023-08-01 cargo clippy -- -Dclippy::correctness
build:
name: Rust Build
runs-on: ubuntu-latest
# TODO: build on more platforms, deploy, etc
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: cargo build
run: cargo build --release

View File

@@ -1,106 +0,0 @@
name: React Compiler (TypeScript)
on:
push:
branches: [main]
pull_request:
paths:
- "compiler/**"
- .github/workflows/compiler-typescript.yml
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 for babel-plugin-react-compiler
prettier:
name: Run prettier
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.x
cache: "yarn"
cache-dependency-path: compiler/yarn.lock
- name: Restore cached node_modules
uses: actions/cache@v4
with:
path: "**/node_modules"
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
- run: yarn install --frozen-lockfile
- run: yarn prettier:ci
# 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: 18.x
cache: "yarn"
cache-dependency-path: compiler/yarn.lock
- name: Restore cached node_modules
uses: actions/cache@v4
with:
path: "**/node_modules"
key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
- run: yarn install --frozen-lockfile
- 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: 18.x
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: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
- run: yarn install --frozen-lockfile
- run: yarn workspace babel-plugin-react-compiler jest
test:
name: Test ${{ matrix.workspace_name }}
needs: discover_yarn_workspaces
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
workspace_name: ${{ fromJSON(needs.discover_yarn_workspaces.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.x
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: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('compiler/**/yarn.lock') }}
- run: yarn install --frozen-lockfile
- run: yarn workspace ${{ matrix.workspace_name }} test

View File

@@ -1,205 +0,0 @@
name: DevTools Check for bug repro
on:
issues:
types: [opened, edited]
issue_comment:
types: [created, edited]
jobs:
check-repro:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const URL_REGEXP = /### Website or app[\r\n]+([^#]+)###/m;
const REPRO_STEPS_REGEXP = /### Repro steps[\r\n]+([^#]+)###/m;
const LABEL_NEEDS_MORE_INFORMATION = "Resolution: Needs More Information";
const LABEL_UNCONFIRMED = "Status: Unconfirmed";
function debug(...args) {
core.info(args.map(JSON.stringify).join(' '));
}
if (context.payload.comment) {
debug('Ignoring comment update.');
return;
}
const user = context.payload.sender.login;
const issue = context.payload.issue;
const body = issue.body;
const urlMatch = body.match(URL_REGEXP);
const reproStepsMatch = body.match(REPRO_STEPS_REGEXP);
const url = urlMatch !== null ? urlMatch[1].trim() : null;
const reproSteps = reproStepsMatch !== null ? reproStepsMatch[1].trim() : null;
if (!url || !reproSteps) {
debug('This issue is not a DevTools bug report.');
return;
}
debug(`found URL "${url}"`);
debug(`found repro steps "${reproSteps}"`);
async function createComment(comment) {
// Format
comment = comment
.split("\n")
.map((line) => line.trim())
.join("\n")
.trim();
await github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment,
});
}
async function getGitHubActionComments() {
debug(`Loading existing comments...`);
const comments = await github.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
return comments.data.filter(comment => {
debug(`comment by user: "${comment.user.login}"`);
return comment.user.login === 'github-actions[bot]';
});
}
async function getIssueLabels() {
const issues = await github.issues.listLabelsOnIssue({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
return issues.data;
}
async function updateIssue(state, assignees = []) {
await github.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state,
assignees,
});
}
async function closeWithComment(comment) {
if (issue.state !== 'open') {
debug(`Issue is not open`);
return;
}
const labels = await getIssueLabels();
const label = labels.find(label => label.name === LABEL_UNCONFIRMED);
if (!label) {
debug(`Issue was not opened via DevTools bug report template`);
return;
}
const comments = await getGitHubActionComments();
if (comments.length > 0) {
debug(`Already commented on issue; won't comment again`);
return;
}
debug(`Missing required information`);
await github.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [LABEL_NEEDS_MORE_INFORMATION],
});
await createComment(comment);
await updateIssue('closed', [user]);
}
async function openWithComment(comment) {
if (issue.state !== 'closed') {
debug(`Issue is already open`);
return;
}
const labels = await getIssueLabels();
const label = labels.find(label => label.name === LABEL_NEEDS_MORE_INFORMATION);
if (!label) {
debug(`Issue was not tagged as needs information`);
return;
}
const comments = await getGitHubActionComments();
if (comments.length === 0) {
debug(`Issue was closed by someone else; won't reopen`);
return;
}
debug(`Re-opening closed issue`);
await github.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: LABEL_NEEDS_MORE_INFORMATION,
});
await createComment(comment);
await updateIssue('open');
}
const PROBABLY_NOT_A_URL_REGEX = /(^Chrome$|^Firefox$| Website)/i;
const COMMENT_HEADER = `
@${user}: We're sorry you've seen this error. ❤️
`.trim();
const COMMENT_FOOTER = `
Please help us by providing a link to a CodeSandbox (https://codesandbox.io/s/new), a repository on GitHub, or a minimal code example that reproduces the problem. (Screenshots or videos can also be helpful if they help provide context on how to repro the bug.)
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
Issues without repros are automatically closed but we will re-open if you update with repro info.
`.trim();
if (url.includes("localhost")) {
closeWithComment(`
${COMMENT_HEADER}
Unfortunately the URL you provided ("localhost") is not publicly accessible. (This means that we will not be able to reproduce the problem you're reporting.)
${COMMENT_FOOTER}
`);
} else if (url.length < 10 || url.match(PROBABLY_NOT_A_URL_REGEX)) {
closeWithComment(`
${COMMENT_HEADER}
It looks like you forgot to specify a valid URL. (This means that we will not be able to reproduce the problem you're reporting.)
${COMMENT_FOOTER}
`);
} else if (reproSteps.length < 25) {
closeWithComment(`
${COMMENT_HEADER}
Unfortunately, it doesn't look like this issue has enough info for one of us to reproduce and fix it though.
${COMMENT_FOOTER}
`);
} else {
openWithComment(`
Thank you for providing repro steps! Re-opening issue now for triage.
`);
}

View File

@@ -1,32 +0,0 @@
name: facebook/react/fuzz_tests
on:
schedule:
- cron: 0 * * * *
push:
branches:
- main
workflow_dispatch:
inputs:
prerelease_commit_sha:
required: false
jobs:
test_fuzz:
if: inputs.prerelease_commit_sha == ''
runs-on: ubuntu-latest
env:
TZ: "/usr/share/zoneinfo/America/Los_Angeles"
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

View File

@@ -1,46 +0,0 @@
# Configuration for stale action workflow - https://github.com/actions/stale
name: 'Manage stale issues and PRs'
on:
schedule:
# Run hourly
- cron: '0 * * * *'
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"

4
.gitignore vendored
View File

@@ -33,7 +33,5 @@ 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
packages/react-devtools-shell/dist

View File

@@ -127,8 +127,6 @@ Rainer Oviir <roviir@gmail.com> <raineroviir@rainers-MacBook-Pro.local>
Ray <ray@tomo.im>
Richard Feldman <richard.t.feldman@gmail.com> <richard@noredink.com>
Richard Livesey <Livesey7@hotmail.co.uk>
Rick Hanlon <rickhanlonii@gmail.com>
Rick Hanlon <rickhanlonii@gmail.com> <rickhanlonii@fb.com>
Rob Arnold <robarnold@cs.cmu.edu>
Robert Binna <rbinna@gmail.com> <speedskater@users.noreply.github.com>
Robin Frischmann <robin@rofrischmann.de>
@@ -147,7 +145,6 @@ Steven Luscher <react@steveluscher.com> <github@steveluscher.com>
Steven Luscher <react@steveluscher.com> <steveluscher@fb.com>
Steven Luscher <react@steveluscher.com> <steveluscher@instagram.com>
Steven Luscher <react@steveluscher.com> <steveluscher@users.noreply.github.com>
Seth Webster <sethwebster@gmail.com> <sethwebster@fb.com>
Stoyan Stefanov <ssttoo@ymail.com>
Tengfei Guo <terryr3rd@yeah.net> <tfguo369@gmail.com>
Thomas Aylott <oblivious@subtlegradient.com> <aylott@fb.com>

2
.nvmrc
View File

@@ -1 +1 @@
v18.20.0
v12.16.2

View File

@@ -1,16 +1,6 @@
build
compiler
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
packages/react-devtools-shell/dist

View File

@@ -1,18 +1,15 @@
'use strict';
const {
esNextPaths,
typescriptPaths,
} = require('./scripts/shared/pathsByLanguageVersion');
const {esNextPaths} = require('./scripts/shared/pathsByLanguageVersion');
module.exports = {
bracketSpacing: false,
singleQuote: true,
bracketSameLine: true,
jsxBracketSameLine: true,
trailingComma: 'es5',
printWidth: 80,
parser: 'flow',
arrowParens: 'avoid',
parser: 'babel',
overrides: [
{
files: esNextPaths,
@@ -20,12 +17,5 @@ module.exports = {
trailingComma: 'all',
},
},
{
files: typescriptPaths,
options: {
trailingComma: 'all',
parser: 'typescript',
},
},
],
};

1131
AUTHORS Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
## March 22, 2024 (18.3.0-canary-670811593-20240322)
## React
- Added `useActionState` to replace `useFormState` and added `pending` value ([#28491](https://github.com/facebook/react/pull/28491)).
## October 5, 2023 (18.3.0-canary-546178f91-20231005)
### React
- Added support for async functions to be passed to `startTransition`.
- `useTransition` now triggers the nearest error boundary instead of a global error.
- Added `useOptimistic`, a new Hook for handling optimistic UI updates. It optimistically updates the UI before receiving confirmation from a server or external source.
### React DOM
- Added support for passing async functions to the `action` prop on `<form>`. When the function passed to `action` is marked with [`'use server'`](https://react.dev/reference/react/use-server), the form is [progressively enhanced](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement).
- Added `useFormStatus`, a new Hook for checking the submission state of a form.
- Added `useFormState`, a new Hook for updating state upon form submission. When the function passed to `useFormState` is marked with [`'use server'`](https://react.dev/reference/react/use-server), the update is [progressively enhanced](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement).

View File

@@ -1,338 +1,3 @@
## 18.3.1 (April 26, 2024)
- Export `act` from `react` [f1338f](https://github.com/facebook/react/commit/f1338f8080abd1386454a10bbf93d67bfe37ce85)
## 18.3.0 (April 25, 2024)
This release is identical to 18.2 but adds warnings for deprecated APIs and other changes that are needed for React 19.
Read the [React 19 Upgrade Guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) for more info.
### React
- Allow writing to `this.refs` to support string ref codemod [909071](https://github.com/facebook/react/commit/9090712fd3ca4e1099e1f92e67933c2cb4f32552)
- Warn for deprecated `findDOMNode` outside StrictMode [c3b283](https://github.com/facebook/react/commit/c3b283964108b0e8dbcf1f9eb2e7e67815e39dfb)
- Warn for deprecated `test-utils` methods [d4ea75](https://github.com/facebook/react/commit/d4ea75dc4258095593b6ac764289f42bddeb835c)
- Warn for deprecated Legacy Context outside StrictMode [415ee0](https://github.com/facebook/react/commit/415ee0e6ea0fe3e288e65868df2e3241143d5f7f)
- Warn for deprecated string refs outside StrictMode [#25383](https://github.com/facebook/react/pull/25383)
- Warn for deprecated `defaultProps` for function components [#25699](https://github.com/facebook/react/pull/25699)
- Warn when spreading `key` [#25697](https://github.com/facebook/react/pull/25697)
- Warn when using `act` from `test-utils` [d4ea75](https://github.com/facebook/react/commit/d4ea75dc4258095593b6ac764289f42bddeb835c)
### React DOM
- Warn for deprecated `unmountComponentAtNode` [8a015b](https://github.com/facebook/react/commit/8a015b68cc060079878e426610e64e86fb328f8d)
- Warn for deprecated `renderToStaticNodeStream` [#28874](https://github.com/facebook/react/pull/28874)
## 18.2.0 (June 14, 2022)
### React DOM
* Provide a component stack as a second argument to `onRecoverableError`. ([@gnoff](https://github.com/gnoff) in [#24591](https://github.com/facebook/react/pull/24591))
* Fix hydrating into `document` causing a blank page on mismatch. ([@gnoff](https://github.com/gnoff) in [#24523](https://github.com/facebook/react/pull/24523))
* Fix false positive hydration errors with Suspense. ([@gnoff](https://github.com/gnoff) in [#24480](https://github.com/facebook/react/pull/24480) and [@acdlite](https://github.com/acdlite) in [#24532](https://github.com/facebook/react/pull/24532))
* Fix ignored `setState` in Safari when adding an iframe. ([@gaearon](https://github.com/gaearon) in [#24459](https://github.com/facebook/react/pull/24459))
### React DOM Server
* Pass information about server errors to the client. ([@salazarm](https://github.com/salazarm) and [@gnoff](https://github.com/gnoff) in [#24551](https://github.com/facebook/react/pull/24551) and [#24591](https://github.com/facebook/react/pull/24591))
* Allow to provide a reason when aborting the HTML stream. ([@gnoff](https://github.com/gnoff) in [#24680](https://github.com/facebook/react/pull/24680))
* Eliminate extraneous text separators in the HTML where possible. ([@gnoff](https://github.com/gnoff) in [#24630](https://github.com/facebook/react/pull/24630))
* Disallow complex children inside `<title>` elements to match the browser constraints. ([@gnoff](https://github.com/gnoff) in [#24679](https://github.com/facebook/react/pull/24679))
* Fix buffering in some worker environments by explicitly setting `highWaterMark` to `0`. ([@jplhomer](https://github.com/jplhomer) in [#24641](https://github.com/facebook/react/pull/24641))
### Server Components (Experimental)
* Add support for `useId()` inside Server Components. ([@gnoff](https://github.com/gnoff) in [#24172](https://github.com/facebook/react/pull/24172))
## 18.1.0 (April 26, 2022)
### React DOM
* Fix the false positive warning about `react-dom/client` when using UMD bundle. ([@alireza-molaee](https://github.com/alireza-molaee) in [#24274](https://github.com/facebook/react/pull/24274))
* Fix `suppressHydrationWarning` to work in production too. ([@gaearon](https://github.com/gaearon) in [#24271](https://github.com/facebook/react/pull/24271))
* Fix `componentWillUnmount` firing twice inside of Suspense. ([@acdlite](https://github.com/acdlite) in [#24308](https://github.com/facebook/react/pull/24308))
* Fix some transition updates being ignored. ([@acdlite](https://github.com/acdlite) in [#24353](https://github.com/facebook/react/pull/24353))
* Fix `useDeferredValue` causing an infinite loop when passed an unmemoized value. ([@acdlite](https://github.com/acdlite) in [#24247](https://github.com/facebook/react/pull/24247))
* Fix throttling of revealing Suspense fallbacks. ([@sunderls](https://github.com/sunderls) in [#24253](https://github.com/facebook/react/pull/24253))
* Fix an inconsistency in whether the props object is the same between renders. ([@Andarist](https://github.com/Andarist) and [@acdlite](https://github.com/acdlite) in [#24421](https://github.com/facebook/react/pull/24421))
* Fix a missing warning about a `setState` loop in `useEffect`. ([@gaearon](https://github.com/gaearon) in [#24298](https://github.com/facebook/react/pull/24298))
* Fix a spurious hydration error. ([@gnoff](https://github.com/gnoff) in [#24404](https://github.com/facebook/react/pull/24404))
* Warn when calling `setState` in `useInsertionEffect`. ([@gaearon](https://github.com/gaearon) in [#24295](https://github.com/facebook/react/pull/24295))
* Ensure the reason for hydration errors is always displayed. ([@gaearon](https://github.com/gaearon) in [#24276](https://github.com/facebook/react/pull/24276))
### React DOM Server
* Fix escaping for the `bootstrapScriptContent` contents. ([@gnoff](https://github.com/gnoff) in [#24385](https://github.com/facebook/react/pull/24385))
* Significantly improve performance of `renderToPipeableStream`. ([@gnoff](https://github.com/gnoff) in [#24291](https://github.com/facebook/react/pull/24291))
### ESLint Plugin: React Hooks
* Fix false positive errors with a large number of branches. ([@scyron6](https://github.com/scyron6) in [#24287](https://github.com/facebook/react/pull/24287))
* Don't consider a known dependency stable when the variable is reassigned. ([@afzalsayed96](https://github.com/afzalsayed96) in [#24343](https://github.com/facebook/react/pull/24343))
### Use Subscription
* Replace the implementation with the `use-sync-external-store` shim. ([@gaearon](https://github.com/gaearon) in [#24289](https://github.com/facebook/react/pull/24289))
## 18.0.0 (March 29, 2022)
Below is a list of all new features, APIs, deprecations, and breaking changes.
Read [React 18 release post](https://reactjs.org/blog/2022/03/29/react-v18.html) and [React 18 upgrade guide](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html) for more information.
### New Features
### React
* `useId` is a new hook for generating unique IDs on both the client and server, while avoiding hydration mismatches. It is primarily useful for component libraries integrating with accessibility APIs that require unique IDs. This solves an issue that already exists in React 17 and below, but its even more important in React 18 because of how the new streaming server renderer delivers HTML out-of-order.
* `startTransition` and `useTransition` let you mark some state updates as not urgent. Other state updates are considered urgent by default. React will allow urgent state updates (for example, updating a text input) to interrupt non-urgent state updates (for example, rendering a list of search results).
* `useDeferredValue` lets you defer re-rendering a non-urgent part of the tree. It is similar to debouncing, but has a few advantages compared to it. There is no fixed time delay, so React will attempt the deferred render right after the first render is reflected on the screen. The deferred render is interruptible and doesn't block user input.
* `useSyncExternalStore` is a new hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. It removes the need for `useEffect` when implementing subscriptions to external data sources, and is recommended for any library that integrates with state external to React.
* `useInsertionEffect` is a new hook that allows CSS-in-JS libraries to address performance issues of injecting styles in render. Unless youve already built a CSS-in-JS library we dont expect you to ever use this. This hook will run after the DOM is mutated, but before layout effects read the new layout. This solves an issue that already exists in React 17 and below, but is even more important in React 18 because React yields to the browser during concurrent rendering, giving it a chance to recalculate layout.
### React DOM Client
These new APIs are now exported from `react-dom/client`:
* `createRoot`: New method to create a root to `render` or `unmount`. Use it instead of `ReactDOM.render`. New features in React 18 don't work without it.
* `hydrateRoot`: New method to hydrate a server rendered application. Use it instead of `ReactDOM.hydrate` in conjunction with the new React DOM Server APIs. New features in React 18 don't work without it.
Both `createRoot` and `hydrateRoot` accept a new option called `onRecoverableError` in case you want to be notified when React recovers from errors during rendering or hydration for logging. By default, React will use [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError), or `console.error` in the older browsers.
### React DOM Server
These new APIs are now exported from `react-dom/server` and have full support for streaming Suspense on the server:
* `renderToPipeableStream`: for streaming in Node environments.
* `renderToReadableStream`: for modern edge runtime environments, such as Deno and Cloudflare workers.
The existing `renderToString` method keeps working but is discouraged.
## Deprecations
* `react-dom`: `ReactDOM.render` has been deprecated. Using it will warn and run your app in React 17 mode.
* `react-dom`: `ReactDOM.hydrate` has been deprecated. Using it will warn and run your app in React 17 mode.
* `react-dom`: `ReactDOM.unmountComponentAtNode` has been deprecated.
* `react-dom`: `ReactDOM.renderSubtreeIntoContainer` has been deprecated.
* `react-dom/server`: `ReactDOMServer.renderToNodeStream` has been deprecated.
## Breaking Changes
### React
* **Automatic batching:** This release introduces a performance improvement that changes to the way React batches updates to do more batching automatically. See [Automatic batching for fewer renders in React 18](https://github.com/reactwg/react-18/discussions/21) for more info. In the rare case that you need to opt out, wrap the state update in `flushSync`.
* **Stricter Strict Mode**: In the future, React will provide a feature that lets components preserve state between unmounts. To prepare for it, React 18 introduces a new development-only check to Strict Mode. React will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount. If this breaks your app, consider removing Strict Mode until you can fix the components to be resilient to remounting with existing state.
* **Consistent useEffect timing**: React now always synchronously flushes effect functions if the update was triggered during a discrete user input event such as a click or a keydown event. Previously, the behavior wasn't always predictable or consistent.
* **Stricter hydration errors**: Hydration mismatches due to missing or extra text content are now treated like errors instead of warnings. React will no longer attempt to "patch up" individual nodes by inserting or deleting a node on the client in an attempt to match the server markup, and will revert to client rendering up to the closest `<Suspense>` boundary in the tree. This ensures the hydrated tree is consistent and avoids potential privacy and security holes that can be caused by hydration mismatches.
* **Suspense trees are always consistent:** If a component suspends before it's fully added to the tree, React will not add it to the tree in an incomplete state or fire its effects. Instead, React will throw away the new tree completely, wait for the asynchronous operation to finish, and then retry rendering again from scratch. React will render the retry attempt concurrently, and without blocking the browser.
* **Layout Effects with Suspense**: When a tree re-suspends and reverts to a fallback, React will now clean up layout effects, and then re-create them when the content inside the boundary is shown again. This fixes an issue which prevented component libraries from correctly measuring layout when used with Suspense.
* **New JS Environment Requirements**: React now depends on modern browsers features including `Promise`, `Symbol`, and `Object.assign`. If you support older browsers and devices such as Internet Explorer which do not provide modern browser features natively or have non-compliant implementations, consider including a global polyfill in your bundled application.
### Scheduler (Experimental)
* Remove unstable `scheduler/tracing` API
## Notable Changes
### React
* **Components can now render `undefined`:** React no longer throws if you return `undefined` from a component. This makes the allowed component return values consistent with values that are allowed in the middle of a component tree. We suggest to use a linter to prevent mistakes like forgetting a `return` statement before JSX.
* **In tests, `act` warnings are now opt-in:** If you're running end-to-end tests, the `act` warnings are unnecessary. We've introduced an [opt-in](https://github.com/reactwg/react-18/discussions/102) mechanism so you can enable them only for unit tests where they are useful and beneficial.
* **No warning about `setState` on unmounted components:** Previously, React warned about memory leaks when you call `setState` on an unmounted component. This warning was added for subscriptions, but people primarily run into it in scenarios where setting state is fine, and workarounds make the code worse. We've [removed](https://github.com/facebook/react/pull/22114) this warning.
* **No suppression of console logs:** When you use Strict Mode, React renders each component twice to help you find unexpected side effects. In React 17, we've suppressed console logs for one of the two renders to make the logs easier to read. In response to [community feedback](https://github.com/facebook/react/issues/21783) about this being confusing, we've removed the suppression. Instead, if you have React DevTools installed, the second log's renders will be displayed in grey, and there will be an option (off by default) to suppress them completely.
* **Improved memory usage:** React now cleans up more internal fields on unmount, making the impact from unfixed memory leaks that may exist in your application code less severe.
### React DOM Server
* **`renderToString`:** Will no longer error when suspending on the server. Instead, it will emit the fallback HTML for the closest `<Suspense>` boundary and then retry rendering the same content on the client. It is still recommended that you switch to a streaming API like `renderToPipeableStream` or `renderToReadableStream` instead.
* **`renderToStaticMarkup`:** Will no longer error when suspending on the server. Instead, it will emit the fallback HTML for the closest `<Suspense>` boundary and retry rendering on the client.
## All Changes
## React
* Add `useTransition` and `useDeferredValue` to separate urgent updates from transitions. ([#10426](https://github.com/facebook/react/pull/10426), [#10715](https://github.com/facebook/react/pull/10715), [#15593](https://github.com/facebook/react/pull/15593), [#15272](https://github.com/facebook/react/pull/15272), [#15578](https://github.com/facebook/react/pull/15578), [#15769](https://github.com/facebook/react/pull/15769), [#17058](https://github.com/facebook/react/pull/17058), [#18796](https://github.com/facebook/react/pull/18796), [#19121](https://github.com/facebook/react/pull/19121), [#19703](https://github.com/facebook/react/pull/19703), [#19719](https://github.com/facebook/react/pull/19719), [#19724](https://github.com/facebook/react/pull/19724), [#20672](https://github.com/facebook/react/pull/20672), [#20976](https://github.com/facebook/react/pull/20976) by [@acdlite](https://github.com/acdlite), [@lunaruan](https://github.com/lunaruan), [@rickhanlonii](https://github.com/rickhanlonii), and [@sebmarkbage](https://github.com/sebmarkbage))
* Add `useId` for generating unique IDs. ([#17322](https://github.com/facebook/react/pull/17322), [#18576](https://github.com/facebook/react/pull/18576), [#22644](https://github.com/facebook/react/pull/22644), [#22672](https://github.com/facebook/react/pull/22672), [#21260](https://github.com/facebook/react/pull/21260) by [@acdlite](https://github.com/acdlite), [@lunaruan](https://github.com/lunaruan), and [@sebmarkbage](https://github.com/sebmarkbage))
* Add `useSyncExternalStore` to help external store libraries integrate with React. ([#15022](https://github.com/facebook/react/pull/15022), [#18000](https://github.com/facebook/react/pull/18000), [#18771](https://github.com/facebook/react/pull/18771), [#22211](https://github.com/facebook/react/pull/22211), [#22292](https://github.com/facebook/react/pull/22292), [#22239](https://github.com/facebook/react/pull/22239), [#22347](https://github.com/facebook/react/pull/22347), [#23150](https://github.com/facebook/react/pull/23150) by [@acdlite](https://github.com/acdlite), [@bvaughn](https://github.com/bvaughn), and [@drarmstr](https://github.com/drarmstr))
* Add `startTransition` as a version of `useTransition` without pending feedback. ([#19696](https://github.com/facebook/react/pull/19696) by [@rickhanlonii](https://github.com/rickhanlonii))
* Add `useInsertionEffect` for CSS-in-JS libraries. ([#21913](https://github.com/facebook/react/pull/21913) by [@rickhanlonii](https://github.com/rickhanlonii))
* Make Suspense remount layout effects when content reappears. ([#19322](https://github.com/facebook/react/pull/19322), [#19374](https://github.com/facebook/react/pull/19374), [#19523](https://github.com/facebook/react/pull/19523), [#20625](https://github.com/facebook/react/pull/20625), [#21079](https://github.com/facebook/react/pull/21079) by [@acdlite](https://github.com/acdlite), [@bvaughn](https://github.com/bvaughn), and [@lunaruan](https://github.com/lunaruan))
* Make `<StrictMode>` re-run effects to check for restorable state. ([#19523](https://github.com/facebook/react/pull/19523) , [#21418](https://github.com/facebook/react/pull/21418) by [@bvaughn](https://github.com/bvaughn) and [@lunaruan](https://github.com/lunaruan))
* Assume Symbols are always available. ([#23348](https://github.com/facebook/react/pull/23348) by [@sebmarkbage](https://github.com/sebmarkbage))
* Remove `object-assign` polyfill. ([#23351](https://github.com/facebook/react/pull/23351) by [@sebmarkbage](https://github.com/sebmarkbage))
* Remove unsupported `unstable_changedBits` API. ([#20953](https://github.com/facebook/react/pull/20953) by [@acdlite](https://github.com/acdlite))
* Allow components to render undefined. ([#21869](https://github.com/facebook/react/pull/21869) by [@rickhanlonii](https://github.com/rickhanlonii))
* Flush `useEffect` resulting from discrete events like clicks synchronously. ([#21150](https://github.com/facebook/react/pull/21150) by [@acdlite](https://github.com/acdlite))
* Suspense `fallback={undefined}` now behaves the same as `null` and isn't ignored. ([#21854](https://github.com/facebook/react/pull/21854) by [@rickhanlonii](https://github.com/rickhanlonii))
* Consider all `lazy()` resolving to the same component equivalent. ([#20357](https://github.com/facebook/react/pull/20357) by [@sebmarkbage](https://github.com/sebmarkbage))
* Don't patch console during first render. ([#22308](https://github.com/facebook/react/pull/22308) by [@lunaruan](https://github.com/lunaruan))
* Improve memory usage. ([#21039](https://github.com/facebook/react/pull/21039) by [@bgirard](https://github.com/bgirard))
* Improve messages if string coercion throws (Temporal.*, Symbol, etc.) ([#22064](https://github.com/facebook/react/pull/22064) by [@justingrant](https://github.com/justingrant))
* Use `setImmediate` when available over `MessageChannel`. ([#20834](https://github.com/facebook/react/pull/20834) by [@gaearon](https://github.com/gaearon))
* Fix context failing to propagate inside suspended trees. ([#23095](https://github.com/facebook/react/pull/23095) by [@gaearon](https://github.com/gaearon))
* Fix `useReducer` observing incorrect props by removing the eager bailout mechanism. ([#22445](https://github.com/facebook/react/pull/22445) by [@josephsavona](https://github.com/josephsavona))
* Fix `setState` being ignored in Safari when appending iframes. ([#23111](https://github.com/facebook/react/pull/23111) by [@gaearon](https://github.com/gaearon))
* Fix a crash when rendering `ZonedDateTime` in the tree. ([#20617](https://github.com/facebook/react/pull/20617) by [@dimaqq](https://github.com/dimaqq))
* Fix a crash when document is set to `null` in tests. ([#22695](https://github.com/facebook/react/pull/22695) by [@SimenB](https://github.com/SimenB))
* Fix `onLoad` not triggering when concurrent features are on. ([#23316](https://github.com/facebook/react/pull/23316) by [@gnoff](https://github.com/gnoff))
* Fix a warning when a selector returns `NaN`. ([#23333](https://github.com/facebook/react/pull/23333) by [@hachibeeDI](https://github.com/hachibeeDI))
* Fix the generated license header. ([#23004](https://github.com/facebook/react/pull/23004) by [@vitaliemiron](https://github.com/vitaliemiron))
* Add `package.json` as one of the entry points. ([#22954](https://github.com/facebook/react/pull/22954) by [@Jack](https://github.com/Jack-Works))
* Allow suspending outside a Suspense boundary. ([#23267](https://github.com/facebook/react/pull/23267) by [@acdlite](https://github.com/acdlite))
* Log a recoverable error whenever hydration fails. ([#23319](https://github.com/facebook/react/pull/23319) by [@acdlite](https://github.com/acdlite))
### React DOM
* Add `createRoot` and `hydrateRoot`. ([#10239](https://github.com/facebook/react/pull/10239), [#11225](https://github.com/facebook/react/pull/11225), [#12117](https://github.com/facebook/react/pull/12117), [#13732](https://github.com/facebook/react/pull/13732), [#15502](https://github.com/facebook/react/pull/15502), [#15532](https://github.com/facebook/react/pull/15532), [#17035](https://github.com/facebook/react/pull/17035), [#17165](https://github.com/facebook/react/pull/17165), [#20669](https://github.com/facebook/react/pull/20669), [#20748](https://github.com/facebook/react/pull/20748), [#20888](https://github.com/facebook/react/pull/20888), [#21072](https://github.com/facebook/react/pull/21072), [#21417](https://github.com/facebook/react/pull/21417), [#21652](https://github.com/facebook/react/pull/21652), [#21687](https://github.com/facebook/react/pull/21687), [#23207](https://github.com/facebook/react/pull/23207), [#23385](https://github.com/facebook/react/pull/23385) by [@acdlite](https://github.com/acdlite), [@bvaughn](https://github.com/bvaughn), [@gaearon](https://github.com/gaearon), [@lunaruan](https://github.com/lunaruan), [@rickhanlonii](https://github.com/rickhanlonii), [@trueadm](https://github.com/trueadm), and [@sebmarkbage](https://github.com/sebmarkbage))
* Add selective hydration. ([#14717](https://github.com/facebook/react/pull/14717), [#14884](https://github.com/facebook/react/pull/14884), [#16725](https://github.com/facebook/react/pull/16725), [#16880](https://github.com/facebook/react/pull/16880), [#17004](https://github.com/facebook/react/pull/17004), [#22416](https://github.com/facebook/react/pull/22416), [#22629](https://github.com/facebook/react/pull/22629), [#22448](https://github.com/facebook/react/pull/22448), [#22856](https://github.com/facebook/react/pull/22856), [#23176](https://github.com/facebook/react/pull/23176) by [@acdlite](https://github.com/acdlite), [@gaearon](https://github.com/gaearon), [@salazarm](https://github.com/salazarm), and [@sebmarkbage](https://github.com/sebmarkbage))
* Add `aria-description` to the list of known ARIA attributes. ([#22142](https://github.com/facebook/react/pull/22142) by [@mahyareb](https://github.com/mahyareb))
* Add `onResize` event to video elements. ([#21973](https://github.com/facebook/react/pull/21973) by [@rileyjshaw](https://github.com/rileyjshaw))
* Add `imageSizes` and `imageSrcSet` to known props. ([#22550](https://github.com/facebook/react/pull/22550) by [@eps1lon](https://github.com/eps1lon))
* Allow non-string `<option>` children if `value` is provided. ([#21431](https://github.com/facebook/react/pull/21431) by [@sebmarkbage](https://github.com/sebmarkbage))
* Fix `aspectRatio` style not being applied. ([#21100](https://github.com/facebook/react/pull/21100) by [@gaearon](https://github.com/gaearon))
* Warn if `renderSubtreeIntoContainer` is called. ([#23355](https://github.com/facebook/react/pull/23355) by [@acdlite](https://github.com/acdlite))
### React DOM Server
* Add the new streaming renderer. ([#14144](https://github.com/facebook/react/pull/14144), [#20970](https://github.com/facebook/react/pull/20970), [#21056](https://github.com/facebook/react/pull/21056), [#21255](https://github.com/facebook/react/pull/21255), [#21200](https://github.com/facebook/react/pull/21200), [#21257](https://github.com/facebook/react/pull/21257), [#21276](https://github.com/facebook/react/pull/21276), [#22443](https://github.com/facebook/react/pull/22443), [#22450](https://github.com/facebook/react/pull/22450), [#23247](https://github.com/facebook/react/pull/23247), [#24025](https://github.com/facebook/react/pull/24025), [#24030](https://github.com/facebook/react/pull/24030) by [@sebmarkbage](https://github.com/sebmarkbage))
* Fix context providers in SSR when handling multiple requests. ([#23171](https://github.com/facebook/react/pull/23171) by [@frandiox](https://github.com/frandiox))
* Revert to client render on text mismatch. ([#23354](https://github.com/facebook/react/pull/23354) by [@acdlite](https://github.com/acdlite))
* Deprecate `renderToNodeStream`. ([#23359](https://github.com/facebook/react/pull/23359) by [@sebmarkbage](https://github.com/sebmarkbage))
* Fix a spurious error log in the new server renderer. ([#24043](https://github.com/facebook/react/pull/24043) by [@eps1lon](https://github.com/eps1lon))
* Fix a bug in the new server renderer. ([#22617](https://github.com/facebook/react/pull/22617) by [@shuding](https://github.com/shuding))
* Ignore function and symbol values inside custom elements on the server. ([#21157](https://github.com/facebook/react/pull/21157) by [@sebmarkbage](https://github.com/sebmarkbage))
### React DOM Test Utils
* Throw when `act` is used in production. ([#21686](https://github.com/facebook/react/pull/21686) by [@acdlite](https://github.com/acdlite))
* Support disabling spurious act warnings with `global.IS_REACT_ACT_ENVIRONMENT`. ([#22561](https://github.com/facebook/react/pull/22561) by [@acdlite](https://github.com/acdlite))
* Expand act warning to cover all APIs that might schedule React work. ([#22607](https://github.com/facebook/react/pull/22607) by [@acdlite](https://github.com/acdlite))
* Make `act` batch updates. ([#21797](https://github.com/facebook/react/pull/21797) by [@acdlite](https://github.com/acdlite))
* Remove warning for dangling passive effects. ([#22609](https://github.com/facebook/react/pull/22609) by [@acdlite](https://github.com/acdlite))
### React Refresh
* Track late-mounted roots in Fast Refresh. ([#22740](https://github.com/facebook/react/pull/22740) by [@anc95](https://github.com/anc95))
* Add `exports` field to `package.json`. ([#23087](https://github.com/facebook/react/pull/23087) by [@otakustay](https://github.com/otakustay))
### Server Components (Experimental)
* Add Server Context support. ([#23244](https://github.com/facebook/react/pull/23244) by [@salazarm](https://github.com/salazarm))
* Add `lazy` support. ([#24068](https://github.com/facebook/react/pull/24068) by [@gnoff](https://github.com/gnoff))
* Update webpack plugin for webpack 5 ([#22739](https://github.com/facebook/react/pull/22739) by [@michenly](https://github.com/michenly))
* Fix a mistake in the Node loader. ([#22537](https://github.com/facebook/react/pull/22537) by [@btea](https://github.com/btea))
* Use `globalThis` instead of `window` for edge environments. ([#22777](https://github.com/facebook/react/pull/22777) by [@huozhi](https://github.com/huozhi))
### Scheduler (Experimental)
* Remove unstable `scheduler/tracing` API ([#20037](https://github.com/facebook/react/pull/20037) by [@bvaughn](https://github.com/bvaughn))
## 17.0.2 (March 22, 2021)
### React DOM
* Remove an unused dependency to address the [`SharedArrayBuffer` cross-origin isolation warning](https://developer.chrome.com/blog/enabling-shared-array-buffer/). ([@koba04](https://github.com/koba04) and [@bvaughn](https://github.com/bvaughn) in [#20831](https://github.com/facebook/react/pull/20831), [#20832](https://github.com/facebook/react/pull/20832), and [#20840](https://github.com/facebook/react/pull/20840))
## 17.0.1 (October 22, 2020)
### React DOM
* Fix a crash in IE11. ([@gaearon](https://github.com/gaearon) in [#20071](https://github.com/facebook/react/pull/20071))
## 17.0.0 (October 20, 2020)
Today, we are releasing React 17!
**[Learn more about React 17 and how to update to it on the official React blog.](https://reactjs.org/blog/2020/10/20/react-v17.html)**
### React
* Add `react/jsx-runtime` and `react/jsx-dev-runtime` for the [new JSX transform](https://babeljs.io/blog/2020/03/16/7.9.0#a-new-jsx-transform-11154-https-githubcom-babel-babel-pull-11154). ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299))
* Build component stacks from native error frames. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18561](https://github.com/facebook/react/pull/18561))
* Allow to specify `displayName` on context for improved stacks. ([@eps1lon](https://github.com/eps1lon) in [#18224](https://github.com/facebook/react/pull/18224))
* Prevent `'use strict'` from leaking in the UMD bundles. ([@koba04](https://github.com/koba04) in [#19614](https://github.com/facebook/react/pull/19614))
* Stop using `fb.me` for redirects. ([@cylim](https://github.com/cylim) in [#19598](https://github.com/facebook/react/pull/19598))
### React DOM
* Delegate events to roots instead of `document`. ([@trueadm](https://github.com/trueadm) in [#18195](https://github.com/facebook/react/pull/18195) and [others](https://github.com/facebook/react/pulls?q=is%3Apr+author%3Atrueadm+modern+event+is%3Amerged))
* Clean up all effects before running any next effects. ([@bvaughn](https://github.com/bvaughn) in [#17947](https://github.com/facebook/react/pull/17947))
* Run `useEffect` cleanup functions asynchronously. ([@bvaughn](https://github.com/bvaughn) in [#17925](https://github.com/facebook/react/pull/17925))
* Use browser `focusin` and `focusout` for `onFocus` and `onBlur`. ([@trueadm](https://github.com/trueadm) in [#19186](https://github.com/facebook/react/pull/19186))
* Make all `Capture` events use the browser capture phase. ([@trueadm](https://github.com/trueadm) in [#19221](https://github.com/facebook/react/pull/19221))
* Don't emulate bubbling of the `onScroll` event. ([@gaearon](https://github.com/gaearon) in [#19464](https://github.com/facebook/react/pull/19464))
* Throw if `forwardRef` or `memo` component returns `undefined`. ([@gaearon](https://github.com/gaearon) in [#19550](https://github.com/facebook/react/pull/19550))
* Remove event pooling. ([@trueadm](https://github.com/trueadm) in [#18969](https://github.com/facebook/react/pull/18969))
* Stop exposing internals that wont be needed by React Native Web. ([@necolas](https://github.com/necolas) in [#18483](https://github.com/facebook/react/pull/18483))
* Attach all known event listeners when the root mounts. ([@gaearon](https://github.com/gaearon) in [#19659](https://github.com/facebook/react/pull/19659))
* Disable `console` in the second render pass of DEV mode double render. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18547](https://github.com/facebook/react/pull/18547))
* Deprecate the undocumented and misleading `ReactTestUtils.SimulateNative` API. ([@gaearon](https://github.com/gaearon) in [#13407](https://github.com/facebook/react/pull/13407))
* Rename private field names used in the internals. ([@gaearon](https://github.com/gaearon) in [#18377](https://github.com/facebook/react/pull/18377))
* Don't call User Timing API in development. ([@gaearon](https://github.com/gaearon) in [#18417](https://github.com/facebook/react/pull/18417))
* Disable console during the repeated render in Strict Mode. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18547](https://github.com/facebook/react/pull/18547))
* In Strict Mode, double-render components without Hooks too. ([@eps1lon](https://github.com/eps1lon) in [#18430](https://github.com/facebook/react/pull/18430))
* Allow calling `ReactDOM.flushSync` during lifecycle methods (but warn). ([@sebmarkbage](https://github.com/sebmarkbage) in [#18759](https://github.com/facebook/react/pull/18759))
* Add the `code` property to the keyboard event objects. ([@bl00mber](https://github.com/bl00mber) in [#18287](https://github.com/facebook/react/pull/18287))
* Add the `disableRemotePlayback` property for `video` elements. ([@tombrowndev](https://github.com/tombrowndev) in [#18619](https://github.com/facebook/react/pull/18619))
* Add the `enterKeyHint` property for `input` elements. ([@eps1lon](https://github.com/eps1lon) in [#18634](https://github.com/facebook/react/pull/18634))
* Warn when no `value` is provided to `<Context.Provider>`. ([@charlie1404](https://github.com/charlie1404) in [#19054](https://github.com/facebook/react/pull/19054))
* Warn when `memo` or `forwardRef` components return `undefined`. ([@bvaughn](https://github.com/bvaughn) in [#19550](https://github.com/facebook/react/pull/19550))
* Improve the error message for invalid updates. ([@JoviDeCroock](https://github.com/JoviDeCroock) in [#18316](https://github.com/facebook/react/pull/18316))
* Exclude forwardRef and memo from stack frames. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18559](https://github.com/facebook/react/pull/18559))
* Improve the error message when switching between controlled and uncontrolled inputs. ([@vcarl](https://github.com/vcarl) in [#17070](https://github.com/facebook/react/pull/17070))
* Keep `onTouchStart`, `onTouchMove`, and `onWheel` passive. ([@gaearon](https://github.com/gaearon) in [#19654](https://github.com/facebook/react/pull/19654))
* Fix `setState` hanging in development inside a closed iframe. ([@gaearon](https://github.com/gaearon) in [#19220](https://github.com/facebook/react/pull/19220))
* Fix rendering bailout for lazy components with `defaultProps`. ([@jddxf](https://github.com/jddxf) in [#18539](https://github.com/facebook/react/pull/18539))
* Fix a false positive warning when `dangerouslySetInnerHTML` is `undefined`. ([@eps1lon](https://github.com/eps1lon) in [#18676](https://github.com/facebook/react/pull/18676))
* Fix Test Utils with non-standard `require` implementation. ([@just-boris](https://github.com/just-boris) in [#18632](https://github.com/facebook/react/pull/18632))
* Fix `onBeforeInput` reporting an incorrect `event.type`. ([@eps1lon](https://github.com/eps1lon) in [#19561](https://github.com/facebook/react/pull/19561))
* Fix `event.relatedTarget` reported as `undefined` in Firefox. ([@claytercek](https://github.com/claytercek) in [#19607](https://github.com/facebook/react/pull/19607))
* Fix "unspecified error" in IE11. ([@hemakshis](https://github.com/hemakshis) in [#19664](https://github.com/facebook/react/pull/19664))
* Fix rendering into a shadow root. ([@Jack-Works](https://github.com/Jack-Works) in [#15894](https://github.com/facebook/react/pull/15894))
* Fix `movementX/Y` polyfill with capture events. ([@gaearon](https://github.com/gaearon) in [#19672](https://github.com/facebook/react/pull/19672))
* Use delegation for `onSubmit` and `onReset` events. ([@gaearon](https://github.com/gaearon) in [#19333](https://github.com/facebook/react/pull/19333))
* Improve memory usage. ([@trueadm](https://github.com/trueadm) in [#18970](https://github.com/facebook/react/pull/18970))
### React DOM Server
* Make `useCallback` behavior consistent with `useMemo` for the server renderer. ([@alexmckenley](https://github.com/alexmckenley) in [#18783](https://github.com/facebook/react/pull/18783))
* Fix state leaking when a function component throws. ([@pmaccart](https://github.com/pmaccart) in [#19212](https://github.com/facebook/react/pull/19212))
### React Test Renderer
* Improve `findByType` error message. ([@henryqdineen](https://github.com/henryqdineen) in [#17439](https://github.com/facebook/react/pull/17439))
### Concurrent Mode (Experimental)
* Revamp the priority batching heuristics. ([@acdlite](https://github.com/acdlite) in [#18796](https://github.com/facebook/react/pull/18796))
* Add the `unstable_` prefix before the experimental APIs. ([@acdlite](https://github.com/acdlite) in [#18825](https://github.com/facebook/react/pull/18825))
* Remove `unstable_discreteUpdates` and `unstable_flushDiscreteUpdates`. ([@trueadm](https://github.com/trueadm) in [#18825](https://github.com/facebook/react/pull/18825))
* Remove the `timeoutMs` argument. ([@acdlite](https://github.com/acdlite) in [#19703](https://github.com/facebook/react/pull/19703))
* Disable `<div hidden />` prerendering in favor of a different future API. ([@acdlite](https://github.com/acdlite) in [#18917](https://github.com/facebook/react/pull/18917))
* Add `unstable_expectedLoadTime` to Suspense for CPU-bound trees. ([@acdlite](https://github.com/acdlite) in [#19936](https://github.com/facebook/react/pull/19936))
* Add an experimental `unstable_useOpaqueIdentifier` Hook. ([@lunaruan](https://github.com/lunaruan) in [#17322](https://github.com/facebook/react/pull/17322))
* Add an experimental `unstable_startTransition` API. ([@rickhanlonii](https://github.com/rickhanlonii) in [#19696](https://github.com/facebook/react/pull/19696))
* Using `act` in the test renderer no longer flushes Suspense fallbacks. ([@acdlite](https://github.com/acdlite) in [#18596](https://github.com/facebook/react/pull/18596))
* Use global render timeout for CPU Suspense. ([@sebmarkbage](https://github.com/sebmarkbage) in [#19643](https://github.com/facebook/react/pull/19643))
* Clear the existing root content before mounting. ([@bvaughn](https://github.com/bvaughn) in [#18730](https://github.com/facebook/react/pull/18730))
* Fix a bug with error boundaries. ([@acdlite](https://github.com/acdlite) in [#18265](https://github.com/facebook/react/pull/18265))
* Fix a bug causing dropped updates in a suspended tree. ([@acdlite](https://github.com/acdlite) in [#18384](https://github.com/facebook/react/pull/18384) and [#18457](https://github.com/facebook/react/pull/18457))
* Fix a bug causing dropped render phase updates. ([@acdlite](https://github.com/acdlite) in [#18537](https://github.com/facebook/react/pull/18537))
* Fix a bug in SuspenseList. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18412](https://github.com/facebook/react/pull/18412))
* Fix a bug causing Suspense fallback to show too early. ([@acdlite](https://github.com/acdlite) in [#18411](https://github.com/facebook/react/pull/18411))
* Fix a bug with class components inside SuspenseList. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18448](https://github.com/facebook/react/pull/18448))
* Fix a bug with inputs that may cause updates to be dropped. ([@jddxf](https://github.com/jddxf) in [#18515](https://github.com/facebook/react/pull/18515) and [@acdlite](https://github.com/acdlite) in [#18535](https://github.com/facebook/react/pull/18535))
* Fix a bug causing Suspense fallback to get stuck. ([@acdlite](https://github.com/acdlite) in [#18663](https://github.com/facebook/react/pull/18663))
* Don't cut off the tail of a SuspenseList if hydrating. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18854](https://github.com/facebook/react/pull/18854))
* Fix a bug in `useMutableSource` that may happen when `getSnapshot` changes. ([@bvaughn](https://github.com/bvaughn) in [#18297](https://github.com/facebook/react/pull/18297))
* Fix a tearing bug in `useMutableSource`. ([@bvaughn](https://github.com/bvaughn) in [#18912](https://github.com/facebook/react/pull/18912))
* Warn if calling setState outside of render but before commit. ([@sebmarkbage](https://github.com/sebmarkbage) in [#18838](https://github.com/facebook/react/pull/18838))
## 16.14.0 (October 14, 2020)
### React
* Add support for the [new JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html). ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299))
## 16.13.1 (March 19, 2020)
### React DOM
@@ -907,7 +572,7 @@ This release was published in a broken state and should be skipped.
### React Is (New)
* First release of the [new package](https://github.com/facebook/react/tree/main/packages/react-is) that libraries can use to detect different React node types. ([@bvaughn](https://github.com/bvaughn) in [#12199](https://github.com/facebook/react/pull/12199))
* First release of the [new package](https://github.com/facebook/react/tree/master/packages/react-is) that libraries can use to detect different React node types. ([@bvaughn](https://github.com/bvaughn) in [#12199](https://github.com/facebook/react/pull/12199))
* Add `ReactIs.isValidElementType()` to help higher-order components validate their inputs. ([@jamesreggio](https://github.com/jamesreggio) in [#12483](https://github.com/facebook/react/pull/12483))
### React Lifecycles Compat (New)
@@ -916,7 +581,7 @@ This release was published in a broken state and should be skipped.
### Create Subscription (New)
* First release of the [new package](https://github.com/facebook/react/tree/main/packages/create-subscription) to subscribe to external data sources safely for async rendering. ([@bvaughn](https://github.com/bvaughn) in [#12325](https://github.com/facebook/react/pull/12325))
* First release of the [new package](https://github.com/facebook/react/tree/master/packages/create-subscription) to subscribe to external data sources safely for async rendering. ([@bvaughn](https://github.com/bvaughn) in [#12325](https://github.com/facebook/react/pull/12325))
### React Reconciler (Experimental)
@@ -1047,12 +712,12 @@ Starting with 16.1.0, we will no longer be publishing new releases on Bower. You
### React Reconciler (Experimental)
* First release of the [new experimental package](https://github.com/facebook/react/blob/main/packages/react-reconciler/README.md) for creating custom renderers. ([@iamdustan](https://github.com/iamdustan) in [#10758](https://github.com/facebook/react/pull/10758))
* First release of the [new experimental package](https://github.com/facebook/react/blob/master/packages/react-reconciler/README.md) for creating custom renderers. ([@iamdustan](https://github.com/iamdustan) in [#10758](https://github.com/facebook/react/pull/10758))
* Add support for React DevTools. ([@gaearon](https://github.com/gaearon) in [#11463](https://github.com/facebook/react/pull/11463))
### React Call Return (Experimental)
* First release of the [new experimental package](https://github.com/facebook/react/tree/main/packages/react-call-return) for parent-child communication. ([@gaearon](https://github.com/gaearon) in [#11364](https://github.com/facebook/react/pull/11364))
* First release of the [new experimental package](https://github.com/facebook/react/tree/master/packages/react-call-return) for parent-child communication. ([@gaearon](https://github.com/gaearon) in [#11364](https://github.com/facebook/react/pull/11364))
## 16.0.1 (August 1, 2018)
@@ -1105,12 +770,6 @@ Starting with 16.1.0, we will no longer be publishing new releases on Bower. You
- There is no `react-with-addons.js` build anymore. All compatible addons are published separately on npm, and have single-file browser versions if you need them.
- The deprecations introduced in 15.x have been removed from the core package. `React.createClass` is now available as create-react-class, `React.PropTypes` as prop-types, `React.DOM` as react-dom-factories, react-addons-test-utils as react-dom/test-utils, and shallow renderer as react-test-renderer/shallow. See [15.5.0](https://reactjs.org/blog/2017/04/07/react-v15.5.0.html) and [15.6.0](https://reactjs.org/blog/2017/06/13/react-v15.6.0.html) blog posts for instructions on migrating code and automated codemods.
## 15.7.0 (October 14, 2020)
### React
* Backport support for the [new JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to 15.x. ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299) and [@gaearon](https://github.com/gaearon) in [#20024](https://github.com/facebook/react/pull/20024))
## 15.6.2 (September 25, 2017)
### All Packages
@@ -1559,14 +1218,9 @@ Each of these changes will continue to work as before with a new warning until t
- Shallow renderer now returns the rendered output from `render()`. ([@simonewebdesign](https://github.com/simonewebdesign) in [#5411](https://github.com/facebook/react/pull/5411))
- React no longer depends on ES5 *shams* for `Object.create` and `Object.freeze` in older environments. It still, however, requires ES5 *shims* in those environments. ([@dgreensp](https://github.com/dgreensp) in [#4959](https://github.com/facebook/react/pull/4959))
- React DOM now allows `data-` attributes with names that start with numbers. ([@nLight](https://github.com/nLight) in [#5216](https://github.com/facebook/react/pull/5216))
- React DOM adds a new `suppressContentEditableWarning` prop for components like [Draft.js](https://draftjs.org/) that intentionally manage `contentEditable` children with React. ([@mxstbr](https://github.com/mxstbr) in [#6112](https://github.com/facebook/react/pull/6112))
- React DOM adds a new `suppressContentEditableWarning` prop for components like [Draft.js](https://facebook.github.io/draft-js/) that intentionally manage `contentEditable` children with React. ([@mxstbr](https://github.com/mxstbr) in [#6112](https://github.com/facebook/react/pull/6112))
- React improves the performance for `createClass()` on complex specs. ([@sophiebits](https://github.com/sophiebits) in [#5550](https://github.com/facebook/react/pull/5550))
## 0.14.10 (October 14, 2020)
### React
* Backport support for the [new JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to 0.14.x. ([@lunaruan](https://github.com/lunaruan) in [#18299](https://github.com/facebook/react/pull/18299) and [@gaearon](https://github.com/gaearon) in [#20024](https://github.com/facebook/react/pull/20024))
## 0.14.8 (March 29, 2016)

View File

@@ -52,10 +52,6 @@ project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
This Code of Conduct also applies outside the project spaces when there is a
reasonable belief that an individual's behavior may have a negative impact on
the project or its community.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be

View File

@@ -2,4 +2,4 @@
Want to contribute to React? There are a few things you need to know.
We wrote a **[contribution guide](https://reactjs.org/docs/how-to-contribute.html)** to help you get started.
We wrote a **[contribution guide](https://reactjs.org/contributing/how-to-contribute.html)** to help you get started.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright (c) Facebook, Inc. and its affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,77 +1,74 @@
# [React](https://react.dev/) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/facebook/react/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/react.svg?style=flat)](https://www.npmjs.com/package/react) [![CircleCI Status](https://circleci.com/gh/facebook/react.svg?style=shield)](https://circleci.com/gh/facebook/react) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://legacy.reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
# [React](https://reactjs.org/) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/facebook/react/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/react.svg?style=flat)](https://www.npmjs.com/package/react) [![CircleCI Status](https://circleci.com/gh/facebook/react.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/facebook/react) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
React is a JavaScript library for building user interfaces.
* **Declarative:** React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. Declarative views make your code more predictable, simpler to understand, and easier to debug.
* **Component-Based:** Build encapsulated components that manage their own state, then compose them to make complex UIs. Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep the state out of the DOM.
* **Learn Once, Write Anywhere:** We don't make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code. React can also render on the server using [Node](https://nodejs.org/en) and power mobile apps using [React Native](https://reactnative.dev/).
* **Component-Based:** Build encapsulated components that manage their own state, then compose them to make complex UIs. Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.
* **Learn Once, Write Anywhere:** We don't make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code. React can also render on the server using Node and power mobile apps using [React Native](https://reactnative.dev/).
[Learn how to use React in your project](https://react.dev/learn).
[Learn how to use React in your own project](https://reactjs.org/docs/getting-started.html).
## Installation
React has been designed for gradual adoption from the start, and **you can use as little or as much React as you need**:
* Use [Quick Start](https://react.dev/learn) to get a taste of React.
* [Add React to an Existing Project](https://react.dev/learn/add-react-to-an-existing-project) to use as little or as much React as you need.
* [Create a New React App](https://react.dev/learn/start-a-new-react-project) if you're looking for a powerful JavaScript toolchain.
* Use [Online Playgrounds](https://reactjs.org/docs/getting-started.html#online-playgrounds) to get a taste of React.
* [Add React to a Website](https://reactjs.org/docs/add-react-to-a-website.html) as a `<script>` tag in one minute.
* [Create a New React App](https://reactjs.org/docs/create-a-new-react-app.html) if you're looking for a powerful JavaScript toolchain.
You can use React as a `<script>` tag from a [CDN](https://reactjs.org/docs/cdn-links.html), or as a `react` package on [npm](https://www.npmjs.com/package/react).
## Documentation
You can find the React documentation [on the website](https://react.dev/).
You can find the React documentation [on the website](https://reactjs.org/docs).
Check out the [Getting Started](https://react.dev/learn) page for a quick overview.
Check out the [Getting Started](https://reactjs.org/docs/getting-started.html) page for a quick overview.
The documentation is divided into several sections:
* [Quick Start](https://react.dev/learn)
* [Tutorial](https://react.dev/learn/tutorial-tic-tac-toe)
* [Thinking in React](https://react.dev/learn/thinking-in-react)
* [Installation](https://react.dev/learn/installation)
* [Describing the UI](https://react.dev/learn/describing-the-ui)
* [Adding Interactivity](https://react.dev/learn/adding-interactivity)
* [Managing State](https://react.dev/learn/managing-state)
* [Advanced Guides](https://react.dev/learn/escape-hatches)
* [API Reference](https://react.dev/reference/react)
* [Where to Get Support](https://react.dev/community)
* [Contributing Guide](https://legacy.reactjs.org/docs/how-to-contribute.html)
* [Tutorial](https://reactjs.org/tutorial/tutorial.html)
* [Main Concepts](https://reactjs.org/docs/hello-world.html)
* [Advanced Guides](https://reactjs.org/docs/jsx-in-depth.html)
* [API Reference](https://reactjs.org/docs/react-api.html)
* [Where to Get Support](https://reactjs.org/community/support.html)
* [Contributing Guide](https://reactjs.org/docs/how-to-contribute.html)
You can improve it by sending pull requests to [this repository](https://github.com/reactjs/react.dev).
You can improve it by sending pull requests to [this repository](https://github.com/reactjs/reactjs.org).
## Examples
We have several examples [on the website](https://react.dev/). Here is the first one to get you started:
We have several examples [on the website](https://reactjs.org/). Here is the first one to get you started:
```jsx
import { createRoot } from 'react-dom/client';
function HelloMessage({ name }) {
return <div>Hello {name}</div>;
}
const root = createRoot(document.getElementById('container'));
root.render(<HelloMessage name="Taylor" />);
ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('container')
);
```
This example will render "Hello Taylor" into a container on the page.
You'll notice that we used an HTML-like syntax; [we call it JSX](https://react.dev/learn#writing-markup-with-jsx). JSX is not required to use React, but it makes code more readable, and writing it feels like writing HTML.
You'll notice that we used an HTML-like syntax; [we call it JSX](https://reactjs.org/docs/introducing-jsx.html). JSX is not required to use React, but it makes code more readable, and writing it feels like writing HTML. If you're using React as a `<script>` tag, read [this section](https://reactjs.org/docs/add-react-to-a-website.html#optional-try-react-with-jsx) on integrating JSX; otherwise, the [recommended JavaScript toolchains](https://reactjs.org/docs/create-a-new-react-app.html) handle it automatically.
## Contributing
The main purpose of this repository is to continue evolving React core, making it faster and easier to use. Development of React happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving React.
The main purpose of this repository is to continue to evolve React core, making it faster and easier to use. Development of React happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving React.
### [Code of Conduct](https://code.fb.com/codeofconduct)
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
### [Contributing Guide](https://legacy.reactjs.org/docs/how-to-contribute.html)
### [Contributing Guide](https://reactjs.org/contributing/how-to-contribute.html)
Read our [contributing guide](https://legacy.reactjs.org/docs/how-to-contribute.html) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React.
Read our [contributing guide](https://reactjs.org/contributing/how-to-contribute.html) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React.
### [Good First Issues](https://github.com/facebook/react/labels/good%20first%20issue)
### Good First Issues
To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/facebook/react/labels/good%20first%20issue) that contain bugs that have a relatively limited scope. This is a great place to get started.
To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/facebook/react/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. This is a great place to get started.
### License

View File

@@ -1,63 +0,0 @@
'use strict';
// This module is the single source of truth for versioning packages that we
// publish to npm.
//
// Packages will not be published unless they are added here.
//
// The @latest channel uses the version as-is, e.g.:
//
// 19.0.0
//
// The @canary channel appends additional information, with the scheme
// <version>-<label>-<commit_sha>, e.g.:
//
// 19.0.0-canary-a1c2d3e4
//
// The @experimental channel doesn't include a version, only a date and a sha, e.g.:
//
// 0.0.0-experimental-241c4467e-20200129
const ReactVersion = '19.0.0';
// The label used by the @canary channel. Represents the upcoming release's
// stability. Most of the time, this will be "canary", but we may temporarily
// choose to change it to "alpha", "beta", "rc", etc.
//
// It only affects the label used in the version string. To customize the
// npm dist tags used during publish, refer to .circleci/config.yml.
const canaryChannelLabel = 'rc';
// If the canaryChannelLabel is "rc", the build pipeline will use this to build
// an RC version of the packages.
const rcNumber = 0;
const stablePackages = {
'eslint-plugin-react-hooks': '5.1.0',
'jest-react': '0.16.0',
react: ReactVersion,
'react-art': ReactVersion,
'react-dom': ReactVersion,
'react-server-dom-webpack': ReactVersion,
'react-server-dom-turbopack': ReactVersion,
'react-is': ReactVersion,
'react-reconciler': '0.31.0',
'react-refresh': '0.16.0',
'react-test-renderer': ReactVersion,
'use-subscription': '1.10.0',
'use-sync-external-store': '1.4.0',
scheduler: '0.25.0',
};
// These packages do not exist in the @canary or @latest channel, only
// @experimental. We don't use semver, just the commit sha, so this is just a
// list of package names instead of a map.
const experimentalPackages = [];
module.exports = {
ReactVersion,
canaryChannelLabel,
rcNumber,
stablePackages,
experimentalPackages,
};

41
appveyor.yml Normal file
View File

@@ -0,0 +1,41 @@
image: Visual Studio 2017
# Fix line endings in Windows. (runs before repo cloning)
init:
- git config --global core.autocrlf input
environment:
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
matrix:
- nodejs_version: 10
# Finish on first failed build
matrix:
fast_finish: true
platform:
- x64
branches:
only:
- master
# Disable Visual Studio build and deploy
build: off
deploy: off
install:
- ps: Install-Product node $env:nodejs_version $env:platform
- yarn install --frozen-lockfile
test_script:
- node --version
- yarn lint
# - yarn flow-ci
- yarn build
- yarn test
- yarn prettier
cache:
- node_modules
- "%LOCALAPPDATA%/Yarn"

View File

@@ -3,6 +3,7 @@
module.exports = {
plugins: [
'@babel/plugin-syntax-jsx',
'@babel/plugin-transform-react-jsx',
'@babel/plugin-transform-flow-strip-types',
['@babel/plugin-proposal-class-properties', {loose: true}],
'syntax-trailing-function-commas',

View File

@@ -1,100 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Our philosophy for linting is that lints should be very high-signal:
* - Error, don't warn. If it's worth mentioning it's worth fixing.
* - Enable rules that consistently identify real problems. If we frequently would have to
* disable the rule due to false positives, it isn't high-signal.
* - Enable rules that help improve consistent style (to avoid code review about style rather
* than substance).
*/
module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
rules: {
/*
* We prefer using const where variables are not reassigned, but occassional mistakes
* aren't a major issue
*/
"prefer-const": "off",
// Not valuable enough to enable
"no-useless-escape": "off",
/*
* There are valid use cases for loops with constant conditions where the body contains the
* break
*/
"no-constant-condition": "off",
// eslint only knows about builtin control flow (eg throw, return, break) and not custom ones
// like invariant.
"no-fallthrough": "off",
/*
* Low-value: this fires even for declarations that capture references which wouldn't be as
* obvious if the declaration was lifted to the parent root
*/
"no-inner-declarations": "off",
"multiline-comment-style": ["error", "starred-block"],
"@typescript-eslint/no-empty-function": "off",
/*
* Explicitly casting to/through any is sometimes required, often for error messages to
* assertExhaustive()
*/
"@typescript-eslint/no-explicit-any": "off",
/*
* We use non-null assertions carefully. Ideally, there would be a TS option to codegen
* a non-null check at the assertion site.
*/
"@typescript-eslint/no-non-null-assertion": "off",
// Being explicit provides value in cases where inference may later change
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/explicit-function-return-type": "error",
/*
* Unused variables are frequently a bug. Prefix unused variables with an _ to fix, but note
* that eslint won't warn you that an underscore prefixed variable is used and that the prefix
* should be dropped.
*/
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
// Consider enabling for consistency. Ideally violations could be auto-fixed.
"@typescript-eslint/consistent-generic-constructors": [
"off",
"constructor",
],
"@typescript-eslint/array-type": ["error", { default: "generic" }],
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/no-var-requires": "off"
},
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
root: true,
ignorePatterns: ["**/__tests__/**/*", "**/*.d.ts", "**/dist/**/*"],
env: {
node: true,
},
/*
* If rules need to be disabled then the rule is insufficiently high signal
* and should be diasbled altogether or customized (in either case via a standalone PR)
*/
noInlineConfig: true,
reportUnusedDisableDirectives: true,
};

24
compiler/.gitignore vendored
View File

@@ -1,24 +0,0 @@
.DS_Store
.spr.yml
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
node_modules
.watchmanconfig
.watchman-cookie-*
dist
.vscode
!packages/playground/.vscode
.spr.yml
testfilter.txt
bundle-oss.sh

View File

View File

@@ -1,21 +0,0 @@
**/dist
**/__tests__/fixtures/**/*.expect.md
**/__tests__/fixtures/**/*.flow.js
**/.next
crates
apps/playground/public
**/LICENSE
.*
*.md*
*.json
*.css
*.webmanifest
*.map
*.sh
*.txt
*.ico
*.svg
*.lock
*.toml

View File

@@ -1,9 +0,0 @@
const config = {
requirePragma: false,
parser: "babel-ts",
semi: true,
singleQuote: false,
trailingComma: "es5"
}
module.exports = config;

View File

@@ -1,8 +0,0 @@
{
"editor.formatOnSave": true,
"[typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1,80 +0,0 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
This Code of Conduct also applies outside the project spaces when there is a
reasonable belief that an individual's behavior may have a negative impact on
the project or its community.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource-conduct@fb.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@@ -1,31 +0,0 @@
# Contributing to react-forget
We want to make contributing to this project as easy and transparent as
possible.
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `main`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. Make sure your code lints.
6. If you haven't already, complete the Contributor License Agreement ("CLA").
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here: <https://code.facebook.com/cla>
## Issues
We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
disclosure of security bugs. In those cases, please go through the process
outlined on that page and do not file a public issue.
## License
By contributing to react-forget, you agree that your contributions will be licensed
under the LICENSE file in the root directory of this source tree.

1217
compiler/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +0,0 @@
[workspace]
resolver = "2"
members = ["crates/*"]
[workspace.package]
authors = ["The React Team https://react.dev/community/team"]
description = "React Compiler"
edition = "2021"
homepage = "https://github.com/facebook/react"
keywords = ["JavaScript", "TypeScript", "React", "React Compiler", "Compiler"]
license = "MIT"
repository = "https://github.com/facebook/react"
[workspace.dependencies]
# workspace crates
react_build_hir = { path = "crates/react_build_hir" }
react_diagnostics = { path = "crates/react_diagnostics" }
react_estree = { path = "crates/react_estree" }
react_estree_codegen = { path = "crates/react_estree_codegen" }
react_fixtures = { path = "crates/react_fixtures" }
react_hermes_parser = { path = "crates/react_hermes_parser" }
react_hir = { path = "crates/react_hir" }
react_optimization = { path = "crates/react_optimization" }
react_semantic_analysis = { path = "crates/react_semantic_analysis" }
react_ssa = { path = "crates/react_ssa" }
react_utils = { path = "crates/react_utils" }
# dependencies
indexmap = { version = "2.0.0", features = ["serde"] }
insta = { version = "1.30.0", features = ["glob"] }
miette = { version = "5.9.0" }
prettyplease = "0.2.10"
quote = "1.0.29"
serde = { version = "1.0.167", features = ["serde_derive"] }
serde_json = "1.0.100"
stacker = "0.1.15"
static_assertions = "1.1.0"
syn = "2.0.23"
thiserror = "1.0.41"
hermes = { git = "https://github.com/facebook/hermes.git" }
juno_support = { git = "https://github.com/facebook/hermes.git" }
[profile.release]
# configuration adapted from oxc
# https://github.com/Boshen/oxc/blob/ea85ee9f2d64dd284c5b7410f491d81fb879abae/Cargo.toml#L89-L97
opt-level = 3
lto = "fat"
codegen-units = 1
strip = "symbols"
debug = false
panic = "abort" # Let it crash and force ourselves to write safe Rust.
# Make insta run faster by compiling with release mode optimizations
# https://docs.rs/insta/latest/insta/#optional-faster-runs
[profile.dev.package.insta]
opt-level = 3
# Make insta diffing libary faster by compiling with release mode optimizations
# https://docs.rs/insta/latest/insta/#optional-faster-runs
[profile.dev.package.similar]
opt-level = 3

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,7 +0,0 @@
# React Compiler
React Compiler is a compiler that optimizes React applications, ensuring that only the minimal parts of components and hooks will re-render when state changes. The compiler also validates that components and hooks follow the Rules of React.
More information about the design and architecture of the compiler are covered in the [Design Goals](./docs/DESIGN_GOALS.md).
More information about developing the compiler itself is covered in the [Development Guide](./docs/DEVELOPMENT_GUIDE.md).

View File

@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -1,45 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
/test-results
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
# external fonts
public/fonts/Optimistic_Display_W_Lt.woff2
public/fonts/Optimistic_Display_W_Md.woff2
public/fonts/Optimistic_Display_W_Bd.woff2
# vscode
.vscode/*
!.vscode/extensions.json

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["bradlc.vscode-tailwindcss", "heybourn.headwind"]
}

View File

@@ -1,35 +0,0 @@
# React Compiler Playground
An interactive playground to demonstrate, test, and have fun with React Compiler.
## Setup
```sh
# Build React Compiler from source and install Playground dependencies.
$ yarn
# Or similarly
$ npm install
```
## Development
```sh
# Start the local development server with
$ yarn dev
# Or
$ npm run dev
# Rerun the following (in a separate terminal window) when React Compiler
# is changed locally to keep Playground in sync.
$ yarn
```
## Deployment
This project has been deployed using Vercel. Vercel does the exact same thing as we would
locally, by running `yarn` at the install step in the Playground directory to build
React Compiler from source and [symlink](https://classic.yarnpkg.com/en/docs/cli/link) it as its dependency.
This means that Playground is automatically deployed on every push and pull requests will reflect
the behaviors of React Compiler of that commit.

View File

@@ -1,11 +0,0 @@
function MyApp() {
  const $ = _c(1);
  let t0;
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
    t0 = <div>Hello World</div>;
    $[0] = t0;
  } else {
    t0 = $[0];
  }
  return t0;
}

View File

@@ -1,13 +0,0 @@
function TestComponent(t0) {
  const $ = _c(2);
  const { x } = t0;
  let t1;
  if ($[0] !== x) {
    t1 = <Button>{x}</Button>;
    $[0] = x;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  return t1;
}

View File

@@ -1,49 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { expect, test } from "@playwright/test";
import { encodeStore, type Store } from "../../lib/stores";
const STORE: Store = {
source: `export default function TestComponent({ x }) {
return <Button>{x}</Button>;
}
`,
};
const HASH = encodeStore(STORE);
function concat(data: Array<string>): string {
return data.join("");
}
test("editor should compile successfully", async ({ page }) => {
await page.goto(`/#${HASH}`, { waitUntil: "networkidle" });
await page.screenshot({
fullPage: true,
path: "test-results/00-on-networkidle.png",
});
// User input from hash compiles
await page.screenshot({
fullPage: true,
path: "test-results/01-show-js-before.png",
});
const userInput =
(await page.locator(".monaco-editor").nth(2).allInnerTexts()) ?? [];
expect(concat(userInput)).toMatchSnapshot("user-input.txt");
// Reset button works
page.on("dialog", (dialog) => dialog.accept());
await page.getByRole("button", { name: "Reset" }).click();
await page.screenshot({
fullPage: true,
path: "test-results/02-show-js-after.png",
});
const defaultInput =
(await page.locator(".monaco-editor").nth(2).allInnerTexts()) ?? [];
expect(concat(defaultInput)).toMatchSnapshot("default-input.txt");
});

View File

@@ -1,58 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { NextPage } from "next";
import Head from "next/head";
import { SnackbarProvider } from "notistack";
import { Editor, Header, StoreProvider } from "../components";
import MessageSnackbar from "../components/Message";
const Home: NextPage = () => {
return (
<div className="flex flex-col w-screen h-screen font-light">
<Head>
<title>
{process.env.NODE_ENV === "development"
? "[DEV] React Compiler Playground"
: "React Compiler Playground"}
</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
></meta>
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/site.webmanifest" />
<link
rel="preload"
href="/fonts/Source-Code-Pro-Regular.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<link
rel="preload"
href="/fonts/Optimistic_Display_W_Lt.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
</Head>
<StoreProvider>
<SnackbarProvider
preventDuplicate
maxSnack={10}
Components={{ message: MessageSnackbar }}
>
<Header />
<Editor />
</SnackbarProvider>
</StoreProvider>
</div>
);
};
export default Home;

View File

@@ -1,48 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import "../styles/globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
"use no memo";
return (
<html lang="en">
<head>
<title>
{process.env.NODE_ENV === "development"
? "[DEV] React Compiler Playground"
: "React Compiler Playground"}
</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"
></meta>
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/site.webmanifest" />
<link
rel="preload"
href="/fonts/Source-Code-Pro-Regular.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<link
rel="preload"
href="/fonts/Optimistic_Display_W_Lt.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
</head>
<body className="font-sans h-screen overflow-y-hidden">{children}</body>
</html>
);
}

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
"use client";
import { SnackbarProvider } from "notistack";
import { Editor, Header, StoreProvider } from "../components";
import MessageSnackbar from "../components/Message";
export default function Hoot() {
return (
<StoreProvider>
<SnackbarProvider
preventDuplicate
maxSnack={10}
Components={{ message: MessageSnackbar }}
>
<Header />
<Editor />
</SnackbarProvider>
</StoreProvider>
);
}

View File

@@ -1,21 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports = function (api) {
api.cache(true);
return {
presets: ["next/babel"],
plugins: [
[
"babel-plugin-react-compiler",
{
runtimeModule: "react-compiler-runtime",
},
],
],
};
};

View File

@@ -1,96 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Sync from <https://github.com/reactjs/reactjs.org/blob/main/beta/colors.js>.
*/
module.exports = {
// Text colors
primary: "#23272F", // gray-90
"primary-dark": "#F6F7F9", // gray-5
secondary: "#404756", // gray-70
"secondary-dark": "#EBECF0", // gray-10
link: "#087EA4", // blue-50
"link-dark": "#149ECA", // blue-40
syntax: "#EBECF0", // gray-10
wash: "#FFFFFF",
"wash-dark": "#23272F", // gray-90
card: "#F6F7F9", // gray-05
"card-dark": "#343A46", // gray-80
highlight: "#E6F7FF", // blue-10
"highlight-dark": "rgba(88,175,223,.1)",
border: "#EBECF0", // gray-10
"border-dark": "#343A46", // gray-80
"secondary-button": "#EBECF0", // gray-10
"secondary-button-dark": "#404756", // gray-70
// Gray
"gray-95": "#16181D",
"gray-90": "#23272F",
"gray-80": "#343A46",
"gray-70": "#404756",
"gray-60": "#4E5769",
"gray-50": "#5E687E", // unused
"gray-40": "#78839B",
"gray-30": "#99A1B3",
"gray-20": "#BCC1CD",
"gray-10": "#EBECF0",
"gray-5": "#F6F7F9",
// Blue
"blue-60": "#045975",
"blue-50": "#087EA4",
"blue-40": "#149ECA", // Brand Blue
"blue-30": "#58C4DC", // unused
"blue-20": "#ABE2ED",
"blue-10": "#E6F7FF", // todo: doesn't match illustrations
"blue-5": "#E6F6FA",
// Yellow
"yellow-60": "#B65700",
"yellow-50": "#C76A15",
"yellow-40": "#DB7D27", // unused
"yellow-30": "#FABD62", // unused
"yellow-20": "#FCDEB0", // unused
"yellow-10": "#FDE7C7",
"yellow-5": "#FEF5E7",
// Purple
"purple-60": "#2B3491", // unused
"purple-50": "#575FB7",
"purple-40": "#6B75DB",
"purple-30": "#8891EC",
"purple-20": "#C3C8F5", // unused
"purple-10": "#E7E9FB",
"purple-5": "#F3F4FD",
// Green
"green-60": "#2B6E62",
"green-50": "#388F7F",
"green-40": "#44AC99",
"green-30": "#7FCCBF",
"green-20": "#ABDED5",
"green-10": "#E5F5F2",
"green-5": "#F4FBF9",
// RED
"red-60": "#712D28",
"red-50": "#A6423A", // unused
"red-40": "#C1554D",
"red-30": "#D07D77",
"red-20": "#E5B7B3", // unused
"red-10": "#F2DBD9", // unused
"red-5": "#FAF1F0",
// MISC
"code-block": "#99a1b30f", // gray-30 @ 6%
"gradient-blue": "#58C4DC", // Only used for the landing gradient for now.
github: {
highlight: "#fffbdd",
},
};

View File

@@ -1,335 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { parse, ParserPlugin } from "@babel/parser";
import traverse, { NodePath } from "@babel/traverse";
import * as t from "@babel/types";
import {
CompilerError,
CompilerErrorDetail,
Effect,
ErrorSeverity,
parseConfigPragma,
printHIR,
printReactiveFunction,
run,
ValueKind,
type Hook,
} from "babel-plugin-react-compiler/src";
import { type ReactFunctionType } from "babel-plugin-react-compiler/src/HIR/Environment";
import clsx from "clsx";
import invariant from "invariant";
import { useSnackbar } from "notistack";
import { useDeferredValue, useMemo } from "react";
import { useMountEffect } from "../../hooks";
import { defaultStore } from "../../lib/defaultStore";
import {
createMessage,
initStoreFromUrlOrLocalStorage,
MessageLevel,
MessageSource,
type Store,
} from "../../lib/stores";
import { useStore, useStoreDispatch } from "../StoreContext";
import Input from "./Input";
import {
CompilerOutput,
default as Output,
PrintedCompilerPipelineValue,
} from "./Output";
function parseFunctions(
source: string
): Array<
NodePath<
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
>
> {
const items: Array<
NodePath<
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
>
> = [];
try {
const isFlow = source
.trim()
.split("\n", 1)[0]
.match(/\s*\/\/\s*\@flow\s*/);
let type_transform: ParserPlugin;
if (isFlow) {
type_transform = "flow";
} else {
type_transform = "typescript";
}
const ast = parse(source, {
plugins: [type_transform, "jsx"],
sourceType: "module",
});
traverse(ast, {
FunctionDeclaration(nodePath) {
items.push(nodePath);
nodePath.skip();
},
ArrowFunctionExpression(nodePath) {
items.push(nodePath);
nodePath.skip();
},
FunctionExpression(nodePath) {
items.push(nodePath);
nodePath.skip();
},
});
} catch (e) {
console.error(e);
CompilerError.throwInvalidJS({
reason: String(e),
description: null,
loc: null,
suggestions: null,
});
}
return items;
}
const COMMON_HOOKS: Array<[string, Hook]> = [
[
"useFragment",
{
valueKind: ValueKind.Frozen,
effectKind: Effect.Freeze,
noAlias: true,
transitiveMixedData: true,
},
],
[
"usePaginationFragment",
{
valueKind: ValueKind.Frozen,
effectKind: Effect.Freeze,
noAlias: true,
transitiveMixedData: true,
},
],
[
"useRefetchableFragment",
{
valueKind: ValueKind.Frozen,
effectKind: Effect.Freeze,
noAlias: true,
transitiveMixedData: true,
},
],
[
"useLazyLoadQuery",
{
valueKind: ValueKind.Frozen,
effectKind: Effect.Freeze,
noAlias: true,
transitiveMixedData: true,
},
],
[
"usePreloadedQuery",
{
valueKind: ValueKind.Frozen,
effectKind: Effect.Freeze,
noAlias: true,
transitiveMixedData: true,
},
],
];
function isHookName(s: string): boolean {
return /^use[A-Z0-9]/.test(s);
}
function getReactFunctionType(
id: NodePath<t.Identifier | null | undefined>
): ReactFunctionType {
if (id && id.node && id.isIdentifier()) {
if (isHookName(id.node.name)) {
return "Hook";
}
const isPascalCaseNameSpace = /^[A-Z].*/;
if (isPascalCaseNameSpace.test(id.node.name)) {
return "Component";
}
}
return "Other";
}
function compile(source: string): CompilerOutput {
const results = new Map<string, PrintedCompilerPipelineValue[]>();
const error = new CompilerError();
const upsert = (result: PrintedCompilerPipelineValue) => {
const entry = results.get(result.name);
if (Array.isArray(entry)) {
entry.push(result);
} else {
results.set(result.name, [result]);
}
};
try {
// Extract the first line to quickly check for custom test directives
const pragma = source.substring(0, source.indexOf("\n"));
const config = parseConfigPragma(pragma);
for (const fn of parseFunctions(source)) {
if (!fn.isFunctionDeclaration()) {
error.pushErrorDetail(
new CompilerErrorDetail({
reason: `Unexpected function type ${fn.node.type}`,
description:
"Playground only supports parsing function declarations",
severity: ErrorSeverity.Todo,
loc: fn.node.loc ?? null,
suggestions: null,
})
);
continue;
}
const id = fn.get("id");
for (const result of run(
fn,
{
...config,
customHooks: new Map([...COMMON_HOOKS]),
},
getReactFunctionType(id),
"_c",
null,
null,
null
)) {
const fnName = fn.node.id?.name ?? null;
switch (result.kind) {
case "ast": {
upsert({
kind: "ast",
fnName,
name: result.name,
value: {
type: "FunctionDeclaration",
id: result.value.id,
async: result.value.async,
generator: result.value.generator,
body: result.value.body,
params: result.value.params,
},
});
break;
}
case "hir": {
upsert({
kind: "hir",
fnName,
name: result.name,
value: printHIR(result.value.body),
});
break;
}
case "reactive": {
upsert({
kind: "reactive",
fnName,
name: result.name,
value: printReactiveFunction(result.value),
});
break;
}
case "debug": {
upsert({
kind: "debug",
fnName,
name: result.name,
value: result.value,
});
break;
}
default: {
const _: never = result;
throw new Error(`Unhandled result ${result}`);
}
}
}
}
} catch (err) {
// error might be an invariant violation or other runtime error
// (i.e. object shape that is not CompilerError)
if (err instanceof CompilerError && err.details.length > 0) {
error.details.push(...err.details);
} else {
// Handle unexpected failures by logging (to get a stack trace)
// and reporting
console.error(err);
error.details.push(
new CompilerErrorDetail({
severity: ErrorSeverity.Invariant,
reason: `Unexpected failure when transforming input! ${err}`,
loc: null,
suggestions: null,
})
);
}
}
if (error.hasErrors()) {
return { kind: "err", results, error: error };
}
return { kind: "ok", results };
}
export default function Editor() {
const store = useStore();
const deferredStore = useDeferredValue(store);
const dispatchStore = useStoreDispatch();
const { enqueueSnackbar } = useSnackbar();
const compilerOutput = useMemo(
() => compile(deferredStore.source),
[deferredStore.source]
);
useMountEffect(() => {
let mountStore: Store;
try {
mountStore = initStoreFromUrlOrLocalStorage();
} catch (e) {
invariant(e instanceof Error, "Only Error may be caught.");
enqueueSnackbar(e.message, {
variant: "message",
...createMessage(
"Bad URL - fell back to the default Playground.",
MessageLevel.Info,
MessageSource.Playground
),
});
mountStore = defaultStore;
}
dispatchStore({
type: "setStore",
payload: { store: mountStore },
});
});
return (
<>
<div className="relative flex basis top-14">
<div className={clsx("relative sm:basis-1/4")}>
<Input
errors={
compilerOutput.kind === "err" ? compilerOutput.error.details : []
}
/>
</div>
<div className={clsx("flex sm:flex flex-wrap")}>
<Output store={deferredStore} compilerOutput={compilerOutput} />
</div>
</div>
</>
);
}

View File

@@ -1,127 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import MonacoEditor, { loader, type Monaco } from "@monaco-editor/react";
import { CompilerErrorDetail } from "babel-plugin-react-compiler/src";
import invariant from "invariant";
import type { editor } from "monaco-editor";
import * as monaco from "monaco-editor";
import { Resizable } from "re-resizable";
import { useEffect, useState } from "react";
import { renderReactCompilerMarkers } from "../../lib/reactCompilerMonacoDiagnostics";
import { useStore, useStoreDispatch } from "../StoreContext";
import { monacoOptions } from "./monacoOptions";
// TODO: Make TS recognize .d.ts files, in addition to loading them with webpack.
// @ts-ignore
import React$Types from "../../node_modules/@types/react/index.d.ts";
loader.config({ monaco });
type Props = {
errors: CompilerErrorDetail[];
};
export default function Input({ errors }: Props) {
const [monaco, setMonaco] = useState<Monaco | null>(null);
const store = useStore();
const dispatchStore = useStoreDispatch();
// Set tab width to 2 spaces for the selected input file.
useEffect(() => {
if (!monaco) return;
const uri = monaco.Uri.parse(`file:///index.js`);
const model = monaco.editor.getModel(uri);
invariant(model, "Model must exist for the selected input file.");
renderReactCompilerMarkers({ monaco, model, details: errors });
// N.B. that `tabSize` is a model property, not an editor property.
// So, the tab size has to be set per model.
model.updateOptions({ tabSize: 2 });
}, [monaco, errors]);
const handleChange = (value: string | undefined) => {
if (!value) return;
dispatchStore({
type: "updateFile",
payload: {
source: value,
},
});
};
const handleMount = (_: editor.IStandaloneCodeEditor, monaco: Monaco) => {
setMonaco(monaco);
// Ignore "can only be used in TypeScript files." errors, since
// we want to support syntax highlighting for Flow (*.js) files
// and Flow is not a built-in language.
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
diagnosticCodesToIgnore: [
8002, 8003, 8004, 8005, 8006, 8008, 8009, 8010, 8011, 8012, 8013,
],
noSemanticValidation: true,
noSyntaxValidation: false,
});
const tscOptions = {
allowNonTsExtensions: true,
target: monaco.languages.typescript.ScriptTarget.ES2015,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
jsx: monaco.languages.typescript.JsxEmit.Preserve,
typeRoots: ["node_modules/@types"],
allowSyntheticDefaultImports: true,
};
monaco.languages.typescript.javascriptDefaults.setCompilerOptions(
tscOptions
);
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
...tscOptions,
checkJs: true,
allowJs: true,
});
// Add React type declarations to Monaco
const reactLib = [
React$Types,
"file:///node_modules/@types/react/index.d.ts",
] as [any, string];
monaco.languages.typescript.javascriptDefaults.addExtraLib(...reactLib);
monaco.languages.typescript.typescriptDefaults.addExtraLib(...reactLib);
// Remeasure the font in case the custom font is loaded only after
// Monaco Editor is mounted.
// N.B. that this applies also to the output editor as it seems
// Monaco Editor instances share the same font config.
document.fonts.ready.then(() => {
monaco.editor.remeasureFonts();
});
};
return (
<div className="relative flex flex-col flex-none border-r border-gray-200">
<Resizable
minWidth={650}
enable={{ right: true }}
// Restrict MonacoEditor's height, since the config autoLayout:true
// will grow the editor to fit within parent element
className="!h-[calc(100vh_-_3.5rem)]"
>
<MonacoEditor
path={"index.js"}
// .js and .jsx files are specified to be TS so that Monaco can actually
// check their syntax using its TS language service. They are still JS files
// due to their extensions, so TS language features don't work.
language={"javascript"}
value={store.source}
onMount={handleMount}
onChange={handleChange}
options={monacoOptions}
/>
</Resizable>
</div>
);
}

View File

@@ -1,306 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import generate from "@babel/generator";
import * as t from "@babel/types";
import {
CodeIcon,
DocumentAddIcon,
InformationCircleIcon,
} from "@heroicons/react/outline";
import MonacoEditor, { DiffEditor } from "@monaco-editor/react";
import { type CompilerError } from "babel-plugin-react-compiler/src";
import parserBabel from "prettier/plugins/babel";
import * as prettierPluginEstree from "prettier/plugins/estree";
import * as prettier from "prettier/standalone";
import { memo, useEffect, useState } from "react";
import { type Store } from "../../lib/stores";
import TabbedWindow from "../TabbedWindow";
import { monacoOptions } from "./monacoOptions";
const MemoizedOutput = memo(Output);
export default MemoizedOutput;
export type PrintedCompilerPipelineValue =
| {
kind: "ast";
name: string;
fnName: string | null;
value: t.FunctionDeclaration;
}
| {
kind: "hir";
name: string;
fnName: string | null;
value: string;
}
| { kind: "reactive"; name: string; fnName: string | null; value: string }
| { kind: "debug"; name: string; fnName: string | null; value: string };
export type CompilerOutput =
| { kind: "ok"; results: Map<string, PrintedCompilerPipelineValue[]> }
| {
kind: "err";
results: Map<string, PrintedCompilerPipelineValue[]>;
error: CompilerError;
};
type Props = {
store: Store;
compilerOutput: CompilerOutput;
};
async function tabify(source: string, compilerOutput: CompilerOutput) {
const tabs = new Map<string, React.ReactNode>();
const reorderedTabs = new Map<string, React.ReactNode>();
const concattedResults = new Map<string, string>();
let topLevelFnDecls: Array<t.FunctionDeclaration> = [];
// Concat all top level function declaration results into a single tab for each pass
for (const [passName, results] of compilerOutput.results) {
for (const result of results) {
switch (result.kind) {
case "hir": {
const prev = concattedResults.get(result.name);
const next = result.value;
const identName = `function ${result.fnName}`;
if (prev != null) {
concattedResults.set(passName, `${prev}\n\n${identName}\n${next}`);
} else {
concattedResults.set(passName, `${identName}\n${next}`);
}
break;
}
case "reactive": {
const prev = concattedResults.get(passName);
const next = result.value;
if (prev != null) {
concattedResults.set(passName, `${prev}\n\n${next}`);
} else {
concattedResults.set(passName, next);
}
break;
}
case "ast":
topLevelFnDecls.push(result.value);
break;
case "debug": {
concattedResults.set(passName, result.value);
break;
}
default: {
const _: never = result;
throw new Error("Unexpected result kind");
}
}
}
}
let lastPassOutput: string | null = null;
for (const [passName, text] of concattedResults) {
tabs.set(
passName,
<TextTabContent
output={text}
diff={lastPassOutput ?? null}
showInfoPanel={true}
></TextTabContent>
);
lastPassOutput = text;
}
// Ensure that JS and the JS source map come first
if (topLevelFnDecls.length > 0) {
// Make a synthetic Program so we can have a single AST with all the top level
// FunctionDeclarations
const ast = t.program(topLevelFnDecls);
const { code, sourceMapUrl } = await codegen(ast, source);
reorderedTabs.set(
"JS",
<TextTabContent
output={code}
diff={null}
showInfoPanel={false}
></TextTabContent>
);
if (sourceMapUrl) {
reorderedTabs.set(
"SourceMap",
<>
<iframe
src={sourceMapUrl}
className="w-full h-monaco_small sm:h-monaco"
title="Generated Code"
/>
</>
);
}
}
tabs.forEach((tab, name) => {
reorderedTabs.set(name, tab);
});
return reorderedTabs;
}
async function codegen(
ast: t.Program,
source: string
): Promise<{ code: any; sourceMapUrl: string | null }> {
const generated = generate(
ast,
{ sourceMaps: true, sourceFileName: "input.js" },
source
);
const sourceMapUrl = getSourceMapUrl(
generated.code,
JSON.stringify(generated.map)
);
const codegenOutput = await prettier.format(generated.code, {
semi: true,
parser: "babel",
plugins: [parserBabel, prettierPluginEstree],
});
return { code: codegenOutput, sourceMapUrl };
}
function utf16ToUTF8(s: string): string {
return unescape(encodeURIComponent(s));
}
function getSourceMapUrl(code: string, map: string): string | null {
code = utf16ToUTF8(code);
map = utf16ToUTF8(map);
return `https://evanw.github.io/source-map-visualization/#${btoa(
`${code.length}\0${code}${map.length}\0${map}`
)}`;
}
function Output({ store, compilerOutput }: Props) {
const [tabsOpen, setTabsOpen] = useState<Set<string>>(() => new Set(["JS"]));
const [tabs, setTabs] = useState<Map<string, React.ReactNode>>(
() => new Map()
);
useEffect(() => {
tabify(store.source, compilerOutput).then((tabs) => {
setTabs(tabs);
});
}, [store.source, compilerOutput]);
const changedPasses: Set<string> = new Set();
let lastResult: string = "";
for (const [passName, results] of compilerOutput.results) {
for (const result of results) {
let currResult = "";
if (result.kind === "hir" || result.kind === "reactive") {
currResult += `function ${result.fnName}\n\n${result.value}`;
}
if (currResult !== lastResult) {
changedPasses.add(passName);
}
lastResult = currResult;
}
}
return (
<>
<TabbedWindow
defaultTab="HIR"
setTabsOpen={setTabsOpen}
tabsOpen={tabsOpen}
tabs={tabs}
changedPasses={changedPasses}
/>
{compilerOutput.kind === "err" ? (
<div
className="flex flex-wrap absolute bottom-0 bg-white grow border-y border-grey-200 transition-all ease-in"
style={{ width: "calc(100vw - 650px)" }}
>
<div className="w-full p-4 basis-full border-b">
<h2>COMPILER ERRORS</h2>
</div>
<pre
className="p-4 basis-full text-red-600 overflow-y-scroll whitespace-pre-wrap"
style={{ width: "calc(100vw - 650px)", height: "150px" }}
>
<code>{compilerOutput.error.toString()}</code>
</pre>
</div>
) : null}
</>
);
}
function TextTabContent({
output,
diff,
showInfoPanel,
}: {
output: string;
diff: string | null;
showInfoPanel: boolean;
}) {
const [diffMode, setDiffMode] = useState(false);
return (
// Restrict MonacoEditor's height, since the config autoLayout:true
// will grow the editor to fit within parent element
<div className="w-full h-monaco_small sm:h-monaco">
{showInfoPanel ? (
<div className="flex items-center gap-1 bg-amber-50 p-2">
{diff != null && output !== diff ? (
<button
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
onClick={() => setDiffMode((diffMode) => !diffMode)}
>
{!diffMode ? (
<>
<DocumentAddIcon className="w-5 h-5" /> Show Diff
</>
) : (
<>
<CodeIcon className="w-5 h-5" /> Show Output
</>
)}
</button>
) : (
<>
<span className="flex items-center gap-1">
<InformationCircleIcon className="w-5 h-5" /> No changes from
previous pass
</span>
</>
)}
</div>
) : null}
{diff != null && diffMode ? (
<DiffEditor
original={diff}
modified={output}
options={{
...monacoOptions,
readOnly: true,
lineNumbers: "off",
glyphMargin: false,
// Undocumented see https://github.com/Microsoft/vscode/issues/30795#issuecomment-410998882
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
}}
/>
) : (
<MonacoEditor
defaultLanguage="javascript"
value={output}
options={{
...monacoOptions,
readOnly: true,
lineNumbers: "off",
glyphMargin: false,
// Undocumented see https://github.com/Microsoft/vscode/issues/30795#issuecomment-410998882
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
}}
/>
)}
</div>
);
}

View File

@@ -1,16 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import dynamic from "next/dynamic";
// monaco-editor is currently not compatible with ssr
// https://github.com/vercel/next.js/issues/31692
const Editor = dynamic(() => import("./EditorImpl"), {
ssr: false,
});
export default Editor;

View File

@@ -1,32 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { EditorProps } from "@monaco-editor/react";
export const monacoOptions: Partial<EditorProps["options"]> = {
fontSize: 14,
padding: { top: 8 },
scrollbar: {
verticalScrollbarSize: 10,
alwaysConsumeMouseWheel: false,
},
minimap: {
enabled: false,
},
formatOnPaste: true,
formatOnType: true,
fontFamily: '"Source Code Pro", monospace',
glyphMargin: true,
autoClosingBrackets: "languageDefined",
autoClosingDelete: "always",
autoClosingOvertype: "always",
automaticLayout: true,
wordWrap: "on",
wrappingIndent: "deepIndent",
};

View File

@@ -1,94 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { RefreshIcon, ShareIcon } from "@heroicons/react/outline";
import { CheckIcon } from "@heroicons/react/solid";
import clsx from "clsx";
import Link from "next/link";
import { useSnackbar } from "notistack";
import { useState } from "react";
import { defaultStore } from "../lib/defaultStore";
import { IconGitHub } from "./Icons/IconGitHub";
import Logo from "./Logo";
import { useStoreDispatch } from "./StoreContext";
export default function Header() {
const [showCheck, setShowCheck] = useState(false);
const dispatchStore = useStoreDispatch();
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const handleReset = () => {
if (confirm("Are you sure you want to reset the playground?")) {
/*
Close open snackbars if any. This is necessary because when displaying
outputs (Preview or not), we only close previous snackbars if we received
new messages, which is needed in order to display "Bad URL" or success
messages when loading Playground for the first time. Otherwise, messages
such as "Bad URL" will be closed by the outputs calling `closeSnackbar`.
*/
closeSnackbar();
dispatchStore({ type: "setStore", payload: { store: defaultStore } });
}
};
const handleShare = () => {
navigator.clipboard.writeText(location.href).then(() => {
enqueueSnackbar("URL copied to clipboard");
setShowCheck(true);
// Show the check mark icon briefly after URL is copied
setTimeout(() => setShowCheck(false), 1000);
});
};
return (
<div className="fixed z-10 flex items-center justify-between w-screen px-5 py-3 bg-white border-b border-gray-200 h-14">
<div className="flex items-center flex-none h-full gap-2 text-lg">
<Logo
className={clsx(
"w-8 h-8 text-link",
process.env.NODE_ENV === "development" && "text-yellow-600"
)}
/>
<p className="hidden select-none sm:block">React Compiler Playground</p>
</div>
<div className="flex items-center text-[15px] gap-4">
<button
title="Reset Playground"
aria-label="Reset Playground"
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
onClick={handleReset}
>
<RefreshIcon className="w-5 h-5" />
<p className="hidden sm:block">Reset</p>
</button>
<button
title="Copy sharable URL"
aria-label="Copy sharable URL"
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
onClick={handleShare}
disabled={showCheck}
>
{!showCheck ? (
<ShareIcon className="w-5 h-5" />
) : (
<CheckIcon className="w-5 h-5 fill-blue-50" />
)}
<p className="hidden sm:block">Share</p>
</button>
<Link
href="https://github.com/facebook/react"
target="_blank"
rel="noreferrer noopener"
aria-label="Open on GitHub"
className="flex items-center gap-1 transition-colors duration-150 ease-in text-secondary hover:text-link"
>
<IconGitHub />
</Link>
</div>
</div>
);
}

View File

@@ -1,25 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { memo } from "react";
export const IconGitHub = memo<JSX.IntrinsicElements["svg"]>(
function IconGitHub(props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.5em"
height="1.5em"
viewBox="0 -2 24 24"
fill="currentColor"
{...props}
>
<path d="M10 0a10 10 0 0 0-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 0 1 .1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 0 1 5 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0 0 10 0"></path>
</svg>
);
}
);

View File

@@ -1,28 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// https://github.com/reactjs/reactjs.org/blob/main/beta/src/components/Logo.tsx
export default function Logo(props: JSX.IntrinsicElements["svg"]) {
return (
<svg
viewBox="0 0 410 369"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M204.995 224.552C226.56 224.552 244.042 207.07 244.042 185.506C244.042 163.941 226.56 146.459 204.995 146.459C183.43 146.459 165.948 163.941 165.948 185.506C165.948 207.07 183.43 224.552 204.995 224.552Z"
fill="currentColor"
/>
<path
d="M409.99 184.505C409.99 153.707 381.437 126.667 335.996 108.925C343.342 60.6535 334.19 22.3878 307.492 6.98883C283.649 -6.77511 250.631 -0.0395641 214.512 25.9753C211.316 28.2692 208.143 30.7097 204.97 33.2477C201.822 30.7097 198.65 28.2692 195.477 25.9753C159.359 -0.0395641 126.34 -6.79951 102.497 6.98883C75.8237 22.3878 66.6721 60.6291 74.0422 108.852C28.5529 126.618 0 153.682 0 184.505C0 215.303 28.5528 242.342 73.9934 260.084C66.6477 308.356 75.7993 346.621 102.497 362.02C110.575 366.682 119.727 369 129.684 369C149.085 369 171.61 360.215 195.477 343.034C198.674 340.74 201.847 338.3 205.019 335.762C208.167 338.3 211.34 340.74 214.512 343.034C238.38 360.239 260.905 369 280.306 369C290.263 369 299.415 366.682 307.492 362.02C331.335 348.256 342 316.287 337.534 271.993C337.143 268.089 336.631 264.135 335.996 260.109C381.461 242.367 409.99 215.327 409.99 184.505ZM225.934 41.8136C246.238 27.1955 265.127 19.5814 280.306 19.5814C286.871 19.5814 292.728 20.9968 297.731 23.8765C315.204 33.9798 322.672 62.9475 317.327 102.433C299.756 97.0401 280.306 92.9158 259.392 90.2802C246.872 73.8074 233.597 58.9453 220.003 46.2551C221.98 44.7421 223.957 43.229 225.934 41.8136ZM112.259 23.8765C117.262 20.9968 123.119 19.5814 129.684 19.5814C144.863 19.5814 163.752 27.1711 184.056 41.8136C186.033 43.229 188.01 44.7176 189.986 46.2551C176.393 58.9453 163.142 73.783 150.622 90.2558C129.732 92.8914 110.258 97.0401 92.687 102.409C87.3424 62.9475 94.7857 33.9798 112.259 23.8765ZM19.5233 184.505C19.5233 164.322 40.9014 143.359 77.776 128.253C81.9003 146.141 88.0502 165.054 96.1768 184.456C88.0014 203.881 81.8515 222.819 77.7272 240.732C40.9014 225.626 19.5233 204.687 19.5233 184.505ZM184.056 327.196C154.966 348.134 128.805 354.675 112.259 345.133C94.7857 335.029 87.3181 306.062 92.6626 266.576C110.234 271.969 129.684 276.093 150.598 278.729C163.117 295.202 176.393 310.064 189.986 322.754C188.01 324.292 186.033 325.78 184.056 327.196ZM204.995 310.04C180.591 287.685 157.138 257.815 137.347 223.551C132.051 214.4 121.344 191.396 117 182.489C113.535 190.786 110.112 198.398 107.427 206.5C109.623 210.575 118.092 229.213 120.434 233.288C125.071 241.317 129.928 249.127 134.931 256.692C120.898 254.227 107.915 251.055 96.1035 247.321C102.815 217.011 116.213 182.064 137.347 145.458C142.545 136.453 153.838 116.346 159.5 108C150.568 109.147 143.395 108.767 135 110.5C132.56 114.453 122.777 131.645 120.434 135.721C115.749 143.823 111.454 151.925 107.427 159.978C102.546 146.581 98.8124 133.744 96.1524 121.64C125.755 112.293 162.727 106.411 204.995 106.411C215.562 106.411 237.63 106.197 247.49 106.905C242.048 99.7544 237.38 93.2819 231.694 86.888C227.082 86.7416 209.705 86.888 204.995 86.888C195.672 86.888 186.545 87.2053 177.589 87.7422C186.472 77.1752 195.672 67.5111 204.995 58.9697C229.375 81.3239 252.851 111.195 272.643 145.458C277.841 154.463 289.073 175.426 293.49 184.505C296.98 176.207 300.281 168.64 302.99 160.489C300.793 156.389 291.898 139.747 289.555 135.696C284.918 127.667 280.062 119.858 275.059 112.317C289.092 114.782 302.075 117.954 313.886 121.688C307.175 151.998 293.777 186.945 272.643 223.551C267.445 232.556 252.651 253.178 246.99 261.524C255.922 260.377 265.595 258.663 273.99 256.93C276.43 252.976 287.212 237.364 289.555 233.288C294.216 225.235 298.512 217.182 302.489 209.153C307.224 222.185 310.982 234.997 313.715 247.394C284.138 256.741 247.214 262.598 204.995 262.598C194.428 262.598 169.859 261.208 160 260.5C165.442 267.65 171.304 275.095 176.99 281.489C181.602 281.635 200.285 282.121 204.995 282.121C214.317 282.121 223.444 281.804 232.401 281.267C223.493 291.834 214.317 301.498 204.995 310.04ZM297.731 345.133C281.185 354.699 254.999 348.159 225.934 327.196C223.957 325.78 221.98 324.292 220.003 322.754C233.597 310.064 246.848 295.226 259.367 278.753C280.233 276.118 299.659 271.993 317.205 266.625C317.547 269.089 317.888 271.554 318.132 273.97C321.72 309.649 314.277 335.566 297.731 345.133ZM332.262 240.756C328.065 222.599 321.842 203.686 313.813 184.578C321.988 165.152 328.138 146.215 332.262 128.302C369.088 143.408 390.466 164.322 390.466 184.505C390.466 204.687 369.113 225.626 332.262 240.756Z"
fill="currentColor"
/>
</svg>
);
}

View File

@@ -1,84 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {
BanIcon,
ExclamationIcon,
InformationCircleIcon,
XIcon,
} from "@heroicons/react/solid";
import { CustomContentProps, SnackbarContent, useSnackbar } from "notistack";
import { forwardRef } from "react";
import { MessageLevel, MessageSource } from "../lib/stores";
// https://notistack.com/examples/advanced/custom-component#custom-variant-(typescript)
declare module "notistack" {
interface VariantOverrides {
message: {
title: string;
level: MessageLevel;
codeframe: string | undefined;
};
}
}
interface MessageProps extends CustomContentProps {
title: string;
level: MessageLevel;
source: MessageSource;
codeframe: string | undefined;
}
const Message = forwardRef<HTMLDivElement, MessageProps>(
({ id, title, level, source, codeframe }, ref) => {
const { closeSnackbar } = useSnackbar();
const isDismissible = source !== MessageSource.Playground;
return (
<SnackbarContent
ref={ref}
className="flex items-start justify-between gap-3 px-4 py-3 text-sm bg-white border rounded-md shadow w-toast"
>
<div className="flex gap-3 w-toast-body">
{level === MessageLevel.Warning ? (
<div className="flex items-center justify-center flex-none rounded-md w-7 h-7 bg-amber-100">
<ExclamationIcon className="w-5 h-5 text-amber-600" />
</div>
) : level === MessageLevel.Error ? (
<div className="flex items-center justify-center flex-none bg-red-100 rounded-md w-7 h-7">
<BanIcon className="w-5 h-5 text-red-600" />
</div>
) : (
<div className="flex items-center justify-center flex-none rounded-md bg-sky-100 w-7 h-7">
<InformationCircleIcon className="w-5 h-5 text-sky-600" />
</div>
)}
<div className="flex flex-col justify-center gap-1 w-toast-title">
<p className="w-full">{title}</p>
{codeframe ? (
<pre className="overflow-x-auto break-words whitespace-pre-wrap">
<code className="text-xs">{codeframe}</code>
</pre>
) : null}
</div>
</div>
{isDismissible ? (
<button
className="flex items-center justify-center flex-none transition-colors duration-150 ease-in rounded-md justify-self-end group w-7 h-7 hover:bg-gray-200"
onClick={() => closeSnackbar(id)}
>
<XIcon className="w-5 h-5 fill-gray-500 group-hover:fill-gray-800" />
</button>
) : null}
</SnackbarContent>
);
}
);
Message.displayName = "MessageComponent";
export default Message;

View File

@@ -1,78 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { Dispatch, ReactNode } from "react";
import { useReducer } from "react";
import createContext from "../lib/createContext";
import { emptyStore } from "../lib/defaultStore";
import type { Store } from "../lib/stores";
import { saveStore } from "../lib/stores";
const StoreContext = createContext<Store>();
/**
* Hook to access the store.
*/
export const useStore = StoreContext.useContext;
const StoreDispatchContext = createContext<Dispatch<ReducerAction>>();
/**
* Hook to access the store dispatch function.
*/
export const useStoreDispatch = StoreDispatchContext.useContext;
/**
* Make Store and dispatch function available to all sub-components in children.
*/
export function StoreProvider({ children }: { children: ReactNode }) {
const [store, dispatch] = useReducer(storeReducer, emptyStore);
return (
<StoreContext.Provider value={store}>
<StoreDispatchContext.Provider value={dispatch}>
{children}
</StoreDispatchContext.Provider>
</StoreContext.Provider>
);
}
type ReducerAction =
| {
type: "setStore";
payload: {
store: Store;
};
}
| {
type: "updateFile";
payload: {
source: string;
};
};
function storeReducer(store: Store, action: ReducerAction): Store {
switch (action.type) {
case "setStore": {
const newStore = action.payload.store;
saveStore(newStore);
return newStore;
}
case "updateFile": {
const { source } = action.payload;
const newStore = {
...store,
source,
};
saveStore(newStore);
return newStore;
}
}
}

View File

@@ -1,102 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { Resizable } from "re-resizable";
import React, { useCallback } from "react";
type TabsRecord = Map<string, React.ReactNode>;
export default function TabbedWindow(props: {
defaultTab: string | null;
tabs: TabsRecord;
tabsOpen: Set<string>;
setTabsOpen: (newTab: Set<string>) => void;
changedPasses: Set<string>;
}): React.ReactElement {
if (props.tabs.size === 0) {
return (
<div
className="flex items-center justify-center"
style={{ width: "calc(100vw - 650px)" }}
>
No compiler output detected, see errors below
</div>
);
}
return (
<div className="flex flex-row">
{Array.from(props.tabs.keys()).map((name) => {
return (
<TabbedWindowItem
name={name}
key={name}
tabs={props.tabs}
tabsOpen={props.tabsOpen}
setTabsOpen={props.setTabsOpen}
hasChanged={props.changedPasses.has(name)}
/>
);
})}
</div>
);
}
function TabbedWindowItem({
name,
tabs,
tabsOpen,
setTabsOpen,
hasChanged,
}: {
name: string;
tabs: TabsRecord;
tabsOpen: Set<string>;
setTabsOpen: (newTab: Set<string>) => void;
hasChanged: boolean;
}): React.ReactElement {
const isShow = tabsOpen.has(name);
const toggleTabs = useCallback(() => {
const nextState = new Set(tabsOpen);
if (nextState.has(name)) {
nextState.delete(name);
} else {
nextState.add(name);
}
setTabsOpen(nextState);
}, [tabsOpen, name, setTabsOpen]);
return (
<div key={name} className="flex flex-row">
{isShow ? (
<Resizable className="border-r" minWidth={550} enable={{ right: true }}>
<h2
title="Minimize tab"
aria-label="Minimize tab"
onClick={toggleTabs}
className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${hasChanged ? "font-bold" : "font-light"} text-secondary hover:text-link`}
>
- {name}
</h2>
{tabs.get(name) ?? <div>No output for {name}</div>}
</Resizable>
) : (
<div className="relative items-center h-full px-1 py-6 align-middle border-r border-grey-200">
<button
title={`Expand compiler tab: ${name}`}
aria-label={`Expand compiler tab: ${name}`}
style={{ transform: "rotate(90deg) translate(-50%)" }}
onClick={toggleTabs}
className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${hasChanged ? "font-bold" : "font-light"} text-secondary hover:text-link`}
>
{name}
</button>
</div>
)}
</div>
);
}

View File

@@ -1,10 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export { default as Editor } from "./Editor";
export { default as Header } from "./Header";
export { StoreProvider } from "./StoreContext";

View File

@@ -1,8 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export { default as useMountEffect } from "./useMountEffect";

View File

@@ -1,13 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { EffectCallback } from "react";
import { useEffect } from "react";
export default function useMountEffect(effect: EffectCallback) {
return useEffect(effect, []);
}

View File

@@ -1,37 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from "react";
/**
* Replacement to React.createContext.
*
* Does not take any default value and avoids non-null assertions when using
* the value of the context, like the following scenario.
*
* ```ts
* const StoreDispatchContext = useContext<Dispatch<ReducerAction>>(null);
* const dispatchStore = useContext(StoreDispatchContext);
* ...
* dipatchStore!({ ... });
* ```
*
* Instead, it throws an error when `useContext` is not called within a
* Provider with a value.
*/
export default function createContext<T>() {
const context = React.createContext<T | null>(null);
function useContext() {
const c = React.useContext(context);
if (!c)
throw new Error("useContext must be within a Provider with a value");
return c;
}
return { useContext, Provider: context.Provider };
}

View File

@@ -1,22 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { Store } from "./stores";
const index = `\
export default function MyApp() {
return <div>Hello World</div>;
}
`;
export const defaultStore: Store = {
source: index,
};
export const emptyStore: Store = {
source: "",
};

View File

@@ -1,89 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { Monaco } from "@monaco-editor/react";
import {
CompilerErrorDetail,
ErrorSeverity,
} from "babel-plugin-react-compiler/src";
import { MarkerSeverity, type editor } from "monaco-editor";
function mapReactCompilerSeverityToMonaco(
level: ErrorSeverity,
monaco: Monaco
): MarkerSeverity {
switch (level) {
case ErrorSeverity.Todo:
return monaco.MarkerSeverity.Warning;
default:
return monaco.MarkerSeverity.Error;
}
}
function mapReactCompilerDiagnosticToMonacoMarker(
detail: CompilerErrorDetail,
monaco: Monaco
): editor.IMarkerData | null {
if (detail.loc == null || typeof detail.loc === "symbol") {
return null;
}
const severity = mapReactCompilerSeverityToMonaco(detail.severity, monaco);
let message = detail.printErrorMessage();
return {
severity,
message,
startLineNumber: detail.loc.start.line,
startColumn: detail.loc.start.column + 1,
endLineNumber: detail.loc.end.line,
endColumn: detail.loc.end.column + 1,
};
}
type ReactCompilerMarkerConfig = {
monaco: Monaco;
model: editor.ITextModel;
details: CompilerErrorDetail[];
};
let decorations: string[] = [];
export function renderReactCompilerMarkers({
monaco,
model,
details,
}: ReactCompilerMarkerConfig): void {
let markers = [];
for (const detail of details) {
const marker = mapReactCompilerDiagnosticToMonacoMarker(detail, monaco);
if (marker == null) {
continue;
}
markers.push(marker);
}
if (markers.length > 0) {
monaco.editor.setModelMarkers(model, "owner", markers);
const newDecorations = markers.map((marker) => {
return {
range: new monaco.Range(
marker.startLineNumber,
marker.startColumn,
marker.endLineNumber,
marker.endColumn
),
options: {
isWholeLine: true,
glyphMarginClassName: "bg-red-300",
},
};
});
decorations = model.deltaDecorations(decorations, newDecorations);
} else {
monaco.editor.setModelMarkers(model, "owner", []);
decorations = model.deltaDecorations(
model.getAllDecorations().map((d) => d.id),
[]
);
}
}

View File

@@ -1,9 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export * from "./messages";
export * from "./store";

View File

@@ -1,41 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export enum MessageSource {
Babel,
Forget,
Playground,
}
export enum MessageLevel {
Info,
Warning,
Error,
}
export interface Message {
title: string;
level: MessageLevel;
source: MessageSource; // Can be used to further style messages differently.
codeframe: string | undefined;
}
export function createMessage(
message: string,
level: MessageLevel,
source: MessageSource
): Message {
const [title, ...body] = message.split("\n");
const codeframe = body.length > 0 ? body.join("\n") : undefined;
return {
source,
level,
title,
codeframe,
};
}

View File

@@ -1,67 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import {
compressToEncodedURIComponent,
decompressFromEncodedURIComponent,
} from "lz-string";
import { defaultStore } from "../defaultStore";
/**
* Global Store for Playground
*/
export interface Store {
source: string;
}
export function encodeStore(store: Store): string {
return compressToEncodedURIComponent(JSON.stringify(store));
}
export function decodeStore(hash: string): Store {
return JSON.parse(decompressFromEncodedURIComponent(hash));
}
/**
* Serialize, encode, and save @param store to localStorage and update URL.
*/
export function saveStore(store: Store) {
const hash = encodeStore(store);
localStorage.setItem("playgroundStore", hash);
history.replaceState({}, "", `#${hash}`);
}
/**
* Check if @param raw is a valid Store by if
* - it has a `source` property and is a string
*/
function isValidStore(raw: unknown): raw is Store {
return (
raw != null &&
typeof raw == "object" &&
"source" in raw &&
typeof raw["source"] === "string"
);
}
/**
* Deserialize, decode, and initialize @param store from URL and then
* localStorage. Throw an error if Store is malformed.
*/
export function initStoreFromUrlOrLocalStorage(): Store {
const encodedSourceFromUrl = location.hash.slice(1);
const encodedSourceFromLocal = localStorage.getItem("playgroundStore");
const encodedSource = encodedSourceFromUrl || encodedSourceFromLocal;
// No data in the URL and no data in the localStorage to fallback to.
// Initialize with the default store.
if (!encodedSource) return defaultStore;
const raw = decodeStore(encodedSource);
invariant(isValidStore(raw), "Invalid Store");
return raw;
}

View File

@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,44 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
const path = require("path");
const nextConfig = {
reactStrictMode: true,
webpack: (config, options) => {
// Load *.d.ts files as strings using https://webpack.js.org/guides/asset-modules/#source-assets.
config.module.rules.push({
test: /\.d\.ts/,
type: "asset/source",
});
// Monaco Editor
if (!options.isServer) {
config.plugins.push(
new MonacoWebpackPlugin({
languages: ["typescript", "javascript"],
filename: "static/[name].worker.js",
})
);
}
config.resolve.alias = {
...config.resolve.alias,
"react-compiler-runtime": path.resolve(
__dirname,
"../../packages/react-compiler-runtime"
),
};
return config;
},
transpilePackages: ["monaco-editor"],
};
module.exports = nextConfig;

View File

@@ -1,61 +0,0 @@
{
"name": "playground",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "NODE_ENV=development next dev",
"build": "next build && node ./scripts/downloadFonts.js",
"vercel-build": "yarn workspaces run build",
"start": "next start",
"lint": "next lint",
"postinstall": "yarn playwright install",
"test": "playwright test"
},
"dependencies": {
"@babel/core": "^7.19.1",
"@babel/parser": "^7.19.1",
"@babel/plugin-syntax-typescript": "^7.18.6",
"@babel/plugin-transform-block-scoping": "^7.18.9",
"@babel/plugin-transform-modules-commonjs": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@babel/traverse": "^7.19.1",
"@babel/types": "^7.19.0",
"@heroicons/react": "^1.0.6",
"@monaco-editor/react": "^4.4.6",
"@playwright/test": "^1.42.1",
"@use-gesture/react": "^10.2.22",
"hermes-eslint": "^0.14.0",
"invariant": "^2.2.4",
"lz-string": "^1.5.0",
"monaco-editor": "^0.34.1",
"next": "^13.5.6",
"notistack": "^3.0.0-alpha.7",
"prettier": "3.0.3",
"pretty-format": "^29.3.1",
"re-resizable": "^6.9.16",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-compiler-runtime": "*"
},
"devDependencies": {
"@types/node": "18.11.9",
"@types/prettier": "^2.7.1",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"autoprefixer": "^10.4.13",
"clsx": "^1.2.1",
"eslint": "^8.28.0",
"eslint-config-next": "^13.5.6",
"monaco-editor-webpack-plugin": "^7.1.0",
"postcss": "^8.4.31",
"tailwindcss": "^3.2.4"
},
"resolutions": {
"./**/@babel/parser": "7.7.4",
"./**/@babel/types": "7.7.4",
"@babel/core": "7.2.0",
"@babel/traverse": "7.1.6",
"@babel/generator": "7.2.0"
}
}

View File

@@ -1,84 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { defineConfig, devices } from "@playwright/test";
import path from "path";
// Use process.env.PORT by default and fallback to port 3000
const PORT = process.env.PORT || 3000;
// Set webServer.url and use.baseURL with the location of the WebServer respecting the correct set port
const baseURL = `http://localhost:${PORT}`;
// Reference: https://playwright.dev/docs/test-configuration
export default defineConfig({
// Timeout per test
timeout: 30 * 1000,
// Test directory
testDir: path.join(__dirname, "__tests__/e2e"),
// If a test fails, retry it additional 2 times
retries: 2,
// Artifacts folder where screenshots, videos, and traces are stored.
outputDir: "test-results/",
// Note: we only use text snapshots, so its safe to omit the host environment name
snapshotPathTemplate: "{testDir}/__snapshots__/{testFilePath}/{arg}{ext}",
// Run your local dev server before starting the tests:
// https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests
webServer: {
command:
"yarn workspace babel-plugin-react-compiler build && yarn workspace react-compiler-runtime build && yarn dev",
url: baseURL,
timeout: 300 * 1000,
reuseExistingServer: !process.env.CI,
},
use: {
// Use baseURL so to make navigations relative.
// More information: https://playwright.dev/docs/api/class-testoptions#test-options-base-url
baseURL,
// Retry a test if its failing with enabled tracing. This allows you to analyze the DOM, console logs, network traffic etc.
// More information: https://playwright.dev/docs/trace-viewer
trace: "retry-with-trace",
// All available context options: https://playwright.dev/docs/api/class-browser#browser-new-context
// contextOptions: {
// ignoreHTTPSErrors: true,
// },
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
// {
// name: 'Desktop Firefox',
// use: {
// ...devices['Desktop Firefox'],
// },
// },
// {
// name: 'Desktop Safari',
// use: {
// ...devices['Desktop Safari'],
// },
// },
// Test against mobile viewports.
// {
// name: "Mobile Chrome",
// use: {
// ...devices["Pixel 5"],
// },
// },
// {
// name: "Mobile Safari",
// use: devices["iPhone 12"],
// },
],
});

View File

@@ -1,13 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -1,16 +0,0 @@
{
"name": "React Compiler Playground",
"short_name": "React REPL",
"display": "standalone",
"start_url": "/",
"theme_color": "#fff",
"background_color": "#000000",
"orientation": "portrait",
"icons": [
{
"src": "/icon-180x180.png",
"type": "image/png",
"sizes": "180x180"
}
]
}

View File

@@ -1,4 +0,0 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,20 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const { execSync } = require("child_process");
// So that we don't need to check them into the repo.
// See https://github.com/reactjs/reactjs.org/blob/main/beta/scripts/downloadFonts.js.
execSync(
"curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Lt.woff2 --output public/fonts/Optimistic_Display_W_Lt.woff2"
);
execSync(
"curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Md.woff2 --output public/fonts/Optimistic_Display_W_Md.woff2"
);
execSync(
"curl https://conf.reactjs.org/fonts/Optimistic_Display_W_Bd.woff2 --output public/fonts/Optimistic_Display_W_Bd.woff2"
);

View File

@@ -1,71 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
@font-face {
font-family: "Source Code Pro";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/Source-Code-Pro-Regular.woff2") format("woff2");
}
@font-face {
font-family: "Optimistic Display";
src: url("/fonts/Optimistic_Display_W_Lt.woff2") format("woff2");
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Optimistic Display";
src: url("/fonts/Optimistic_Display_W_Md.woff2") format("woff2");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Optimistic Display";
src: url("/fonts/Optimistic_Display_W_Bd.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
html,
body {
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
}
@layer utilities {
/* Chrome, Safari, Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none; /* IE, Edge */
scrollbar-width: none; /* Firefox */
}
}

View File

@@ -1,41 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const defaultTheme = require("tailwindcss/defaultTheme");
const colors = require("./colors");
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
"./lib/forgetMonacoDiagnostics.ts",
],
theme: {
extend: {
colors,
width: {
toast: "min(900px, 100vw - 40px)",
"toast-body": "calc(100% - 60px)",
"toast-title": "calc(100% - 40px)",
},
height: {
content: "calc(100vh - 45px)",
monaco: "calc(100vh - 93px)",
monaco_small: "calc(100vh - 129px)",
},
fontFamily: {
sans: [
"Optimistic Display",
"-apple-system",
...defaultTheme.fontFamily.sans,
],
},
},
},
plugins: [],
};

View File

@@ -1,36 +0,0 @@
{
"compilerOptions": {
"target": "ES2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,13 +0,0 @@
{
"headers": [
{
"source": "/fonts/(.*).woff2",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}

View File

@@ -1,21 +0,0 @@
[package]
name = "react_build_hir"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
react_hir = { workspace = true }
react_estree = { workspace = true}
indexmap = { workspace = true }
react_diagnostics = { workspace = true }
react_semantic_analysis = { workspace = true }
thiserror = { workspace = true }

View File

@@ -1,3 +0,0 @@
# Build-HIR
This crate converts from `react_estree` into React Compiler's HIR format as the first phase of compilation.

View File

@@ -1,746 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
use std::collections::HashSet;
use react_diagnostics::Diagnostic;
use react_estree::{
AssignmentPropertyOrRestElement, AssignmentTarget, BlockStatement, Expression,
ExpressionOrSpread, ExpressionOrSuper, ForInit, Function, IntoFunction, JsValue, Pattern,
Statement, VariableDeclaration, VariableDeclarationKind,
};
use react_hir::{
ArrayDestructureItem, BlockKind, BranchTerminal, Destructure, DestructurePattern, Environment,
ForTerminal, GotoKind, Identifier, IdentifierOperand, InstructionKind, InstructionValue,
JSXAttribute, JSXElement, LValue, LoadGlobal, LoadLocal, ObjectDestructureItem,
ObjectDestructureProperty, PlaceOrSpread, TerminalValue,
};
use crate::builder::{Builder, LoopScope};
use crate::context::get_context_identifiers;
use crate::error::BuildHIRError;
/// Converts a React function in ESTree format into HIR. Returns the HIR
/// if it was constructed sucessfully, otherwise a list of diagnostics
/// if the input could be not be converted to HIR.
///
/// Failures generally include nonsensical input (`delete 1`) or syntax
/// that is not yet supported.
pub fn build(env: &Environment, fun: &Function) -> Result<Box<react_hir::Function>, Diagnostic> {
let mut builder = Builder::new(env);
let mut params = Vec::with_capacity(fun.params.len());
for param in &fun.params {
match param {
Pattern::Identifier(param) => {
let identifier = lower_identifier_for_assignment(
env,
&mut builder,
InstructionKind::Let,
param,
)?;
params.push(identifier);
}
_ => {
return Err(Diagnostic::todo(
"Support non-identifier params",
param.range(),
));
}
}
}
match &fun.body {
Some(react_estree::FunctionBody::BlockStatement(body)) => {
lower_block_statement(env, &mut builder, body)?
}
Some(react_estree::FunctionBody::Expression(body)) => {
lower_expression(env, &mut builder, body)?;
}
None => {
return Err(Diagnostic::invalid_syntax(
BuildHIRError::EmptyFunction,
fun.range,
));
}
}
// In case the function did not explicitly return, terminate the final
// block with an explicit `return undefined`. If the function *did* return,
// this will be unreachable and get pruned later.
let implicit_return_value = builder.push(InstructionValue::Primitive(react_hir::Primitive {
value: JsValue::Undefined,
}));
builder.terminate(
TerminalValue::Return(react_hir::ReturnTerminal {
value: implicit_return_value,
}),
react_hir::BlockKind::Block,
);
let body = builder.build()?;
Ok(Box::new(react_hir::Function {
id: fun.id.as_ref().map(|id| id.name.clone()),
body,
params,
// TODO: populate context!
context: Default::default(),
is_async: fun.is_async,
is_generator: fun.is_generator,
}))
}
fn lower_block_statement(
env: &Environment,
builder: &mut Builder,
stmt: &BlockStatement,
) -> Result<(), Diagnostic> {
for stmt in &stmt.body {
lower_statement(env, builder, stmt, None)?;
}
Ok(())
}
/// Convert a statement to HIR. This will often result in multiple instructions and blocks
/// being created as statements often describe control flow.
fn lower_statement(
env: &Environment,
builder: &mut Builder,
stmt: &Statement,
label: Option<String>,
) -> Result<(), Diagnostic> {
match stmt {
Statement::BlockStatement(stmt) => {
lower_block_statement(env, builder, stmt)?;
}
Statement::BreakStatement(stmt) => {
let block = builder.resolve_break(stmt.label.as_ref())?;
builder.terminate(
TerminalValue::Goto(react_hir::GotoTerminal {
block,
kind: GotoKind::Break,
}),
BlockKind::Block,
);
}
Statement::ContinueStatement(stmt) => {
let block = builder.resolve_continue(stmt.label.as_ref())?;
builder.terminate(
TerminalValue::Goto(react_hir::GotoTerminal {
block,
kind: GotoKind::Continue,
}),
BlockKind::Block,
);
}
Statement::ReturnStatement(stmt) => {
let value = match &stmt.argument {
Some(argument) => lower_expression(env, builder, argument)?,
None => builder.push(InstructionValue::Primitive(react_hir::Primitive {
value: JsValue::Undefined,
})),
};
builder.terminate(
TerminalValue::Return(react_hir::ReturnTerminal { value }),
BlockKind::Block,
);
}
Statement::ExpressionStatement(stmt) => {
lower_expression(env, builder, &stmt.expression)?;
}
Statement::EmptyStatement(_) => {
// no-op
}
Statement::VariableDeclaration(stmt) => {
lower_variable_declaration(env, builder, stmt)?;
}
Statement::IfStatement(stmt) => {
// block for what follows the if statement, though this may
// not be reachable
let fallthrough_block = builder.reserve(BlockKind::Block);
let consequent_block = builder.enter(BlockKind::Block, |builder| {
lower_statement(env, builder, &stmt.consequent, None)?;
Ok(TerminalValue::Goto(react_hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
}))
})?;
let alternate_block = builder.enter(BlockKind::Block, |builder| {
if let Some(alternate) = &stmt.alternate {
lower_statement(env, builder, alternate, None)?;
}
Ok(TerminalValue::Goto(react_hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
}))
})?;
let test = lower_expression(env, builder, &stmt.test)?;
let terminal = TerminalValue::If(react_hir::IfTerminal {
test,
consequent: consequent_block,
alternate: alternate_block,
fallthrough: Some(fallthrough_block.id),
});
builder.terminate_with_fallthrough(terminal, fallthrough_block);
}
Statement::ForStatement(stmt) => {
// Block for the loop's test condition
let test_block = builder.reserve(BlockKind::Loop);
// Block for code following the loop
let fallthrough_block = builder.reserve(BlockKind::Block);
let init_block = builder.enter(BlockKind::Loop, |builder| {
if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
lower_variable_declaration(env, builder, decl)?;
Ok(TerminalValue::Goto(react_hir::GotoTerminal {
block: test_block.id,
kind: GotoKind::Break,
}))
} else {
Err(Diagnostic::todo(
BuildHIRError::ForStatementIsMissingInitializer,
None,
))
}
})?;
let update_block = stmt
.update
.as_ref()
.map(|update| {
builder.enter(BlockKind::Loop, |builder| {
lower_expression(env, builder, update)?;
Ok(TerminalValue::Goto(react_hir::GotoTerminal {
block: test_block.id,
kind: GotoKind::Break,
}))
})
})
.transpose()?;
let body_block = builder.enter(BlockKind::Block, |builder| {
let loop_ = LoopScope {
label,
continue_block: update_block.unwrap_or(test_block.id),
break_block: fallthrough_block.id,
};
builder.enter_loop(loop_, |builder| {
lower_statement(env, builder, &stmt.body, None)?;
Ok(TerminalValue::Goto(react_hir::GotoTerminal {
block: update_block.unwrap_or(test_block.id),
kind: GotoKind::Continue,
}))
})
})?;
let terminal = TerminalValue::For(ForTerminal {
body: body_block,
init: init_block,
test: test_block.id,
fallthrough: fallthrough_block.id,
update: update_block,
});
builder.terminate_with_fallthrough(terminal, test_block);
if let Some(test) = &stmt.test {
let test_value = lower_expression(env, builder, test)?;
let terminal = TerminalValue::Branch(BranchTerminal {
test: test_value,
consequent: body_block,
alternate: fallthrough_block.id,
});
builder.terminate_with_fallthrough(terminal, fallthrough_block);
} else {
return Err(Diagnostic::todo(
BuildHIRError::ForStatementIsMissingTest,
stmt.range,
));
}
}
_ => todo!("Lower {stmt:#?}"),
}
Ok(())
}
fn lower_variable_declaration(
env: &Environment,
builder: &mut Builder,
stmt: &VariableDeclaration,
) -> Result<(), Diagnostic> {
let kind = match stmt.kind {
VariableDeclarationKind::Const => InstructionKind::Const,
VariableDeclarationKind::Let => InstructionKind::Let,
VariableDeclarationKind::Var => {
return Err(Diagnostic::unsupported(
BuildHIRError::VariableDeclarationKindIsVar,
stmt.range,
));
}
};
for declaration in &stmt.declarations {
if let Some(init) = &declaration.init {
let value = lower_expression(env, builder, init)?;
lower_assignment_pattern(env, builder, kind, &declaration.id, value)?;
} else {
match &declaration.id {
Pattern::Identifier(id) => {
let identifier = env.resolve_variable_declaration(id.as_ref(), &id.name);
if let Some(identifier) = identifier {
builder.push(InstructionValue::DeclareLocal(react_hir::DeclareLocal {
lvalue: LValue {
identifier: IdentifierOperand {
identifier,
effect: None,
},
kind,
},
}));
} else {
return Err(Diagnostic::invariant(
BuildHIRError::VariableDeclarationBindingIsNonLocal,
id.range,
));
}
}
_ => {
return Err(Diagnostic::invalid_syntax(
"Expected an identifier for variable declaration without an intializer. Destructuring requires an initial value",
declaration.range,
));
}
}
}
}
Ok(())
}
/// Converts an ESTree Expression into an HIR InstructionValue. Note that while only a single
/// InstructionValue is returned, this function is recursive and may cause multiple instructions
/// to be emitted, possibly across multiple basic blocks (in the case of expressions with control
/// flow semenatics such as logical, conditional, and optional expressions).
fn lower_expression(
env: &Environment,
builder: &mut Builder,
expr: &Expression,
) -> Result<IdentifierOperand, Diagnostic> {
let value = match expr {
Expression::Identifier(expr) => {
let identifier = env.resolve_variable_reference(expr.as_ref());
if let Some(identifier) = identifier {
let place = IdentifierOperand {
effect: None,
identifier,
};
InstructionValue::LoadLocal(LoadLocal { place })
} else {
InstructionValue::LoadGlobal(LoadGlobal {
name: expr.name.clone(),
})
}
}
Expression::Literal(expr) => InstructionValue::Primitive(react_hir::Primitive {
value: expr.value.clone(),
}),
Expression::NumericLiteral(expr) => InstructionValue::Primitive(react_hir::Primitive {
value: JsValue::Number(expr.value),
}),
Expression::BooleanLiteral(expr) => InstructionValue::Primitive(react_hir::Primitive {
value: JsValue::Boolean(expr.value),
}),
Expression::StringLiteral(expr) => InstructionValue::Primitive(react_hir::Primitive {
value: JsValue::String(expr.value.clone()),
}),
Expression::NullLiteral(_expr) => InstructionValue::Primitive(react_hir::Primitive {
value: JsValue::Null,
}),
Expression::ArrayExpression(expr) => {
let mut elements = Vec::with_capacity(expr.elements.len());
for expr in &expr.elements {
let element = match expr {
Some(react_estree::ExpressionOrSpread::SpreadElement(expr)) => Some(
PlaceOrSpread::Spread(lower_expression(env, builder, &expr.argument)?),
),
Some(react_estree::ExpressionOrSpread::Expression(expr)) => {
Some(PlaceOrSpread::Place(lower_expression(env, builder, expr)?))
}
None => None,
};
elements.push(element);
}
InstructionValue::Array(react_hir::Array { elements })
}
Expression::AssignmentExpression(expr) => match expr.operator {
react_estree::AssignmentOperator::Equals => {
let right = lower_expression(env, builder, &expr.right)?;
return lower_assignment(
env,
builder,
InstructionKind::Reassign,
&expr.left,
right,
);
}
_ => todo!("lower assignment expr {:#?}", expr),
},
Expression::BinaryExpression(expr) => {
let left = lower_expression(env, builder, &expr.left)?;
let right = lower_expression(env, builder, &expr.right)?;
InstructionValue::Binary(react_hir::Binary {
left,
operator: expr.operator,
right,
})
}
Expression::FunctionExpression(expr) => {
InstructionValue::Function(lower_function(env, builder, expr.as_ref())?)
}
Expression::ArrowFunctionExpression(expr) => {
InstructionValue::Function(lower_function(env, builder, expr.as_ref())?)
}
Expression::CallExpression(expr) => {
let callee_expr = match &expr.callee {
ExpressionOrSuper::Super(callee) => {
return Err(Diagnostic::unsupported(
BuildHIRError::UnsupportedSuperExpression,
callee.range,
));
}
ExpressionOrSuper::Expression(callee) => callee,
};
if matches!(&callee_expr, Expression::MemberExpression(_)) {
return Err(Diagnostic::todo("Support method calls", expr.range));
}
let callee = lower_expression(env, builder, callee_expr)?;
let arguments = lower_arguments(env, builder, &expr.arguments)?;
InstructionValue::Call(react_hir::Call { callee, arguments })
}
Expression::JSXElement(expr) => {
InstructionValue::JSXElement(lower_jsx_element(env, builder, expr)?)
}
_ => todo!("Lower expr {expr:#?}"),
};
Ok(builder.push(value))
}
fn lower_arguments(
env: &Environment,
builder: &mut Builder,
args: &[ExpressionOrSpread],
) -> Result<Vec<PlaceOrSpread>, Diagnostic> {
let mut arguments = Vec::with_capacity(args.len());
for arg in args {
let element = match arg {
react_estree::ExpressionOrSpread::SpreadElement(arg) => {
PlaceOrSpread::Spread(lower_expression(env, builder, &arg.argument)?)
}
react_estree::ExpressionOrSpread::Expression(arg) => {
PlaceOrSpread::Place(lower_expression(env, builder, arg)?)
}
};
arguments.push(element);
}
Ok(arguments)
}
fn lower_function<T: IntoFunction>(
env: &Environment,
_builder: &mut Builder,
function: &T,
) -> Result<react_hir::FunctionExpression, Diagnostic> {
let context_identifiers = get_context_identifiers(env, function);
let mut context = Vec::new();
let mut seen = HashSet::new();
for declaration_id in context_identifiers {
if let Some(identifier) = env.resolve_declaration_id(declaration_id) {
if !seen.insert(identifier.id) {
continue;
}
context.push(IdentifierOperand {
effect: None,
identifier,
});
}
}
let mut fun = build(env, function.function())?;
fun.context = context;
Ok(react_hir::FunctionExpression {
// TODO: collect dependencies!
dependencies: Default::default(),
lowered_function: fun,
})
}
fn lower_jsx_element(
env: &Environment,
builder: &mut Builder,
expr: &react_estree::JSXElement,
) -> Result<JSXElement, Diagnostic> {
let props: Result<Vec<JSXAttribute>, Diagnostic> = expr
.opening_element
.attributes
.iter()
.map(|attr| lower_jsx_attribute(env, builder, attr))
.collect();
let _props = props?;
let children: Result<Vec<IdentifierOperand>, Diagnostic> = expr
.children
.iter()
.map(|child| {
let child = lower_jsx_child(env, builder, child)?;
Ok(child)
})
.collect();
let _children = children?;
todo!("lower jsx element");
// Ok(JSXElement {
// tag: todo!(),
// props,
// children: if children.is_empty() {
// None
// } else {
// Some(children)
// },
// })
}
fn lower_jsx_attribute(
_env: &Environment,
_builder: &mut Builder,
_attr: &react_estree::JSXAttributeOrSpread,
) -> Result<JSXAttribute, Diagnostic> {
todo!("lower jsx attribute")
}
fn lower_jsx_child(
_env: &Environment,
_builder: &mut Builder,
_child: &react_estree::JSXChildItem,
) -> Result<IdentifierOperand, Diagnostic> {
todo!("lower jsx child")
}
fn lower_assignment(
env: &Environment,
builder: &mut Builder,
kind: InstructionKind,
lvalue: &AssignmentTarget,
value: IdentifierOperand,
) -> Result<IdentifierOperand, Diagnostic> {
Ok(match lvalue {
AssignmentTarget::Pattern(lvalue) => {
lower_assignment_pattern(env, builder, kind, lvalue, value)?
}
_ => todo!("lower assignment for {:#?}", lvalue),
})
}
// TODO: change the success type to void, no caller uses it
fn lower_assignment_pattern(
env: &Environment,
builder: &mut Builder,
kind: InstructionKind,
lvalue: &Pattern,
value: IdentifierOperand,
) -> Result<IdentifierOperand, Diagnostic> {
Ok(match lvalue {
Pattern::Identifier(lvalue) => {
let identifier = lower_identifier_for_assignment(env, builder, kind, lvalue)?;
builder.push(InstructionValue::StoreLocal(react_hir::StoreLocal {
lvalue: LValue { identifier, kind },
value,
}))
}
Pattern::ArrayPattern(lvalue) => {
let mut items = Vec::with_capacity(lvalue.elements.len());
let mut followups: Vec<(Identifier, &Pattern)> = Vec::new();
for element in &lvalue.elements {
match element {
None => items.push(ArrayDestructureItem::Hole),
Some(Pattern::Identifier(element)) => {
let identifier =
lower_identifier_for_assignment(env, builder, kind, element)?;
items.push(ArrayDestructureItem::Value(identifier));
}
Some(Pattern::RestElement(element)) => {
if let Pattern::Identifier(element) = &element.argument {
let identifier = lower_identifier_for_assignment(
env,
builder,
kind,
element.as_ref(),
)?;
items.push(ArrayDestructureItem::Spread(identifier));
} else {
let temporary = env.new_temporary();
items.push(ArrayDestructureItem::Spread(IdentifierOperand {
identifier: temporary.clone(),
effect: None,
}));
followups.push((temporary, &element.argument));
}
}
Some(element) => {
let temporary = env.new_temporary();
items.push(ArrayDestructureItem::Value(IdentifierOperand {
identifier: temporary.clone(),
effect: None,
}));
followups.push((temporary, element));
}
}
}
let temporary = builder.push(InstructionValue::Destructure(Destructure {
kind,
pattern: DestructurePattern::Array(items),
value,
}));
for (temporary, pattern) in followups {
lower_assignment_pattern(
env,
builder,
kind,
pattern,
IdentifierOperand {
identifier: temporary,
effect: None,
},
)?;
}
temporary
}
Pattern::ObjectPattern(lvalue) => {
let mut properties = Vec::with_capacity(lvalue.properties.len());
let mut followups: Vec<(Identifier, &Pattern)> = Vec::new();
for property in &lvalue.properties {
match property {
AssignmentPropertyOrRestElement::RestElement(property) => {
if let Pattern::Identifier(element) = &property.argument {
let identifier = lower_identifier_for_assignment(
env,
builder,
kind,
element.as_ref(),
)?;
properties.push(ObjectDestructureItem::Spread(identifier));
} else {
let temporary = env.new_temporary();
properties.push(ObjectDestructureItem::Spread(IdentifierOperand {
identifier: temporary.clone(),
effect: None,
}));
followups.push((temporary, &property.argument));
}
}
AssignmentPropertyOrRestElement::AssignmentProperty(property) => {
if property.is_computed {
return Err(Diagnostic::todo(
"Handle computed properties in ObjectPattern",
property.range,
));
}
let key = if let Expression::Identifier(key) = &property.key {
key.name.as_str()
} else {
return Err(Diagnostic::todo(
"Support non-identifier object keys in non-computed ObjectPattern",
property.range,
));
};
if let Pattern::Identifier(value) = &property.value {
let value = lower_identifier_for_assignment(env, builder, kind, value)?;
properties.push(ObjectDestructureItem::Property(
ObjectDestructureProperty {
name: key.to_string(),
value,
},
));
} else {
let temporary = env.new_temporary();
properties.push(ObjectDestructureItem::Property(
ObjectDestructureProperty {
name: key.to_string(),
value: IdentifierOperand {
identifier: temporary.clone(),
effect: None,
},
},
));
followups.push((temporary, &property.value));
}
}
}
}
let temporary = builder.push(InstructionValue::Destructure(Destructure {
kind,
pattern: DestructurePattern::Object(properties),
value,
}));
for (temporary, pattern) in followups {
lower_assignment_pattern(
env,
builder,
kind,
pattern,
IdentifierOperand {
identifier: temporary,
effect: None,
},
)?;
}
temporary
}
_ => todo!("lower assignment pattern for {:#?}", lvalue),
})
}
fn lower_identifier_for_assignment(
env: &Environment,
_builder: &mut Builder,
kind: InstructionKind,
node: &react_estree::Identifier,
) -> Result<IdentifierOperand, Diagnostic> {
match kind {
InstructionKind::Reassign => {
let identifier = env.resolve_variable_reference(node);
if let Some(identifier) = identifier {
Ok(IdentifierOperand {
identifier,
effect: None,
})
} else {
// Reassigning a global
Err(
Diagnostic::invalid_react(BuildHIRError::ReassignedGlobal, node.range)
.annotate(format!("Cannot reassign `{}`", &node.name), node.range),
)
}
}
_ => {
// Declaration
let identifier = env.resolve_variable_declaration(node, &node.name).unwrap();
Ok(IdentifierOperand {
identifier,
effect: None,
})
}
}
}

View File

@@ -1,322 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
use std::cell::RefCell;
use std::rc::Rc;
use react_diagnostics::Diagnostic;
use react_hir::{
initialize_hir, BasicBlock, BlockId, BlockKind, Blocks, Environment, GotoKind, IdentifierData,
IdentifierOperand, InstrIx, Instruction, InstructionIdGenerator, InstructionValue, Terminal,
TerminalValue, Type, HIR,
};
use crate::BuildHIRError;
/// Helper struct used when converting from ESTree to HIR. Includes:
/// - Variable resolution
/// - Label resolution (for labeled statements and break/continue)
/// - Access to the environment
///
/// As well as representing the incomplete form of the HIR. Usage
/// generally involves driving calls to enter/exit blocks, resolve
/// labels and variables, and then calling `build()` when the HIR
/// is complete.
pub(crate) struct Builder<'e> {
#[allow(dead_code)]
environment: &'e Environment,
completed: Blocks,
instructions: Vec<Instruction>,
entry: BlockId,
wip: WipBlock,
id_gen: InstructionIdGenerator,
scopes: Vec<ControlFlowScope>,
}
pub(crate) struct WipBlock {
pub id: BlockId,
pub kind: BlockKind,
pub instructions: Vec<InstrIx>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum ControlFlowScope {
Loop(LoopScope),
// Switch(SwitchScope),
#[allow(dead_code)]
Label(LabelScope),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct LoopScope {
pub label: Option<String>,
pub continue_block: BlockId,
pub break_block: BlockId,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct LabelScope {
pub label: String,
pub block: BlockId,
}
impl ControlFlowScope {
fn label(&self) -> Option<&String> {
match self {
Self::Loop(scope) => scope.label.as_ref(),
Self::Label(scope) => Some(&scope.label),
}
}
fn break_block(&self) -> BlockId {
match self {
Self::Loop(scope) => scope.break_block,
Self::Label(scope) => scope.block,
}
}
}
impl<'e> Builder<'e> {
pub(crate) fn new(environment: &'e Environment) -> Self {
let entry = environment.next_block_id();
let current = WipBlock {
id: entry,
kind: BlockKind::Block,
instructions: Default::default(),
};
Self {
environment,
completed: Default::default(),
instructions: Default::default(),
entry,
wip: current,
id_gen: InstructionIdGenerator::new(),
scopes: Default::default(),
}
}
/// Completes the builder and returns the HIR if it was valid,
/// or a Diagnostic if a validation error occured.
///
/// TODO: refine the type, only invariants should be possible here,
/// not other types of errors
pub(crate) fn build(self) -> Result<HIR, Diagnostic> {
let mut hir = HIR {
entry: self.entry,
blocks: self.completed,
instructions: self.instructions,
};
// Run all the initialization passes
initialize_hir(&mut hir)?;
Ok(hir)
}
/// Adds a new instruction to the end of the work in progress block
pub(crate) fn push(&mut self, value: InstructionValue) -> IdentifierOperand {
let lvalue = IdentifierOperand {
identifier: self.environment.new_temporary(),
effect: None,
};
let instr = Instruction {
id: self.id_gen.next(),
lvalue: lvalue.clone(),
value,
};
let ix = InstrIx::new(self.instructions.len() as u32);
self.instructions.push(instr);
self.wip.instructions.push(ix);
lvalue
}
/// Terminates the work in progress block with the given terminal, and starts a new
/// work in progress block with the given kind
pub(crate) fn terminate(&mut self, terminal: TerminalValue, next_kind: BlockKind) {
let next_wip = WipBlock {
id: self.environment.next_block_id(),
kind: next_kind,
instructions: Default::default(),
};
self.terminate_with_fallthrough(terminal, next_wip)
}
pub(crate) fn terminate_with_fallthrough(
&mut self,
terminal: TerminalValue,
fallthrough: WipBlock,
) {
let prev_wip = std::mem::replace(&mut self.wip, fallthrough);
self.completed.insert(Box::new(BasicBlock {
id: prev_wip.id,
kind: prev_wip.kind,
instructions: prev_wip.instructions,
terminal: Terminal {
id: self.id_gen.next(),
value: terminal,
},
predecessors: Default::default(),
phis: Default::default(),
}));
}
pub(crate) fn reserve(&mut self, kind: BlockKind) -> WipBlock {
WipBlock {
id: self.environment.next_block_id(),
kind,
instructions: Default::default(),
}
}
pub(crate) fn enter<F>(&mut self, kind: BlockKind, f: F) -> Result<BlockId, Diagnostic>
where
F: FnOnce(&mut Self) -> Result<TerminalValue, Diagnostic>,
{
let wip = self.reserve(kind);
let id = wip.id;
self.enter_reserved(wip, f)?;
Ok(id)
}
fn enter_reserved<F>(&mut self, wip: WipBlock, f: F) -> Result<(), Diagnostic>
where
F: FnOnce(&mut Self) -> Result<TerminalValue, Diagnostic>,
{
let current = std::mem::replace(&mut self.wip, wip);
let (result, terminal) = match f(self) {
Ok(terminal) => (Ok(()), terminal),
Err(error) => (
Err(error),
// TODO: add a `Terminal::Error` variant
TerminalValue::Goto(react_hir::GotoTerminal {
block: current.id,
kind: GotoKind::Break,
}),
),
};
let completed = std::mem::replace(&mut self.wip, current);
self.completed.insert(Box::new(BasicBlock {
id: completed.id,
kind: completed.kind,
instructions: completed.instructions,
terminal: Terminal {
id: self.id_gen.next(),
value: terminal,
},
predecessors: Default::default(),
phis: Default::default(),
}));
result
}
pub(crate) fn enter_loop<F>(
&mut self,
scope: LoopScope,
f: F,
) -> Result<TerminalValue, Diagnostic>
where
F: FnOnce(&mut Self) -> Result<TerminalValue, Diagnostic>,
{
self.scopes.push(ControlFlowScope::Loop(scope.clone()));
let terminal = f(self);
let last = self.scopes.pop().unwrap();
assert_eq!(last, ControlFlowScope::Loop(scope));
terminal
}
/// Returns a new temporary identifier
/// This may be necessary for destructuring with default values. there
/// we synthesize a temporary identifier to store the possibly-missing value
/// into, and emit a later StoreLocal for the original identifier
#[allow(dead_code)]
pub(crate) fn make_temporary(&self) -> react_hir::Identifier {
react_hir::Identifier {
id: self.environment.next_identifier_id(),
name: None,
data: Rc::new(RefCell::new(IdentifierData {
mutable_range: Default::default(),
scope: None,
type_: Type::Var(self.environment.next_type_var_id()),
})),
}
}
/// Resolves the target for the given break label (if present), or returns the default
/// break target given the current context. Returns a diagnostic if the label is
/// provided but cannot be resolved.
pub(crate) fn resolve_break(
&self,
label: Option<&react_estree::Identifier>,
) -> Result<BlockId, Diagnostic> {
for scope in self.scopes.iter().rev() {
match (label, scope.label()) {
// If this is an unlabeled break, return the most recent break target
(None, _) => return Ok(scope.break_block()),
// If the break is labeled and matches the current scope, return its break target
(Some(label), Some(scope_label)) if &label.name == scope_label => {
return Ok(scope.break_block());
}
// Otherwise keep searching
_ => continue,
}
}
Err(Diagnostic::invalid_syntax(
BuildHIRError::UnresolvedBreakTarget,
None,
))
}
/// Resolves the target for the given continue label (if present), or returns the default
/// continue target given the current context. Returns a diagnostic if the label is
/// provided but cannot be resolved.
pub(crate) fn resolve_continue(
&self,
label: Option<&react_estree::Identifier>,
) -> Result<BlockId, Diagnostic> {
for scope in self.scopes.iter().rev() {
match scope {
ControlFlowScope::Loop(scope) => {
match (label, &scope.label) {
// If this is an unlabeled continue, return the first matching loop
(None, _) => return Ok(scope.continue_block),
// If the continue is labeled and matches the current scope, return its continue target
(Some(label), Some(scope_label))
if label.name.as_str() == scope_label.as_str() =>
{
return Ok(scope.continue_block);
}
// Otherwise keep searching
_ => continue,
}
}
_ => {
match (label, scope.label()) {
(Some(label), Some(scope_label)) if label.name.as_str() == scope_label => {
// Error, the continue referred to a label that is not a loop
return Err(Diagnostic::invalid_syntax(
BuildHIRError::ContinueTargetIsNotALoop,
None,
));
}
_ => continue,
}
}
}
}
Err(Diagnostic::invalid_syntax(
BuildHIRError::UnresolvedContinueTarget,
None,
))
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
use std::collections::HashSet;
use react_estree::IntoFunction;
use react_hir::Environment;
use react_semantic_analysis::{DeclarationId, ScopeView};
pub(crate) fn get_context_identifiers<T: IntoFunction>(
env: &Environment,
node: &T,
) -> Vec<DeclarationId> {
let function_scope = env.scope(node.function()).unwrap();
let mut free = FreeVariables::default();
let mut seen = HashSet::new();
populate_free_variable_references(&mut free, &mut seen, function_scope);
free
}
type FreeVariables = Vec<DeclarationId>;
fn populate_free_variable_references(
free: &mut FreeVariables,
seen: &mut HashSet<DeclarationId>,
scope: ScopeView<'_>,
) {
for reference in scope.references() {
if !seen.insert(reference.declaration().id()) {
continue;
}
let declaration_scope = reference.declaration().scope();
if !declaration_scope.is_descendant_of(scope) {
free.push(reference.declaration().id())
}
}
for child in scope.children() {
populate_free_variable_references(free, seen, child);
}
}

Some files were not shown because too many files have changed in this diff Show More