Skip to content

Roblox/react-lua

Repository files navigation

Roact Alignment

Tests Coverage Status Docs

A comprehensive, but not exhaustive, translation of upstream ReactJS 17.x into Roblox Lua.

TL;DR

  • In a new project, you can consume this library by adding this line to your rotriever.toml
    • React = "github.com/roblox/roact-alignment@17.0.1"
  • If you have legacy Roact code, use the roact-compat library instead
    • RoactCompat = "github.com/roblox/roact-alignment@17.0.1"
  • Make sure you are using the latest rotriever 0.5 (or later) release
    • you can download the release binary, or add it to your foreman.toml: rotrieve = { source = "roblox/rotriever", version = "=0.5.12" }
  • For unit testing components and trees of components, you'll want to use the act() API exported from the react-roblox package.

Status

The react repo is a monorepo with a number of member projects in its packages folder, managed by a yarn workspace. Below is a description of each of those package, its status in our alignment repo, and how it likely fits into our future plans.

πŸ“Œ Considered part of react's core functionality or testing capabilities; some or all of this package is necessary to build and validate an MVP.

Project Description Status Plan Notes
create-subscription Used for subscribing to external data ❌ Not ported βž– Unlikely to be ported Intended to help transition from pre-React 17
dom-event-testing-library Dom event simulation for tests ❌ Not ported βž– Unlikely to be ported Obviated by ReactTestingLibrary
eslint-plugin-react-hooks Linting plugin for hooks rules ❌ Not ported πŸ” Revisit if needed Should be a reference for future linting tools
jest-mock-scheduler Reexports scheduler testing utilities ❌ Not ported βž– Unlikely to be ported Exports internal utilities for mocking scheduler, not as useful as act()
πŸ“Œjest-react Jest matchers and utilities βœ”οΈ Ported Used for internal framework tests
πŸ“Œreact Base react interface βœ”οΈ Ported Defines basic shape of internals like Components and Elements. We added added Roblox-specifics like Bindings, but otherwise comply with upstream ReactJS.
react-art For drawing vector graphics ❌ Not ported βž– Unlikely to be ported No vector graphics on Roblox
react-cache Basic cache for use with experimental React features βœ”οΈ Ported API is flagged as unstable, is stable in React 18, used in advanced Suspense cases
react-client Experimental package for consuming React streaming models ❌ Not ported πŸ” Revisit if needed API considered unstable. Might be worth investigating if it stabilizes
react-debug-tools Experimental debugger package βœ”οΈ Ported Used by DevTools and Roblox Studio Inspector
react-devtools Top-level app for react devtools ❌ Not ported βž• Likely to be ported Devtools needs to be addressed as a whole to see where/how it translates
react-devtools-core Standalone devtools impl ❌ Not ported βž• Likely to be ported Devtools needs to be addressed as a whole to see where/how it translates
react-devtools-extensions Devtools browser extension ❌ Not ported βž– Unlikely to be ported Devtools needs to be addressed as a whole to see where/how it translates
react-devtools-inline Impl for embedding in browser-based IDEs ❌ Not ported βž• Likely to be ported Devtools needs to be addressed as a whole to see where/how it translates
react-devtools-scheduling-profiler Experimental concurrent mode profiler ❌ Not ported πŸ” Revisit if needed Supplanted in React 18 by react-devtools-timeline package
react-devtools-shared Private shared utilities for devtools βœ”οΈ Ported Used by Roblox Studio Inspector
react-devtools-shell Harness for testing other devtools packages ❌ Not ported βž– Unlikely to be ported Devtools needs to be addressed as a whole to see where/how it translates
react-dom Entrypoint for DOM and server renderers ❌ Not ported βž– Unlikely to be ported Not ported directly, but it heavily inspired the React-Roblox renderer interface and implementation
react-fetch For use with experimental React features ❌ Not ported βž– Unlikely to be ported API considered unstable, removed in React 18
react-interactions For use with experimental React features ❌ Not ported βž– Unlikely to be ported API unstable, used only with internal features
πŸ“Œreact-is Runtime type checks for React elements βœ”οΈ Ported
react-native-renderer Renderer interface for react-native ❌ Not ported βž– Unlikely to be ported Not well documented, likely does not apply to Roblox
πŸ“Œreact-noop-renderer Renderer used for debugging Fiber βœ”οΈ Ported Used heavily for internal framework testing
πŸ“Œreact-reconciler Reconciler implementation used with various renderers βœ”οΈ Ported Bulk of React's complicated logic lives here
react-refresh Wiring for Fast Refresh ❌ Not ported πŸ” Revisit if needed Successor to "hot reloading", but relies on bundler step
react-server Experimental package for creating React streaming server renderers ❌ Not ported πŸ” Revisit if needed API considered unstable. Might be worth investigating if it stabilizes
react-test-renderer Test renderer helpful utilities and snapshot support βœ”οΈ Ported Used for testing much of React's internals, can be used by client developers
react-transport-dom-delay Internal package, likely for testing ❌ Not ported βž– Unlikely to be ported Internal library for experimental React Flight feature
react-transport-dom-webpack Related to above ❌ Not ported βž– Unlikely to be ported Webpack-specific bindings for experimental React Flight feature
πŸ“Œscheduler Cooperative scheduling implementation βœ”οΈ Ported Includes Tracing and Profiling features, which are enabled through ReactFeatureFlags
πŸ“Œshared Loose collection of shared utilities and definitions βœ”οΈ Ported We pushed many things into this leaf node module to fix circular dependencies. Working with upstream to clean this up.
use-subscription Hook for managing subscriptions in concurrent mode ❌ Not ported πŸ” Revisit if needed Supplanted by use-sync-external-store in upstream

