├── .darklua-dev.json ├── .darklua-wally.json ├── .darklua.json ├── .editorconfig ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── docs.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── .styluaignore ├── .yarnrc.yml ├── CODE_OF_CONDUCT.md ├── DEVIATIONS.md ├── LICENSE ├── README.md ├── aftman.toml ├── bin ├── ci-benchmarks.sh ├── ci.sh ├── compare-benchmarks.py ├── convert.sh ├── run-benchmarks.py ├── run-deep-tree-benchmark.luau ├── run-first-render-benchmark.luau ├── run-frame-rate-benchmark.luau ├── run-sierpinski-triangle-benchmark.luau ├── run-wide-tree-benchmark.luau ├── run-with-cachegrind.sh ├── spec.luau ├── svg.py ├── testing.sh └── upstream-tag.sh ├── default.project.json ├── docs ├── align-files-guide.md ├── api-reference │ ├── additional-libraries.md │ ├── react-roblox.md │ ├── react.md │ └── roact-compat.md ├── bench.md ├── bench │ ├── data.json │ └── index.js ├── configuration.md ├── deviations.md ├── diff-bridge-shutdown-method-after.png ├── diff-bridge-shutdown-method-before.png ├── diff-class-arrow-function-method-new.png ├── diff-class-arrow-function-method-old.png ├── diff-packages.png ├── diff-spread-flow-type-no-deviation-anymore.png ├── diff-upstream-comment.png ├── extra.css ├── images │ ├── aligned.svg │ ├── apichange.svg │ ├── deviation.svg │ ├── reactjs.svg │ └── roblox.svg ├── index.md ├── migrating-from-legacy │ ├── adopt-new-features.md │ ├── convert-legacy-conventions.md │ ├── minimum-requirements.md │ └── upgrading-to-react-lua.md └── requirements.txt ├── examples.project.json ├── foreman.toml ├── jest-setup ├── matchers │ ├── __tests__ │ │ └── toWarnDev.spec.luau │ ├── createConsoleMatcher.luau │ ├── interactionTracingMatchers.luau │ ├── reactTestMatchers.luau │ ├── schedulerTestMatchers.luau │ ├── toErrorDev.luau │ ├── toLogDev.luau │ └── toWarnDev.luau └── testSetupFile.luau ├── jest.config.luau ├── js-to-lua.config.js ├── mkdocs.yml ├── modules ├── TestRunner │ ├── .luaurc │ ├── default.project.json │ └── src │ │ └── init.luau ├── jest-react │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── JestReact.luau │ │ └── init.luau ├── react-cache │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── LRU.luau │ │ ├── ReactCacheOld.luau │ │ ├── __tests__ │ │ └── ReactCacheOld-internal.spec.luau │ │ └── init.luau ├── react-debug-tools │ ├── .luaurc │ ├── .npmignore │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── ReactDebugHooks.luau │ │ ├── ReactDebugTools.luau │ │ ├── __tests__ │ │ ├── ReactDevToolsHooksIntegration.spec.luau │ │ ├── ReactHooksInspection.spec.luau │ │ └── ReactHooksInspectionIntegration.spec.luau │ │ └── init.luau ├── react-devtools-extensions │ ├── .luaurc │ ├── .npmignore │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── __tests__ │ │ └── devtools-integration.roblox.spec.luau │ │ ├── backend.luau │ │ └── init.luau ├── react-devtools-shared │ ├── .luaurc │ ├── .npmignore │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── storeComponentFilters.spec.snap.luau │ │ ├── bridge.spec.luau │ │ ├── console.spec.luau │ │ ├── events.spec.luau │ │ ├── profilerStore.spec.luau │ │ ├── profilingCache.spec.luau │ │ ├── profilingCharts.spec.luau │ │ ├── profilingCommitTreeBuilder.spec.luau │ │ ├── profilingUtils.spec.luau │ │ ├── setupTests.luau │ │ ├── store.spec.luau │ │ ├── storeComponentFilters.spec.luau │ │ ├── storeOwners.spec.luau │ │ ├── utils.luau │ │ └── utils.spec.luau │ │ ├── backend │ │ ├── NativeStyleEditor │ │ │ └── types.luau │ │ ├── ReactSymbols.luau │ │ ├── agent.luau │ │ ├── console.luau │ │ ├── init.luau │ │ ├── renderer.luau │ │ ├── types.luau │ │ └── utils.luau │ │ ├── bridge.luau │ │ ├── clipboardjs.mock.luau │ │ ├── constants.luau │ │ ├── devtools │ │ ├── ProfilerStore.luau │ │ ├── ProfilingCache.luau │ │ ├── cache.luau │ │ ├── init.luau │ │ ├── store.luau │ │ ├── types.luau │ │ ├── utils.luau │ │ └── views │ │ │ ├── Components │ │ │ └── types.luau │ │ │ └── Profiler │ │ │ ├── CommitTreeBuilder.luau │ │ │ ├── FlamegraphChartBuilder.luau │ │ │ ├── InteractionsChartBuilder.luau │ │ │ ├── RankedChartBuilder.luau │ │ │ ├── types.luau │ │ │ └── utils.luau │ │ ├── events.luau │ │ ├── hook.luau │ │ ├── hydration.luau │ │ ├── init.luau │ │ ├── jest.config.luau │ │ ├── storage.luau │ │ ├── types.luau │ │ └── utils.luau ├── react-is │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── __tests__ │ │ └── ReactIs.spec.luau │ │ └── init.luau ├── react-noop-renderer │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── ReactNoop.luau │ │ ├── createReactNoop.luau │ │ └── init.luau ├── react-reconciler │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── DebugTracing.luau │ │ ├── MaxInts.luau │ │ ├── ReactCapturedValue.luau │ │ ├── ReactChildFiber.new.luau │ │ ├── ReactCurrentFiber.luau │ │ ├── ReactFiber.new.luau │ │ ├── ReactFiberBeginWork.new.luau │ │ ├── ReactFiberClassComponent.new.luau │ │ ├── ReactFiberCommitWork.new.luau │ │ ├── ReactFiberCompleteWork.new.luau │ │ ├── ReactFiberComponentStack.luau │ │ ├── ReactFiberContext.new.luau │ │ ├── ReactFiberDevToolsHook.new.luau │ │ ├── ReactFiberErrorDialog.luau │ │ ├── ReactFiberErrorLogger.luau │ │ ├── ReactFiberFlags.luau │ │ ├── ReactFiberHooks.new.luau │ │ ├── ReactFiberHostConfig.luau │ │ ├── ReactFiberHostContext.new.luau │ │ ├── ReactFiberHotReloading.new.luau │ │ ├── ReactFiberHydrationContext.new.luau │ │ ├── ReactFiberLane.luau │ │ ├── ReactFiberLazyComponent.new.luau │ │ ├── ReactFiberNewContext.new.luau │ │ ├── ReactFiberOffscreenComponent.luau │ │ ├── ReactFiberReconciler.luau │ │ ├── ReactFiberReconciler.new.luau │ │ ├── ReactFiberRoot.new.luau │ │ ├── ReactFiberSchedulerPriorities.roblox.luau │ │ ├── ReactFiberStack.new.luau │ │ ├── ReactFiberSuspenseComponent.new.luau │ │ ├── ReactFiberSuspenseContext.new.luau │ │ ├── ReactFiberThrow.new.luau │ │ ├── ReactFiberTransition.luau │ │ ├── ReactFiberTreeReflection.luau │ │ ├── ReactFiberUnwindWork.new.luau │ │ ├── ReactFiberWorkInProgress.luau │ │ ├── ReactFiberWorkLoop.new.luau │ │ ├── ReactHookEffectTags.luau │ │ ├── ReactInternalTypes.luau │ │ ├── ReactMutableSource.new.luau │ │ ├── ReactPortal.luau │ │ ├── ReactProfilerTimer.new.luau │ │ ├── ReactRootTags.luau │ │ ├── ReactStrictModeWarnings.new.luau │ │ ├── ReactTestSelectors.luau │ │ ├── ReactTypeOfMode.luau │ │ ├── ReactUpdateQueue.new.luau │ │ ├── ReactWorkTags.luau │ │ ├── RobloxReactProfiling.luau │ │ ├── SchedulerWithReactIntegration.new.luau │ │ ├── SchedulingProfiler.luau │ │ ├── __tests__ │ │ ├── DebugTracing-test.internal.spec.luau │ │ ├── ReactClassSetStateCallback.spec.luau │ │ ├── ReactComponentLifeCycle.roblox.spec.luau │ │ ├── ReactComponentLifeCycle.spec.luau │ │ ├── ReactErrorBoundaries-internal.spec.luau │ │ ├── ReactFiberComponentStack.roblox.spec.luau │ │ ├── ReactFiberContext-internal.spec.luau │ │ ├── ReactFiberDevToolsHook-internal.spec.luau │ │ ├── ReactFiberHostContext-internal.spec.luau │ │ ├── ReactFiberLane.roblox.spec.luau │ │ ├── ReactFiberRoot.roblox.spec.luau │ │ ├── ReactFiberStack-test.roblox.spec.luau │ │ ├── ReactFiberSuspenseComponent.roblox.spec.luau │ │ ├── ReactFiberSuspenseContext.roblox.spec.luau │ │ ├── ReactFiberTreeReflection.roblox.spec.luau │ │ ├── ReactHooks-internal.spec.luau │ │ ├── ReactHooksWithNoopRenderer.spec.luau │ │ ├── ReactIdentity.spec.luau │ │ ├── ReactIncremental.spec.luau │ │ ├── ReactIncrementalErrorReplay.spec.luau │ │ ├── ReactIncrementalReflection.spec.luau │ │ ├── ReactIncrementalScheduling.spec.luau │ │ ├── ReactIncrementalSideEffects.spec.luau │ │ ├── ReactIncrementalUpdates.spec.luau │ │ ├── ReactIncrementalUpdatesMinimalism.spec.luau │ │ ├── ReactLazy-internal.spec.luau │ │ ├── ReactNewContext.spec.luau │ │ ├── ReactNoopRendererAct.spec.luau │ │ ├── ReactSuspense-internal.spec.luau │ │ ├── ReactTopLevelFragment.spec.luau │ │ ├── ReactTopLevelText.spec.luau │ │ ├── ReactUpdateQueue.roblox.spec.luau │ │ ├── ReactUseRef.roblox.spec.luau │ │ ├── SchedulingProfiler-internal.spec.luau │ │ └── useMutableSource-internal.spec.luau │ │ ├── forks │ │ └── ReactFiberHostConfig.test.luau │ │ └── init.luau ├── react-roblox │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── ReactReconciler.roblox.luau │ │ ├── client │ │ ├── ReactRoblox.luau │ │ ├── ReactRobloxComponent.luau │ │ ├── ReactRobloxComponentTree.luau │ │ ├── ReactRobloxHostConfig.luau │ │ ├── ReactRobloxHostTypes.roblox.luau │ │ ├── ReactRobloxRoot.luau │ │ ├── __tests__ │ │ │ ├── PropAssignmentErrors.roblox.spec.luau │ │ │ ├── ReactRobloxBindings.roblox.spec.luau │ │ │ ├── ReactRobloxComponentTree.roblox.spec.luau │ │ │ ├── ReactRobloxFiber.spec.luau │ │ │ └── RobloxRenderer.roblox.spec.luau │ │ └── roblox │ │ │ ├── RobloxComponentProps.luau │ │ │ ├── SingleEventManager.luau │ │ │ ├── __tests__ │ │ │ ├── RobloxComponentProps.roblox.spec.luau │ │ │ ├── SingleEventManager.spec.luau │ │ │ ├── Tagging.spec.luau │ │ │ ├── getDefaultInstanceProperty.spec.luau │ │ │ └── waitForEvents.luau │ │ │ └── getDefaultInstanceProperty.luau │ │ └── init.luau ├── react-shallow-renderer │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── __tests__ │ │ ├── ReactShallowRenderer.spec.luau │ │ └── ReactShallowRendererHooks.spec.luau │ │ └── init.luau ├── react-test-renderer │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── ReactTestHostConfig.luau │ │ ├── ReactTestRenderer.luau │ │ ├── __tests__ │ │ ├── ReactTestRenderer-internal.spec.luau │ │ ├── ReactTestRenderer.spec.luau │ │ ├── ReactTestRendererAct.spec.luau │ │ ├── ReactTestRendererMockPropMarkers.roblox.spec.luau │ │ ├── ReactTestRendererTraversal.spec.luau │ │ └── RobloxComponentProps.roblox.spec.luau │ │ ├── init.luau │ │ └── roblox │ │ └── RobloxComponentProps.luau ├── react │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── None.roblox.luau │ │ ├── React.luau │ │ ├── ReactBaseClasses.luau │ │ ├── ReactBinding.roblox.luau │ │ ├── ReactChildren.luau │ │ ├── ReactContext.luau │ │ ├── ReactCreateRef.luau │ │ ├── ReactElement.luau │ │ ├── ReactElementValidator.luau │ │ ├── ReactForwardRef.luau │ │ ├── ReactHooks.luau │ │ ├── ReactLazy.luau │ │ ├── ReactMemo.luau │ │ ├── ReactMutableSource.luau │ │ ├── ReactNoopUpdateQueue.luau │ │ ├── __tests__ │ │ ├── ReactBaseClasses.roblox.spec.luau │ │ ├── ReactBinding.spec.luau │ │ ├── ReactChildren.spec.luau │ │ ├── ReactDeprecationWarnings-internal.spec.luau │ │ ├── ReactElement.roblox.spec.luau │ │ ├── ReactElementValidator-internal.spec.luau │ │ ├── ReactProfiler-internal.spec.luau │ │ ├── ReactProfilerDevToolsIntegration-internal.spec.luau │ │ ├── ReactStrictMode.spec.luau │ │ ├── ReactUpdates.spec.luau │ │ ├── SetStateInConstructor.roblox.spec.luau │ │ ├── createSignal.spec.luau │ │ ├── forwardRef-internal.spec.luau │ │ └── forwardRef.spec.luau │ │ ├── createSignal.roblox.luau │ │ └── init.luau ├── roact-compat │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── Portal.luau │ │ ├── RoactTree.luau │ │ ├── __tests__ │ │ ├── RoactCompatibility.spec.luau │ │ ├── RoactRecursiveLayoutPcallDepth.spec.luau │ │ ├── RoactTree.spec.luau │ │ ├── act.spec.luau │ │ └── warnOnce.spec.luau │ │ ├── createFragment.luau │ │ ├── init.luau │ │ ├── oneChild.luau │ │ ├── setGlobalConfig.luau │ │ └── warnOnce.luau ├── scheduler │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ │ ├── Scheduler.luau │ │ ├── SchedulerFeatureFlags.luau │ │ ├── SchedulerHostConfig.luau │ │ ├── SchedulerMinHeap.luau │ │ ├── SchedulerPriorities.luau │ │ ├── SchedulerProfiling.luau │ │ ├── Tracing.luau │ │ ├── TracingSubscriptions.luau │ │ ├── __tests__ │ │ ├── Scheduler.spec.luau │ │ ├── SchedulerMinHeap.roblox.spec.luau │ │ ├── SchedulerNoDOM.spec.luau │ │ ├── SchedulerProfiling.spec.luau │ │ ├── Tracing-internal.spec.luau │ │ ├── Tracing.spec.luau │ │ └── TracingSubscriptions-internal.spec.luau │ │ ├── forks │ │ ├── SchedulerHostConfig.default.luau │ │ └── SchedulerHostConfig.mock.luau │ │ ├── init.luau │ │ └── unstable_mock.luau └── shared │ ├── .luaurc │ ├── .npmignore │ ├── README.md │ ├── default.project.json │ ├── package.json │ └── src │ ├── ConsolePatchingDev.roblox.luau │ ├── ErrorHandling.roblox.luau │ ├── ExecutionEnvironment.luau │ ├── PropMarkers │ ├── Change.luau │ ├── Event.luau │ ├── Tag.luau │ └── __tests__ │ │ ├── Change.spec.luau │ │ └── Event.spec.luau │ ├── ReactComponentStackFrame.luau │ ├── ReactElementType.luau │ ├── ReactErrorUtils.luau │ ├── ReactFeatureFlags.luau │ ├── ReactFiberHostConfig │ ├── WithNoHydration.luau │ ├── WithNoPersistence.luau │ ├── WithNoTestSelectors.luau │ └── init.luau │ ├── ReactInstanceMap.luau │ ├── ReactSharedInternals │ ├── IsSomeRendererActing.luau │ ├── ReactCurrentBatchConfig.luau │ ├── ReactCurrentDispatcher.luau │ ├── ReactCurrentOwner.luau │ ├── ReactDebugCurrentFrame.luau │ └── init.luau │ ├── ReactSymbols.luau │ ├── ReactTypes.luau │ ├── ReactVersion.luau │ ├── Symbol.roblox.luau │ ├── Type.roblox.luau │ ├── UninitializedState.roblox.luau │ ├── __tests__ │ ├── ErrorHandling.roblox.spec.luau │ ├── ReactComponentStackFrame.roblox.spec.luau │ ├── ReactErrorProd-internal.spec.luau │ ├── ReactErrorUtils-internal.spec.luau │ ├── ReactInstanceMap.roblox.spec.luau │ ├── ReactSymbols-internal.spec.luau │ ├── checkPropTypes.roblox.spec.luau │ ├── getComponentName.roblox.spec.luau │ └── isValidElementType.roblox.spec.luau │ ├── checkPropTypes.luau │ ├── console.luau │ ├── consoleWithStackDev.luau │ ├── enqueueTask.roblox.luau │ ├── flowtypes.roblox.luau │ ├── formatProdErrorMessage.luau │ ├── getComponentName.luau │ ├── init.luau │ ├── invariant.luau │ ├── invokeGuardedCallbackImpl.luau │ ├── isValidElementType.luau │ ├── objectIs.luau │ └── shallowEqual.luau ├── package.json ├── roblox-model ├── JestReact.luau ├── React.luau ├── ReactCache.luau ├── ReactDebugTools.luau ├── ReactDevtoolsExtensions.luau ├── ReactDevtoolsShared.luau ├── ReactIs.luau ├── ReactReconciler.luau ├── ReactRoblox.luau ├── ReactShallowRenderer.luau ├── ReactTestRenderer.luau ├── RoactCompat.luau ├── Scheduler.luau └── Shared.luau ├── scripts ├── build-assets.sh ├── build-roblox-model.sh ├── build-wally-package.sh ├── npm-to-wally.js └── remove-tests.sh ├── selene.toml ├── selene_definitions.yml ├── stylua.toml └── yarn.lock /.darklua-dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "process": [ 3 | { 4 | "rule": "inject_global_value", 5 | "identifier": "__DEV__", 6 | "value": true 7 | }, 8 | { 9 | "rule": "convert_require", 10 | "current": { 11 | "name": "path", 12 | "sources": { 13 | "@pkg": "node_modules/.luau-aliases" 14 | } 15 | }, 16 | "target": { 17 | "name": "roblox", 18 | "rojo_sourcemap": "./sourcemap.json", 19 | "indexing_style": "wait_for_child" 20 | } 21 | }, 22 | "compute_expression", 23 | "remove_unused_if_branch", 24 | "remove_unused_while", 25 | "filter_after_early_return", 26 | "remove_nil_declaration", 27 | "remove_empty_do" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.darklua-wally.json: -------------------------------------------------------------------------------- 1 | { 2 | "process": [ 3 | { 4 | "rule": "convert_require", 5 | "current": { 6 | "name": "path", 7 | "sources": { 8 | "@pkg": "." 9 | } 10 | }, 11 | "target": { 12 | "name": "roblox", 13 | "indexing_style": "wait_for_child", 14 | "rojo_sourcemap": "./sourcemap.json" 15 | } 16 | }, 17 | "compute_expression", 18 | "remove_unused_if_branch", 19 | "remove_unused_while", 20 | "filter_after_early_return", 21 | "remove_nil_declaration", 22 | "remove_empty_do" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.darklua.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "rule": "inject_global_value", 5 | "identifier": "__DEV__", 6 | "value": false 7 | }, 8 | { 9 | "rule": "convert_require", 10 | "current": { 11 | "name": "path", 12 | "sources": { 13 | "@pkg": "node_modules/.luau-aliases" 14 | } 15 | }, 16 | "target": { 17 | "name": "roblox", 18 | "rojo_sourcemap": "./sourcemap.json", 19 | "indexing_style": "wait_for_child" 20 | } 21 | }, 22 | "compute_expression", 23 | "remove_unused_if_branch", 24 | "remove_unused_while", 25 | "filter_after_early_return", 26 | "remove_nil_declaration", 27 | "remove_empty_do" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.lua] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = false 8 | 9 | [*.json] 10 | indent_style = spaces 11 | indent_width = 2 -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes (ISSUES HERE). 2 | 3 | *Put your pull request body here!* 4 | 5 | Checklist before submitting: 6 | * [ ] Added/updated relevant tests 7 | * [ ] Added/updated documentation -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Configure Git Credentials 14 | run: | 15 | git config user.name github-actions[bot] 16 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 17 | - uses: actions/setup-python@v4 18 | with: 19 | python-version: 3.x 20 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 21 | - uses: actions/cache@v3 22 | with: 23 | key: mkdocs-material-${{ env.cache_id }} 24 | path: .cache 25 | restore-keys: | 26 | mkdocs-material- 27 | - run: pip install mkdocs-material 28 | - run: mkdocs gh-deploy --force 29 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | name: Run tests 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - uses: Roblox/setup-foreman@v1 20 | with: 21 | token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - name: Enable corepack 24 | run: corepack enable 25 | 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: "latest" 29 | cache: "yarn" 30 | cache-dependency-path: "yarn.lock" 31 | 32 | - name: Install packages 33 | run: yarn install --immutable 34 | 35 | - name: Run npmluau 36 | run: yarn run prepare 37 | 38 | - name: Run linter 39 | run: yarn run lint 40 | 41 | - name: Verify code style 42 | run: yarn run style-check 43 | 44 | - name: Build assets 45 | run: yarn run build-assets 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | sourcemap.json 2 | Packages/ 3 | **/*.rbx[lm]* 4 | .DS_Store 5 | .vscode 6 | roblox.yml 7 | roblox.toml 8 | **/luacov.* 9 | .idea/ 10 | site/ 11 | 12 | default_modules 13 | modules_* 14 | 15 | .yarn 16 | /*.tgz 17 | 18 | **/sourcemap.json 19 | **/node_modules 20 | /build 21 | /roblox 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.* 2 | /bin 3 | /Packages 4 | **/.robloxrc 5 | 6 | /.github/ 7 | /.vscode/ 8 | 9 | /roblox 10 | /build 11 | 12 | /*.json 13 | /*.toml 14 | /*.yml 15 | /*.md 16 | /*.tgz 17 | 18 | /globalTypes.d.lua 19 | **/sourcemap.json 20 | **/*.project.json 21 | 22 | **/__tests__ 23 | **/*.test.lua 24 | **/*.spec.lua 25 | **/jest.config.lua 26 | 27 | **/*.rbxl 28 | **/*.rbxlx 29 | **/*.rbxl.lock 30 | **/*.rbxlx.lock 31 | **/*.rbxm 32 | **/*.rbxmx 33 | -------------------------------------------------------------------------------- /.styluaignore: -------------------------------------------------------------------------------- 1 | *.snap.luau -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2023 Roblox Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /aftman.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | rojo = "rojo-rbx/rojo@7.3.0" 3 | selene = "Kampfkarren/selene@0.26.1" 4 | stylua = "JohnnyMorganz/StyLua@0.19.1" 5 | darklua = "seaofvoices/darklua@0.13.1" 6 | -------------------------------------------------------------------------------- /bin/ci-benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | rotrieve install 6 | rojo build tests.project.json --output model.rbxm 7 | 8 | echo "Remove .robloxrc from dependencies" 9 | find Packages/_Index -name "*.robloxrc" | xargs rm -f 10 | 11 | echo "Run static analysis" 12 | roblox-cli analyze tests.project.json 13 | selene --version 14 | selene --config selene.toml modules/ 15 | stylua --version 16 | stylua -c modules -g "*[a-bdh-km-oquvyz].lua" 17 | 18 | echo "Run benchmarks" 19 | robloxdev-cli run --load.model model.rbxm --run bin/run-first-render-benchmark.lua --fastFlags.allOnLuau --fastFlags.overrides EnableLoadModule=true EnableDelayedTaskMethods=true --headlessRenderer 1 20 | robloxdev-cli run --load.model model.rbxm --run bin/run-frame-rate-benchmark.lua --fastFlags.allOnLuau --fastFlags.overrides EnableLoadModule=true EnableDelayedTaskMethods=true --headlessRenderer 1 21 | robloxdev-cli run --load.model model.rbxm --run bin/run-deep-tree-benchmark.lua --fastFlags.allOnLuau --fastFlags.overrides EnableLoadModule=true EnableDelayedTaskMethods=true --headlessRenderer 1 22 | robloxdev-cli run --load.model model.rbxm --run bin/run-wide-tree-benchmark.lua --fastFlags.allOnLuau --fastFlags.overrides EnableLoadModule=true EnableDelayedTaskMethods=true --headlessRenderer 1 23 | robloxdev-cli run --load.model model.rbxm --run bin/run-sierpinski-triangle-benchmark.lua --fastFlags.allOnLuau --fastFlags.overrides EnableLoadModule=true EnableDelayedTaskMethods=true --headlessRenderer 1 24 | -------------------------------------------------------------------------------- /bin/ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | rotrieve install 6 | 7 | echo "Remove .robloxrc from dependencies" 8 | find Packages/_Index -name "*.robloxrc" | xargs rm -f 9 | 10 | echo "Run static analysis" 11 | roblox-cli analyze --project tests.project.json 12 | selene --version 13 | selene --config selene.toml modules/ WorkspaceStatic/ 14 | stylua --version 15 | stylua -c modules bin WorkspaceStatic 16 | 17 | echo "Run tests in DEV" 18 | robloxdev-cli run --load.model tests.project.json \ 19 | --run bin/spec.lua \ 20 | --fastFlags.allOnLuau --fastFlags.overrides UseDateTimeType3=true EnableLoadModule=true DebugDisableOptimizedBytecode=true EnableDelayedTaskMethods=true MaxDeferReentrancyDepth=65 \ 21 | --load.asRobloxScript --headlessRenderer 1 --virtualInput 1 --fs.readwrite=$PWD --lua.globals=__COMPAT_WARNINGS__=true \ 22 | --lua.globals=UPDATESNAPSHOT=false --lua.globals=CI=true --lua.globals=__ROACT_17_MOCK_SCHEDULER__=true \ 23 | --lua.globals=__DEV__=true 24 | 25 | echo "Run tests in release" 26 | robloxdev-cli run --load.model tests.project.json \ 27 | --run bin/spec.lua \ 28 | --fastFlags.allOnLuau --fastFlags.overrides UseDateTimeType3=true EnableLoadModule=true DebugDisableOptimizedBytecode=true EnableDelayedTaskMethods=true MaxDeferReentrancyDepth=65 \ 29 | --load.asRobloxScript --headlessRenderer 1 --virtualInput 1 --fs.readwrite=$PWD \ 30 | --lua.globals=UPDATESNAPSHOT=false --lua.globals=CI=true --lua.globals=__ROACT_17_MOCK_SCHEDULER__=true 31 | -------------------------------------------------------------------------------- /bin/convert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for file in *.js 4 | do 5 | mv "$file" "${file/.js/.lua}" 6 | done 7 | 8 | for file in *.lua 9 | do 10 | sed -i '' -e "s/\/\*/--[[/g" "$file" 11 | sed -i '' -e "s/\*\//]]/g" "$file" 12 | sed -i '' -e "s/\/\//--/g" "$file" 13 | sed -i '' -e "s/;$//g" "$file" 14 | sed -i '' -e "s/let\ /local\ /g" "$file" 15 | sed -i '' -e "s/const\ /local\ /g" "$file" 16 | sed -i '' -e "s/===/==/g" "$file" 17 | sed -i '' -e "s/!==/!=/g" "$file" 18 | sed -i '' -e "s/!=/~=/g" "$file" 19 | sed -i '' -e "s/&&/and/g" "$file" 20 | sed -i '' -e "s/||/or/g" "$file" 21 | sed -i '' -e "s/\ null/\ nil/g" "$file" 22 | sed -i '' -e "s/^’\s}$/end/g" "$file" 23 | sed -i '' -e "s/\s\}$/\ end/g" "$file" 24 | sed -i '' -e "s/if\ (/if\ /g" "$file" 25 | sed -i '' -e "s/export\ function\ /exports./g" "$file" 26 | sed -i '' -e "s/)\ {$/)/g" "$file" 27 | sed -i '' -e "s/^}$/end/g" "$file" 28 | sed -i '' -e "s/'object'/'table’'/g" "$file" 29 | sed -i '' -e "s/import\ /local\ /g" "$file" 30 | sed -i '' -e "s/\ from\ '/\ =\ require(Packages./g" "$file" 31 | # sed -i '' -E -e "s/(typeof\s+)(\w+)/typeof($2)/g" "$file" 32 | done 33 | -------------------------------------------------------------------------------- /bin/run-deep-tree-benchmark.luau: -------------------------------------------------------------------------------- 1 | local Packages = script.Parent.RoactAlignment 2 | local RotrieverWorkspace = Packages._Workspace 3 | 4 | local Roact = require(RotrieverWorkspace.React.React) 5 | local ReactRoblox = require(RotrieverWorkspace.ReactRoblox.ReactRoblox) 6 | 7 | local config = {} 8 | if _G.minSamples ~= nil then 9 | config.sampleCount = tonumber(_G.minSamples) 10 | end 11 | 12 | require(RotrieverWorkspace.React.Dev.PerformanceBenchmarks).deepTreeBenchmark( 13 | Roact, 14 | ReactRoblox 15 | )(config) 16 | -------------------------------------------------------------------------------- /bin/run-first-render-benchmark.luau: -------------------------------------------------------------------------------- 1 | local Packages = script.Parent.RoactAlignment 2 | local RotrieverWorkspace = Packages._Workspace 3 | 4 | local Roact = require(RotrieverWorkspace.React.React) 5 | local ReactRoblox = require(RotrieverWorkspace.ReactRoblox.ReactRoblox) 6 | local Scheduler = require(RotrieverWorkspace.Scheduler.Scheduler) 7 | local firstRenderBenchmark = 8 | require(RotrieverWorkspace.React.Dev.PerformanceBenchmarks).firstRenderBenchmark 9 | 10 | local config = { 11 | minSamples = 200, 12 | } 13 | if _G.minSamples ~= nil then 14 | config.minSamples = tonumber(_G.minSamples) 15 | end 16 | 17 | firstRenderBenchmark(Roact, ReactRoblox, Scheduler)(config) 18 | -------------------------------------------------------------------------------- /bin/run-frame-rate-benchmark.luau: -------------------------------------------------------------------------------- 1 | local Packages = script.Parent.RoactAlignment 2 | local RotrieverWorkspace = Packages._Workspace 3 | 4 | local Roact = require(RotrieverWorkspace.React.React) 5 | local ReactRoblox = require(RotrieverWorkspace.ReactRoblox.ReactRoblox) 6 | local Scheduler = require(RotrieverWorkspace.Scheduler.Scheduler) 7 | local frameRateBenchmark = 8 | require(RotrieverWorkspace.React.Dev.PerformanceBenchmarks).frameRateBenchmark 9 | 10 | local config = { 11 | minSamples = 600, 12 | } 13 | if _G.minSamples ~= nil then 14 | config.minSamples = tonumber(_G.minSamples) 15 | end 16 | 17 | frameRateBenchmark(Roact, ReactRoblox, Scheduler)(config) 18 | -------------------------------------------------------------------------------- /bin/run-sierpinski-triangle-benchmark.luau: -------------------------------------------------------------------------------- 1 | local Packages = script.Parent.RoactAlignment 2 | local RotrieverWorkspace = Packages._Workspace 3 | 4 | local Roact = require(RotrieverWorkspace.React.React) 5 | local ReactRoblox = require(RotrieverWorkspace.ReactRoblox.ReactRoblox) 6 | local sierpinskiTriangleBenchmark = require( 7 | RotrieverWorkspace.React.Dev.PerformanceBenchmarks 8 | ).sierpinskiTriangleBenchmark 9 | 10 | local config = {} 11 | if _G.minSamples ~= nil then 12 | config.sampleCount = tonumber(_G.minSamples) 13 | end 14 | 15 | sierpinskiTriangleBenchmark(Roact, ReactRoblox)(config) 16 | wait(1) 17 | -------------------------------------------------------------------------------- /bin/run-wide-tree-benchmark.luau: -------------------------------------------------------------------------------- 1 | local Packages = script.Parent.RoactAlignment 2 | local RotrieverWorkspace = Packages._Workspace 3 | 4 | local Roact = require(RotrieverWorkspace.React.React) 5 | local ReactRoblox = require(RotrieverWorkspace.ReactRoblox.ReactRoblox) 6 | local wideTreeBenchmark = 7 | require(RotrieverWorkspace.React.Dev.PerformanceBenchmarks).wideTreeBenchmark 8 | 9 | local config = {} 10 | if _G.minSamples ~= nil then 11 | config.sampleCount = tonumber(_G.minSamples) 12 | end 13 | 14 | wideTreeBenchmark(Roact, ReactRoblox)(config) 15 | -------------------------------------------------------------------------------- /bin/spec.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | local Workspace = script.Parent.RoactAlignment._Workspace 16 | local runCLI = require(Workspace.TestRunner.Dev.Jest).runCLI 17 | 18 | local processServiceExists, ProcessService = pcall(function() 19 | return game:GetService("ProcessService") 20 | end) 21 | 22 | local status, result = runCLI(Workspace, { 23 | verbose = if _G.verbose == "true" then true else nil, 24 | ci = _G.CI == "true", 25 | updateSnapshot = _G.UPDATESNAPSHOT == "true", 26 | }, { Workspace }):awaitStatus() 27 | 28 | if status == "Rejected" then 29 | print(result) 30 | end 31 | 32 | if 33 | status == "Resolved" 34 | and result.results.numFailedTestSuites == 0 35 | and result.results.numFailedTests == 0 36 | then 37 | if processServiceExists then 38 | ProcessService:ExitAsync(0) 39 | end 40 | end 41 | 42 | if processServiceExists then 43 | ProcessService:ExitAsync(1) 44 | end 45 | 46 | return nil 47 | -------------------------------------------------------------------------------- /bin/upstream-tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | REACT_PATH=$1 4 | EQUIVALENT_FOLDER=$2 5 | 6 | echo "Matching upstream files $REACT_PATH/$EQUIVALENT_FOLDER..." 7 | 8 | if [ $# -ne 2 ]; then 9 | echo "Usage: 10 | upstream-tag.sh 11 | 12 | path_to_react: 13 | The path to the local copy of the react repository; this is where we find 14 | the upstream version information (using 'git log' commands) 15 | 16 | target_folder: 17 | For example, if you run this script from the 'modules' folder, then 18 | target_folder should be 'packages'. If you run it from 'modules/react-is/src, 19 | then target_folder should be 'packages/react-is/src'" 20 | exit 1 21 | fi 22 | 23 | count=0 24 | for file in $(find * -name "*.lua") 25 | do 26 | if [[ "$file" == *"roblox-jest"* ]] || [[ "$file" == *"roblox-js-polyfill"* ]]; then 27 | echo "SKIP: $file is Roblox-only" 28 | continue 29 | fi 30 | 31 | if [[ "$file" == *".roblox."*"lua" ]]; then 32 | echo "SKIP: $file is Roblox-only" 33 | continue 34 | fi 35 | 36 | if [[ `head -n 1 $file` == "-- ROBLOX upstream:"* ]]; then 37 | echo "SKIP: $file already has 'upstream' comment" 38 | continue 39 | fi 40 | 41 | targetFileName="${file/-internal.spec/-test.internal}" 42 | targetFileName="${targetFileName/.spec/-test}" 43 | targetFileName="${targetFileName/.lua/.js}" 44 | targetFile="$EQUIVALENT_FOLDER/$targetFileName" 45 | 46 | if [[ ! -f "$REACT_PATH/$targetFile" ]]; then 47 | echo "SKIP: Equivalent file $targetFileName not found" 48 | continue 49 | fi 50 | 51 | pushd $REACT_PATH > /dev/null 52 | COMMIT=`git log -- $targetFile | head -n 1 | sed "s/commit //g"` 53 | REPO_PATH=`realpath --relative-to=$REACT_PATH $targetFile` 54 | PREFIX="-- ROBLOX upstream: https://github.com/facebook/react/blob/$COMMIT/$REPO_PATH" 55 | if [[ "$COMMIT" == "" ]]; then 56 | echo "SKIP: Could not find commit for $targetFile -> $file" 57 | continue 58 | fi 59 | 60 | count=$((count+1)) 61 | 62 | echo "" 63 | echo "Prepend to $file..." 64 | echo $PREFIX 65 | popd > /dev/null 66 | echo "$PREFIX 67 | $(cat $file)" > $file 68 | done 69 | 70 | echo -e "\nAdded upstream tag to $count files" -------------------------------------------------------------------------------- /default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactLua", 3 | "tree": { 4 | "$path": "roblox-model", 5 | "node_modules": { 6 | "$path": "node_modules" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /docs/api-reference/additional-libraries.md: -------------------------------------------------------------------------------- 1 | # Additional Libraries 2 | 3 | ## ReactIs 4 | *Under construction 🔨* 5 | 6 | ## ReactTestRender 7 | *Under construction 🔨* 8 | 9 | ## ReactDevToolsExtensions 10 | *Under construction 🔨* 11 | -------------------------------------------------------------------------------- /docs/bench.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | 13 |
14 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/diff-bridge-shutdown-method-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-bridge-shutdown-method-after.png -------------------------------------------------------------------------------- /docs/diff-bridge-shutdown-method-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-bridge-shutdown-method-before.png -------------------------------------------------------------------------------- /docs/diff-class-arrow-function-method-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-class-arrow-function-method-new.png -------------------------------------------------------------------------------- /docs/diff-class-arrow-function-method-old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-class-arrow-function-method-old.png -------------------------------------------------------------------------------- /docs/diff-packages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-packages.png -------------------------------------------------------------------------------- /docs/diff-spread-flow-type-no-deviation-anymore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-spread-flow-type-no-deviation-anymore.png -------------------------------------------------------------------------------- /docs/diff-upstream-comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsdotlua/react-lua/df4b4072e3aa78fc64850f8ca8b696084f305d75/docs/diff-upstream-comment.png -------------------------------------------------------------------------------- /docs/extra.css: -------------------------------------------------------------------------------- 1 | .md-typeset hr { 2 | border-bottom: 2px solid rgba(0, 0, 0, 0.15); 3 | } 4 | -------------------------------------------------------------------------------- /docs/images/aligned.svg: -------------------------------------------------------------------------------- 1 | AlignedAligned -------------------------------------------------------------------------------- /docs/images/apichange.svg: -------------------------------------------------------------------------------- 1 | API ChangeAPI Change -------------------------------------------------------------------------------- /docs/images/deviation.svg: -------------------------------------------------------------------------------- 1 | 2 | Deviation 3 | 4 | 5 | 6 | 7 | Deviation 8 | 9 | -------------------------------------------------------------------------------- /docs/images/reactjs.svg: -------------------------------------------------------------------------------- 1 | 2 | View React Docs 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | View React Docs 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/images/roblox.svg: -------------------------------------------------------------------------------- 1 | RobloxRoblox -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # React Lua 2 | 3 | React Lua is a Lua port of Facebook's [React JS](https://reactjs.org) UI library. 4 | 5 | By and large, [React's documentation](https://reactjs.org/docs/getting-started.html) should be able to serve most React Lua users' needs. This documentation site serves as a comprehensive guide to the _differences_ between Roact and React. 6 | 7 | If you're new to the React Lua library and want **to start learning the concepts of React**, begin with the [React JS documentation](https://reactjs.org/docs/getting-started.html). 8 | 9 | If you want **to find out if a React JS feature is present in React Lua (and if there are any differences to be aware of)**, check out the [API Reference](api-reference/react.md). 10 | 11 | If you're familiar with React JS and want **to learn where React Lua differs**, start with the [Deviations page](deviations.md). 12 | 13 | And if you want **to migrate an existing project from Legacy Roact to React Lua**, check out the guide on [Migrating From Legacy Roact](migrating-from-legacy/minimum-requirements.md). 14 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-material 3 | pymdown-extensions -------------------------------------------------------------------------------- /examples.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "React Test Place", 3 | "tree": { 4 | "$className": "DataModel", 5 | 6 | "ReplicatedStorage": { 7 | "$className": "ReplicatedStorage", 8 | "Packages": { 9 | "$path": "Packages" 10 | }, 11 | "DeveloperTools": { 12 | "$className": "BindableEvent" 13 | } 14 | }, 15 | 16 | "StarterPlayer": { 17 | "$className": "StarterPlayer", 18 | 19 | "StarterPlayerScripts": { 20 | "$className": "StarterPlayerScripts", 21 | 22 | "RoactExamples": { 23 | "$path": "examples" 24 | } 25 | } 26 | }, 27 | 28 | "Players": { 29 | "$className": "Players", 30 | "$properties": { 31 | "CharacterAutoLoads": false 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /foreman.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | rojo = { source = "rojo-rbx/rojo", version = "7.3.0" } 3 | selene = { source = "Kampfkarren/selene", version = "0.26.1" } 4 | stylua = { source = "JohnnyMorganz/StyLua", version = "=0.15.1" } 5 | wally = { github = "UpliftGames/wally", version = "=0.3.2" } 6 | luau-lsp = { github = "johnnymorganz/luau-lsp", version = "=1.35.0" } 7 | darklua = { github = "seaofvoices/darklua", version = "=0.14.0" } 8 | lune = { github = "filiptibell/lune", version = "0.7.11" } 9 | -------------------------------------------------------------------------------- /jest-setup/matchers/toErrorDev.luau: -------------------------------------------------------------------------------- 1 | local createConsoleMatcher = require(script.Parent.createConsoleMatcher) 2 | 3 | return createConsoleMatcher("error", "toErrorDev") 4 | -------------------------------------------------------------------------------- /jest-setup/matchers/toLogDev.luau: -------------------------------------------------------------------------------- 1 | local createConsoleMatcher = require(script.Parent.createConsoleMatcher) 2 | 3 | return createConsoleMatcher("log", "toLogDev") 4 | -------------------------------------------------------------------------------- /jest-setup/matchers/toWarnDev.luau: -------------------------------------------------------------------------------- 1 | local createConsoleMatcher = require(script.Parent.createConsoleMatcher) 2 | 3 | return createConsoleMatcher("warn", "toWarnDev") 4 | -------------------------------------------------------------------------------- /jest-setup/testSetupFile.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX note: no upstream 2 | -- loosely based on https://github.com/facebook/react/blob/9abc2785cb070148d64fae81e523246b90b92016/scripts/jest/setupTests.js 3 | -- in a way that we use this file to extend jestExpect with custom matchers 4 | 5 | local Packages = script.Parent.Parent.TestRunner 6 | local JestGlobals = require(Packages.Dev.JestGlobals) 7 | local jestExpect = JestGlobals.expect 8 | 9 | local InteractionTracingMatchers = 10 | require(script.Parent.matchers.interactionTracingMatchers) 11 | 12 | jestExpect.extend(require(script.Parent.matchers.reactTestMatchers)) 13 | jestExpect.extend({ 14 | toErrorDev = require(script.Parent.matchers.toErrorDev), 15 | toWarnDev = require(script.Parent.matchers.toWarnDev), 16 | toLogDev = require(script.Parent.matchers.toLogDev), 17 | toContainNoInteractions = InteractionTracingMatchers.toContainNoInteractions, 18 | toHaveBeenLastNotifiedOfInteraction = InteractionTracingMatchers.toHaveBeenLastNotifiedOfInteraction, 19 | toHaveBeenLastNotifiedOfWork = InteractionTracingMatchers.toHaveBeenLastNotifiedOfWork, 20 | toMatchInteraction = InteractionTracingMatchers.toMatchInteraction, 21 | toMatchInteractions = InteractionTracingMatchers.toMatchInteractions, 22 | }) 23 | 24 | _G.jest = true 25 | -------------------------------------------------------------------------------- /jest.config.luau: -------------------------------------------------------------------------------- 1 | local Workspace = script.Parent 2 | 3 | -- In case we need to specify a custom testSetupFile for a project, we need to do that in in a separate jest.config.lua file that's in the project's root folder. 4 | -- Therefore we specify the project here and provide it to the "projects" field in this config file. 5 | -- We also need to add the project to the "testPathIgnorePatterns" field so that Jest doesn't try to run the project's tests again. 6 | local projectsWithCustomJestConfig = { 7 | Workspace.ReactDevtoolsShared.ReactDevtoolsShared, 8 | } 9 | local testPathIgnorePatterns = {} 10 | local allProjects = { Workspace } 11 | 12 | for _, project in projectsWithCustomJestConfig do 13 | table.insert(testPathIgnorePatterns, project) 14 | table.insert(allProjects, project) 15 | end 16 | 17 | return { 18 | setupFilesAfterEnv = { Workspace.jest.testSetupFile }, 19 | projects = allProjects, 20 | testMatch = { 21 | "**/__tests__/*.(spec|test)", 22 | }, 23 | testPathIgnorePatterns = testPathIgnorePatterns, 24 | } 25 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: React Lua Documentation 2 | site_url: https://jsdotlua.github.io/react-lua/ 3 | repo_name: jsdotlua/react-lua 4 | repo_url: https://github.com/jsdotlua/react-lua 5 | 6 | theme: 7 | name: material 8 | palette: 9 | - media: "(prefers-color-scheme: light)" 10 | primary: indigo 11 | scheme: default 12 | toggle: 13 | icon: material/toggle-switch-off-outline 14 | name: Switch to dark mode 15 | - media: "(prefers-color-scheme: dark)" 16 | primary: indigo 17 | scheme: slate 18 | toggle: 19 | icon: material/toggle-switch 20 | name: Switch to light mode 21 | 22 | plugins: 23 | - search: 24 | separator: '[\s\-\.]' 25 | 26 | nav: 27 | - Home: index.md 28 | - Deviations: deviations.md 29 | - Configuration: configuration.md 30 | - Migrating From Legacy Roact: 31 | - Minimum Requirements: migrating-from-legacy/minimum-requirements.md 32 | - Add React Lua Dependency: migrating-from-legacy/upgrading-to-react-lua.md 33 | - Adopt New Features: migrating-from-legacy/adopt-new-features.md 34 | - Convert Legacy Conventions: migrating-from-legacy/convert-legacy-conventions.md 35 | - API Reference: 36 | - React: api-reference/react.md 37 | - ReactRoblox: api-reference/react-roblox.md 38 | - RoactCompat: api-reference/roact-compat.md 39 | - Additional Libraries: api-reference/additional-libraries.md 40 | - Benchmarks: bench.md 41 | 42 | extra_css: 43 | - extra.css 44 | 45 | markdown_extensions: 46 | - admonition 47 | - codehilite: 48 | guess_lang: false 49 | - toc: 50 | permalink: true 51 | - pymdownx.superfences 52 | # FIXME: Add this back when the tabbed extension is supported by docs-deploy 53 | # - pymdownx.tabbed: 54 | # alternate_style: false 55 | -------------------------------------------------------------------------------- /modules/TestRunner/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/TestRunner/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-runner", 3 | "tree": { 4 | "$className": "DataModel", 5 | "Src": { 6 | "$path": "src" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /modules/TestRunner/src/init.luau: -------------------------------------------------------------------------------- 1 | return {} 2 | -------------------------------------------------------------------------------- /modules/jest-react/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/jest-react/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/jest-react/README.md: -------------------------------------------------------------------------------- 1 | # jest-react 2 | A Roblox Lua port of the jest-react package re-exported from React. It provides Jest matchers and utilities for testing React Test Renderer. 3 | 4 | Status: ✔️ Ported 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/jest-react -------------------------------------------------------------------------------- /modules/jest-react/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jest-react", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/jest-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/jest-react", 3 | "version": "17.2.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/jest-react" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 16 | "@jsdotlua/luau-polyfill": "^1.2.6", 17 | "@jsdotlua/shared": "workspace:^" 18 | }, 19 | "devDependencies": { 20 | "npmluau": "^0.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modules/jest-react/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/jest-react/index.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | ]] 8 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 9 | local Object = LuauPolyfill.Object 10 | local exports = {} 11 | -- ROBLOX deviation START: extract to variable, fix import and export type 12 | -- Object.assign(exports, require(script.src.JestReact)) 13 | local jestReactModule = Object.assign(exports, require("./JestReact")) 14 | return exports :: typeof(exports) & typeof(jestReactModule) 15 | -- ROBLOX deviation END 16 | -------------------------------------------------------------------------------- /modules/react-cache/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-cache/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-cache/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # react-cache 4 | 5 | A basic cache for React applications. It also serves as a reference for more 6 | advanced caching implementations. 7 | 8 | This package is meant to be used alongside yet-to-be-released, experimental 9 | React features. It's unlikely to be useful in any other context. 10 | 11 | **Do not use in a real application.** We're publishing this early for 12 | demonstration purposes. 13 | 14 | **Use it at your own risk.** 15 | 16 | # No, Really, It Is Unstable 17 | 18 | The API ~~may~~ will change wildly between versions. 19 | -------------------------------------------------------------------------------- /modules/react-cache/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-cache", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-cache/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-cache", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-cache" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/scheduler": "workspace:^", 18 | "@jsdotlua/shared": "workspace:^" 19 | }, 20 | "devDependencies": { 21 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 22 | "@jsdotlua/jest-react": "workspace:^", 23 | "@jsdotlua/promise": "^3.5.0", 24 | "@jsdotlua/react-test-renderer": "workspace:^", 25 | "npmluau": "^0.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/react-cache/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-cache/index.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | -- ROBLOX deviation START: simplify 11 | -- local Packages --[[ ROBLOX comment: must define Packages module ]] 12 | -- local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 13 | -- local Object = LuauPolyfill.Object 14 | -- local exports = {} 15 | -- 16 | -- Object.assign(exports, require(script.src.ReactCacheOld)) 17 | -- return exports 18 | return require("./ReactCacheOld") 19 | -- ROBLOX deviation END 20 | -------------------------------------------------------------------------------- /modules/react-debug-tools/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-debug-tools/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-debug-tools/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactDebugTools", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /modules/react-debug-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-debug-tools", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-debug-tools" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react-reconciler": "workspace:^", 17 | "@jsdotlua/shared": "workspace:^" 18 | }, 19 | "devDependencies": { 20 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 21 | "@jsdotlua/promise": "^3.5.0", 22 | "@jsdotlua/react": "workspace:^", 23 | "@jsdotlua/react-test-renderer": "workspace:^", 24 | "@jsdotlua/scheduler": "workspace:^", 25 | "npmluau": "^0.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/react-debug-tools/src/ReactDebugTools.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-debug-tools/src/ReactDebugTools.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | local exports = {} 11 | local reactDebugHooksModule = require("./ReactDebugHooks") 12 | -- ROBLOX deviation START: add re-exporting of types 13 | export type HooksNode = reactDebugHooksModule.HooksNode 14 | export type HooksTree = reactDebugHooksModule.HooksTree 15 | -- ROBLOX deviation END 16 | local inspectHooks = reactDebugHooksModule.inspectHooks 17 | local inspectHooksOfFiber = reactDebugHooksModule.inspectHooksOfFiber 18 | exports.inspectHooks = inspectHooks 19 | exports.inspectHooksOfFiber = inspectHooksOfFiber 20 | return exports 21 | -------------------------------------------------------------------------------- /modules/react-debug-tools/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-debug-tools/index.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | ]] 8 | -- ROBLOX deviation START: simplify and re-export types 9 | -- local Packages --[[ ROBLOX comment: must define Packages module ]] 10 | -- local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 11 | -- local Object = LuauPolyfill.Object 12 | -- local exports = {} 13 | -- Object.assign(exports, require(script.src.ReactDebugTools)) 14 | -- return exports 15 | local reactDebugToolsModule = require("./ReactDebugTools") 16 | export type HooksNode = reactDebugToolsModule.HooksNode 17 | export type HooksTree = reactDebugToolsModule.HooksTree 18 | return reactDebugToolsModule 19 | -- ROBLOX deviation END 20 | -------------------------------------------------------------------------------- /modules/react-devtools-extensions/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-devtools-extensions/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-devtools-extensions/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-devtools-extensions", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /modules/react-devtools-extensions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-devtools-extensions", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-devtools-extensions" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/react-devtools-shared": "workspace:^", 18 | "@jsdotlua/react-roblox": "workspace:^", 19 | "@jsdotlua/shared": "workspace:^" 20 | }, 21 | "devDependencies": { 22 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 23 | "@jsdotlua/react-test-renderer": "workspace:^", 24 | "npmluau": "^0.1.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /modules/react-devtools-extensions/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX note: no upstream 2 | -- ROBLOX note: The setup function adds the glue required for DeveloperTools to initialize the Roact devtools correctly 3 | 4 | return { 5 | setup = function(debugMode: boolean) 6 | -- ROBLOX note: Set globals for React devtools to work 7 | _G.__DEV__ = true 8 | _G.__DEBUG__ = debugMode or false 9 | _G.__PROFILE__ = true 10 | _G.__EXPERIMENTAL__ = true 11 | -- ROBLOX note: Don't hide host coomponents as the current Developer Inspector uses on these to preserve a 12 | -- direct mapping between the Inspector tree and the Explorer tree as requested by design. 13 | _G.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = {} 14 | 15 | local ReactDevtoolsShared = require("@pkg/@jsdotlua/react-devtools-shared") 16 | local setup = require("./backend").setup 17 | local installHook = ReactDevtoolsShared.hook.installHook 18 | local Store = ReactDevtoolsShared.devtools.store 19 | 20 | -- ROBLOX note: Ensure that the global hook is installed before the injection into DevTools 21 | installHook(_G) 22 | 23 | -- ROBLOX note: Ensure that ReactRoblox is loaded after injection so that the ReactHostConfig is populated correctly 24 | require("@pkg/@jsdotlua/react") 25 | require("@pkg/@jsdotlua/react-roblox") 26 | 27 | local hook = _G.__REACT_DEVTOOLS_GLOBAL_HOOK__ 28 | 29 | -- ROBLOX note: Make sure that this method was called before ReactRoblox was first required, 30 | -- otherwise the profiler will not be enabled for the session. 31 | local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags 32 | if not ReactFeatureFlags.enableSchedulingProfiler then 33 | warn( 34 | "[DeveloperTools] React was initialized before DeveloperTools. Call inspector.setupReactDevtools before requiring React to enable profiling." 35 | ) 36 | end 37 | 38 | local result = setup(hook) 39 | 40 | -- ROBLOX note: The DeveloperTools library is only passed the ReactDevtoolsExtensions API to keep the 41 | -- devtools init process compact for users. Initialize the store so DeveloperTools doesn't also need to be 42 | -- passed the ReactDevtoolsShared API. 43 | return { 44 | agent = result.agent, 45 | bridge = result.bridge, 46 | hook = result.hook, 47 | store = Store.new(result.bridge), 48 | } 49 | end, 50 | } 51 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-devtools-shared/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-devtools-shared", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-devtools-shared", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-devtools-shared" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/react-debug-tools": "workspace:^", 18 | "@jsdotlua/react-is": "workspace:^", 19 | "@jsdotlua/react-reconciler": "workspace:^", 20 | "@jsdotlua/react-roblox": "workspace:^", 21 | "@jsdotlua/shared": "workspace:^" 22 | }, 23 | "devDependencies": { 24 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 25 | "@jsdotlua/promise": "^3.5.0", 26 | "@jsdotlua/react-test-renderer": "workspace:^", 27 | "@jsdotlua/scheduler": "workspace:^", 28 | "npmluau": "^0.1.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/__tests__/bridge.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/bridge-test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | ]] 8 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 9 | local describe = JestGlobals.describe 10 | local it = JestGlobals.it 11 | local beforeEach = JestGlobals.beforeEach 12 | local jestExpect = JestGlobals.expect 13 | local jest = JestGlobals.jest 14 | 15 | describe("bridge", function() 16 | local Bridge 17 | 18 | beforeEach(function() 19 | jest.resetModules() 20 | jest.useFakeTimers() 21 | Bridge = require("../bridge") 22 | end) 23 | 24 | it("should shutdown properly", function() 25 | local wall = { 26 | listen = jest.fn(function() 27 | return function() end 28 | end), 29 | send = jest.fn(), 30 | } 31 | local bridge = Bridge.new(wall) 32 | 33 | -- Check that we're wired up correctly. 34 | bridge:send("reloadAppForProfiling") 35 | jest.runAllTimers() 36 | jestExpect(wall.send).toHaveBeenCalledWith("reloadAppForProfiling") 37 | 38 | -- Should flush pending messages and then shut down. 39 | wall.send.mockClear() 40 | bridge:send("update", "1") 41 | bridge:send("update", "2") 42 | bridge:shutdown() 43 | jest.runAllTimers() 44 | jestExpect(wall.send).toHaveBeenCalledWith("update", "1") 45 | jestExpect(wall.send).toHaveBeenCalledWith("update", "2") 46 | jestExpect(wall.send).toHaveBeenCalledWith("shutdown") 47 | 48 | -- Verify that the Bridge doesn't send messages after shutdown. 49 | 50 | wall.send.mockClear() 51 | -- ROBLOX deviation: instead of spying on console, use toWarnDev matcher 52 | jestExpect(function() 53 | bridge:send("should not send") 54 | end).toWarnDev( 55 | 'Cannot send message "should not send" through a Bridge that has been shutdown.', 56 | { withoutStack = true } 57 | ) 58 | jest.runAllTimers() 59 | jestExpect(wall.send).never.toHaveBeenCalled() 60 | end) 61 | end) 62 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/__tests__/profilingUtils.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/profilingUtils-test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 12 | local jestExpect = JestGlobals.expect 13 | local describe = JestGlobals.describe 14 | local it = JestGlobals.it 15 | local beforeEach = JestGlobals.beforeEach 16 | 17 | describe("profiling utils", function() 18 | local utils 19 | beforeEach(function() 20 | utils = require("../devtools/views/Profiler/utils") 21 | end) 22 | it("should throw if importing older/unsupported data", function() 23 | jestExpect(function() 24 | return utils.prepareProfilingDataFrontendFromExport({ 25 | version = 0, 26 | dataForRoots = {}, 27 | }) 28 | end).toThrow('Unsupported profiler export version "0"') 29 | end) 30 | end) 31 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/backend/NativeStyleEditor/types.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/backend/NativeStyleEditor/types.js 2 | -- /** 3 | -- * Copyright (c) Facebook, Inc. and its affiliates. 4 | -- * 5 | -- * This source code is licensed under the MIT license found in the 6 | -- * LICENSE file in the root directory of this source tree. 7 | -- * 8 | -- * @flow 9 | -- */ 10 | type Object = { [string]: any } 11 | 12 | export type BoxStyle = { bottom: number, left: number, right: number, top: number } 13 | 14 | export type Layout = { 15 | x: number, 16 | y: number, 17 | width: number, 18 | height: number, 19 | left: number, 20 | top: number, 21 | margin: BoxStyle, 22 | padding: BoxStyle, 23 | } 24 | 25 | export type Style = Object 26 | 27 | export type StyleAndLayout = { id: number, style: Style | nil, layout: Layout | nil } 28 | 29 | return {} 30 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/backend/ReactSymbols.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/backend/ReactSymbols.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | ]] 8 | local exports = {} 9 | exports.CONCURRENT_MODE_NUMBER = 0xeacf 10 | exports.CONCURRENT_MODE_SYMBOL_STRING = "Symbol(react.concurrent_mode)" 11 | 12 | exports.CONTEXT_NUMBER = 0xeace 13 | exports.CONTEXT_SYMBOL_STRING = "Symbol(react.context)" 14 | 15 | exports.DEPRECATED_ASYNC_MODE_SYMBOL_STRING = "Symbol(react.async_mode)" 16 | 17 | exports.ELEMENT_NUMBER = 0xeac7 18 | exports.ELEMENT_SYMBOL_STRING = "Symbol(react.element)" 19 | 20 | exports.DEBUG_TRACING_MODE_NUMBER = 0xeae1 21 | exports.DEBUG_TRACING_MODE_SYMBOL_STRING = "Symbol(react.debug_trace_mode)" 22 | 23 | exports.FORWARD_REF_NUMBER = 0xead0 24 | exports.FORWARD_REF_SYMBOL_STRING = "Symbol(react.forward_ref)" 25 | 26 | exports.FRAGMENT_NUMBER = 0xeacb 27 | exports.FRAGMENT_SYMBOL_STRING = "Symbol(react.fragment)" 28 | 29 | exports.LAZY_NUMBER = 0xead4 30 | exports.LAZY_SYMBOL_STRING = "Symbol(react.lazy)" 31 | 32 | exports.MEMO_NUMBER = 0xead3 33 | exports.MEMO_SYMBOL_STRING = "Symbol(react.memo)" 34 | 35 | exports.OPAQUE_ID_NUMBER = 0xeae0 36 | exports.OPAQUE_ID_SYMBOL_STRING = "Symbol(react.opaque.id)" 37 | 38 | exports.PORTAL_NUMBER = 0xeaca 39 | exports.PORTAL_SYMBOL_STRING = "Symbol(react.portal)" 40 | 41 | exports.PROFILER_NUMBER = 0xead2 42 | exports.PROFILER_SYMBOL_STRING = "Symbol(react.profiler)" 43 | 44 | exports.PROVIDER_NUMBER = 0xeacd 45 | exports.PROVIDER_SYMBOL_STRING = "Symbol(react.provider)" 46 | 47 | exports.SCOPE_NUMBER = 0xead7 48 | exports.SCOPE_SYMBOL_STRING = "Symbol(react.scope)" 49 | 50 | exports.STRICT_MODE_NUMBER = 0xeacc 51 | exports.STRICT_MODE_SYMBOL_STRING = "Symbol(react.strict_mode)" 52 | 53 | exports.SUSPENSE_NUMBER = 0xead1 54 | exports.SUSPENSE_SYMBOL_STRING = "Symbol(react.suspense)" 55 | 56 | exports.SUSPENSE_LIST_NUMBER = 0xead8 57 | exports.SUSPENSE_LIST_SYMBOL_STRING = "Symbol(react.suspense_list)" 58 | 59 | return exports 60 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/backend/console.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/backend/console.js 2 | -- /** 3 | -- * Copyright (c) Facebook, Inc. and its affiliates. 4 | -- * 5 | -- * This source code is licensed under the MIT license found in the 6 | -- * LICENSE file in the root directory of this source tree. 7 | -- * 8 | -- * @flow 9 | -- */ 10 | 11 | local Types = require("./types") 12 | type ReactRenderer = Types.ReactRenderer 13 | 14 | local exports = {} 15 | 16 | -- ROBLOX FIXME: Stub for now 17 | function exports.patch(_object: { 18 | appendComponentStack: boolean, 19 | breakOnConsoleErrors: boolean, 20 | }): () end 21 | 22 | function exports.unpatch(): () end 23 | 24 | function exports.error(...) 25 | error(...) 26 | end 27 | 28 | function exports.warn(...) 29 | warn(...) 30 | end 31 | 32 | function exports.log(...) 33 | print(...) 34 | end 35 | 36 | function exports.registerRenderer(_renderer: ReactRenderer): () end 37 | 38 | return exports 39 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/clipboardjs.mock.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX TODO: stub for clipboardjs, remove when we know how we'll handle its intent in a Roblox way 2 | return {} 3 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/constants.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/constants.js 2 | -- /** 3 | -- * Copyright (c) Facebook, Inc. and its affiliates. 4 | -- * 5 | -- * This source code is licensed under the MIT license found in the 6 | -- * LICENSE file in the root directory of this source tree. 7 | -- * 8 | -- * @flow 9 | -- */ 10 | 11 | local exports = {} 12 | 13 | -- Flip this flag to true to enable verbose console debug logging. 14 | exports.__DEBUG__ = _G.__DEBUG__ 15 | 16 | exports.TREE_OPERATION_ADD = 1 17 | exports.TREE_OPERATION_REMOVE = 2 18 | exports.TREE_OPERATION_REORDER_CHILDREN = 3 19 | exports.TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4 20 | 21 | exports.LOCAL_STORAGE_FILTER_PREFERENCES_KEY = "React::DevTools::componentFilters" 22 | 23 | exports.SESSION_STORAGE_LAST_SELECTION_KEY = "React::DevTools::lastSelection" 24 | 25 | exports.SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = 26 | "React::DevTools::recordChangeDescriptions" 27 | 28 | exports.SESSION_STORAGE_RELOAD_AND_PROFILE_KEY = "React::DevTools::reloadAndProfile" 29 | 30 | exports.LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS = 31 | "React::DevTools::breakOnConsoleErrors" 32 | 33 | exports.LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY = "React::DevTools::appendComponentStack" 34 | 35 | exports.LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY = "React::DevTools::traceUpdatesEnabled" 36 | 37 | exports.PROFILER_EXPORT_VERSION = 4 38 | 39 | exports.CHANGE_LOG_URL = 40 | "https://github.com/facebook/react/blob/master/packages/react-devtools/CHANGELOG.md" 41 | 42 | exports.UNSUPPORTED_VERSION_URL = 43 | "https://reactjs.org/blog/2019/08/15/new-react-devtools.html#how-do-i-get-the-old-version-back" 44 | 45 | -- HACK 46 | -- 47 | -- Extracting during build time avoids a temporarily invalid state for the inline target. 48 | -- Sometimes the inline target is rendered before root styles are applied, 49 | -- which would result in e.g. NaN itemSize being passed to react-window list. 50 | -- 51 | local COMFORTABLE_LINE_HEIGHT 52 | local COMPACT_LINE_HEIGHT 53 | 54 | -- ROBLOX deviation: we won't use the CSS, and don't have a bundler, so always use the 'fallback' 55 | -- We can't use the Webpack loader syntax in the context of Jest, 56 | -- so tests need some reasonably meaningful fallback value. 57 | COMFORTABLE_LINE_HEIGHT = 15 58 | COMPACT_LINE_HEIGHT = 10 59 | 60 | exports.COMFORTABLE_LINE_HEIGHT = COMFORTABLE_LINE_HEIGHT 61 | exports.COMPACT_LINE_HEIGHT = COMPACT_LINE_HEIGHT 62 | 63 | return exports 64 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/devtools/init.luau: -------------------------------------------------------------------------------- 1 | return { 2 | utils = require("./utils"), 3 | store = require("./store"), 4 | cache = require("./cache"), 5 | devtools = { 6 | Components = { 7 | views = { 8 | types = require("./views/Components/types"), 9 | }, 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/devtools/views/Profiler/InteractionsChartBuilder.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 11 | local Array = LuauPolyfill.Array 12 | local Map = LuauPolyfill.Map 13 | 14 | type Map = LuauPolyfill.Map 15 | type Array = LuauPolyfill.Array 16 | 17 | local exports = {} 18 | 19 | local devtoolsTypes = require("../../types") 20 | type ProfilerStore = devtoolsTypes.ProfilerStore 21 | local typesModule = require("./types") 22 | type Interaction = typesModule.Interaction 23 | export type ChartData = { 24 | interactions: Array, 25 | lastInteractionTime: number, 26 | maxCommitDuration: number, 27 | } 28 | local cachedChartData: Map = Map.new() 29 | local function getChartData( 30 | ref: { profilerStore: ProfilerStore, rootID: number } 31 | ): ChartData 32 | local profilerStore, rootID = ref.profilerStore, ref.rootID 33 | if cachedChartData:has(rootID) then 34 | return (cachedChartData:get(rootID) :: any) :: ChartData 35 | end 36 | local dataForRoot = profilerStore:getDataForRoot(rootID) 37 | if dataForRoot == nil then 38 | error( 39 | string.format('Could not find profiling data for root "%s"', tostring(rootID)) 40 | ) 41 | end 42 | -- ROBLOX FIXME Luau: any cast necessary to work around: Property 'interactions' is not compatible. Type 'Array | Array | Array' could not be converted into 'Array' 43 | local commitData, interactions: any = dataForRoot.commitData, dataForRoot.interactions 44 | local lastInteractionTime = if #commitData > 0 45 | then commitData[#commitData].timestamp 46 | else 0 47 | local maxCommitDuration = 0 48 | Array.forEach(commitData, function(commitDatum) 49 | maxCommitDuration = math.max(maxCommitDuration, commitDatum.duration) 50 | end) 51 | local chartData = { 52 | interactions = Array.from(interactions:values()) :: Array, 53 | lastInteractionTime = lastInteractionTime, 54 | maxCommitDuration = maxCommitDuration, 55 | } 56 | cachedChartData:set(rootID, chartData) 57 | return chartData 58 | end 59 | exports.getChartData = getChartData 60 | local function invalidateChartData(): any? 61 | return cachedChartData:clear() 62 | end 63 | exports.invalidateChartData = invalidateChartData 64 | return exports 65 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX note: upstream doesn't have a root index.js, we may want to contribute a proper contract upstream 2 | return { 3 | backend = require("./backend"), 4 | bridge = require("./bridge"), 5 | devtools = require("./devtools"), 6 | hydration = require("./hydration"), 7 | hook = require("./hook"), 8 | utils = require("./utils"), 9 | } 10 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/jest.config.luau: -------------------------------------------------------------------------------- 1 | return { 2 | setupFilesAfterEnv = { script.Parent.__tests__.setupTests }, 3 | testMatch = { "**/*.(spec|test)" }, 4 | } 5 | -------------------------------------------------------------------------------- /modules/react-devtools-shared/src/storage.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/storage.js 2 | -- /* 3 | -- * Copyright (c) Facebook, Inc. and its affiliates. 4 | -- * 5 | -- * This source code is licensed under the MIT license found in the 6 | -- * LICENSE file in the root directory of this source tree. 7 | -- * 8 | -- */ 9 | 10 | local exports = {} 11 | if _G.__LOCALSTORAGE__ == nil then 12 | _G.__LOCALSTORAGE__ = {} 13 | end 14 | 15 | if _G.__SESSIONSTORAGE__ == nil then 16 | _G.__SESSIONSTORAGE__ = {} 17 | end 18 | 19 | -- ROBLOX FIXME: what's a high-performance storage that for temporal (current DM lifetime) and permanent (beyond current DM lifetime) 20 | local localStorage = _G.__LOCALSTORAGE__ 21 | local sessionStorage = _G.__SESSIONSTORAGE__ 22 | 23 | exports.localStorageGetItem = function(key: string): any 24 | return localStorage[key] 25 | end 26 | exports.localStorageRemoveItem = function(key: string): () 27 | localStorage[key] = nil 28 | end 29 | exports.localStorageSetItem = function(key: string, value: any): () 30 | localStorage[key] = value 31 | end 32 | exports.sessionStorageGetItem = function(key: string): any 33 | return sessionStorage[key] 34 | end 35 | exports.sessionStorageRemoveItem = function(key: string): () 36 | sessionStorage[key] = nil 37 | end 38 | exports.sessionStorageSetItem = function(key: string, value: any): () 39 | sessionStorage[key] = value 40 | end 41 | 42 | return exports 43 | -------------------------------------------------------------------------------- /modules/react-is/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-is/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-is/README.md: -------------------------------------------------------------------------------- 1 | # react-is 2 | A Roblox Lua port of the `react-is` package from React, which provides constants and runtime check functions for the various valid types of elements that can be created. 3 | 4 | Status: ✔️ Ported 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/react-is 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | * Includes some guards where we confirm that values are tables before 12 | 13 | ### ❌ Excluded 14 | -------------------------------------------------------------------------------- /modules/react-is/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-is", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-is/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-is", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-is" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/shared": "workspace:^" 16 | }, 17 | "devDependencies": { 18 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 19 | "@jsdotlua/luau-polyfill": "^1.2.6", 20 | "@jsdotlua/promise": "^3.5.0", 21 | "@jsdotlua/react": "workspace:^", 22 | "@jsdotlua/react-roblox": "workspace:^", 23 | "npmluau": "^0.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /modules/react-noop-renderer/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-noop-renderer/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-noop-renderer/README.md: -------------------------------------------------------------------------------- 1 | # ReactNoopRenderer 2 | A Roblox Lua port of the react-noop-renderer package from React. 3 | 4 | Original source: https://github.com/facebook/react/tree/master/packages/react-noop-renderer 5 | 6 | ## Status 7 | 8 | ### Ported 9 | Files: 10 | * `src/createReactNoop.js` -> `src/createReactNoop.lua` 11 | * `src/ReactNoop.js` -> `src/ReactNoop.lua` 12 | 13 | The entire implementation of the NoopRenderer, plus an entry point that exports all the relevant members. 14 | 15 | ### Not Ported 16 | Files: 17 | * `src/ReactNoopFlightClient.js` 18 | * `src/ReactNoopFlightServer.js` 19 | * `src/ReactNoopServer.js` 20 | 21 | Serves additional entry points that enable use with the `react-client` and `react-server` packages. We can revisit this if we port those 22 | 23 | Files: 24 | * `src/ReactNoopPersistent.js` 25 | 26 | A version of the NoopRenderer that's created with `useMutation = false`. If this is needed at some point, it'll be trivial to port. 27 | 28 | ### Intentional Deviations 29 | Currently, we're not supporting JSX or an equivalent markup syntax. Select parts of `createReactNoop.lua` are only partially translated and are commented out. -------------------------------------------------------------------------------- /modules/react-noop-renderer/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-noop-renderer", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-noop-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-noop-renderer", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-noop-renderer" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 16 | "@jsdotlua/luau-polyfill": "^1.2.6", 17 | "@jsdotlua/react-reconciler": "workspace:^", 18 | "@jsdotlua/scheduler": "workspace:^", 19 | "@jsdotlua/shared": "workspace:^" 20 | }, 21 | "devDependencies": { 22 | "npmluau": "^0.1.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/react-noop-renderer/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/e7b255341b059b4e2a109847395d0d0ba2633999/packages/react-noop-renderer/src/index.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | *]] 10 | 11 | --!strict 12 | return require("./ReactNoop") 13 | -------------------------------------------------------------------------------- /modules/react-reconciler/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-reconciler/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-reconciler/README.md: -------------------------------------------------------------------------------- 1 | # react-reconciler 2 | A Roblox Lua port of the `react-reconciler` package from React, which contains the core reconciler logic that drives the various renderers that can be attached. 3 | 4 | Status: 🔨 Port in progress 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/react-reconciler 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | 12 | #### Profiling 13 | 14 | ``` 15 | src/SchedulingProfiler.js 16 | src/__tests__/SchedulingProfiler-test.internal.js 17 | ``` 18 | 19 | Profiling logic used for debugging the Scheduler. Includes tests that produce flamegraphs of processed tasks. This functionality is gated behind the `enableProfiling` flag defined in `SchedulerFeatureFlags.js`. Additional functionality is gated behind the enableSchedulingProfiler in ReactFeatureFlags. When enabling the Scheduling Profiler, you'll need to plug in a table with a `mark` function to the `_G.performance` global, like this: 20 | ```lua 21 | _G.performance = { 22 | mark = function(str) 23 | debug.profileBegin(str) 24 | debug.profileEnd(str) 25 | } 26 | ``` 27 | 28 | #### Debug Tracing 29 | 30 | ``` 31 | src/DebugTracing.js 32 | src/__tests__/DebugTracing-test.internal.js 33 | ``` 34 | 35 | Debug Tracing is enabled with the enableDebugTracing ReactFeatureFlag. The current Lua implementation outputs using Lua `print`, and strips out the color and styling versus upstream. We may want to more deeply customize this based on real-world use cases of Roblox UI developers. 36 | 37 | 38 | ### ❌ Excluded 39 | 40 | ``` 41 | src/__tests__/ReactSuspenseList-test.js 42 | ``` 43 | 44 | The initial release of React Lua includes support for Suspense, but not the unstable SuspenseList API. This was purely to pull in the delivery schedule and narrow the support surface for the initial release. 45 | -------------------------------------------------------------------------------- /modules/react-reconciler/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-reconciler", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-reconciler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-reconciler", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-reconciler" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/promise": "^3.5.0", 17 | "@jsdotlua/react": "workspace:^", 18 | "@jsdotlua/scheduler": "workspace:^", 19 | "@jsdotlua/shared": "workspace:^" 20 | }, 21 | "devDependencies": { 22 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 23 | "@jsdotlua/jest-react": "workspace:^", 24 | "@jsdotlua/react-cache": "workspace:^", 25 | "@jsdotlua/react-devtools-shared": "workspace:^", 26 | "@jsdotlua/react-noop-renderer": "workspace:^", 27 | "@jsdotlua/react-roblox": "workspace:^", 28 | "@jsdotlua/react-test-renderer": "workspace:^", 29 | "npmluau": "^0.1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/MaxInts.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/MaxInts.js 3 | -- /** 4 | -- * Copyright (c) Facebook, Inc. and its affiliates. 5 | -- * 6 | -- * This source code is licensed under the MIT license found in the 7 | -- * LICENSE file in the root directory of this source tree. 8 | -- * 9 | -- * @flow 10 | -- */ 11 | 12 | -- // Max 31 bit integer. The max integer size in V8 for 32-bit systems. 13 | -- // Math.pow(2, 30) - 1 14 | -- // 0b111111111111111111111111111111 15 | return { MAX_SIGNED_31_BIT_INT = 1073741823 } 16 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactCapturedValue.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactCapturedValue.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | local ReactInternalTypes = require("./ReactInternalTypes") 13 | type Fiber = ReactInternalTypes.Fiber 14 | 15 | local getStackByFiberInDevAndProd = 16 | require("./ReactFiberComponentStack").getStackByFiberInDevAndProd 17 | 18 | export type CapturedValue = { 19 | value: T, 20 | source: Fiber | nil, 21 | stack: string | nil, 22 | } 23 | 24 | local exports = {} 25 | 26 | exports.createCapturedValue = function(value: T, source: Fiber | nil): CapturedValue 27 | -- If the value is an error, call this function immediately after it is thrown 28 | -- so the stack is accurate. 29 | return { 30 | value = value, 31 | source = source, 32 | stack = getStackByFiberInDevAndProd(source), 33 | } 34 | end 35 | 36 | return exports 37 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberErrorDialog.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactFiberErrorDialog.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | -- This module is forked in different environments. 13 | -- By default, return `true` to log errors to the console. 14 | -- Forks can return `false` if this isn't desirable. 15 | local exports = {} 16 | 17 | exports.showErrorDialog = function(boundary, errorInfo): boolean 18 | -- ROBLOX TODO: we may replace this with something that sends telemetry LUAFDN-222 19 | return true 20 | end 21 | 22 | return exports 23 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberHostConfig.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/9ac42dd074c42b66ecc0334b75200b1d2989f892/packages/react-reconciler/src/ReactFiberHostConfig.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | --[[ eslint-disable react-internal/invariant-args ]] 13 | 14 | -- ROBLOX FIXME: Cannot carry types over via the module overriding that's in use 15 | -- here; this is a particularly tricky case of cross-dependency type definitions 16 | -- Use a common set of typedefs across ReactTestHostConfig and ReactRobloxHostTypes 17 | type Object = { [string]: any } 18 | 19 | export type Instance = Object 20 | export type HostInstance = Instance 21 | export type TextInstance = Instance 22 | export type Container = Object 23 | export type HostContext = Object 24 | export type HydratableInstance = Instance | SuspenseInstance 25 | export type SuspenseInstance = Object 26 | export type PublicInstance = HostInstance 27 | 28 | export type Type = string 29 | export type Props = Object 30 | export type ChildSet = {} -- void, unused 31 | export type RendererInspectionConfig = Object 32 | 33 | -- if _G.__NO_LOADMODULE__ then 34 | local exports: { [string]: any } = {} 35 | return exports 36 | -- end 37 | 38 | -- -- We expect that our Rollup, Jest, and Flow configurations 39 | -- -- always shim this module with the corresponding host config 40 | -- -- (either provided by a renderer, or a generic shim for npm). 41 | -- -- 42 | -- -- We should never resolve to this file, but it exists to make 43 | -- -- sure that if we *do* accidentally break the configuration, 44 | -- -- the failure isn't silent. 45 | 46 | -- -- deviation: FIXME (roblox): is there a way to configure luau to account for this module 47 | -- -- being shimmed? 48 | -- error('This module must be shimmed by a specific renderer.') 49 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberLazyComponent.new.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/17f582e0453b808860be59ed3437c6a426ae52de/packages/react-reconciler/src/ReactFiberLazyComponent.new.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | type Object = { [any]: any } 13 | 14 | local function resolveDefaultProps(Component: any, baseProps: Object): Object 15 | -- ROBLOX deviation: check if type is table before checking defaultProps to prevent non-table index 16 | if Component and typeof(Component) == "table" and Component.defaultProps then 17 | -- Resolve default props. Taken from ReactElement 18 | -- ROBLOX FIXME Luau: hard cast to object until we can model this better in Luau. avoids Expected type table, got 'Object & any & any & { [any]: any }' instead 19 | local props = table.clone(baseProps) :: Object 20 | local defaultProps = Component.defaultProps 21 | for propName, _ in defaultProps do 22 | if props[propName] == nil then 23 | props[propName] = defaultProps[propName] 24 | end 25 | end 26 | return props 27 | end 28 | return baseProps 29 | end 30 | 31 | return { 32 | resolveDefaultProps = resolveDefaultProps, 33 | } 34 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberOffscreenComponent.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/1faf9e3dd5d6492f3607d5c721055819e4106bc6/packages/react-reconciler/src/ReactFiberOffscreenComponent.js 2 | --!strict 3 | -- /** 4 | -- * Copyright (c) Facebook, Inc. and its affiliates. 5 | -- * 6 | -- * This source code is licensed under the MIT license found in the 7 | -- * LICENSE file in the root directory of this source tree. 8 | -- * 9 | -- * @flow 10 | -- */ 11 | 12 | local ReactTypes = require("@pkg/@jsdotlua/shared") 13 | type ReactNodeList = ReactTypes.ReactNodeList 14 | 15 | local ReactFiberLanes = require("./ReactFiberLane") 16 | type Lanes = ReactFiberLanes.Lanes 17 | 18 | export type OffscreenProps = { 19 | -- TODO: Pick an API before exposing the Offscreen type. I've chosen an enum 20 | -- for now, since we might have multiple variants. For example, hiding the 21 | -- content without changing the layout. 22 | -- 23 | -- Default mode is visible. Kind of a weird default for a component 24 | -- called "Offscreen." Possible alt: ? 25 | mode: string | nil, 26 | children: ReactNodeList, 27 | } 28 | 29 | -- We use the existence of the state object as an indicator that the component 30 | -- is hidden. 31 | export type OffscreenState = { 32 | -- TODO: This doesn't do anything, yet. It's always NoLanes. But eventually it 33 | -- will represent the pending work that must be included in the render in 34 | -- order to unhide the component. 35 | baseLanes: Lanes, 36 | } 37 | 38 | return {} 39 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberReconciler.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/faa697f4f9afe9f1c98e315b2a9e70f5a74a7a74/packages/react-reconciler/src/ReactFiberReconciler.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | -- deviation: old version of reconciler not ported 13 | return require("./ReactFiberReconciler.new.luau") 14 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberSchedulerPriorities.roblox.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/d17086c7c813402a550d15a2f56dc43f1dbd1735/packages/react-reconciler/src/SchedulerWithReactIntegration.new.js 3 | --[[ 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | -- deviation: Type definition and values extracted from 13 | -- SchedulerWithReactIntegration.new. This helps avoid a cyclic dependency that 14 | -- can occur between SchedulerWithReactIntegration.new, ReactFiberLanes, and 15 | -- various files that depend upon them 16 | 17 | export type ReactPriorityLevel = number 18 | 19 | local exports: { [string]: ReactPriorityLevel } = { 20 | -- // Except for NoPriority, these correspond to Scheduler priorities. We use 21 | -- // ascending numbers so we can compare them like numbers. They start at 90 to 22 | -- // avoid clashing with Scheduler's priorities. 23 | ImmediatePriority = 99, 24 | UserBlockingPriority = 98, 25 | NormalPriority = 97, 26 | LowPriority = 96, 27 | IdlePriority = 95, 28 | -- // NoPriority is the absence of priority. Also React-only. 29 | NoPriority = 90, 30 | } 31 | 32 | return exports 33 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberTransition.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/ddd1faa1972b614dfbfae205f2aa4a6c0b39a759/packages/react-reconciler/src/ReactFiberTransition.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | local ReactSharedInternals = require("@pkg/@jsdotlua/shared").ReactSharedInternals 13 | 14 | local ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig 15 | 16 | return { 17 | NoTransition = 0, 18 | requestCurrentTransition = function(): number 19 | return ReactCurrentBatchConfig.transition 20 | end, 21 | } 22 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactFiberWorkInProgress.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX deviation: this is an extraction of a single state field 3 | -- (and associated mutation/getters) from ReactFiberWorkLooop.new 4 | -- which allows us to break dependency cycles involving that module 5 | -- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactFiberWorkLoop.new.js 6 | --[[* 7 | * Copyright (c) Facebook, Inc. and its affiliates. 8 | * 9 | * This source code is licensed under the MIT license found in the 10 | * LICENSE file in the root directory of this source tree. 11 | * 12 | * @flow 13 | ]] 14 | 15 | local ReactFiberLane = require("./ReactFiberLane") 16 | local _workInProgressRootSkippedLanes: Lanes = ReactFiberLane.NoLanes 17 | local mergeLanes = ReactFiberLane.mergeLanes 18 | type Lanes = ReactFiberLane.Lanes 19 | type Lane = ReactFiberLane.Lane 20 | 21 | local exports = {} 22 | 23 | -- ROBLOX TODO: turn this into newindex property accessor 24 | exports.workInProgressRootSkippedLanes = function(value: Lanes?): Lanes 25 | if value == nil then 26 | return _workInProgressRootSkippedLanes 27 | end 28 | 29 | -- ROBLOX FIXME Luau: Luau should narrow based on guard above 30 | _workInProgressRootSkippedLanes = value :: Lanes 31 | return _workInProgressRootSkippedLanes 32 | end 33 | 34 | exports.markSkippedUpdateLanes = function(lane: Lane | Lanes): () 35 | _workInProgressRootSkippedLanes = mergeLanes(lane, _workInProgressRootSkippedLanes) 36 | end 37 | 38 | return exports 39 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactHookEffectTags.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/16654436039dd8f16a63928e71081c7745872e8f/packages/react-reconciler/src/ReactHookEffectTags.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | export type HookFlags = number 13 | 14 | return { 15 | --[[ ]] 16 | NoFlags = 0b000, 17 | 18 | -- Represents whether effect should fire. 19 | --[[ ]] 20 | HasEffect = 0b001, 21 | 22 | -- Represents the phase in which the effect (not the clean-up) fires. 23 | --[[ ]] 24 | Layout = 0b010, 25 | --[[ ]] 26 | Passive = 0b100, 27 | } 28 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactPortal.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/142d4f1c00c66f3d728177082dbc027fd6335115/packages/react-reconciler/src/ReactPortal.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | local REACT_PORTAL_TYPE = require("@pkg/@jsdotlua/shared").ReactSymbols.REACT_PORTAL_TYPE 12 | 13 | local ReactTypes = require("@pkg/@jsdotlua/shared") 14 | type ReactNodeList = ReactTypes.ReactNodeList 15 | type ReactPortal = ReactTypes.ReactPortal 16 | 17 | local function createPortal( 18 | children: ReactNodeList, 19 | containerInfo: any, 20 | -- TODO: figure out the API for cross-renderer implementation. 21 | implementation: any, 22 | key: string? 23 | ): ReactPortal 24 | if key ~= nil then 25 | key = tostring(key) 26 | end 27 | return { 28 | -- This tag allow us to uniquely identify this as a React Portal 29 | ["$$typeof"] = REACT_PORTAL_TYPE, 30 | key = key, 31 | children = children, 32 | containerInfo = containerInfo, 33 | implementation = implementation, 34 | } 35 | end 36 | 37 | return { 38 | createPortal = createPortal, 39 | } 40 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactRootTags.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/ReactRootTags.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | export type RootTag = number 13 | 14 | return { 15 | LegacyRoot = 0, 16 | BlockingRoot = 1, 17 | ConcurrentRoot = 2, 18 | } 19 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactTypeOfMode.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/22dc2e42bdc00d87fc19c5e75fc7c0b3fdcdc572/packages/react-reconciler/src/ReactTypeOfMode.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | export type TypeOfMode = number 13 | 14 | return { 15 | NoMode = 0b00000, 16 | StrictMode = 0b00001, 17 | -- TODO: Remove BlockingMode and ConcurrentMode by reading from the root 18 | -- tag instead 19 | BlockingMode = 0b00010, 20 | ConcurrentMode = 0b00100, 21 | ProfileMode = 0b01000, 22 | DebugTracingMode = 0b10000, 23 | } 24 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/ReactWorkTags.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactWorkTags.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | export type WorkTag = number 13 | 14 | return { 15 | FunctionComponent = 0, 16 | ClassComponent = 1, 17 | IndeterminateComponent = 2, -- Before we know whether it is function or class 18 | HostRoot = 3, -- Root of a host tree. Could be nested inside another node. 19 | HostPortal = 4, -- A subtree. Could be an entry point to a different renderer. 20 | HostComponent = 5, 21 | HostText = 6, 22 | Fragment = 7, 23 | Mode = 8, 24 | ContextConsumer = 9, 25 | ContextProvider = 10, 26 | ForwardRef = 11, 27 | Profiler = 12, 28 | SuspenseComponent = 13, 29 | MemoComponent = 14, 30 | SimpleMemoComponent = 15, 31 | LazyComponent = 16, 32 | IncompleteClassComponent = 17, 33 | DehydratedFragment = 18, 34 | SuspenseListComponent = 19, 35 | FundamentalComponent = 20, 36 | ScopeComponent = 21, 37 | Block = 22, 38 | OffscreenComponent = 23, 39 | LegacyHiddenComponent = 24, 40 | } 41 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactClassSetStateCallback.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/d7dce572c7453737a685e791e7afcbc7e2b2fe16/packages/react-reconciler/src/__tests__/ReactClassSetStateCallback-test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @emails react-core 9 | * @jest-environment node 10 | ]] 11 | 12 | --[[ eslint-disable no-func-assign ]] 13 | local React 14 | 15 | local ReactNoop 16 | local Scheduler 17 | 18 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 19 | local jestExpect = JestGlobals.expect 20 | local beforeEach = JestGlobals.beforeEach 21 | local it = JestGlobals.it 22 | local jest = JestGlobals.jest 23 | 24 | beforeEach(function() 25 | jest.resetModules() 26 | jest.useFakeTimers() 27 | 28 | React = require("@pkg/@jsdotlua/react") 29 | ReactNoop = require("@pkg/@jsdotlua/react-noop-renderer") 30 | Scheduler = require("@pkg/@jsdotlua/scheduler") 31 | end) 32 | 33 | local function Text(props) 34 | Scheduler.unstable_yieldValue(props.text) 35 | return React.createElement("span", { 36 | prop = props.text, 37 | }) 38 | end 39 | 40 | it( 41 | "regression: setState callback (2nd arg) should only fire once, even after a rebase", 42 | function() 43 | local app 44 | local App = React.Component:extend("App") 45 | function App:init() 46 | self:setState({ step = 0 }) 47 | end 48 | function App:render() 49 | app = self 50 | return React.createElement(Text, { text = self.state.step }) 51 | end 52 | 53 | local root = ReactNoop.createRoot() 54 | ReactNoop.act(function() 55 | root.render(React.createElement(App)) 56 | end) 57 | jestExpect(Scheduler).toHaveYielded({ 0 }) 58 | 59 | ReactNoop.act(function() 60 | app:setState({ step = 1 }, function() 61 | return Scheduler.unstable_yieldValue("Callback 1") 62 | end) 63 | 64 | ReactNoop.flushSync(function() 65 | app:setState({ step = 2 }, function() 66 | return Scheduler.unstable_yieldValue("Callback 2") 67 | end) 68 | end) 69 | end) 70 | jestExpect(Scheduler).toHaveYielded({ 2, "Callback 2", 2, "Callback 1" }) 71 | end 72 | ) 73 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactFiberComponentStack.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 2 | local Error = LuauPolyfill.Error 3 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 4 | local jestExpect = JestGlobals.expect 5 | local describe = JestGlobals.describe 6 | local beforeEach = JestGlobals.beforeEach 7 | local it = JestGlobals.it 8 | local jest = JestGlobals.jest 9 | local ReactInternalTypes = require("../ReactInternalTypes") 10 | type Fiber = ReactInternalTypes.Fiber 11 | 12 | local ReactFiberComponentStack 13 | 14 | describe("ReactFiberComponentStack", function() 15 | beforeEach(function() 16 | jest.resetModules() 17 | ReactFiberComponentStack = require("../ReactFiberComponentStack") 18 | end) 19 | 20 | it("given a nil fiber then it gives correct error message", function() 21 | local message = 22 | ReactFiberComponentStack.getStackByFiberInDevAndProd((nil :: any) :: Fiber) 23 | jestExpect(message).toContain("attempt to index nil") 24 | end) 25 | 26 | it("given a fiber that throws Error then it gives correct error message", function() 27 | local throwingFiber = {} 28 | setmetatable(throwingFiber, { 29 | __index = function(t, k) 30 | if k == "tag" then 31 | error(Error.new("this was an error object in a spec file")) 32 | end 33 | return nil 34 | end, 35 | }) 36 | 37 | local message = ReactFiberComponentStack.getStackByFiberInDevAndProd( 38 | (throwingFiber :: any) :: Fiber 39 | ) 40 | jestExpect(message).toContain("this was an error object in a spec file") 41 | end) 42 | 43 | it( 44 | "given a fiber that throws a non-Error table then it gives correct error message", 45 | function() 46 | local customErrorTable = {} 47 | setmetatable(customErrorTable, { 48 | __tostring = function(t, k) 49 | return "this was a custom __tostring" 50 | end, 51 | }) 52 | 53 | local throwingFiber = {} 54 | setmetatable(throwingFiber, { 55 | __index = function(t, k) 56 | if k == "tag" then 57 | error(customErrorTable) 58 | end 59 | return nil 60 | end, 61 | }) 62 | 63 | local message = ReactFiberComponentStack.getStackByFiberInDevAndProd( 64 | (throwingFiber :: any) :: Fiber 65 | ) 66 | jestExpect(message).toContain("this was a custom __tostring") 67 | end 68 | ) 69 | end) 70 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactFiberContext-internal.spec.luau: -------------------------------------------------------------------------------- 1 | -- awaiting pull request: https://github.com/facebook/react/pull/20155 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @emails react-core 9 | * @jest-environment node 10 | ]] 11 | 12 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 13 | local jestExpect = JestGlobals.expect 14 | local jest = JestGlobals.jest 15 | local beforeEach = JestGlobals.beforeEach 16 | local describe = JestGlobals.describe 17 | local it = JestGlobals.it 18 | 19 | local ReactFiberContext 20 | local ReactFiber 21 | local ReactRootTags 22 | local ReactFeatureFlags 23 | 24 | beforeEach(function() 25 | jest.resetModules() 26 | 27 | ReactFiberContext = require("../ReactFiberContext.new.luau") 28 | ReactFiber = require("../ReactFiber.new.luau") 29 | ReactRootTags = require("../ReactRootTags") 30 | ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags 31 | ReactFeatureFlags.disableLegacyContext = false 32 | end) 33 | 34 | describe("Context stack", function() 35 | it("should throw when pushing to top level of non-empty stack", function() 36 | local fiber = ReactFiber.createHostRootFiber(ReactRootTags.BlockingRoot) 37 | local context = { 38 | foo = 1, 39 | } 40 | -- The first call here is a valid use of pushTopLevelContextObject 41 | ReactFiberContext.pushTopLevelContextObject(fiber, context, true) 42 | jestExpect(function() 43 | local moreContext = { 44 | bar = 2, 45 | } 46 | ReactFiberContext.pushTopLevelContextObject(fiber, moreContext, true) 47 | end).toThrow("Unexpected context found on stack.") 48 | end) 49 | 50 | it("should throw if when invalidating a provider that isn't initialized", function() 51 | local fiber = ReactFiber.createHostRootFiber(ReactRootTags.BlockingRoot) 52 | jestExpect(function() 53 | ReactFiberContext.invalidateContextProvider(fiber, nil, true) 54 | end).toThrow("Expected to have an instance by this point.") 55 | end) 56 | end) 57 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactFiberRoot.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | -- awaiting pull request: https://github.com/facebook/react/pull/20155 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @emails react-core 9 | * @jest-environment node 10 | ]] 11 | 12 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 13 | local jestExpect = JestGlobals.expect 14 | local jest = JestGlobals.jest 15 | local beforeEach = JestGlobals.beforeEach 16 | local it = JestGlobals.it 17 | 18 | local ReactFiberRoot 19 | local ReactRootTags 20 | 21 | beforeEach(function() 22 | jest.resetModules() 23 | 24 | ReactFiberRoot = require("../ReactFiberRoot.new.luau") 25 | ReactRootTags = require("../ReactRootTags") 26 | end) 27 | 28 | it("should properly initialize a fiber created with createFiberRoot", function() 29 | local fiberRoot = 30 | ReactFiberRoot.createFiberRoot({}, ReactRootTags.BlockingRoot, false) 31 | 32 | jestExpect(fiberRoot.current).toBeDefined() 33 | jestExpect(fiberRoot.current.updateQueue).toBeDefined() 34 | end) 35 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactFiberStack-test.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 2 | local jestExpect = JestGlobals.expect 3 | local describe = JestGlobals.describe 4 | local beforeEach = JestGlobals.beforeEach 5 | local jest = JestGlobals.jest 6 | local it = JestGlobals.it 7 | 8 | local ReactFiberStack 9 | 10 | describe("ReactFiberStack", function() 11 | beforeEach(function() 12 | jest.resetModules() 13 | ReactFiberStack = require("../ReactFiberStack.new.luau") 14 | end) 15 | 16 | it("creates a cursor with the given default value", function() 17 | local defaultValue = { foo = 3 } 18 | jestExpect(ReactFiberStack.createCursor(defaultValue)).toEqual({ 19 | current = defaultValue, 20 | }) 21 | end) 22 | 23 | it("initializes the stack empty", function() 24 | jestExpect(ReactFiberStack.isEmpty()).toBe(true) 25 | end) 26 | 27 | describe("stack manipulations", function() 28 | local cursor 29 | local fiber 30 | 31 | beforeEach(function() 32 | cursor = ReactFiberStack.createCursor(nil) 33 | fiber = {} 34 | end) 35 | 36 | it("pushes an element and the stack is not empty", function() 37 | ReactFiberStack.push(cursor, true, fiber) 38 | jestExpect(ReactFiberStack.isEmpty()).toBe(false) 39 | end) 40 | 41 | it("pushes an element and assigns the value to the cursor", function() 42 | local pushedElement = { foo = 3 } 43 | ReactFiberStack.push(cursor, pushedElement, fiber) 44 | jestExpect(cursor.current).toEqual(pushedElement) 45 | end) 46 | 47 | it("pushes an element, pops it back and the stack is empty", function() 48 | ReactFiberStack.push(cursor, true, fiber) 49 | ReactFiberStack.pop(cursor, fiber) 50 | jestExpect(ReactFiberStack.isEmpty()).toBe(true) 51 | end) 52 | 53 | it( 54 | "pushes an element, pops it back and the cursor has its initial value", 55 | function() 56 | local initialCursorValue = "foo" 57 | cursor.current = initialCursorValue 58 | 59 | ReactFiberStack.push(cursor, true, fiber) 60 | ReactFiberStack.pop(cursor, fiber) 61 | jestExpect(cursor.current).toBe(initialCursorValue) 62 | end 63 | ) 64 | end) 65 | end) 66 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactFiberSuspenseContext.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 2 | local jestExpect = JestGlobals.expect 3 | local jest = JestGlobals.jest 4 | local beforeEach = JestGlobals.beforeEach 5 | local describe = JestGlobals.describe 6 | local it = JestGlobals.it 7 | local ReactFiber = require("../ReactFiber.new.luau") 8 | 9 | local ReactFiberSuspenseContext 10 | 11 | describe("ReactFiberSuspenseContext", function() 12 | beforeEach(function() 13 | jest.resetModules() 14 | ReactFiberSuspenseContext = require("../ReactFiberSuspenseContext.new.luau") 15 | end) 16 | 17 | describe("suspense context stack", function() 18 | local someContext 19 | local fiber 20 | local suspenseStackCursor 21 | 22 | beforeEach(function() 23 | someContext = 0b1000 24 | fiber = ReactFiber.createFiberFromText("", 0, 0) 25 | suspenseStackCursor = ReactFiberSuspenseContext.suspenseStackCursor 26 | end) 27 | 28 | it("pushes the context and assigns the value to the cursor", function() 29 | ReactFiberSuspenseContext.pushSuspenseContext(fiber, someContext) 30 | jestExpect(suspenseStackCursor).toEqual({ current = someContext }) 31 | end) 32 | 33 | it("pushes and pops and sets the cursor to its initial value", function() 34 | local initialValue = suspenseStackCursor.current 35 | 36 | ReactFiberSuspenseContext.pushSuspenseContext(fiber, someContext) 37 | ReactFiberSuspenseContext.popSuspenseContext(fiber) 38 | jestExpect(suspenseStackCursor).toEqual({ current = initialValue }) 39 | end) 40 | end) 41 | 42 | describe("hasSuspenseContext", function() 43 | it("is true for parent context and its subtree context", function() 44 | local subtree = 0b1000 45 | local parent = 46 | ReactFiberSuspenseContext.addSubtreeSuspenseContext(10000, subtree) 47 | 48 | jestExpect(ReactFiberSuspenseContext.hasSuspenseContext(parent, subtree)).toBe( 49 | true 50 | ) 51 | end) 52 | 53 | it("is false for two different context", function() 54 | jestExpect(ReactFiberSuspenseContext.hasSuspenseContext(0b1000, 0b10000)).toBe( 55 | false 56 | ) 57 | end) 58 | end) 59 | end) 60 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactIncrementalErrorReplay.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @emails react-core 9 | * @jest-environment node 10 | --]] 11 | --!strict 12 | 13 | local React 14 | local ReactNoop 15 | local Scheduler 16 | 17 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 18 | local jestExpect = JestGlobals.expect 19 | local beforeEach = JestGlobals.beforeEach 20 | local jest = JestGlobals.jest 21 | local it = JestGlobals.it 22 | 23 | beforeEach(function() 24 | jest.resetModules() 25 | 26 | React = require("@pkg/@jsdotlua/react") 27 | ReactNoop = require("@pkg/@jsdotlua/react-noop-renderer") 28 | Scheduler = require("@pkg/@jsdotlua/scheduler") 29 | end) 30 | 31 | -- ROBLOX deviation: this test doesn't make sense in not JSX 32 | -- it('should fail gracefully on error in the host environment', () => { 33 | -- ReactNoop.render(); 34 | -- jestExpect(Scheduler).toFlushAndThrow('Error in host config.'); 35 | -- }); 36 | 37 | it("should ignore error if it doesn't throw on retry", function() 38 | local didInit = false 39 | 40 | local function badLazyInit() 41 | local needsInit = not didInit 42 | didInit = true 43 | if needsInit then 44 | error("Hi") 45 | end 46 | end 47 | 48 | local App = React.Component:extend("App") 49 | function App:render() 50 | badLazyInit() 51 | return React.createElement("TextLabel", { Text = "Hello" }) 52 | end 53 | ReactNoop.render(React.createElement(App)) 54 | jestExpect(Scheduler).toFlushWithoutYielding() 55 | end) 56 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactNoopRendererAct.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/d17086c7c813402a550d15a2f56dc43f1dbd1735/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @jest-environment node 9 | ]] 10 | 11 | -- sanity tests for ReactNoop.act() 12 | 13 | local React 14 | local ReactNoop 15 | local Scheduler 16 | 17 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 18 | local Promise = require("@pkg/@jsdotlua/promise") 19 | local jestExpect = JestGlobals.expect 20 | local beforeEach = JestGlobals.beforeEach 21 | local it = JestGlobals.it 22 | local jest = JestGlobals.jest 23 | 24 | beforeEach(function() 25 | jest.resetModules() 26 | 27 | React = require("@pkg/@jsdotlua/react") 28 | ReactNoop = require("@pkg/@jsdotlua/react-noop-renderer") 29 | Scheduler = require("@pkg/@jsdotlua/scheduler") 30 | end) 31 | 32 | it("can use act to flush effects", function() 33 | local function App(props) 34 | React.useEffect(props.callback) 35 | return nil 36 | end 37 | 38 | local calledLog = {} 39 | ReactNoop.act(function() 40 | ReactNoop.render(React.createElement(App, { 41 | callback = function() 42 | table.insert(calledLog, #calledLog) 43 | end, 44 | })) 45 | end) 46 | jestExpect(Scheduler).toFlushWithoutYielding() 47 | jestExpect(calledLog).toEqual({ 0 }) 48 | end) 49 | it("should work with async/await", function() 50 | local function App() 51 | local ctr, setCtr = React.useState(0) 52 | local function someAsyncFunction() 53 | Scheduler.unstable_yieldValue("stage 1") 54 | Scheduler.unstable_yieldValue("stage 2") 55 | setCtr(1) 56 | end 57 | React.useEffect(function() 58 | someAsyncFunction() 59 | end, {}) 60 | return ctr 61 | end 62 | Promise.try(function() 63 | ReactNoop.act(function() 64 | ReactNoop.render(React.createElement(App)) 65 | end) 66 | end):await() 67 | jestExpect(Scheduler).toHaveYielded({ "stage 1", "stage 2" }) 68 | jestExpect(Scheduler).toFlushWithoutYielding() 69 | jestExpect(ReactNoop.getChildren()).toEqual({ { text = "1", hidden = false } }) 70 | end) 71 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/__tests__/ReactTopLevelText.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/69060e1da6061af845162dcf6854a5d9af28350a/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @emails react-core 9 | * @jest-environment node 10 | ]] 11 | --!strict 12 | 13 | local React 14 | local ReactNoop 15 | local Scheduler 16 | 17 | -- This is a new feature in Fiber so I put it in its own test file. It could 18 | -- probably move to one of the other test files once it is official. 19 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 20 | local jestExpect = JestGlobals.expect 21 | local beforeEach = JestGlobals.beforeEach 22 | local it = JestGlobals.it 23 | local jest = JestGlobals.jest 24 | local describe = JestGlobals.describe 25 | 26 | describe("ReactTopLevelText", function() 27 | beforeEach(function() 28 | jest.resetModules() 29 | 30 | React = require("@pkg/@jsdotlua/react") 31 | ReactNoop = require("@pkg/@jsdotlua/react-noop-renderer") 32 | Scheduler = require("@pkg/@jsdotlua/scheduler") 33 | end) 34 | 35 | it("should render a component returning strings directly from render", function() 36 | local Text = function(props) 37 | return props.value 38 | end 39 | ReactNoop.render(React.createElement(Text, { value = "foo" })) 40 | jestExpect(Scheduler).toFlushWithoutYielding() 41 | 42 | jestExpect(ReactNoop).toMatchRenderedOutput("foo") 43 | end) 44 | 45 | it("should render a component returning numbers directly from renderß", function() 46 | local Text = function(props) 47 | return props.value 48 | end 49 | ReactNoop.render(React.createElement(Text, { value = 10 })) 50 | jestExpect(Scheduler).toFlushWithoutYielding() 51 | 52 | jestExpect(ReactNoop).toMatchRenderedOutput("10") 53 | end) 54 | end) 55 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/forks/ReactFiberHostConfig.test.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-reconciler/src/forks/ReactFiberHostConfig.test.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | return require("@pkg/@jsdotlua/react-test-renderer") 12 | -------------------------------------------------------------------------------- /modules/react-reconciler/src/init.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/43363e2795393a00fd77312a16d6b80e626c29de/packages/react-reconciler/src/index.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | --!strict 12 | local ReactInternalTypes = require("./ReactInternalTypes") 13 | local ReactRootTags = require("./ReactRootTags") 14 | 15 | export type Dispatcher = ReactInternalTypes.Dispatcher 16 | export type Fiber = ReactInternalTypes.Fiber 17 | export type FiberRoot = ReactInternalTypes.FiberRoot 18 | 19 | -- ROBLOX deviation: explicit export for use in createReactNoop 20 | export type UpdateQueue = ReactInternalTypes.UpdateQueue 21 | 22 | export type RootTag = ReactRootTags.RootTag 23 | 24 | -- ROBLOX deviation: In order to allow host config to be spliced in, we export 25 | -- this top-level package as an initializer function that returns the configured 26 | -- reconciler module 27 | -- ROBLOX TODO: this effectively disconnects type checking from above to reconciler to below 28 | local function initialize(config): { [string]: any } 29 | local ReactFiberHostConfig = require("./ReactFiberHostConfig") 30 | for name, implementation in config do 31 | ReactFiberHostConfig[name] = implementation 32 | end 33 | 34 | return require("./ReactFiberReconciler") 35 | end 36 | 37 | return initialize 38 | -------------------------------------------------------------------------------- /modules/react-roblox/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-roblox/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-roblox/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-roblox", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-roblox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-roblox", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-roblox" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/react-reconciler": "workspace:^", 18 | "@jsdotlua/scheduler": "workspace:^", 19 | "@jsdotlua/shared": "workspace:^" 20 | }, 21 | "devDependencies": { 22 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 23 | "@jsdotlua/promise": "^3.5.0", 24 | "npmluau": "^0.1.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /modules/react-roblox/src/ReactReconciler.roblox.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX deviation: Initializes the reconciler with this package's host 3 | -- config and returns the resulting module 4 | 5 | local initializeReconciler = require("@pkg/@jsdotlua/react-reconciler") 6 | 7 | local ReactRobloxHostConfig = require("./client/ReactRobloxHostConfig") 8 | 9 | return initializeReconciler(ReactRobloxHostConfig) 10 | -------------------------------------------------------------------------------- /modules/react-roblox/src/client/roblox/__tests__/getDefaultInstanceProperty.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/Roblox/roact/blob/b2ba9cf4c219c2654e6572219a68d0bf1b541418/src/getDefaultInstanceProperty.spec.lua 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 18 | local jestExpect = JestGlobals.expect 19 | local it = JestGlobals.it 20 | local getDefaultInstanceProperty = require("../getDefaultInstanceProperty") 21 | 22 | it("should get default name string values", function() 23 | local _, defaultName = getDefaultInstanceProperty("StringValue", "Name") 24 | 25 | jestExpect(defaultName).toBe("Value") 26 | end) 27 | 28 | it("should get default empty string values", function() 29 | local _, defaultValue = getDefaultInstanceProperty("StringValue", "Value") 30 | 31 | jestExpect(defaultValue).toBe("") 32 | end) 33 | 34 | it("should get default number values", function() 35 | local _, defaultValue = getDefaultInstanceProperty("IntValue", "Value") 36 | 37 | jestExpect(defaultValue).toBe(0) 38 | end) 39 | 40 | it("should get nil default values", function() 41 | local _, defaultValue = getDefaultInstanceProperty("ObjectValue", "Value") 42 | 43 | jestExpect(defaultValue).toBe(nil) 44 | end) 45 | 46 | it("should get bool default values", function() 47 | local _, defaultValue = getDefaultInstanceProperty("BoolValue", "Value") 48 | 49 | jestExpect(defaultValue).toBe(false) 50 | end) 51 | -------------------------------------------------------------------------------- /modules/react-roblox/src/client/roblox/__tests__/waitForEvents.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/MIT 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | -- Defers remaining execution until after deferred lua events have run 18 | return function() 19 | task.defer(coroutine.running()) 20 | coroutine.yield() 21 | end 22 | -------------------------------------------------------------------------------- /modules/react-roblox/src/client/roblox/getDefaultInstanceProperty.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/Roblox/roact/blob/b2ba9cf4c219c2654e6572219a68d0bf1b541418/src/getDefaultInstanceProperty.lua 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | --[[ 18 | Attempts to get the default value of a given property on a Roblox instance. 19 | 20 | This is used by the reconciler in cases where a prop was previously set on a 21 | primitive component, but is no longer present in a component's new props. 22 | 23 | Eventually, Roblox might provide a nicer API to query the default property 24 | of an object without constructing an instance of it. 25 | ]] 26 | 27 | local Symbol = require("@pkg/@jsdotlua/shared").Symbol 28 | 29 | local Nil = Symbol.named("Nil") 30 | local _cachedPropertyValues = {} 31 | 32 | local function tryPropertyName(instance, propertyName) 33 | return instance[propertyName] 34 | end 35 | 36 | local function getDefaultInstanceProperty(className, propertyName) 37 | local classCache = _cachedPropertyValues[className] 38 | 39 | if classCache then 40 | local propValue = classCache[propertyName] 41 | 42 | -- We have to use a marker here, because Lua doesn't distinguish 43 | -- between 'nil' and 'not in a table' 44 | if propValue == Nil then 45 | return true, nil 46 | end 47 | 48 | if propValue ~= nil then 49 | return true, propValue 50 | end 51 | else 52 | classCache = {} 53 | _cachedPropertyValues[className] = classCache 54 | end 55 | 56 | local created = Instance.new(className) 57 | local ok, defaultValue = pcall(tryPropertyName, created, propertyName) 58 | 59 | created:Destroy() 60 | 61 | if ok then 62 | if defaultValue == nil then 63 | classCache[propertyName] = Nil 64 | else 65 | classCache[propertyName] = defaultValue 66 | end 67 | end 68 | 69 | return ok, defaultValue 70 | end 71 | 72 | return getDefaultInstanceProperty 73 | -------------------------------------------------------------------------------- /modules/react-roblox/src/init.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/efd8f6442d1aa7c4566fe812cba03e7e83aaccc3/packages/react-native-renderer/index.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | local HostTypes = require("./client/ReactRobloxHostTypes.roblox.luau") 13 | export type RootType = HostTypes.RootType 14 | return require("./client/ReactRoblox") 15 | -------------------------------------------------------------------------------- /modules/react-shallow-renderer/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-shallow-renderer/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-shallow-renderer/README.md: -------------------------------------------------------------------------------- 1 | # react-shallow-renderer 2 | A Roblox Lua port of the react-shallow-reconciler package re-exported from React. Used to run integration-level tests in `react-dom`. Will likely be useful with the Roblox renderer when it's more thoroughly integrated. 3 | 4 | Status: ✔️ Ported 5 | 6 | Source: https://github.com/NMinhNguyen/react-shallow-renderer/ 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | * Includes some minor adjustments to the shallow renderer interface to better facilitate its translation 12 | * Most member functions use `:` function calls instead of `.` to avoid having to explicitly bind them to self. We could revisit this to align it more directly. 13 | * Implementation for `useState` returns multiple values instead of an array. This will likely be carried over to the full implementation in the reconciler as well 14 | * Context narrowing via `contextTypes` is only available on class components (since functions cannot have fields in Luau) 15 | * PropTypes are unsupported for now 16 | 17 | ### ❌ Excluded 18 | 19 | ``` 20 | src/__tests__/ReactShallowRendererMemo-test.js 21 | ``` 22 | 23 | Small test that didn't seem critical to port right now. -------------------------------------------------------------------------------- /modules/react-shallow-renderer/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-shallow-renderer", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-shallow-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-shallow-renderer", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-shallow-renderer" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/react-is": "workspace:^", 18 | "@jsdotlua/shared": "workspace:^" 19 | }, 20 | "devDependencies": { 21 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 22 | "npmluau": "^0.1.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/react-test-renderer/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react-test-renderer/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react-test-renderer/README.md: -------------------------------------------------------------------------------- 1 | # react-test-renderer 2 | A Roblox Lua port of the react-test-reconciler package re-exported from React. Used to run integration-level tests in `react-dom`. Will likely be useful with the Roblox renderer when it's more thoroughly integrated. 3 | 4 | Status: 🔨 Partially Ported 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/react-test-renderer 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | * For now, only includes configs for testing with the noop renderer, 12 | 13 | ### ❌ Excluded 14 | 15 | ``` 16 | src/__tests__/ReactTestRenderer-test.internal.js 17 | src/__tests__/ReactTestRenderer-test.js 18 | src/__tests__/ReactTestRendererAct-test.js 19 | src/__tests__/ReactTestRendererAsync-test.js 20 | src/__tests__/ReactTestRendererTraversal-test.js 21 | src/ReactTestRenderer.js 22 | ``` 23 | 24 | Actual test renderer impl is excluded for now. 25 | 26 | ``` 27 | src/__tests__/ReactShallowRenderer-test.js 28 | src/__tests__/ReactShallowRendererHooks-test.js 29 | src/__tests__/ReactShallowRendererMemo-test.js 30 | ``` 31 | 32 | These tests appear to be copied from the shallow renderer, which react depends upon and re-exports via `react-test-renderer` -------------------------------------------------------------------------------- /modules/react-test-renderer/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-test-renderer", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react-test-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react-test-renderer", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react-test-renderer" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/react-reconciler": "workspace:^", 18 | "@jsdotlua/scheduler": "workspace:^", 19 | "@jsdotlua/shared": "workspace:^" 20 | }, 21 | "devDependencies": { 22 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 23 | "@jsdotlua/jest-react": "workspace:^", 24 | "@jsdotlua/promise": "^3.5.0", 25 | "@jsdotlua/react-roblox": "workspace:^", 26 | "npmluau": "^0.1.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /modules/react-test-renderer/src/init.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/702fad4b1b48ac8f626ed3f35e8f86f5ea728084/packages/react-test-renderer/src/index.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @emails react-core 10 | ]] 11 | return require("./ReactTestRenderer") 12 | -------------------------------------------------------------------------------- /modules/react/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/react/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/react/README.md: -------------------------------------------------------------------------------- 1 | # react 2 | A Roblox Lua port of the `react` package from React. This is the main React package 3 | 4 | Status: 🔨 Port in progress 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/react 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | * Includes some relatively significant deviations in `ReactBaseClasses.lua` to accommodate Lua idioms for objects, constructors, and inheritance. 12 | * Exports React-related type definitions from DefinitelyTyped, like `ReactElement`, used by Apollo GraphQL and jest. These are the types users of the library should prefer to use. 13 | * Exports React-related type definitions that are built-in to flowtype. Only used by the framework internals itself, and some sibling projects like VirtualizedList. It is not recommended to use these types in application code. 14 | 15 | ### ❌ Excluded 16 | -------------------------------------------------------------------------------- /modules/react/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/react", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/react" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/shared": "workspace:^" 17 | }, 18 | "devDependencies": { 19 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 20 | "@jsdotlua/jest-react": "workspace:^", 21 | "@jsdotlua/promise": "^3.5.0", 22 | "@jsdotlua/react-cache": "workspace:^", 23 | "@jsdotlua/react-noop-renderer": "workspace:^", 24 | "@jsdotlua/react-roblox": "workspace:^", 25 | "@jsdotlua/react-test-renderer": "workspace:^", 26 | "npmluau": "^0.1.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /modules/react/src/None.roblox.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- code derived from https://github.com/Roblox/roact/blob/master/src/None.lua 3 | --[[ 4 | * Copyright (c) Roblox Corporation. All rights reserved. 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | ]] 17 | 18 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 19 | 20 | -- Roact uses `Object.assign` internally to assign new state values; the same 21 | -- None value should give us the proper semantics. We can re-export this value 22 | -- as React.None for easy use, and to mirror Roact.None in legacy Roact. 23 | return LuauPolyfill.Object.None 24 | -------------------------------------------------------------------------------- /modules/react/src/ReactCreateRef.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react/src/ReactCreateRef.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * @flow 9 | *]] 10 | 11 | local ReactTypes = require("@pkg/@jsdotlua/shared") 12 | type RefObject = ReactTypes.RefObject 13 | 14 | -- ROBLOX DEVIATION: In Roact, refs are implemented in terms of bindings 15 | --[[ 16 | A ref is nothing more than a binding with a special field 'current' 17 | that maps to the getValue method of the binding 18 | ]] 19 | local Binding = require("./ReactBinding.roblox.luau") 20 | 21 | local exports = {} 22 | 23 | -- an immutable object with a single mutable value 24 | exports.createRef = function(): RefObject 25 | local binding, _ = Binding.create(nil) 26 | 27 | local ref = {} 28 | 29 | -- ROBLOX DEVIATION: Since refs are used as bindings, they can often be 30 | -- assigned to fields of other Instances; we track creation here parallel to 31 | -- how we do with bindings created via `createBinding` to improve messaging 32 | -- when something goes wrong 33 | if _G.__DEV__ then 34 | -- ROBLOX TODO: LUAFDN-619 - improve debug stacktraces for refs 35 | binding._source = debug.traceback("Ref created at:", 1) 36 | end 37 | 38 | --[[ 39 | A ref is just redirected to a binding via its metatable 40 | ]] 41 | setmetatable(ref, { 42 | __index = function(self, key) 43 | if key == "current" then 44 | return binding:getValue() 45 | else 46 | return (binding :: any)[key] 47 | end 48 | end, 49 | __newindex = function(self, key, value) 50 | if key == "current" then 51 | -- ROBLOX FIXME: Bindings - This is not allowed in Roact, but is okay in 52 | -- React. Lots of discussion at 53 | -- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065 54 | -- error("Cannot assign to the 'current' property of refs", 2) 55 | Binding.update(binding, value) 56 | end 57 | 58 | (binding :: any)[key] = value 59 | end, 60 | __tostring = function(self) 61 | return string.format("Ref(%s)", tostring(binding:getValue())) 62 | end, 63 | }) 64 | 65 | return (ref :: any) :: RefObject 66 | end 67 | 68 | return exports 69 | -------------------------------------------------------------------------------- /modules/react/src/ReactMutableSource.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/142d4f1c00c66f3d728177082dbc027fd6335115/packages/react/src/ReactMutableSource.js 3 | -- [[ 4 | -- * Copyright (c) Facebook, Inc. and its affiliates. 5 | -- * 6 | -- * This source code is licensed under the MIT license found in the 7 | -- * LICENSE file in the root directory of this source tree. 8 | -- * 9 | -- * @flow 10 | -- ]] 11 | 12 | local ReactTypes = require("@pkg/@jsdotlua/shared") 13 | type MutableSourceGetVersionFn = ReactTypes.MutableSourceGetVersionFn 14 | type MutableSource = ReactTypes.MutableSource 15 | 16 | local function createMutableSource( 17 | source: Source, 18 | getVersion: MutableSourceGetVersionFn 19 | ): MutableSource 20 | local mutableSource: MutableSource = { 21 | _getVersion = getVersion, 22 | _source = source, 23 | _workInProgressVersionPrimary = nil, 24 | _workInProgressVersionSecondary = nil, 25 | } 26 | 27 | if _G.__DEV__ then 28 | mutableSource._currentPrimaryRenderer = nil 29 | mutableSource._currentSecondaryRenderer = nil 30 | end 31 | 32 | return mutableSource 33 | end 34 | 35 | return createMutableSource 36 | -------------------------------------------------------------------------------- /modules/react/src/__tests__/ReactBaseClasses.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | local ReactBaseClasses = require("../ReactBaseClasses") 3 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 4 | local jestExpect = JestGlobals.expect 5 | local describe = JestGlobals.describe 6 | local it = JestGlobals.it 7 | local Component = ReactBaseClasses.Component 8 | local PureComponent = ReactBaseClasses.Component 9 | local component 10 | 11 | describe("Component", function() 12 | it("should prevent extending a second time", function() 13 | component = Component:extend("Sheev") 14 | 15 | jestExpect(function() 16 | (component :: any):extend("Frank") 17 | end).toThrow() 18 | end) 19 | 20 | it("should use a given name", function() 21 | component = Component:extend("FooBar") 22 | 23 | local name = tostring(component) 24 | 25 | jestExpect(name).toEqual(jestExpect.any("string")) 26 | jestExpect(name).toContain("FooBar") 27 | end) 28 | end) 29 | 30 | describe("PureComponent", function() 31 | it("should prevent extending a second time", function() 32 | component = PureComponent:extend("Sheev") 33 | 34 | jestExpect(function() 35 | (component :: any):extend("Frank") 36 | end).toThrow() 37 | end) 38 | 39 | it("should use a given name", function() 40 | component = PureComponent:extend("FooBar") 41 | 42 | local name = tostring(component) 43 | 44 | jestExpect(name).toEqual(jestExpect.any("string")) 45 | jestExpect(name).toContain("FooBar") 46 | end) 47 | end) 48 | -------------------------------------------------------------------------------- /modules/react/src/__tests__/createSignal.spec.luau: -------------------------------------------------------------------------------- 1 | local createSignal = require("../createSignal.roblox.luau") 2 | 3 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 4 | local jestExpect = JestGlobals.expect 5 | local jest = JestGlobals.jest 6 | 7 | local it = JestGlobals.it 8 | 9 | it("should fire subscribers and disconnect them", function() 10 | local subscribe, fire = createSignal() 11 | 12 | local spy = jest.fn() 13 | local disconnect = subscribe(function(...) 14 | spy(...) 15 | end) 16 | 17 | jestExpect(spy).never.toBeCalled() 18 | 19 | local a = 1 20 | local b = {} 21 | local c = "hello" 22 | fire(a, b, c) 23 | 24 | jestExpect(spy).toBeCalledTimes(1) 25 | jestExpect(spy).toBeCalledWith(a, b, c) 26 | 27 | disconnect() 28 | 29 | fire() 30 | 31 | jestExpect(spy).toBeCalledTimes(1) 32 | end) 33 | 34 | it("should handle multiple subscribers", function() 35 | local subscribe, fire = createSignal() 36 | 37 | local spyA = jest.fn() 38 | local spyB = jest.fn() 39 | 40 | local disconnectA = subscribe(function(...) 41 | spyA(...) 42 | end) 43 | local disconnectB = subscribe(function(...) 44 | spyB(...) 45 | end) 46 | 47 | jestExpect(spyA).never.toBeCalled() 48 | jestExpect(spyB).never.toBeCalled() 49 | 50 | local a = {} 51 | local b = 67 52 | fire(a, b) 53 | 54 | jestExpect(spyA).toBeCalledTimes(1) 55 | jestExpect(spyA).toBeCalledWith(a, b) 56 | 57 | jestExpect(spyB).toBeCalledTimes(1) 58 | jestExpect(spyB).toBeCalledWith(a, b) 59 | 60 | disconnectA() 61 | 62 | fire(b, a) 63 | 64 | jestExpect(spyA).toBeCalledTimes(1) 65 | 66 | jestExpect(spyB).toBeCalledTimes(2) 67 | jestExpect(spyB).toBeCalledWith(b, a) 68 | 69 | disconnectB() 70 | end) 71 | 72 | it("should stop firing a connection if disconnected mid-fire", function() 73 | local subscribe, fire = createSignal() 74 | 75 | -- In this test, we'll connect two listeners that each try to disconnect 76 | -- the other. Because the order of listeners firing isn't defined, we 77 | -- have to be careful to handle either case. 78 | 79 | local disconnectA 80 | local disconnectB 81 | 82 | local spyA = jest.fn(function() 83 | disconnectB() 84 | end) 85 | 86 | local spyB = jest.fn(function() 87 | disconnectA() 88 | end) 89 | 90 | disconnectA = subscribe(function(...) 91 | spyA(...) 92 | end) 93 | disconnectB = subscribe(function(...) 94 | spyB(...) 95 | end) 96 | 97 | fire() 98 | 99 | jestExpect(#spyA.mock.calls + #spyB.mock.calls).toBe(1) 100 | end) 101 | -------------------------------------------------------------------------------- /modules/roact-compat/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/roact-compat/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/roact-compat/README.md: -------------------------------------------------------------------------------- 1 | # roact-compat 2 | A thin compatibility layer connecting Roact 1.4.0 to the new roact alignment effort (often referred to as "Roact 17"). 3 | 4 | This does _not_ match an upstream package. Its purpose is to encapsulate any deviations whose sole purpose is to provide compatibility with production Roact during the transition. 5 | -------------------------------------------------------------------------------- /modules/roact-compat/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roact-compat", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/roact-compat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/roact-compat", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/roact-compat" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/react": "workspace:^", 17 | "@jsdotlua/react-roblox": "workspace:^", 18 | "@jsdotlua/shared": "workspace:^" 19 | }, 20 | "devDependencies": { 21 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 22 | "@jsdotlua/scheduler": "workspace:^", 23 | "npmluau": "^0.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /modules/roact-compat/src/Portal.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/MIT 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | local ReactRoblox = require("@pkg/@jsdotlua/react-roblox") 18 | 19 | local warnOnce = require("./warnOnce") 20 | 21 | local function PortalComponent(props) 22 | if _G.__DEV__ and _G.__COMPAT_WARNINGS__ then 23 | warnOnce("Roact.Portal", "Please use the createPortal API on ReactRoblox instead") 24 | end 25 | return ReactRoblox.createPortal(props.children, props.target) 26 | end 27 | 28 | return PortalComponent 29 | -------------------------------------------------------------------------------- /modules/roact-compat/src/__tests__/warnOnce.spec.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/MIT 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 18 | local beforeEach = JestGlobals.beforeEach 19 | local jestExpect = JestGlobals.expect 20 | local it = JestGlobals.it 21 | local jest = JestGlobals.jest 22 | local warnOnce 23 | 24 | beforeEach(function() 25 | jest.resetModules() 26 | warnOnce = require("../warnOnce") 27 | end) 28 | 29 | it("warns exactly once", function() 30 | jestExpect(function() 31 | warnOnce("oldAPI", "Foo") 32 | end).toWarnDev( 33 | "Warning: The legacy Roact API 'oldAPI' is deprecated, and will be " 34 | .. "removed in a future release.\n\nFoo", 35 | { withoutStack = true } 36 | ) 37 | 38 | jestExpect(function() 39 | warnOnce("oldAPI", "Foo") 40 | end).toWarnDev({}) 41 | end) 42 | -------------------------------------------------------------------------------- /modules/roact-compat/src/createFragment.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/MIT 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | local React = require("@pkg/@jsdotlua/react") 17 | 18 | local warnOnce = require("./warnOnce") 19 | 20 | return function(elements) 21 | if _G.__DEV__ and _G.__COMPAT_WARNINGS__ then 22 | warnOnce( 23 | "createFragment", 24 | "Please instead use:\n\tReact.createElement(React.Fragment, ...)" 25 | ) 26 | end 27 | return React.createElement(React.Fragment, nil, elements) 28 | end 29 | -------------------------------------------------------------------------------- /modules/roact-compat/src/oneChild.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/Roblox/roact/blob/master/src/oneChild.lua 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | local React = require("@pkg/@jsdotlua/react") 17 | 18 | local warnOnce = require("./warnOnce") 19 | 20 | local function oneChild(children) 21 | if _G.__DEV__ and _G.__COMPAT_WARNINGS__ then 22 | warnOnce( 23 | "oneChild", 24 | "You likely don't need this at all! If you were assigning children " 25 | .. "via `React.oneChild(someChildren)`, you can simply use " 26 | .. "`someChildren` directly." 27 | ) 28 | end 29 | 30 | -- This behavior is a bit different from upstream, so we're adapting current 31 | -- Roact's logic (which will unwrap a table with a single member) 32 | if not children then 33 | return nil 34 | end 35 | 36 | local key, child = next(children) 37 | 38 | if not child then 39 | return nil 40 | end 41 | 42 | local after = next(children, key) 43 | 44 | if after then 45 | error("Expected at most one child, had more than one child.", 2) 46 | end 47 | 48 | return React.Children.only(child) 49 | end 50 | 51 | return oneChild 52 | -------------------------------------------------------------------------------- /modules/roact-compat/src/setGlobalConfig.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/MIT 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | local warnOnce = require("./warnOnce") 18 | 19 | return function(_config) 20 | if _G.__DEV__ and _G.__COMPAT_WARNINGS__ then 21 | warnOnce( 22 | "setGlobalConfig", 23 | "React Lua uses a `_G.__DEV__` flag to enable development behavior. " 24 | .. "If you're seeing this warning, you already have it enabled. " 25 | .. "Please remove any redundant uses of `setGlobalConfig`." 26 | ) 27 | end 28 | -- No equivalent behavior can be applied here 29 | end 30 | -------------------------------------------------------------------------------- /modules/roact-compat/src/warnOnce.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[ 3 | * Copyright (c) Roblox Corporation. All rights reserved. 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/MIT 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ]] 16 | 17 | local console = require("@pkg/@jsdotlua/shared").console 18 | 19 | local warnedAbout = {} 20 | 21 | local function warnOnce(name: string, message: string) 22 | if not warnedAbout[name] then 23 | console.warn( 24 | "The legacy Roact API '%s' is deprecated, and will be removed " 25 | .. "in a future release.\n\n%s", 26 | name, 27 | message 28 | ) 29 | end 30 | warnedAbout[name] = true 31 | end 32 | 33 | return warnOnce 34 | -------------------------------------------------------------------------------- /modules/scheduler/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/scheduler/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/scheduler/README.md: -------------------------------------------------------------------------------- 1 | # scheduler 2 | A Roblox Lua port of the `scheduler` package from React, which is used under the hood by the Fiber Reconciler logic. 3 | 4 | Status: ✔️ Ported 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/scheduler 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | * The upstream implementation of min-heap does not include tests. To validate the port, `src/__tests__/SchedulerMinHeap.spec.lua` was added 12 | * The scheduler contains two sets of tests 13 | * A small, basic set of tests using the real scheduler and mock timers. 14 | * A more thorough test suite that uses `MockSchedulerHostConfig.lua`, which mocks the entire HostConfig interface and provides functionality to manipulate it within tests. 15 | 16 | #### Profiling 17 | 18 | ``` 19 | src/SchedulerProfiling.js 20 | src/__tests__/SchedulerProfiling-test.js 21 | ``` 22 | 23 | Profiling logic used for debugging the Scheduler. Includes tests that produce flamegraphs of processed tasks. This functionality is gated behind the `enableProfiling` flag defined in `SchedulerFeatureFlags.js`. Additional functionality is gated behind the enableSchedulingProfiler flag in ReactFeatureFlags. When enabling the Scheduling Profiler, you'll need to plug in a table with a `mark` function to the `_G.performance` global, like this: 24 | ```lua 25 | _G.performance = { 26 | mark = function(str) 27 | debug.profileBegin(str) 28 | debug.profileEnd(str) 29 | } 30 | ``` 31 | 32 | We may customize this for deeper integration with the Roblox Studio profiler in the future, based on specific performance optimization workflow needs. 33 | 34 | 35 | ### ❌ Excluded 36 | 37 | #### Post Task 38 | 39 | ``` 40 | unstable_post_task.js 41 | src/SchedulerPostTask.js 42 | src/__tests__/SchedulerPostTask-test.js 43 | ``` 44 | 45 | An alternate implementation of the Scheduler interface based on the new postTask browser API. There are relatively recent commits to these files, as well, suggesting new or ongoing development. Since the underlying browser API isn't relevant to the Roblox environment, this logic has been excluded. 46 | -------------------------------------------------------------------------------- /modules/scheduler/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scheduler", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } -------------------------------------------------------------------------------- /modules/scheduler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/scheduler", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/scheduler" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6", 16 | "@jsdotlua/shared": "workspace:^" 17 | }, 18 | "devDependencies": { 19 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 20 | "npmluau": "^0.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modules/scheduler/src/SchedulerFeatureFlags.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/9abc2785cb070148d64fae81e523246b90b92016/packages/scheduler/src/SchedulerFeatureFlags.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | ]] 10 | 11 | return { 12 | enableSchedulerDebugging = false, 13 | enableIsInputPending = false, 14 | enableProfiling = _G.__PROFILE__, 15 | } 16 | -------------------------------------------------------------------------------- /modules/scheduler/src/SchedulerHostConfig.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/00748c53e183952696157088a858352cc77b0010/packages/scheduler/src/SchedulerHostConfig.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | -- deviation: In React, this module throws an error and is expected to be 13 | -- replaced via a bundler. In our case, we mock it explicitly when we need to 14 | -- mock it, and return the "default" here 15 | return require("./forks/SchedulerHostConfig.default.luau") 16 | -------------------------------------------------------------------------------- /modules/scheduler/src/SchedulerPriorities.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/00748c53e183952696157088a858352cc77b0010/packages/scheduler/src/SchedulerHostConfig.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | export type PriorityLevel = number 13 | 14 | -- TODO: Use symbols? 15 | return { 16 | NoPriority = 0, 17 | ImmediatePriority = 1, 18 | UserBlockingPriority = 2, 19 | NormalPriority = 3, 20 | LowPriority = 4, 21 | IdlePriority = 5, 22 | } 23 | -------------------------------------------------------------------------------- /modules/scheduler/src/__tests__/Tracing.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/9abc2785cb070148d64fae81e523246b90b92016/packages/scheduler/src/__tests__/Tracing-test.js 2 | -- /** 3 | -- * Copyright (c) Facebook, Inc. and its affiliates. 4 | -- * 5 | -- * This source code is licensed under the MIT license found in the 6 | -- * LICENSE file in the root directory of this source tree. 7 | -- * 8 | -- * @jest-environment node 9 | -- */ 10 | 11 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 12 | local beforeEach = JestGlobals.beforeEach 13 | local describe = JestGlobals.describe 14 | local it = JestGlobals.it 15 | local jest = JestGlobals.jest 16 | 17 | describe("Tracing", function() 18 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 19 | local jestExpect = JestGlobals.expect 20 | 21 | local SchedulerTracing 22 | 23 | beforeEach(function() 24 | jest.resetModules() 25 | 26 | SchedulerTracing = require("@pkg/@jsdotlua/scheduler").tracing 27 | end) 28 | 29 | it("should return the value of a traced function", function() 30 | jestExpect(SchedulerTracing.unstable_trace("arbitrary", 0, function() 31 | return 123 32 | end)).toBe(123) 33 | end) 34 | 35 | it("should return the value of a wrapped function", function() 36 | local wrapped 37 | SchedulerTracing.unstable_trace("arbitrary", 0, function() 38 | wrapped = SchedulerTracing.unstable_wrap(function() 39 | return 123 40 | end) 41 | end) 42 | jestExpect(wrapped()).toBe(123) 43 | end) 44 | 45 | it("should execute traced callbacks", function() 46 | local done = false 47 | 48 | SchedulerTracing.unstable_trace("some event", 0, function() 49 | done = true 50 | end) 51 | 52 | jestExpect(done).toBe(true) 53 | end) 54 | 55 | it("should return the value of a clear function", function() 56 | jestExpect(SchedulerTracing.unstable_clear(function() 57 | return 123 58 | end)).toBe(123) 59 | end) 60 | 61 | it("should execute wrapped callbacks", function() 62 | local done = false 63 | local wrappedCallback = SchedulerTracing.unstable_wrap(function() 64 | done = true 65 | end) 66 | 67 | wrappedCallback() 68 | jestExpect(done).toBe(true) 69 | end) 70 | end) 71 | -------------------------------------------------------------------------------- /modules/scheduler/src/unstable_mock.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | *]] 8 | local Tracing = require("./Tracing") 9 | local TracingSubscriptions = require("./TracingSubscriptions") 10 | -- ROBLOX deviation: export Tracing type from the package exports to avoid direct file access 11 | export type Interaction = Tracing.Interaction 12 | 13 | local initializeScheduler = require("./Scheduler") 14 | local HostConfig = require("./forks/SchedulerHostConfig.mock.luau") 15 | 16 | local Scheduler = initializeScheduler(HostConfig) 17 | 18 | local exports = {} 19 | exports.tracing = {} 20 | -- ROBLOX FIXME Luau: need to fix CLI-56768 to remove any casts 21 | for key, value in Scheduler :: any do 22 | exports[key] = value 23 | end 24 | for key, value in Tracing :: any do 25 | exports.tracing[key] = value 26 | end 27 | for key, value in TracingSubscriptions :: any do 28 | exports.tracing[key] = value 29 | end 30 | 31 | exports.unstable_flushAllWithoutAsserting = HostConfig.unstable_flushAllWithoutAsserting 32 | exports.unstable_flushNumberOfYields = HostConfig.unstable_flushNumberOfYields 33 | exports.unstable_flushExpired = HostConfig.unstable_flushExpired 34 | exports.unstable_clearYields = HostConfig.unstable_clearYields 35 | exports.unstable_flushUntilNextPaint = HostConfig.unstable_flushUntilNextPaint 36 | exports.unstable_flushAll = HostConfig.unstable_flushAll 37 | exports.unstable_yieldValue = HostConfig.unstable_yieldValue 38 | exports.unstable_advanceTime = HostConfig.unstable_advanceTime 39 | exports.unstable_Profiling = Scheduler.unstable_Profiling 40 | 41 | return exports 42 | -------------------------------------------------------------------------------- /modules/shared/.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict" 3 | } -------------------------------------------------------------------------------- /modules/shared/.npmignore: -------------------------------------------------------------------------------- 1 | **/.robloxrc 2 | rotriever.toml 3 | 4 | /roblox 5 | /build 6 | 7 | .darklua* 8 | .luau-analyze.json 9 | .luaurc 10 | default.project.json 11 | 12 | /globalTypes.d.lua 13 | **/sourcemap.json 14 | **/*.project.json 15 | 16 | **/__tests__ 17 | **/*.test.lua 18 | **/*.spec.lua 19 | **/jest.config.lua 20 | 21 | **/*.rbxl 22 | **/*.rbxlx 23 | **/*.rbxl.lock 24 | **/*.rbxlx.lock 25 | **/*.rbxm 26 | **/*.rbxmx 27 | -------------------------------------------------------------------------------- /modules/shared/README.md: -------------------------------------------------------------------------------- 1 | # shared 2 | A Roblox Lua port of the `shared` pseudo-package from React, which contains a number of common utilities and definitions used across the React monorepo. 3 | 4 | Status: ✔️ Ported 5 | 6 | Source: https://github.com/facebook/react/tree/master/packages/shared 7 | 8 | --- 9 | 10 | ### ✏️ Notes 11 | * `ReactTypes.js` contains a number of complex flow-type definitions that are not yet possible with Luau, so it's been simplified to a stub called `ReactTypes.roblox.lua` 12 | * `ReactComponentStackFrame.js` is replaced by a partially-ported stub (`ReactComponentStackFrame.roblox.lua`) since it contains logic for parsing/navigating JS-specific stack structure. This needs to be ported to the equivalent functionality in Luau. 13 | * Some slight changes to `isValidElement.lua` that account for the divergent shape of Component and PureComponent objects in our port (for `react`, they're functions; for us, they're tables) 14 | 15 | ### ❌ Excluded 16 | 17 | #### Forked Config 18 | ``` 19 | forks/ReactFeatureFlags.native-fb.js 20 | forks/ReactFeatureFlags.native-oss.js 21 | forks/ReactFeatureFlags.readonly.js 22 | forks/ReactFeatureFlags.test-renderer.js 23 | forks/ReactFeatureFlags.test-renderer.native.js 24 | forks/ReactFeatureFlags.test-renderer.www.js 25 | forks/ReactFeatureFlags.testing.js 26 | forks/ReactFeatureFlags.testing.www.js 27 | forks/ReactFeatureFlags.www-dynamic.js 28 | forks/ReactFeatureFlags.www.js 29 | forks/Scheduler.umd.js 30 | forks/SchedulerTracing.umd.js 31 | forks/consoleWithStackDev.www.js 32 | forks/invokeGuardedCallbackImpl.www.js 33 | forks/object-assign.inline-umd.js 34 | forks/object-assign.umd.js 35 | ``` 36 | 37 | Forks that specify different flag states are used in React with the help of a bundler that swaps in the correct file for the given environment. We don't have this kind of functionality yet, nor the same set of environments. 38 | 39 | #### Integration Tests 40 | ``` 41 | __tests__/describeComponentFrame-test.js 42 | __tests__/ReactError-test.internal.js 43 | ``` 44 | These tests required use of React and ReactDOM, and are not viable to port until we have more of the reconciler ported. -------------------------------------------------------------------------------- /modules/shared/default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared", 3 | "tree": { 4 | "$path": "src" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /modules/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsdotlua/shared", 3 | "version": "17.2.1", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jsdotlua/react-lua.git", 7 | "directory": "modules/shared" 8 | }, 9 | "license": "MIT", 10 | "main": "src/init.luau", 11 | "scripts": { 12 | "prepare": "npmluau" 13 | }, 14 | "dependencies": { 15 | "@jsdotlua/luau-polyfill": "^1.2.6" 16 | }, 17 | "devDependencies": { 18 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 19 | "@jsdotlua/jest-react": "workspace:^", 20 | "@jsdotlua/react": "workspace:^", 21 | "@jsdotlua/react-noop-renderer": "workspace:^", 22 | "@jsdotlua/scheduler": "workspace:^", 23 | "npmluau": "^0.1.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /modules/shared/src/ExecutionEnvironment.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/55cb0b7eeb0e539d89858b8ed69beabf7fe2fb46/packages/shared/ExecutionEnvironment.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | local exports = {} 12 | 13 | exports.canUseDOM = function() 14 | -- ROBLOX deviation START 15 | return false 16 | -- ROBLOX deviation END 17 | end 18 | 19 | return exports 20 | -------------------------------------------------------------------------------- /modules/shared/src/PropMarkers/Change.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --[[ 16 | Change is used to generate special prop keys that can be used to connect to 17 | GetPropertyChangedSignal. 18 | 19 | Generally, Change is indexed by a Roblox property name: 20 | 21 | Roact.createElement("TextBox", { 22 | [Roact.Change.Text] = function(rbx) 23 | print("The TextBox", rbx, "changed text to", rbx.Text) 24 | end, 25 | }) 26 | ]] 27 | 28 | local Type = require("../Type.roblox.luau") 29 | 30 | local Change = {} 31 | 32 | local changeMetatable = { 33 | __tostring = function(self) 34 | return string.format("RoactHostChangeEvent(%s)", self.name) 35 | end, 36 | } 37 | 38 | setmetatable(Change, { 39 | __index = function(self, propertyName) 40 | local changeListener = { 41 | [Type] = Type.HostChangeEvent, 42 | name = propertyName, 43 | } 44 | 45 | setmetatable(changeListener, changeMetatable) 46 | Change[propertyName] = changeListener 47 | 48 | return changeListener 49 | end, 50 | }) 51 | 52 | return Change 53 | -------------------------------------------------------------------------------- /modules/shared/src/PropMarkers/Event.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --[[ 16 | Index into `Event` to get a prop key for attaching to an event on a Roblox 17 | Instance. 18 | 19 | Example: 20 | 21 | Roact.createElement("TextButton", { 22 | Text = "Hello, world!", 23 | 24 | [Roact.Event.MouseButton1Click] = function(rbx) 25 | print("Clicked", rbx) 26 | end 27 | }) 28 | ]] 29 | 30 | local Type = require("../Type.roblox.luau") 31 | 32 | local Event = {} 33 | 34 | local eventMetatable = { 35 | __tostring = function(self) 36 | return string.format("RoactHostEvent(%s)", self.name) 37 | end, 38 | } 39 | 40 | setmetatable(Event, { 41 | __index = function(self, eventName) 42 | local event = { 43 | [Type] = Type.HostEvent, 44 | name = eventName, 45 | } 46 | 47 | setmetatable(event, eventMetatable) 48 | 49 | Event[eventName] = event 50 | 51 | return event 52 | end, 53 | }) 54 | 55 | return Event 56 | -------------------------------------------------------------------------------- /modules/shared/src/PropMarkers/Tag.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --[[ 16 | Special value for assigning tags to roblox instances via Roact 17 | ]] 18 | local Symbol = require("../Symbol.roblox.luau") 19 | 20 | local Tag = Symbol.named("RobloxTag") 21 | 22 | return Tag 23 | -------------------------------------------------------------------------------- /modules/shared/src/PropMarkers/__tests__/Change.spec.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | 16 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 17 | local jestExpect = JestGlobals.expect 18 | local it = JestGlobals.it 19 | 20 | local Type = require("../../Type.roblox.luau") 21 | local Change = require("../Change") 22 | 23 | it("should yield change listener objects when indexed", function() 24 | jestExpect(Type.of(Change.Text)).toBe(Type.HostChangeEvent) 25 | jestExpect(Type.of(Change.Selected)).toBe(Type.HostChangeEvent) 26 | end) 27 | 28 | it("should yield the same object when indexed again", function() 29 | local a = Change.Text 30 | local b = Change.Text 31 | local c = Change.Selected 32 | 33 | jestExpect(a).toBe(b) 34 | jestExpect(a).never.toBe(c) 35 | end) 36 | -------------------------------------------------------------------------------- /modules/shared/src/PropMarkers/__tests__/Event.spec.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | 16 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 17 | local jestExpect = JestGlobals.expect 18 | local it = JestGlobals.it 19 | 20 | local Type = require("../../Type.roblox.luau") 21 | local Event = require("../Event") 22 | 23 | it("should yield event objects when indexed", function() 24 | jestExpect(Type.of(Event.MouseButton1Click)).toBe(Type.HostEvent) 25 | jestExpect(Type.of(Event.Touched)).toBe(Type.HostEvent) 26 | end) 27 | 28 | it("should yield the same object when indexed again", function() 29 | local a = Event.MouseButton1Click 30 | local b = Event.MouseButton1Click 31 | local c = Event.Touched 32 | 33 | jestExpect(a).toBe(b) 34 | jestExpect(a).never.toBe(c) 35 | end) 36 | -------------------------------------------------------------------------------- /modules/shared/src/ReactElementType.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 11 | type Object = LuauPolyfill.Object 12 | 13 | local flowtypes = require("./flowtypes.roblox.luau") 14 | type React_Element = flowtypes.React_Element 15 | type React_StatelessFunctionalComponent

= flowtypes.React_StatelessFunctionalComponent< 16 | P 17 | > 18 | type React_ComponentType

= flowtypes.React_ComponentType

19 | 20 | export type Source = { 21 | fileName: string, 22 | lineNumber: number, 23 | } 24 | type Key = string | number 25 | -- ROBLOX deviation: we're using the TypeScript definition here, which is more strict 26 | export type ReactElement

= { 27 | ["$$typeof"]: number, 28 | 29 | -- ROBLOX FIXME Luau: Luau has some trouble and inlining the type param from createElement doesn't help 30 | type: React_StatelessFunctionalComponent

| React_ComponentType

| string, 31 | -- type: T, 32 | key: Key | nil, 33 | ref: any, 34 | props: P, 35 | 36 | -- ROBLOX deviation: upstream has this as interface, which is extensible, Luau types are closed by default 37 | -- ReactFiber 38 | _owner: any, 39 | 40 | -- __DEV__ 41 | _store: any?, 42 | _self: React_Element?, 43 | _shadowChildren: any?, 44 | _source: Source?, 45 | } 46 | 47 | -- deviation: Return something so that the module system is happy 48 | return {} 49 | -------------------------------------------------------------------------------- /modules/shared/src/ReactFiberHostConfig/WithNoHydration.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/ReactFiberHostConfigWithNoHydration.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | local invariant = require("../invariant") 12 | 13 | -- Renderers that don't support hydration 14 | -- can re-export everything from this module. 15 | 16 | function shim(...) 17 | invariant( 18 | false, 19 | "The current renderer does not support hydration. " 20 | .. "This error is likely caused by a bug in React. " 21 | .. "Please file an issue." 22 | ) 23 | end 24 | 25 | -- Hydration (when unsupported) 26 | export type SuspenseInstance = any 27 | return { 28 | supportsHydration = false, 29 | canHydrateInstance = shim, 30 | canHydrateTextInstance = shim, 31 | canHydrateSuspenseInstance = shim, 32 | isSuspenseInstancePending = shim, 33 | isSuspenseInstanceFallback = shim, 34 | registerSuspenseInstanceRetry = shim, 35 | getNextHydratableSibling = shim, 36 | getFirstHydratableChild = shim, 37 | hydrateInstance = shim, 38 | hydrateTextInstance = shim, 39 | hydrateSuspenseInstance = shim, 40 | getNextHydratableInstanceAfterSuspenseInstance = shim, 41 | commitHydratedContainer = shim, 42 | commitHydratedSuspenseInstance = shim, 43 | clearSuspenseBoundary = shim, 44 | clearSuspenseBoundaryFromContainer = shim, 45 | didNotMatchHydratedContainerTextInstance = shim, 46 | didNotMatchHydratedTextInstance = shim, 47 | didNotHydrateContainerInstance = shim, 48 | didNotHydrateInstance = shim, 49 | didNotFindHydratableContainerInstance = shim, 50 | didNotFindHydratableContainerTextInstance = shim, 51 | didNotFindHydratableContainerSuspenseInstance = shim, 52 | didNotFindHydratableInstance = shim, 53 | didNotFindHydratableTextInstance = shim, 54 | didNotFindHydratableSuspenseInstance = shim, 55 | } 56 | -------------------------------------------------------------------------------- /modules/shared/src/ReactFiberHostConfig/WithNoPersistence.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | local invariant = require("../invariant") 12 | 13 | -- Renderers that don't support persistence 14 | -- can re-export everything from this module. 15 | 16 | local function shim(...) 17 | invariant( 18 | false, 19 | "The current renderer does not support persistence. " 20 | .. "This error is likely caused by a bug in React. " 21 | .. "Please file an issue." 22 | ) 23 | end 24 | 25 | -- Persistence (when unsupported) 26 | return { 27 | supportsPersistence = false, 28 | cloneInstance = shim, 29 | cloneFundamentalInstance = shim, 30 | createContainerChildSet = shim, 31 | appendChildToContainerChildSet = shim, 32 | finalizeContainerChildren = shim, 33 | replaceContainerChildren = shim, 34 | cloneHiddenInstance = shim, 35 | cloneHiddenTextInstance = shim, 36 | } 37 | -------------------------------------------------------------------------------- /modules/shared/src/ReactFiberHostConfig/WithNoTestSelectors.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/3cde22a84e246fc5361f038bf0c23405b2572c22/packages/react-reconciler/src/ReactFiberHostConfigWithNoTestSelectors.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @flow 9 | ]] 10 | 11 | local invariant = require("../invariant") 12 | 13 | -- Renderers that don't support test selectors 14 | -- can re-export everything from this module. 15 | 16 | local function shim(...) 17 | invariant( 18 | false, 19 | "The current renderer does not support test selectors. " 20 | .. "This error is likely caused by a bug in React. " 21 | .. "Please file an issue." 22 | ) 23 | end 24 | 25 | -- Test selectors (when unsupported) 26 | return { 27 | supportsTestSelectors = false, 28 | findFiberRoot = shim, 29 | getBoundingRect = shim, 30 | getTextContent = shim, 31 | isHiddenSubtree = shim, 32 | matchAccessibilityRole = shim, 33 | setFocusIfFocusable = shim, 34 | setupIntersectionObserver = shim, 35 | } 36 | -------------------------------------------------------------------------------- /modules/shared/src/ReactFiberHostConfig/init.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --[[ 16 | ROBLOX deviation: ReactFiberHostConfig captures singleton state across the 17 | whole workspace. This file and the modules it requires were moved from React 18 | to untangle a cyclic workspace member dependency. 19 | 20 | Before: 21 | * ReactFiberHostConfig (and the 5 associated modules) lived in React 22 | * React had a dependency on Shared 23 | * Shared reached into React source to re-export ReactFiberHostConfig (cycle) 24 | 25 | After: 26 | * ReactFiberHostConfig (and the 5 associated modules) live in Shared 27 | * React depends on Shared 28 | * Shared has no intra-workspace dependencies (no cycles) 29 | ]] 30 | 31 | -- types that are common across ReactFiberHostConfig files, moved here to avoid circular deps 32 | type Object = { [string]: any } 33 | export type OpaqueIDType = string | Object 34 | 35 | return { 36 | WithNoHydration = require("./WithNoHydration"), 37 | WithNoPersistence = require("./WithNoPersistence"), 38 | WithNoTestSelectors = require("./WithNoTestSelectors"), 39 | } 40 | -------------------------------------------------------------------------------- /modules/shared/src/ReactSharedInternals/IsSomeRendererActing.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/a457e02ae3a2d3903fcf8748380b1cc293a2445e/packages/react/src/IsSomeRendererActing.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | --[[* 13 | * Used by act() to track whether you're inside an act() scope. 14 | ]] 15 | 16 | local IsSomeRendererActing = { 17 | current = false, 18 | } 19 | return IsSomeRendererActing 20 | -------------------------------------------------------------------------------- /modules/shared/src/ReactSharedInternals/ReactCurrentBatchConfig.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/92fcd46cc79bbf45df4ce86b0678dcef3b91078d/packages/react/src/ReactCurrentBatchConfig.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | --[[* 13 | * Keeps track of the current batch's configuration such as how long an update 14 | * should suspend for if it needs to. 15 | ]] 16 | local ReactCurrentBatchConfig = { 17 | transition = 0, 18 | } 19 | 20 | return ReactCurrentBatchConfig 21 | -------------------------------------------------------------------------------- /modules/shared/src/ReactSharedInternals/ReactCurrentOwner.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/376d5c1b5aa17724c5fea9412f8fcde14a7b23f1/packages/react/src/ReactCurrentOwner.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | --[[* 13 | * Keeps track of the current owner. 14 | * 15 | * The current owner is the component who should own any components that are 16 | * currently being constructed. 17 | ]] 18 | local ReactCurrentOwner = { 19 | --[[* 20 | * @internal 21 | * @type {ReactComponent} 22 | ]] 23 | -- ROBLOX deviation START: upstream types this as Fiber, but that would incur a circular dependency between reconciler and shared 24 | current = nil :: any, 25 | -- ROBLOX deviation END 26 | } 27 | 28 | return ReactCurrentOwner 29 | -------------------------------------------------------------------------------- /modules/shared/src/ReactSharedInternals/ReactDebugCurrentFrame.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/98d410f5005988644d01c9ec79b7181c3dd6c847/packages/react/src/ReactDebugCurrentFrame.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | local ReactDebugCurrentFrame = {} 13 | 14 | local currentExtraStackFrame = nil :: nil | string 15 | 16 | function ReactDebugCurrentFrame.setExtraStackFrame(stack: string?): () 17 | if _G.__DEV__ then 18 | currentExtraStackFrame = stack 19 | end 20 | end 21 | 22 | if _G.__DEV__ then 23 | -- deviation: in Lua, the implementation is duplicated 24 | -- function ReactDebugCurrentFrame.setExtraStackFrame(stack: string?) 25 | -- if _G.__DEV__ then 26 | -- currentExtraStackFrame = stack 27 | -- end 28 | -- end 29 | 30 | -- Stack implementation injected by the current renderer. 31 | ReactDebugCurrentFrame.getCurrentStack = nil :: nil | (() -> string) 32 | 33 | function ReactDebugCurrentFrame.getStackAddendum(): string 34 | local stack = "" 35 | 36 | -- Add an extra top frame while an element is being validated 37 | if currentExtraStackFrame then 38 | stack = stack .. currentExtraStackFrame 39 | end 40 | 41 | -- Delegate to the injected renderer-specific implementation 42 | local impl = ReactDebugCurrentFrame.getCurrentStack 43 | if impl then 44 | stack = stack .. (impl() or "") 45 | end 46 | 47 | return stack 48 | end 49 | end 50 | 51 | return ReactDebugCurrentFrame 52 | -------------------------------------------------------------------------------- /modules/shared/src/ReactSharedInternals/init.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/607148673b3156d051d1fed17cd49e83698dce54/packages/react/src/ReactSharedInternals.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | ]] 9 | 10 | --[[ 11 | ROBLOX deviation: ReactSharedInternals captures singleton state across the 12 | whole workspace. This file and the modules it requires were moved from React 13 | to untangle a cyclic workspace member dependency. 14 | 15 | Before: 16 | * ReactSharedInternals (and the 5 associated modules) lived in React 17 | * React had a dependency on Shared 18 | * Shared reached into React source to re-export ReactSharedInternals (cycle) 19 | 20 | After: 21 | * ReactSharedInternals (and the 5 associated modules) live in Shared 22 | * React depends on Shared 23 | * Shared has no intra-workspace dependencies (no cycles) 24 | ]] 25 | local console = require("@pkg/@jsdotlua/luau-polyfill").console 26 | local function onlyInTestError(functionName: string) 27 | return function() 28 | console.error(functionName .. " is only available in tests, not in production") 29 | end 30 | end 31 | 32 | -- import assign from 'object-assign'; 33 | local ReactCurrentDispatcher = require("./ReactCurrentDispatcher") 34 | export type Dispatcher = ReactCurrentDispatcher.Dispatcher 35 | local ReactCurrentBatchConfig = require("./ReactCurrentBatchConfig") 36 | local ReactCurrentOwner = require("./ReactCurrentOwner") 37 | local ReactDebugCurrentFrame = require("./ReactDebugCurrentFrame") 38 | local IsSomeRendererActing = require("./IsSomeRendererActing") 39 | 40 | local ReactSharedInternals = { 41 | ReactCurrentDispatcher = ReactCurrentDispatcher, 42 | ReactCurrentBatchConfig = ReactCurrentBatchConfig, 43 | ReactCurrentOwner = ReactCurrentOwner, 44 | IsSomeRendererActing = IsSomeRendererActing, 45 | -- ROBLOX deviation: Luau type checking requires us to have a consistent export shape regardless of __DEV__ 46 | -- ROBLOX TODO: use if-expressions when all clients are on 503+ 47 | ReactDebugCurrentFrame = if _G.__DEV__ 48 | then ReactDebugCurrentFrame 49 | else { 50 | setExtraStackFrame = function(_: string?): () 51 | onlyInTestError("setExtraStackFrame") 52 | end, 53 | }, 54 | -- deviation: We shouldn't have to worry about duplicate bundling here 55 | -- Used by renderers to avoid bundling object-assign twice in UMD bundles: 56 | -- assign, 57 | } 58 | 59 | return ReactSharedInternals 60 | -------------------------------------------------------------------------------- /modules/shared/src/ReactVersion.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/a89854bc936668d325cac9a22e2ebfa128c7addf/packages/shared/ReactVersion.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | ]] 9 | 10 | -- TODO: this is special because it gets imported during build. 11 | return "17.2.1" 12 | -------------------------------------------------------------------------------- /modules/shared/src/Symbol.roblox.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --[[ 16 | A 'Symbol' is an opaque marker type. 17 | 18 | Symbols have the type 'userdata', but when printed to the console, the name 19 | of the symbol is shown. 20 | ]] 21 | 22 | local Symbol = {} 23 | 24 | --[[ 25 | Creates a Symbol with the given name. 26 | 27 | When printed or coerced to a string, the symbol will turn into the string 28 | given as its name. 29 | ]] 30 | function Symbol.named(name) 31 | assert(type(name) == "string", "Symbols must be created using a string name!") 32 | 33 | local self = newproxy(true) 34 | 35 | local wrappedName = string.format("Symbol(%s)", name) 36 | 37 | getmetatable(self).__tostring = function() 38 | return wrappedName 39 | end 40 | 41 | return self 42 | end 43 | 44 | return Symbol 45 | -------------------------------------------------------------------------------- /modules/shared/src/Type.roblox.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --[[ 16 | Contains markers for annotating objects with types. 17 | 18 | To set the type of an object, use `Type` as a key and the actual marker as 19 | the value: 20 | 21 | local foo = { 22 | [Type] = Type.Foo, 23 | } 24 | ]] 25 | 26 | local Symbol = require("./Symbol.roblox.luau") 27 | 28 | local Type = newproxy(true) 29 | 30 | local TypeInternal = {} 31 | 32 | local function addType(name) 33 | TypeInternal[name] = Symbol.named("Roact" .. name) 34 | end 35 | 36 | addType("HostChangeEvent") 37 | addType("HostEvent") 38 | 39 | function TypeInternal.of(value) 40 | if typeof(value) ~= "table" then 41 | return nil 42 | end 43 | 44 | return value[Type] 45 | end 46 | 47 | getmetatable(Type).__index = TypeInternal 48 | 49 | getmetatable(Type).__tostring = function() 50 | return "RoactType" 51 | end 52 | 53 | return Type 54 | -------------------------------------------------------------------------------- /modules/shared/src/UninitializedState.roblox.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | --!strict 16 | local console = require("./console") 17 | 18 | -- ROBLOX DEVIATION: Initialize state to a singleton that warns on access and errors on assignment 19 | -- initial state singleton 20 | local UninitializedState = {} 21 | 22 | setmetatable(UninitializedState, { 23 | __index = function(table, key) 24 | if _G.__DEV__ then 25 | console.warn( 26 | "Attempted to access uninitialized state. Use setState to initialize state" 27 | ) 28 | end 29 | return nil 30 | end, 31 | __newindex = function(table, key) 32 | if _G.__DEV__ then 33 | console.error( 34 | "Attempted to directly mutate state. Use setState to assign new values to state." 35 | ) 36 | end 37 | return nil 38 | end, 39 | __tostring = function(self) 40 | return "" 41 | end, 42 | __metatable = "UninitializedState", 43 | }) 44 | 45 | return UninitializedState 46 | -------------------------------------------------------------------------------- /modules/shared/src/__tests__/ReactErrorProd-internal.spec.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/9a5576f4d263ac5d7a9462a287d1524fda3355b8/packages/shared/__tests__/ReactErrorProd-test.internal.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * @emails react-core 9 | ]] 10 | --!strict 11 | 12 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 13 | local beforeEach = JestGlobals.beforeEach 14 | local jestExpect = JestGlobals.expect 15 | local it = JestGlobals.it 16 | local jest = JestGlobals.jest 17 | local formatProdErrorMessage 18 | 19 | beforeEach(function() 20 | jest.resetModules() 21 | formatProdErrorMessage = require("../formatProdErrorMessage") 22 | end) 23 | 24 | it("should throw with the correct number of `%s`s in the URL", function() 25 | jestExpect(formatProdErrorMessage(124, "foo", "bar")).toEqual( 26 | "Minified React error #124; visit " 27 | .. "https://reactjs.org/docs/error-decoder.html?invariant=124&args[]=foo&args[]=bar" 28 | .. " for the full message or use the non-minified dev environment" 29 | .. " for full errors and additional helpful warnings." 30 | ) 31 | 32 | jestExpect(formatProdErrorMessage(20)).toEqual( 33 | "Minified React error #20; visit " 34 | .. "https://reactjs.org/docs/error-decoder.html?invariant=20" 35 | .. " for the full message or use the non-minified dev environment" 36 | .. " for full errors and additional helpful warnings." 37 | ) 38 | 39 | jestExpect(formatProdErrorMessage(77, "

", "&?bar")).toEqual( 40 | "Minified React error #77; visit " 41 | .. "https://reactjs.org/docs/error-decoder.html?invariant=77&args[]=%3Cdiv%3E&args[]=%26%3Fbar" 42 | .. " for the full message or use the non-minified dev environment" 43 | .. " for full errors and additional helpful warnings." 44 | ) 45 | end) 46 | -------------------------------------------------------------------------------- /modules/shared/src/__tests__/getComponentName.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 2 | local beforeEach = JestGlobals.beforeEach 3 | local jestExpect = JestGlobals.expect 4 | local describe = JestGlobals.describe 5 | local it = JestGlobals.it 6 | local React 7 | 8 | local getComponentName 9 | local function MyComponent() end 10 | local anonymous = function() end 11 | 12 | beforeEach(function() 13 | React = require("@pkg/@jsdotlua/react") 14 | 15 | getComponentName = require("@pkg/@jsdotlua/shared").getComponentName 16 | end) 17 | 18 | describe("function components", function() 19 | it("gets name from non-anonymous function", function() 20 | jestExpect(getComponentName(MyComponent)).toBe("MyComponent") 21 | end) 22 | it("gets nil from anonymous function", function() 23 | local anonymous = function() end 24 | jestExpect(getComponentName(anonymous)).toBe(nil) 25 | end) 26 | end) 27 | describe("Lazy components", function() 28 | it("gets name from lazy-wrapped non-anonymous function", function() 29 | local lazyMyComponent = React.lazy(function() 30 | return { 31 | andThen = function(self, resolve) 32 | resolve({ default = MyComponent }) 33 | end, 34 | } 35 | end) 36 | jestExpect(getComponentName(lazyMyComponent)).toBe("MyComponent") 37 | end) 38 | it("gets nil from lazy-wrapped anonymous function", function() 39 | local lazyAnonymous = React.lazy(function() 40 | return { 41 | andThen = function(self, resolve) 42 | resolve({ default = anonymous }) 43 | end, 44 | } 45 | end) 46 | jestExpect(getComponentName(lazyAnonymous)).toBe(nil) 47 | end) 48 | end) 49 | -------------------------------------------------------------------------------- /modules/shared/src/__tests__/isValidElementType.roblox.spec.luau: -------------------------------------------------------------------------------- 1 | local JestGlobals = require("@pkg/@jsdotlua/jest-globals") 2 | local jestExpect = JestGlobals.expect 3 | local describe = JestGlobals.describe 4 | local it = JestGlobals.it 5 | 6 | local isValidElementType = require("@pkg/@jsdotlua/shared").isValidElementType 7 | local ReactSymbols = require("@pkg/@jsdotlua/shared").ReactSymbols 8 | local element 9 | 10 | describe("accept element primitives", function() 11 | it("from strings", function() 12 | element = "TextLabel" 13 | jestExpect(isValidElementType(element)).toBe(true) 14 | end) 15 | 16 | it("from functions", function() 17 | element = function() end 18 | jestExpect(isValidElementType(element)).toBe(true) 19 | end) 20 | 21 | it("from tables", function() 22 | element = {} 23 | element["$$typeof"] = ReactSymbols.REACT_CONTEXT_TYPE 24 | jestExpect(isValidElementType(element)).toBe(true) 25 | end) 26 | end) 27 | 28 | describe("does not accept", function() 29 | it("REACT_ELEMENT_TYPE", function() 30 | element = {} 31 | element["$$typeof"] = ReactSymbols.REACT_ELEMENT_TYPE 32 | jestExpect(isValidElementType(element)).toBe(false) 33 | end) 34 | end) 35 | -------------------------------------------------------------------------------- /modules/shared/src/console.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | * Copyright (c) Roblox Corporation. All rights reserved. 3 | * Licensed under the MIT License (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * https://opensource.org/licenses/MIT 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | ]] 15 | 16 | -- deviation: this lets us have the same functionality as in React, without 17 | -- having something like Babel to inject a different implementation of 18 | -- console.warn and console.error into the code 19 | -- Instead of using `LuauPolyfill.console`, React internals should use this 20 | -- wrapper to be able to use consoleWithStackDev in dev mode 21 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 22 | local console = LuauPolyfill.console 23 | local consoleWithStackDev = require("./consoleWithStackDev") 24 | 25 | if _G.__DEV__ then 26 | local newConsole = setmetatable({ 27 | warn = consoleWithStackDev.warn, 28 | error = consoleWithStackDev.error, 29 | }, { 30 | __index = console, 31 | }) 32 | return newConsole 33 | end 34 | 35 | return console 36 | -------------------------------------------------------------------------------- /modules/shared/src/consoleWithStackDev.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/cb141681750c8221ac799074df09df2bb448c7a4/packages/shared/consoleWithStackDev.js 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | ]] 8 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 9 | local console = LuauPolyfill.console 10 | local Array = LuauPolyfill.Array 11 | 12 | local ReactSharedInternals = require("./ReactSharedInternals") 13 | -- In DEV, calls to console.warn and console.error get replaced 14 | -- by calls to these methods by a Babel plugin. 15 | -- 16 | -- In PROD (or in packages without access to React internals), 17 | -- they are left as they are instead. 18 | 19 | -- deviation: declare this ahead of time so that `warn` and `error` are able to 20 | -- reference it 21 | local printWarning 22 | 23 | local exports = {} 24 | exports.warn = function(format, ...) 25 | if _G.__DEV__ then 26 | printWarning("warn", format, { ... }) 27 | end 28 | end 29 | exports.error = function(format, ...) 30 | if _G.__DEV__ then 31 | printWarning("error", format, { ... }) 32 | end 33 | end 34 | 35 | function printWarning(level, format, args) 36 | -- When changing this logic, you might want to also 37 | -- update consoleWithStackDev.www.js as well. 38 | if _G.__DEV__ then 39 | local ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame 40 | local stack = ReactDebugCurrentFrame.getStackAddendum() 41 | 42 | if stack ~= "" then 43 | format ..= "%s" 44 | -- deviation: no array `concat` function in lua 45 | args = Array.slice(args, 1) 46 | table.insert(args, stack) 47 | end 48 | 49 | local argsWithFormat = Array.map(args, tostring) 50 | -- Careful: RN currently depends on this prefix 51 | table.insert(argsWithFormat, 1, "Warning: " .. format) 52 | -- We intentionally don't use spread (or .apply) directly because it 53 | -- breaks IE9: https://github.com/facebook/react/issues/13610 54 | -- eslint-disable-next-line react-internal/no-production-logging 55 | console[level](unpack(argsWithFormat)) 56 | end 57 | end 58 | 59 | return exports 60 | -------------------------------------------------------------------------------- /modules/shared/src/enqueueTask.roblox.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --[[* 3 | * Copyright (c) Facebook, Inc. and its affiliates. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | * 9 | ]] 10 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 11 | local setTimeout = LuauPolyfill.setTimeout 12 | 13 | return function(task) 14 | -- deviation: Replace with setImmediate once we create an equivalent polyfill 15 | return setTimeout(task, 0) 16 | end 17 | -------------------------------------------------------------------------------- /modules/shared/src/formatProdErrorMessage.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/75955bf1d7ff6c2c1f4052f4a84dd2ce6944c62e/packages/shared/formatProdErrorMessage.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | ]] 10 | 11 | -- Do not require this module directly! Use normal `invariant` calls with 12 | -- template literal strings. The messages will be replaced with error codes 13 | -- during build. 14 | 15 | local HttpService = game:GetService("HttpService") 16 | 17 | local function formatProdErrorMessage(code, ...) 18 | local url = "https://reactjs.org/docs/error-decoder.html?invariant=" .. tostring(code) 19 | local argsLength = select("#", ...) 20 | for i = 1, argsLength, 1 do 21 | -- deviation: UrlEncode should be equivalent to encodeURIComponent 22 | url = url .. "&args[]=" .. HttpService:UrlEncode(select(i, ...)) 23 | end 24 | return string.format( 25 | "Minified React error #%d; visit %s for the full message or " 26 | .. "use the non-minified dev environment for full errors and additional " 27 | .. "helpful warnings.", 28 | code, 29 | url 30 | ) 31 | end 32 | 33 | return formatProdErrorMessage 34 | -------------------------------------------------------------------------------- /modules/shared/src/invariant.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/42c3c967d1e4ca4731b47866f2090bc34caa086c/packages/shared/invariant.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | ]] 10 | 11 | --[[* 12 | * Use invariant() to assert state which your program assumes to be true. 13 | * 14 | * Provide sprintf-style format (only %s is supported) and arguments 15 | * to provide information about what broke and what you were 16 | * expecting. 17 | * 18 | * The invariant message will be stripped in production, but the invariant 19 | * will remain to ensure logic does not differ in production. 20 | ]] 21 | local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") 22 | local Error = LuauPolyfill.Error 23 | 24 | local function invariant(condition, format, ...) 25 | -- ROBLOX TODO: we should encapsulate all formatting compatibility here, 26 | -- rather than spreading workarounds throughout the codebase, eg this 27 | -- should print an array without the need for a table.concat on the consumer side 28 | if not condition then 29 | error(Error(string.format(format, ...))) 30 | end 31 | end 32 | 33 | return invariant 34 | -------------------------------------------------------------------------------- /modules/shared/src/objectIs.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | -- ROBLOX upstream: https://github.com/facebook/react/blob/6faf6f5eb1705eef39a1d762d6ee381930f36775/packages/shared/objectIs.js 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * @flow 10 | ]] 11 | 12 | --[[* 13 | * inlined Object.is polyfill to avoid requiring consumers ship their own 14 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is 15 | ]] 16 | local function is(x: any, y: any): boolean 17 | return x == y and (x ~= 0 or 1 / x == 1 / y) or x ~= x and y ~= y -- eslint-disable-line no-self-compare 18 | end 19 | 20 | -- deviation: Object isn't a global in lua, so `Object.is` will never exist 21 | local objectIs = is 22 | 23 | return objectIs 24 | -------------------------------------------------------------------------------- /modules/shared/src/shallowEqual.luau: -------------------------------------------------------------------------------- 1 | -- ROBLOX upstream: https://github.com/facebook/react/blob/a9b035b0c2b8235405835beca0c4db2cc37f18d0/packages/shared/shallowEqual.js 2 | --!strict 3 | --[[* 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | * 9 | * 10 | ]] 11 | local is = require("./objectIs") 12 | 13 | --[[* 14 | * Performs equality by iterating through keys on an object and returning false 15 | * when any key has values which are not strictly equal between the arguments. 16 | * Returns true when the values of all keys are strictly equal. 17 | ]] 18 | local function shallowEqual(objA, objB) 19 | if is(objA, objB) then 20 | return true 21 | end 22 | 23 | if 24 | typeof(objA) ~= "table" 25 | or objA == nil 26 | or typeof(objB) ~= "table" 27 | or objB == nil 28 | then 29 | return false 30 | end 31 | 32 | -- deviation: `Object.keys` does not have an equivalent in Lua, so we 33 | -- iterate through each table instead 34 | for key, value in objA do 35 | if not is(objB[key], value) then 36 | return false 37 | end 38 | end 39 | 40 | for key, value in objB do 41 | if not is(objA[key], value) then 42 | return false 43 | end 44 | end 45 | 46 | return true 47 | end 48 | 49 | return shallowEqual 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace", 3 | "private": true, 4 | "workspaces": [ 5 | "modules/*" 6 | ], 7 | "scripts": { 8 | "prepare": "npmluau", 9 | "build-assets": "sh ./scripts/build-assets.sh", 10 | "lint": "selene modules", 11 | "format": "stylua modules", 12 | "style-check": "stylua modules --check", 13 | "clean": "rm -rf roblox build node_modules" 14 | }, 15 | "devDependencies": { 16 | "@jsdotlua/jest": "^3.6.1-rc.2", 17 | "@jsdotlua/jest-globals": "^3.6.1-rc.2", 18 | "commander": "^11.1.0", 19 | "npmluau": "^0.1.1" 20 | }, 21 | "packageManager": "yarn@4.5.0" 22 | } 23 | -------------------------------------------------------------------------------- /roblox-model/JestReact.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/jest-react") 2 | -------------------------------------------------------------------------------- /roblox-model/React.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactCache.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-cache") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactDebugTools.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-debug-tools") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactDevtoolsExtensions.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-devtools-extensions") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactDevtoolsShared.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-devtools-shared") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactIs.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-is") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactReconciler.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-reconciler") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactRoblox.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-roblox") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactShallowRenderer.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-shallow-renderer") 2 | -------------------------------------------------------------------------------- /roblox-model/ReactTestRenderer.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/react-test-renderer") 2 | -------------------------------------------------------------------------------- /roblox-model/RoactCompat.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/roact-compat") 2 | -------------------------------------------------------------------------------- /roblox-model/Scheduler.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/scheduler") 2 | -------------------------------------------------------------------------------- /roblox-model/Shared.luau: -------------------------------------------------------------------------------- 1 | return require("@pkg/@jsdotlua/shared") 2 | -------------------------------------------------------------------------------- /scripts/build-assets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | ./scripts/build-roblox-model.sh 6 | ./scripts/build-wally-package.sh 7 | -------------------------------------------------------------------------------- /scripts/build-roblox-model.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | build_with_darklua_config () { 6 | DARKLUA_CONFIG=$1 7 | OUTPUT=build/$2 8 | 9 | rm -rf roblox 10 | rm -f $OUTPUT 11 | 12 | mkdir -p roblox 13 | 14 | cp -rL node_modules/ roblox/node_modules/ 15 | cp -r roblox-model/ roblox/roblox-model/ 16 | 17 | for module_path in roblox/roblox-model/*; do 18 | module_name=$(basename $module_path) 19 | 20 | react_module=$(cat $module_path | sed 's/return require(\"\@pkg\/\@jsdotlua\/\(.*\)\")/\1/') 21 | alias_path=roblox/node_modules/.luau-aliases/@jsdotlua/$react_module.luau 22 | 23 | echo "local module = require('@pkg/@jsdotlua/${react_module}')" > $module_path 24 | 25 | tail -n +2 $alias_path >> $module_path 26 | done 27 | 28 | ./scripts/remove-tests.sh roblox 29 | 30 | cp default.project.json roblox 31 | 32 | rojo sourcemap roblox/default.project.json -o roblox/sourcemap.json 33 | 34 | cp $DARKLUA_CONFIG roblox 35 | 36 | darklua process --config roblox/$DARKLUA_CONFIG roblox/node_modules roblox/node_modules 37 | darklua process --config roblox/$DARKLUA_CONFIG roblox/roblox-model roblox/roblox-model 38 | 39 | mkdir -p $(dirname $OUTPUT) 40 | 41 | rojo build roblox/default.project.json -o $OUTPUT 42 | } 43 | 44 | build_with_darklua_config .darklua.json react-lua.rbxm 45 | build_with_darklua_config .darklua-dev.json debug/react-lua.rbxm 46 | -------------------------------------------------------------------------------- /scripts/build-wally-package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | rm -rf roblox/node_modules 6 | 7 | mkdir -p roblox 8 | 9 | cp -rL node_modules/ roblox/node_modules/ 10 | 11 | ./scripts/remove-tests.sh roblox/node_modules 12 | 13 | rm -rf build/wally 14 | 15 | for module_path in modules/*; do 16 | module_name=$(basename $module_path) 17 | 18 | original_package=node_modules/@jsdotlua/$module_name 19 | 20 | if [ -f $original_package/package.json ]; then 21 | echo Process package $module_name 22 | 23 | wally_package=build/wally/$module_name 24 | roblox_package=roblox/node_modules/@jsdotlua/$module_name 25 | 26 | mkdir -p $wally_package 27 | mkdir -p $wally_package/src 28 | cp LICENSE $wally_package/LICENSE 29 | cp $original_package/default.project.json $wally_package 30 | node ./scripts/npm-to-wally.js $roblox_package/package.json $wally_package/wally.toml $roblox_package/wally-package.project.json --workspace-path modules 31 | 32 | cp .darklua-wally.json $roblox_package 33 | cp -r roblox/node_modules/.luau-aliases/* $roblox_package 34 | 35 | rojo sourcemap $roblox_package/wally-package.project.json --output $roblox_package/sourcemap.json 36 | 37 | darklua process --config $roblox_package/.darklua-wally.json $roblox_package/src $wally_package/src 38 | 39 | wally package --project-path $wally_package --list 40 | 41 | echo "" 42 | fi 43 | done 44 | 45 | -------------------------------------------------------------------------------- /scripts/remove-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | FOLDER=$1 6 | 7 | find $FOLDER -name '__tests__' -type d -exec rm -r {} + 8 | find $FOLDER -name '.robloxrc' -type f -exec rm -r {} + 9 | find $FOLDER -name '*.spec.lua' -type f -exec rm -r {} + 10 | find $FOLDER -name 'jest.config.lua' -type f -exec rm -r {} + 11 | -------------------------------------------------------------------------------- /selene.toml: -------------------------------------------------------------------------------- 1 | std = "selene_definitions" 2 | 3 | [config] 4 | empty_if = { comments_count = true } 5 | unused_variable = { ignore_pattern = "result|ok|^_" } 6 | # this comes up when translating nested try/finally scenarios 7 | shadowing = { ignore_pattern = "result|ok|^_" } 8 | global_usage = { ignore_pattern = "^__" } 9 | 10 | [rules] 11 | global_usage = "allow" 12 | unused_variable = "allow" 13 | # remove when the Luau type narrowing issues (and the workarounds) are resolved 14 | shadowing = "allow" 15 | 16 | # remove when this issue is fixed: https://github.com/Kampfkarren/selene/issues/179 17 | if_same_then_else = "allow" 18 | -------------------------------------------------------------------------------- /selene_definitions.yml: -------------------------------------------------------------------------------- 1 | base: roblox 2 | name: selene_defs 3 | globals: 4 | # override Roblox require style with string requires 5 | require: 6 | args: 7 | - type: string 8 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | column_width = 90 --------------------------------------------------------------------------------