Projects not in the upstream React repo:

Project Description Notes
πŸ“Œreact-shallow-renderer Shallow renderer used in tests for some older React features. Re-exported alongside react-test-renderer, source of truth here. βœ”οΈ Ported - with tests that are helping us exercise functionality in the react package
react-roblox Based on react-dom renderer, shares much of its code and public interface. Also exports act() functionality, which is required for testing components that are asynchronously rendered (the default).
roact-compat A comaptibility layer that emulates some deprecated behaviors of legacy Roact Meant to ease initial adoption of Roact 17, using React APIs directly is encouraged and necessary for newer functonality (eg Hooks)

Deviations from Roact

This repo is meant to supplant the Roact project, which is an open-source project that currently powers the majority of the Lua App and is used by the community as well. Our goal is to be as compatible as possible with Roact by the time we're ready to start adopting this alignment effort for use.

With that in mind, however, there will still be a small number of behavioral deviations that make the transition from existing Roact smoother, or account for nuances of the Roblox ecosystem:

  • Stable Keys: Aligned Roact will allow table keys to be used as stable keys for child elements, equivalent to the behavior relied upon in Roact today
  • Context: Legacy Roact's deprecated _context feature will not be present in aligned Roact; users will have to switch to the createContext feature, which is present in both current and aligned Roact and is semantically equivalent
  • Class Component Refs: Aligned Roact will allow refs provided to class components (referred to in Roact documentation as "stateful components") to point to the actual component instance. This is not supported in current Roact, and there may be changes around the Roact.Ref prop key to support this with minimal disruption
  • Bindings: We intend to keep createBinding and joinBindings, a feature unique to Roact and documented here

See this document for details about any deviations and the design and refactoring efforts being proposed to address them.

How to run the tests

You need to create a GitHub Access Token:

  • GitHub.com -> Settings -> Developer Settings -> Personal Access Tokens
  • Your token must have the repo and read:packages` scopes
  • On that same page, you then need to click Enable SSO
  • BE SURE TO COPY THE ACCESS TOKEN SOMEWHERE
npm login --registry=https://npm.pkg.github.com/ --scope=@roblox

For your password here, you will enter the GitHub Access Token from the instructions above.

npm install --global @roblox/rbx-aged-cli

Before you can use rbx-aged-cli, you need to be logged into the VPN so the Artifactory repository is accessible.

mkdir ~/bin
rbx-aged-cli download roblox-cli --dst ~/bin
export PATH=$PATH:~/bin
roblox-cli --help

You should see roblox-cli output its help text. Before going ahead, you might want to add export PATH=$PATH:~/bin to your bash profile file of choice, so you don't have to run it every time you open your terminal.

The next step is to clone the repo. When prompted for it, use the personal access token as your password.

git clone https://github.com/Roblox/roact-alignment.git
cd roact-alignment
roblox-cli analyze default.project.json

Foreman is an un-package manager that retrieves code directly from GitHub repositories. We'll use this to get a Lua package manager and other utilities. The Foreman packages are listed in foreman.toml.

You can install Foreman from a binary by downloading the appropriate version for your platform from the GitHub releases page.

Alternatively, since Foreman uses Rust, you can install Rust and use cargo to install it:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
export PATH=$PATH:$HOME/.cargo/bin
cargo install foreman

Once Foreman is installed, provide an API token so that it can access private tools like rotriever and make sure that it's added to your path.

foreman github-auth <your GitHub API token that you used for npm login above>
foreman install
export PATH=~/.foreman/bin/:$PATH # you might want to add this to your bash profile file too

Now you can run the tests, edit code, and contribute! Next we need to install our Lua package dependencies. We do this with a tool called Rotriever, which Foreman just installed for us. The package dependencies are listed in rotriever.toml.

rotrieve install

Now we can use roblox-cli to run our tests. We need to specify some additional arguments to make sure that the latest luau language features are enabled as well as debug.loadmodule, which allows us to reset module state between tests.

roblox-cli run --load.model tests.project.json --run bin/spec.lua --fastFlags.overrides EnableLoadModule=true --fastFlags.allOnLuau

Common Issues

If rojo doesn't understand the nested project structure, exemplified by require statements not finding things, make sure you don't have a globally-installed rojo binary that is shadowing the one this project specifies locally. You must be using rojo 6.0 or above.

Once you remove the global rojo, you'll need to tickle bash's PATH hash cache so it doesn't keep looking in the place rojo was. (Yes, this is weird.) To update the bash path hash cache, run: hash -d rojo

To avoid this in the future, be sure that your foreman binary path is before the carbo binary path in your PATH enviroment.

Contribution Guidelines

  • Try to keep the directory structure, file name/location, and code symbol names aligned with React upstream. At the top of the mirrored files, put a comment in this format that includes the specific hash of the version of the file you're mirroring:
-- ROBLOX upstream https://github.com/facebook/react/blob/9abc2785cb070148d64fae81e523246b90b92016/packages/scheduler/src/Scheduler.js
  • If you have a deviation from upstream code logic for Lua-specific reasons (1-based array indices, etc) put a comment above the deviated line:
-- ROBLOX deviation: use explicit nil check instead of falsey
  • For deviations due to Lua language differences (no spread operator) that don't involve changing the logic, don't put a deviation comment. Just use the appropriate equivalent from the es7-luau (fka LuauPolyfill) and other utility libraries.

  • For files that are new and Roblox-specific, use the file name: Timeout.roblox.lua

  • and for Roblox-specific tests, use the file name format: Timeout.roblox.spec.lua

How to debug local tests

First, install the roblox-lrdb debugger extension for VSCode (following the installation instructions here).

Note: As specified in the lrdb installation instructions, you must be connected to the VPN in order for the Internal Tool Provider to be able to download what it needs.

In order for breakpoints to work correctly, you'll need to disable the module reloading behavior that relies on debug.loadmodule by providing the __NO_LOADMODULE__ global. To do so, create a launch.json file with the following contents:

{
	"version": "0.2.0",
	"configurations": [
		{
			"type": "roblox-lrdb",
			"request": "launch",
			"name": "Debug Unit Tests",
			"args": [
				"--load.project",
				"tests.project.json",
				"--run",
				"bin/spec.lua",
				"--lua.globals",
				"__NO_LOADMODULE__=true",
			],
			"cwd": "${workspaceFolder}",
			"stopOnEntry": true,
		},
	]
}

With module resetting disabled, most tests will only work if they're the first test run. Use itFOCUS or fit to focus a single test in your test suite when debugging this way. Once you've set that up, use Run -> Start Debugging or the equivalent keyboard shortcut to start debugging.

How to debug upstream tests

To run the upstream tests, you will need to install yarn and have it download the development dependencies:

npm install --global yarn
yarn

First, set a breakpoint in the ReactJS code you want to step through, like at the beginning of a specific test.

Note: You probably don't want to set a breakpoint on the fit, it, or itFOCUS line itself, but the first line of the test after that.

Note: Using https://github.com/Roblox/vscode-rbx-lrdb , you can set the same breakpoint in your equivalent Lua test. This allows you to single step in both the JS and Lua implementations, lock-step, to see where they deviate.

In VS Code, press F1 key. Search for "auto attach" and select it. image

Set the Auto Attach option to "Smart" image

Open a JavaScript Debug Terminal, by first using View -> Terminal. image

In the Terminal window, select "Create Debug JavaScript Terminal". image

In Debug JavaScript Terminal window, paste in the command you use to run the specific React test file you are interested in. An example: yarn test --watch -v -r www-modern packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js Note that to match the React feature set that Roblox is aligning to, you need to enable the variant and www-modern flags.

image

You should see your status bar turn orange (depending on your local theme), indicating that VS Code auto attach worked.

image

Shortly after you see the status bar change colors to indicate an active debugger attachment, you should hit your breakpoint. image

Note that VS Code even tells you the value of the variables on the line. Pretty cool!

About

A comprehensive translation of upstream React 17.x into Lua. This is a read-only mirror

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Languages