diff --git a/WorkspaceStatic/jest/matchers/createConsoleMatcher.lua b/WorkspaceStatic/jest/matchers/createConsoleMatcher.lua index 9e3070d7..40707ff3 100644 --- a/WorkspaceStatic/jest/matchers/createConsoleMatcher.lua +++ b/WorkspaceStatic/jest/matchers/createConsoleMatcher.lua @@ -1,8 +1,7 @@ --!nonstrict --- ROBLOX upstream: https://github.com/facebook/react/blob/6d50a9d090a2a672fc3dea5ce77a3a05332a6caa/fixtures/legacy-jsx-runtimes/setupTests.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/fixtures/legacy-jsx-runtimes/setupTests.js local Packages = script.Parent.Parent.Parent.TestRunner local JestDiff = require(Packages.Dev.JestDiff) - local function shouldIgnoreConsoleError(format, args) -- deviation: instead of checking if `process.env.NODE_ENV ~= "production"` -- we use the __DEV__ global diff --git a/WorkspaceStatic/jest/matchers/reactTestMatchers.lua b/WorkspaceStatic/jest/matchers/reactTestMatchers.lua index 308d6b19..aa834148 100644 --- a/WorkspaceStatic/jest/matchers/reactTestMatchers.lua +++ b/WorkspaceStatic/jest/matchers/reactTestMatchers.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/scripts/jest/matchers/reactTestMatchers.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/scripts/jest/matchers/reactTestMatchers.js local Packages = script.Parent.Parent.Parent.TestRunner -- ROBLOX deviation START: fix import -- local LuauPolyfill = require(Packages.LuauPolyfill) diff --git a/WorkspaceStatic/jest/matchers/schedulerTestMatchers.lua b/WorkspaceStatic/jest/matchers/schedulerTestMatchers.lua index 65eae617..efdfd1d5 100644 --- a/WorkspaceStatic/jest/matchers/schedulerTestMatchers.lua +++ b/WorkspaceStatic/jest/matchers/schedulerTestMatchers.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/scripts/jest/matchers/schedulerTestMatchers.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/scripts/jest/matchers/schedulerTestMatchers.js local Packages = script.Parent.Parent.Parent.TestRunner -- ROBLOX deviation START: fix import -- local LuauPolyfill = require(Packages.LuauPolyfill) diff --git a/modules/jest-react/src/JestReact.lua b/modules/jest-react/src/JestReact.lua index 3eabfcf7..9a08e39d 100644 --- a/modules/jest-react/src/JestReact.lua +++ b/modules/jest-react/src/JestReact.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/jest-react/src/JestReact.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/jest-react/src/JestReact.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -10,6 +10,7 @@ local Array = LuauPolyfill.Array -- ROBLOX deviation START: not used -- local Boolean = LuauPolyfill.Boolean -- ROBLOX deviation END +local Error = LuauPolyfill.Error local Object = LuauPolyfill.Object local exports = {} -- ROBLOX deviation START: fix import diff --git a/modules/react-cache/src/LRU.lua b/modules/react-cache/src/LRU.lua index 71b7eb40..41fb9728 100644 --- a/modules/react-cache/src/LRU.lua +++ b/modules/react-cache/src/LRU.lua @@ -15,8 +15,7 @@ local exports = {} local Scheduler = require("@pkg/@jsdotlua/scheduler") -- ROBLOX deviation END -- use dynamic dispatch for CommonJS interop named imports. -local scheduleCallback, IdlePriority = - Scheduler.unstable_scheduleCallback, Scheduler.unstable_IdlePriority +local scheduleCallback, IdlePriority = Scheduler.unstable_scheduleCallback, Scheduler.unstable_IdlePriority -- ROBLOX deviation START: use next_ instead -- type Entry = { value: T, onDelete: () -> unknown, previous: Entry, next: Entry } export type Entry = { @@ -32,8 +31,7 @@ local function createLRU(limit: number) local cleanUp -- ROBLOX deviation END local LIMIT = limit -- Circular, doubly-linked list - local first: Entry | nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] = - nil + local first: Entry | nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] = nil local size: number = 0 local cleanUpIsScheduled: boolean = false local function scheduleCleanUp() diff --git a/modules/react-cache/src/ReactCacheOld.lua b/modules/react-cache/src/ReactCacheOld.lua index f3cebdc4..d777074b 100644 --- a/modules/react-cache/src/ReactCacheOld.lua +++ b/modules/react-cache/src/ReactCacheOld.lua @@ -121,12 +121,7 @@ local lru = createLRU(CACHE_LIMIT) local entries: Record, Record>> = {} -- ROBLOX deviation END local CacheContext = React.createContext(nil) -local function accessResult( - resource: any, - fetch: (I) -> Thenable, - input: I, - key: K -): Result +local function accessResult(resource: any, fetch: (I) -> Thenable, input: I, key: K): Result -- ROBLOX deviation START: use regular indexing instead -- local entriesForResource = entries:get(resource) local entriesForResource = entries[resource] @@ -143,9 +138,7 @@ local function accessResult( end -- ROBLOX deviation START: cast as Luau doesn't narrow type on itself -- local entry = entriesForResource:get(key) - local entriesForResource_ = ( - entriesForResource :: Record> - ) :: Record> + local entriesForResource_ = (entriesForResource :: Record>) :: Record> local entry = entriesForResource_[key] -- ROBLOX deviation END if entry == nil then @@ -227,20 +220,15 @@ local function unstable_createResource< I, K, --[[ ROBLOX CHECK: upstream type uses type constraint which is not supported by Luau ]] --[[ K: string | number ]] V ->( - fetch: ( - I - ) -> Thenable, - maybeHashInput: ( - (I) -> K - )? -): Resource< +>(fetch: ( + I +) -> Thenable, maybeHashInput: ( + (I) -> K +)?): Resource< I, V > - local hashInput: (I) -> K = if maybeHashInput ~= nil - then maybeHashInput - else identityHashFn :: any + local hashInput: (I) -> K = if maybeHashInput ~= nil then maybeHashInput else identityHashFn :: any -- ROBLOX deviation START: split declaration and assignment -- local resource = { local resource diff --git a/modules/react-cache/src/__tests__/ReactCacheOld-internal.spec.lua b/modules/react-cache/src/__tests__/ReactCacheOld-internal.spec.lua index e2a236e4..6ef26edd 100644 --- a/modules/react-cache/src/__tests__/ReactCacheOld-internal.spec.lua +++ b/modules/react-cache/src/__tests__/ReactCacheOld-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -150,19 +150,14 @@ describe("ReactCache", function() listeners = { { resolve = resolve, reject = reject } } LuauPolyfill.setTimeout(function() if textResourceShouldFail then - Scheduler.unstable_yieldValue( - string.format("Promise rejected [%s]", text) - ) + Scheduler.unstable_yieldValue(string.format("Promise rejected [%s]", text)) status = "rejected" - value = - LuauPolyfill.Error.new("Failed to load: " .. text) + value = LuauPolyfill.Error.new("Failed to load: " .. text) for _, listener in listeners do listener.reject(value) end else - Scheduler.unstable_yieldValue( - string.format("Promise resolved [%s]", text) - ) + Scheduler.unstable_yieldValue(string.format("Promise resolved [%s]", text)) status = "resolved" value = text for _, listener in listeners do @@ -171,10 +166,7 @@ describe("ReactCache", function() end end, ms) else - table.insert( - listeners, - { resolve = resolve, reject = reject } - ) + table.insert(listeners, { resolve = resolve, reject = reject }) end elseif status == "resolved" then resolve(value) @@ -259,10 +251,7 @@ describe("ReactCache", function() React.createElement(AsyncText, { ms = 100, text = "Hi" }) ) end - ReactTestRenderer.create( - React.createElement(App, nil), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(App, nil), { unstable_isConcurrent = true }) expect(Scheduler).toFlushAndYield({ "Suspend! [Hi]", "Loading..." }) jest.advanceTimersByTime(100) expect(Scheduler).toHaveYielded({ "Promise resolved [Hi]" }) @@ -277,10 +266,7 @@ describe("ReactCache", function() React.createElement(AsyncText, { ms = 100, text = "Hi" }) ) end - local root = ReactTestRenderer.create( - React.createElement(App, nil), - { unstable_isConcurrent = true } - ) + local root = ReactTestRenderer.create(React.createElement(App, nil), { unstable_isConcurrent = true }) expect(Scheduler).toFlushAndYield({ "Suspend! [Hi]", "Loading..." }) textResourceShouldFail = true jest.advanceTimersByTime(100) @@ -295,68 +281,65 @@ describe("ReactCache", function() expect(Scheduler).toHaveYielded({ "Error! [Hi]", "Error! [Hi]" }) end) end) - it( - "warns if non-primitive key is passed to a resource without a hash function", - function() - -- ROBLOX deviation START: explicit type - -- local BadTextResource = createResource(function(ref0) - -- local text = ref0[1] - local BadTextResource = createResource(function(ref0: { string | number }) - local text = ref0[1] :: string - -- ROBLOX deviation END - -- ROBLOX deviation START: simplify - -- local ms = (function() - -- local element = table.unpack(ref0, 2, 2) - -- if element == nil then - -- return 0 - -- else - -- return element - -- end - -- end)() - local ms = ref0[2] or 0 - -- ROBLOX deviation END - return Promise.new(function(resolve, reject) - return setTimeout(function() - resolve(text) - end, ms) - end) + it("warns if non-primitive key is passed to a resource without a hash function", function() + -- ROBLOX deviation START: explicit type + -- local BadTextResource = createResource(function(ref0) + -- local text = ref0[1] + local BadTextResource = createResource(function(ref0: { string | number }) + local text = ref0[1] :: string + -- ROBLOX deviation END + -- ROBLOX deviation START: simplify + -- local ms = (function() + -- local element = table.unpack(ref0, 2, 2) + -- if element == nil then + -- return 0 + -- else + -- return element + -- end + -- end)() + local ms = ref0[2] or 0 + -- ROBLOX deviation END + return Promise.new(function(resolve, reject) + return setTimeout(function() + resolve(text) + end, ms) end) - local function App() - -- ROBLOX deviation START: use dot notation and cast type because luau doesn't support mixed arrays - -- Scheduler:unstable_yieldValue("App") - -- return BadTextResource:read({ "Hi", 100 }) - Scheduler.unstable_yieldValue("App") - return BadTextResource.read({ "Hi" :: string | number, 100 }) - -- ROBLOX deviation END - end - ReactTestRenderer.create( - React.createElement( - Suspense, - { fallback = React.createElement(Text, { text = "Loading..." }) }, - React.createElement(App, nil) - ), - { unstable_isConcurrent = true } - ) - -- ROBLOX deviation START: remove toJSBoolean and use _G - -- if Boolean.toJSBoolean(__DEV__) then - if _G.__DEV__ then - -- ROBLOX deviation END - expect(function() - expect(Scheduler).toFlushAndYield({ "App", "Loading..." }) - end).toErrorDev({ - "Invalid key type. Expected a string, number, symbol, or " - -- ROBLOX deviation START: FIXME - make console polyfill format arrays the same as JS - -- .. "boolean, but instead received: Hi,100\n\n" - .. 'boolean, but instead received: ["Hi", 100]\n\n' - -- ROBLOX deviation END - .. "To use non-primitive values as keys, you must pass a hash " - .. "function as the second argument to createResource().", - }) - else + end) + local function App() + -- ROBLOX deviation START: use dot notation and cast type because luau doesn't support mixed arrays + -- Scheduler:unstable_yieldValue("App") + -- return BadTextResource:read({ "Hi", 100 }) + Scheduler.unstable_yieldValue("App") + return BadTextResource.read({ "Hi" :: string | number, 100 }) + -- ROBLOX deviation END + end + ReactTestRenderer.create( + React.createElement( + Suspense, + { fallback = React.createElement(Text, { text = "Loading..." }) }, + React.createElement(App, nil) + ), + { unstable_isConcurrent = true } + ) + -- ROBLOX deviation START: remove toJSBoolean and use _G + -- if Boolean.toJSBoolean(__DEV__) then + if _G.__DEV__ then + -- ROBLOX deviation END + expect(function() expect(Scheduler).toFlushAndYield({ "App", "Loading..." }) - end + end).toErrorDev({ + "Invalid key type. Expected a string, number, symbol, or " + -- ROBLOX deviation START: FIXME - make console polyfill format arrays the same as JS + -- .. "boolean, but instead received: Hi,100\n\n" + .. 'boolean, but instead received: ["Hi", 100]\n\n' + -- ROBLOX deviation END + .. "To use non-primitive values as keys, you must pass a hash " + .. "function as the second argument to createResource().", + }) + else + expect(Scheduler).toFlushAndYield({ "App", "Loading..." }) end - ) + end) it("evicts least recently used values", function() return Promise.resolve():andThen(function() -- ROBLOX deviation START: use dot notation @@ -479,147 +462,142 @@ describe("ReactCache", function() expect(root).toMatchRenderedOutput("Result") end) end) - it( - "if a thenable resolves multiple times, does not update the first cached value", - function() - local resolveThenable - local BadTextResource = createResource(function(ref0) - -- ROBLOX deviation START: unused - -- local text = ref0[1] - -- local ms = (function() - -- local element = table.unpack(ref0, 2, 2) - -- if element == nil then - -- return 0 - -- else - -- return element - -- end - -- end)() - -- ROBLOX deviation END - local listeners = nil - local value = nil - return { - -- ROBLOX deviation START: use andThen - -- ["then"] = function(self, resolve, reject) - andThen = function(self, resolve, reject) - -- ROBLOX deviation END - if value ~= nil then - resolve(value) - else - if listeners == nil then - listeners = { resolve } - resolveThenable = function(v) - -- ROBLOX deviation START: use for..in loop instead - -- Array.forEach(listeners, function(listener) - -- return listener(v) - -- end) --[[ ROBLOX CHECK: check if 'listeners' is an Array ]] - for _, listener in listeners do - listener(v) - end - -- ROBLOX deviation END + it("if a thenable resolves multiple times, does not update the first cached value", function() + local resolveThenable + local BadTextResource = createResource(function(ref0) + -- ROBLOX deviation START: unused + -- local text = ref0[1] + -- local ms = (function() + -- local element = table.unpack(ref0, 2, 2) + -- if element == nil then + -- return 0 + -- else + -- return element + -- end + -- end)() + -- ROBLOX deviation END + local listeners = nil + local value = nil + return { + -- ROBLOX deviation START: use andThen + -- ["then"] = function(self, resolve, reject) + andThen = function(self, resolve, reject) + -- ROBLOX deviation END + if value ~= nil then + resolve(value) + else + if listeners == nil then + listeners = { resolve } + resolveThenable = function(v) + -- ROBLOX deviation START: use for..in loop instead + -- Array.forEach(listeners, function(listener) + -- return listener(v) + -- end) --[[ ROBLOX CHECK: check if 'listeners' is an Array ]] + for _, listener in listeners do + listener(v) end - else - table.insert(listeners, resolve) --[[ ROBLOX CHECK: check if 'listeners' is an Array ]] + -- ROBLOX deviation END end + else + table.insert(listeners, resolve) --[[ ROBLOX CHECK: check if 'listeners' is an Array ]] end - end, - } - -- ROBLOX deviation START: explicit type - -- end, function(ref0) - end, function(ref0: { any }) - -- ROBLOX deviation END - -- ROBLOX deviation START: ms not used - -- local text, ms = table.unpack(ref0, 1, 2) - -- return text - return ref0[1] - -- ROBLOX deviation END - end) - local function BadAsyncText(props) - local text = props.text - do --[[ ROBLOX COMMENT: try-catch block conversion ]] - -- ROBLOX deviation START: use pcall - -- local ok, result, hasReturned = xpcall(function() - local ok, result = pcall(function() + end + end, + } + -- ROBLOX deviation START: explicit type + -- end, function(ref0) + end, function(ref0: { any }) + -- ROBLOX deviation END + -- ROBLOX deviation START: ms not used + -- local text, ms = table.unpack(ref0, 1, 2) + -- return text + return ref0[1] + -- ROBLOX deviation END + end) + local function BadAsyncText(props) + local text = props.text + do --[[ ROBLOX COMMENT: try-catch block conversion ]] + -- ROBLOX deviation START: use pcall + -- local ok, result, hasReturned = xpcall(function() + local ok, result = pcall(function() + -- ROBLOX deviation END + -- ROBLOX deviation START: use dot notation + -- local actualText = BadTextResource:read({ props.text, props.ms }) + -- Scheduler:unstable_yieldValue(actualText) + local actualText = BadTextResource.read({ props.text, props.ms }) + Scheduler.unstable_yieldValue(actualText) + -- ROBLOX deviation END + -- ROBLOX deviation START: using pcall + -- return actualText, true + -- end, function(promise) + return actualText + end) + if not ok then + local promise = result + -- ROBLOX deviation END + -- ROBLOX deviation START: use andThen + -- if typeof(promise["then"]) == "function" then + if typeof(promise.andThen) == "function" then -- ROBLOX deviation END -- ROBLOX deviation START: use dot notation - -- local actualText = BadTextResource:read({ props.text, props.ms }) - -- Scheduler:unstable_yieldValue(actualText) - local actualText = BadTextResource.read({ props.text, props.ms }) - Scheduler.unstable_yieldValue(actualText) - -- ROBLOX deviation END - -- ROBLOX deviation START: using pcall - -- return actualText, true - -- end, function(promise) - return actualText - end) - if not ok then - local promise = result - -- ROBLOX deviation END - -- ROBLOX deviation START: use andThen - -- if typeof(promise["then"]) == "function" then - if typeof(promise.andThen) == "function" then + -- Scheduler:unstable_yieldValue( + Scheduler.unstable_yieldValue( -- ROBLOX deviation END - -- ROBLOX deviation START: use dot notation - -- Scheduler:unstable_yieldValue( - Scheduler.unstable_yieldValue( - -- ROBLOX deviation END - ("Suspend! [%s]"):format(tostring(text)) - ) - else - -- ROBLOX deviation START: use dot notation - -- Scheduler:unstable_yieldValue( - Scheduler.unstable_yieldValue( - -- ROBLOX deviation END - ("Error! [%s]"):format(tostring(text)) - ) - end - error(promise) - -- ROBLOX deviation START: using pcall - -- end) - -- if hasReturned then - -- return result - -- end + ("Suspend! [%s]"):format(tostring(text)) + ) + else + -- ROBLOX deviation START: use dot notation + -- Scheduler:unstable_yieldValue( + Scheduler.unstable_yieldValue( + -- ROBLOX deviation END + ("Error! [%s]"):format(tostring(text)) + ) end - return result - -- ROBLOX deviation END + error(promise) + -- ROBLOX deviation START: using pcall + -- end) + -- if hasReturned then + -- return result + -- end end - end - local root = ReactTestRenderer.create( - React.createElement( - Suspense, - { fallback = React.createElement(Text, { text = "Loading..." }) }, - React.createElement(BadAsyncText, { text = "Hi" }) - ), - { unstable_isConcurrent = true } - ) - expect(Scheduler).toFlushAndYield({ "Suspend! [Hi]", "Loading..." }) - resolveThenable("Hi") -- This thenable improperly resolves twice. We should not update the - -- cached value. - resolveThenable("Hi muahahaha I am different") - -- ROBLOX deviation START: use dot notation - -- root:update( - root.update( + return result -- ROBLOX deviation END - React.createElement( - Suspense, - { fallback = React.createElement(Text, { text = "Loading..." }) }, - React.createElement(BadAsyncText, { text = "Hi" }) - ), - { unstable_isConcurrent = true } - ) - expect(Scheduler).toHaveYielded({}) - expect(Scheduler).toFlushAndYield({ "Hi" }) - expect(root).toMatchRenderedOutput("Hi") + end end - ) + local root = ReactTestRenderer.create( + React.createElement( + Suspense, + { fallback = React.createElement(Text, { text = "Loading..." }) }, + React.createElement(BadAsyncText, { text = "Hi" }) + ), + { unstable_isConcurrent = true } + ) + expect(Scheduler).toFlushAndYield({ "Suspend! [Hi]", "Loading..." }) + resolveThenable("Hi") -- This thenable improperly resolves twice. We should not update the + -- cached value. + resolveThenable("Hi muahahaha I am different") + -- ROBLOX deviation START: use dot notation + -- root:update( + root.update( + -- ROBLOX deviation END + React.createElement( + Suspense, + { fallback = React.createElement(Text, { text = "Loading..." }) }, + React.createElement(BadAsyncText, { text = "Hi" }) + ), + { unstable_isConcurrent = true } + ) + expect(Scheduler).toHaveYielded({}) + expect(Scheduler).toFlushAndYield({ "Hi" }) + expect(root).toMatchRenderedOutput("Hi") + end) it("throws if read is called outside render", function() expect(function() -- ROBLOX deviation START: use dot notation -- return TextResource:read({ "A", 1000 }) TextResource.read({ "A", 1000 }) -- ROBLOX deviation END - end).toThrow( - "read and preload may only be called from within a component's render" - ) + end).toThrow("read and preload may only be called from within a component's render") end) it("throws if preload is called outside render", function() expect(function() @@ -627,8 +605,6 @@ describe("ReactCache", function() -- return TextResource:preload({ "A", 1000 }) TextResource.preload({ "A", 1000 }) -- ROBLOX deviation END - end).toThrow( - "read and preload may only be called from within a component's render" - ) + end).toThrow("read and preload may only be called from within a component's render") end) end) diff --git a/modules/react-debug-tools/src/ReactDebugHooks.lua b/modules/react-debug-tools/src/ReactDebugHooks.lua index 72584691..0cd96a9f 100644 --- a/modules/react-debug-tools/src/ReactDebugHooks.lua +++ b/modules/react-debug-tools/src/ReactDebugHooks.lua @@ -32,14 +32,8 @@ local exports = {} -- type ReactProviderType = sharedReactTypesModule.ReactProviderType local ReactTypes = require("@pkg/@jsdotlua/shared") type MutableSource = ReactTypes.MutableSource -type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn< - Source, - Snapshot -> -type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn< - Source, - Snapshot -> +type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn +type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn type ReactContext = ReactTypes.ReactContext type ReactProviderType = ReactTypes.ReactProviderType @@ -55,8 +49,7 @@ type ReactBindingUpdater = ReactTypes.ReactBindingUpdater -- ROBLOX deviation START: fix import -- local reactReconcilerSrcReactInternalTypesModule = -- require(Packages["react-reconciler"].src.ReactInternalTypes) -local reactReconcilerSrcReactInternalTypesModule = - require("@pkg/@jsdotlua/react-reconciler") +local reactReconcilerSrcReactInternalTypesModule = require("@pkg/@jsdotlua/react-reconciler") -- ROBLOX deviation END type Fiber = reactReconcilerSrcReactInternalTypesModule.Fiber type DispatcherType = reactReconcilerSrcReactInternalTypesModule.Dispatcher @@ -82,12 +75,9 @@ local ErrorStackParser = { if error_.stack == nil then return {} end - local filtered = Array.filter( - string.split(error_.stack :: string, "\n"), - function(line) - return string.find(line, "^LoadedCode") ~= nil - end - ) + local filtered = Array.filter(string.split(error_.stack :: string, "\n"), function(line) + return string.find(line, "^LoadedCode") ~= nil + end) return Array.map(filtered, function(stackTraceLine) -- ROBLOX FIXME Luau: shouldn't need to explicitly provide nilable field local functionName = string.match(stackTraceLine, "function (%w+)$") @@ -124,8 +114,7 @@ type BasicStateAction = (S) -> S | S type Dispatch = (A) -> () local primitiveStackCache: nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] | Map> = nil -local currentFiber: Fiber | nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] = - nil +local currentFiber: Fiber | nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] = nil type Hook = { memoizedState: any, next: Hook | nil,--[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] @@ -215,31 +204,20 @@ local function getPrimitiveStackCache(): Map> return primitiveStackCache :: Map> -- ROBLOX deviation END end -local currentHook: nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] | Hook = - nil -local function nextHook( -): nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] | Hook +local currentHook: nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] | Hook = nil +local function nextHook(): nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] | Hook local hook = currentHook if hook ~= nil then currentHook = hook.next end return hook end -local function readContext( - context: ReactContext, - observedBits: void | number | boolean -): T +local function readContext(context: ReactContext, observedBits: void | number | boolean): T -- For now we don't expose readContext usage in the hooks debugging info. return context._currentValue end -local function useContext( - context: ReactContext, - observedBits: void | number | boolean -): T - table.insert( - hookLog, - { primitive = "Context", stackError = Error.new(), value = context._currentValue } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] +local function useContext(context: ReactContext, observedBits: void | number | boolean): T + table.insert(hookLog, { primitive = "Context", stackError = Error.new(), value = context._currentValue }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] return context._currentValue end -- ROBLOX deviation START: return 2 values instead of a tuple @@ -255,10 +233,7 @@ local function useState(initialState: (() -> S) | S): (S, Dispatch) end } return state, function(action: BasicStateAction) end @@ -270,11 +245,7 @@ end -- initialArg: I, -- init: ((I) -> S)? -- ): any --[[ ROBLOX TODO: Unhandled node for type: TupleTypeAnnotation ]] --[[ [S, Dispatch] ]] -local function useReducer( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? -): (S, Dispatch) +local function useReducer(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) -- ROBLOX deviation END local hook = nextHook() local state @@ -283,10 +254,7 @@ local function useReducer( else state = if init ~= nil then init(initialArg) else (initialArg :: any) :: S end - table.insert( - hookLog, - { primitive = "Reducer", stackError = Error.new(), value = state } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "Reducer", stackError = Error.new(), value = state }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] -- ROBLOX deviation START: return 2 values instead of a tuple -- return { state, function(action: A) end } return state, function(action: A) end @@ -298,10 +266,7 @@ local function useRef(initialValue: T): { current: T | nil } -- ROBLOX deviation END local hook = nextHook() local ref = if hook ~= nil then hook.memoizedState else { current = initialValue } - table.insert( - hookLog, - { primitive = "Ref", stackError = Error.new(), value = ref.current } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "Ref", stackError = Error.new(), value = ref.current }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] return ref end -- ROBLOX deviation START: add binding support; these aren't fully working hooks, so this @@ -328,28 +293,22 @@ end local function useLayoutEffect( -- ROBLOX deviation START: Luau needs union type packs for this type to translate idiomatically -- create: () -> () -> () | void, - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), -- ROBLOX deviation END inputs: Array | void | nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] ): () nextHook() - table.insert( - hookLog, - { primitive = "LayoutEffect", stackError = Error.new(), value = create } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "LayoutEffect", stackError = Error.new(), value = create }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] end local function useEffect( -- ROBLOX deviation START: Luau needs union type packs for this type to translate idiomatically -- create: () -> () -> () | void, - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), -- ROBLOX deviation END inputs: Array | void | nil --[[ ROBLOX CHECK: verify if `null` wasn't used differently than `undefined` ]] ): () nextHook() - table.insert( - hookLog, - { primitive = "Effect", stackError = Error.new(), value = create } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "Effect", stackError = Error.new(), value = create }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] end local function useImperativeHandle( ref: { @@ -368,10 +327,7 @@ local function useImperativeHandle( if ref ~= nil and typeof(ref) == "table" then instance = ref.current end - table.insert( - hookLog, - { primitive = "ImperativeHandle", stackError = Error.new(), value = instance } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "ImperativeHandle", stackError = Error.new(), value = instance }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] end -- ROBLOX deviation START: add generic params -- local function useDebugValue(value: any, formatterFn: ((value: any) -> any)?) @@ -424,14 +380,8 @@ local function useMemo(nextCreate: () -> T..., inputs: Array | nil): end local function useMutableSource( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot -- useMutableSource() composes multiple hooks internally. -- Advance the current hook index the same number of times @@ -441,10 +391,7 @@ local function useMutableSource( nextHook() -- Effect nextHook() -- Effect local value = getSnapshot(source._source) - table.insert( - hookLog, - { primitive = "MutableSource", stackError = Error.new(), value = value } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "MutableSource", stackError = Error.new(), value = value }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] return value end -- ROBLOX deviation START: enable these once they are fully enabled in the Dispatcher type and in ReactFiberHooks' myriad dispatchers @@ -501,10 +448,7 @@ local function useOpaqueIdentifier(): OpaqueIDType | void -- ROBLOX deviation END value = nil end - table.insert( - hookLog, - { primitive = "OpaqueIdentifier", stackError = Error.new(), value = value } - ) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] + table.insert(hookLog, { primitive = "OpaqueIdentifier", stackError = Error.new(), value = value }) --[[ ROBLOX CHECK: check if 'hookLog' is an Array ]] return value end -- ROBLOX deviation START: predefined variable @@ -716,17 +660,11 @@ local function findPrimitiveIndex(hookStack, hook) -- If the next two frames are functions called `useX` then we assume that they're part of the -- wrappers that the React packager or other packages adds around the dispatcher. -- ROBLOX NOTE: 1-indexed so drop -1 - if - i < #hookStack - and isReactWrapper(hookStack[i].functionName, hook.primitive) - then + if i < #hookStack and isReactWrapper(hookStack[i].functionName, hook.primitive) then i += 1 end -- ROBLOX NOTE: 1-indexed so drop -1 - if - i < #hookStack - and isReactWrapper(hookStack[i].functionName, hook.primitive) - then + if i < #hookStack and isReactWrapper(hookStack[i].functionName, hook.primitive) then i += 1 end return i @@ -909,8 +847,7 @@ local function buildTree(rootStack, readHookLog: Array): HooksTree end -- Pop back the stack as many steps as were not common. for j = #prevStack - 1, commonSteps + 1, -1 do - levelChildren = - table.remove(stackOfChildren :: Array) :: Array + levelChildren = table.remove(stackOfChildren :: Array) :: Array end end @@ -944,9 +881,7 @@ local function buildTree(rootStack, readHookLog: Array): HooksTree -- For now, the "id" of stateful hooks is just the stateful hook index. -- Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue). -- ROBLOX FIXME Luau: Luau doesn't infer number | nil like it should - local id = if primitive == "Context" or primitive == "DebugValue" - then nil - else POSTFIX_INCREMENT() + local id = if primitive == "Context" or primitive == "DebugValue" then nil else POSTFIX_INCREMENT() -- For the time being, only State and Reducer hooks support runtime overrides. local isStateEditable = primitive == "Reducer" or primitive == "State" @@ -1198,9 +1133,7 @@ local function inspectHooksOfFiber(fiber: Fiber, currentDispatcher: CurrentDispa and fiber.tag ~= ForwardRef and fiber.tag ~= Block then - error( - Error.new("Unknown Fiber. Needs to be a function component to inspect hooks.") - ) + error(Error.new("Unknown Fiber. Needs to be a function component to inspect hooks.")) end -- Warm up the cache so that it doesn't consume the currentHook. getPrimitiveStackCache() local type_ = fiber.type diff --git a/modules/react-debug-tools/src/ReactDebugTools.lua b/modules/react-debug-tools/src/ReactDebugTools.lua index 70e75f6b..d22c110b 100644 --- a/modules/react-debug-tools/src/ReactDebugTools.lua +++ b/modules/react-debug-tools/src/ReactDebugTools.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-debug-tools/src/ReactDebugTools.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-debug-tools/src/ReactDebugTools.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration.spec.lua b/modules/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration.spec.lua index 9f2fc835..a260f696 100644 --- a/modules/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration.spec.lua +++ b/modules/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-debug-tools/src/__tests__/ReactDevToolsHooksIntegration-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -33,6 +33,7 @@ describe("React hooks DevTools integration", function() local overrideHookState local scheduleUpdate local setSuspenseHandler + global.IS_REACT_ACT_ENVIRONMENT = true beforeEach(function() -- ROBLOX deviation START: use _G instead of global -- global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { @@ -111,7 +112,9 @@ describe("React hooks DevTools integration", function() -- if Boolean.toJSBoolean(__DEV__) then if _G.__DEV__ then -- ROBLOX deviation END - overrideHookState(fiber, stateHook.id, {}, 10) + act(function() + return overrideHookState(fiber, stateHook.id, {}, 10) + end) expect(renderer:toJSON()).toEqual({ -- ROBLOX deviation START: use Frame instead -- type = "div", @@ -401,7 +404,9 @@ describe("React hooks DevTools integration", function() setSuspenseHandler(function() return false end) - scheduleUpdate(fiber) -- Re-render + act(function() + return scheduleUpdate(fiber) + end) -- Re-render -- ROBLOX deviation START: use TextLabel instead -- expect(renderer:toJSON().children).toEqual({ "Done" }) expect(renderer:toJSON().children).toEqual({ @@ -424,7 +429,9 @@ describe("React hooks DevTools integration", function() setSuspenseHandler(function() return true end) - scheduleUpdate(fiber) -- Re-render + act(function() + return scheduleUpdate(fiber) + end) -- Re-render -- ROBLOX deviation START: use TextLabel instead -- expect(renderer:toJSON().children).toEqual({ "Loading" }) -- Release the lock again expect(renderer:toJSON().children).toEqual({ @@ -437,7 +444,9 @@ describe("React hooks DevTools integration", function() setSuspenseHandler(function() return false end) - scheduleUpdate(fiber) -- Re-render + act(function() + return scheduleUpdate(fiber) + end) -- Re-render -- ROBLOX deviation START: use TextLabel instead -- expect(renderer:toJSON().children).toEqual({ "Done" }) -- Ensure it checks specific fibers. expect(renderer:toJSON().children).toEqual({ @@ -484,7 +493,7 @@ describe("React hooks DevTools integration", function() }) -- ROBLOX deviation END end - end) + end) -- @gate __DEV__ it("should support overriding suspense in concurrent mode", function() -- ROBLOX deviation START: add useFakeTimers jest.useFakeTimers() diff --git a/modules/react-debug-tools/src/__tests__/ReactHooksInspection.spec.lua b/modules/react-debug-tools/src/__tests__/ReactHooksInspection.spec.lua index 23520f81..6d81e9ce 100644 --- a/modules/react-debug-tools/src/__tests__/ReactHooksInspection.spec.lua +++ b/modules/react-debug-tools/src/__tests__/ReactHooksInspection.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration.spec.lua b/modules/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration.spec.lua index 9a60ff9a..684476e1 100644 --- a/modules/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration.spec.lua +++ b/modules/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -401,6 +401,123 @@ describe("ReactHooksInspectionIntegration", function() }, }) end) + it("should inspect the current state of all stateful hooks, including useInsertionEffect", function() + local useInsertionEffect = React.useInsertionEffect + local outsideRef = React.createRef() + local function effect() end + local function Foo(props) + local state1, setState = table.unpack(React.useState("a"), 1, 2) + local state2, dispatch = table.unpack( + React.useReducer(function(s, a) + return a.value + end, "b"), + 1, + 2 + ) + local ref = React.useRef("c") + useInsertionEffect(effect) + React.useLayoutEffect(effect) + React.useEffect(effect) + React.useImperativeHandle(outsideRef, function() + -- Return a function so that jest treats them as non-equal. + return function() end + end, {}) + React.useMemo(function() + return state1 + state2 + end, { state1 }) + local function update() + act(function() + setState("A") + end) + act(function() + dispatch({ value = "B" }) + end) + ref.current = "C" + end + local memoizedUpdate = React.useCallback(update, {}) + return React.createElement("div", { onClick = memoizedUpdate }, state1, " ", state2) + end + local renderer + act(function() + renderer = ReactTestRenderer.create(React.createElement(Foo, { prop = "prop" })) + end) + local childFiber = renderer.root:findByType(Foo):_currentFiber() + local updateStates = renderer.root:findByType("div").props.onClick + local tree = ReactDebugTools:inspectHooksOfFiber(childFiber) + expect(tree).toEqual({ + { isStateEditable = true, id = 0, name = "State", value = "a", subHooks = {} }, + { isStateEditable = true, id = 1, name = "Reducer", value = "b", subHooks = {} }, + { isStateEditable = false, id = 2, name = "Ref", value = "c", subHooks = {} }, + { + isStateEditable = false, + id = 3, + name = "InsertionEffect", + value = effect, + subHooks = {}, + }, + { + isStateEditable = false, + id = 4, + name = "LayoutEffect", + value = effect, + subHooks = {}, + }, + { isStateEditable = false, id = 5, name = "Effect", value = effect, subHooks = {} }, + { + isStateEditable = false, + id = 6, + name = "ImperativeHandle", + value = outsideRef.current, + subHooks = {}, + }, + { isStateEditable = false, id = 7, name = "Memo", value = "ab", subHooks = {} }, + { + isStateEditable = false, + id = 8, + name = "Callback", + value = updateStates, + subHooks = {}, + }, + }) + updateStates() + childFiber = renderer.root:findByType(Foo):_currentFiber() + tree = ReactDebugTools:inspectHooksOfFiber(childFiber) + expect(tree).toEqual({ + { isStateEditable = true, id = 0, name = "State", value = "A", subHooks = {} }, + { isStateEditable = true, id = 1, name = "Reducer", value = "B", subHooks = {} }, + { isStateEditable = false, id = 2, name = "Ref", value = "C", subHooks = {} }, + { + isStateEditable = false, + id = 3, + name = "InsertionEffect", + value = effect, + subHooks = {}, + }, + { + isStateEditable = false, + id = 4, + name = "LayoutEffect", + value = effect, + subHooks = {}, + }, + { isStateEditable = false, id = 5, name = "Effect", value = effect, subHooks = {} }, + { + isStateEditable = false, + id = 6, + name = "ImperativeHandle", + value = outsideRef.current, + subHooks = {}, + }, + { isStateEditable = false, id = 7, name = "Memo", value = "Ab", subHooks = {} }, + { + isStateEditable = false, + id = 8, + name = "Callback", + value = updateStates, + subHooks = {}, + }, + }) + end) it("should inspect the value of the current provider in useContext", function() local MyContext = React.createContext("default") local function Foo(props) @@ -535,15 +652,13 @@ describe("ReactHooksInspectionIntegration", function() }, }, }) - end) -- @gate experimental + end) -- ROBLOX deviation START: unstable_useTransition is not implemented -- it("should support composite useTransition hook", function() it.skip("should support composite useTransition hook", function() -- ROBLOX deviation END local function Foo(props) - -- ROBLOX deviation START: not supported - -- React.unstable_useTransition() - -- ROBLOX deviation END + React.useTransition() local memoizedValue = React.useMemo(function() return "hello" end, {}) @@ -582,15 +697,13 @@ describe("ReactHooksInspectionIntegration", function() subHooks = {}, }, }) - end) -- @gate experimental + end) -- ROBLOX deviation START: unstable_useDeferredValue not implemented -- it("should support composite useDeferredValue hook", function() it.skip("should support composite useDeferredValue hook", function() -- ROBLOX deviation END local function Foo(props) - -- ROBLOX deviation START: not implemented - -- React.unstable_useDeferredValue("abc", { timeoutMs = 500 }) - -- ROBLOX deviation END + React.useDeferredValue("abc", { timeoutMs = 500 }) local state = React.useState(function() return "hello" -- ROBLOX deviation START: useState returns 2 values @@ -630,11 +743,8 @@ describe("ReactHooksInspectionIntegration", function() subHooks = {}, }, }) - end) -- @gate experimental - -- ROBLOX deviation START: unstable_useOpaqueIdentifier not implemented - -- it("should support composite useOpaqueIdentifier hook", function() - it.skip("should support composite useOpaqueIdentifier hook", function() - -- ROBLOX deviation END + end) + it("should support useId hook", function() local function Foo(props) -- ROBLOX deviation START: not implemented -- local id = React.unstable_useOpaqueIdentifier() @@ -1165,19 +1275,31 @@ describe("ReactHooksInspectionIntegration", function() -- ROBLOX deviation END local renderer = ReactTestRenderer.create(React.createElement(Foo, nil)) local childFiber = renderer.root:_currentFiber() - expect(function() - -- ROBLOX deviation START: use dot notation - -- ReactDebugTools:inspectHooksOfFiber(childFiber, FakeDispatcherRef) + local didCatch = false + do --[[ ROBLOX COMMENT: try-catch block conversion ]] + local ok, result, hasReturned = xpcall(function() + -- ROBLOX deviation START: use dot notation + -- ReactDebugTools:inspectHooksOfFiber(childFiber, FakeDispatcherRef) ReactDebugTools.inspectHooksOfFiber(childFiber, FakeDispatcherRef) - -- ROBLOX deviation END - end).toThrow( - "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for" - .. " one of the following reasons:\n" - .. "1. You might have mismatching versions of React and the renderer (such as React DOM)\n" - .. "2. You might be breaking the Rules of Hooks\n" - .. "3. You might have more than one copy of React in the same app\n" - .. "See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem." - ) + -- ROBLOX deviation END + end, function(error_) + expect(error_.message).toBe("Error rendering inspected component") + expect(error_.cause).toBeInstanceOf(Error) + expect(error_.cause.message).toBe( + "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for" + .. " one of the following reasons:\n" + .. "1. You might have mismatching versions of React and the renderer (such as React DOM)\n" + .. "2. You might be breaking the Rules of Hooks\n" + .. "3. You might have more than one copy of React in the same app\n" + .. "See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem." + ) + didCatch = true + end) + if hasReturned then + return result + end + end -- avoid false positive if no error was thrown at all + expect(didCatch).toBe(true) expect(getterCalls).toBe(1) expect(setterCalls).toHaveLength(2) expect(setterCalls[ @@ -1190,7 +1312,7 @@ describe("ReactHooksInspectionIntegration", function() 2 --[[ ROBLOX adaptation: added 1 to array index ]] ]).toBe(initial) end) -- This test case is based on an open source bug report: - -- facebookincubator/redux-react-hook/issues/34#issuecomment-466693787 + -- https://github.com/facebookincubator/redux-react-hook/issues/34#issuecomment-466693787 it("should properly advance the current hook for useContext", function() local MyContext = React.createContext(1) local incrementCount diff --git a/modules/react-devtools-extensions/src/backend.lua b/modules/react-devtools-extensions/src/backend.lua index ccda7494..c186ffc8 100644 --- a/modules/react-devtools-extensions/src/backend.lua +++ b/modules/react-devtools-extensions/src/backend.lua @@ -1,8 +1,9 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-devtools-extensions/src/backend.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-extensions/src/backend.js local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") -- ROBLOX deviation START: not needed -- local Boolean = LuauPolyfill.Boolean -- ROBLOX deviation END +local console = LuauPolyfill.console type Array = LuauPolyfill.Array -- Do not use imports or top-level requires here! -- Running module factories is intentionally delayed until we know the hook exists. diff --git a/modules/react-devtools-shared/src/__tests__/bridge.spec.lua b/modules/react-devtools-shared/src/__tests__/bridge.spec.lua index 586a5995..97f77292 100644 --- a/modules/react-devtools-shared/src/__tests__/bridge.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/bridge.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/bridge-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/__tests__/bridge-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-devtools-shared/src/__tests__/console.spec.lua b/modules/react-devtools-shared/src/__tests__/console.spec.lua index 23279e5f..9dde66ad 100644 --- a/modules/react-devtools-shared/src/__tests__/console.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/console.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/console-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/__tests__/console-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -82,6 +82,7 @@ describe("console", function() ReactRoblox = require("@pkg/@jsdotlua/react-roblox") act = utils.act + legacyRender = utils.legacyRender end) local function normalizeCodeLocInfo(str) diff --git a/modules/react-devtools-shared/src/__tests__/events.spec.lua b/modules/react-devtools-shared/src/__tests__/events.spec.lua index 353d8f1d..c927a179 100644 --- a/modules/react-devtools-shared/src/__tests__/events.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/events.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/events-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/__tests__/events-test.js -- /** -- * Copyright (c) Facebook, Inc. and its affiliates. -- * @@ -16,7 +16,6 @@ local it = JestGlobals.it local beforeEach = JestGlobals.beforeEach local jestExpect = JestGlobals.expect local jest = JestGlobals.jest - describe("events", function() local dispatcher beforeEach(function() diff --git a/modules/react-devtools-shared/src/__tests__/profilerStore.spec.lua b/modules/react-devtools-shared/src/__tests__/profilerStore.spec.lua index 2245d2dd..3f05e54e 100644 --- a/modules/react-devtools-shared/src/__tests__/profilerStore.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/profilerStore.spec.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/profilerStore-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/__tests__/profilerStore-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-devtools-shared/src/__tests__/profilingCache.spec.lua b/modules/react-devtools-shared/src/__tests__/profilingCache.spec.lua index 5aa33ebe..6025b668 100644 --- a/modules/react-devtools-shared/src/__tests__/profilingCache.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/profilingCache.spec.lua @@ -86,102 +86,93 @@ xdescribe("ProfilingCache", function() _G.__PROFILE__ = nil end) - it( - "should collect data for each root (including ones added or mounted after profiling started)", - function() - local function Child(props) - Scheduler.unstable_advanceTime(props.duration) - return nil - end - - local MemoizedChild = React.memo(Child) + it("should collect data for each root (including ones added or mounted after profiling started)", function() + local function Child(props) + Scheduler.unstable_advanceTime(props.duration) + return nil + end - local function Parent(props) - Scheduler.unstable_advanceTime(10) + local MemoizedChild = React.memo(Child) - local count = props.count - local children = table.create(count) :: any - for index = 0, count - 1 do - children[index + 1] = - React.createElement(Child, { key = index, duration = index }) - end + local function Parent(props) + Scheduler.unstable_advanceTime(10) - return React.createElement( - React.Fragment, - nil, - children, - React.createElement(MemoizedChild, { duration = 1 }) - ) + local count = props.count + local children = table.create(count) :: any + for index = 0, count - 1 do + children[index + 1] = React.createElement(Child, { key = index, duration = index }) end - local containerA = ReactRoblox.createRoot(Instance.new("Frame")) - local containerB = ReactRoblox.createRoot(Instance.new("Frame")) - local containerC = ReactRoblox.createRoot(Instance.new("Frame")) - act(function() - return containerA:render(React.createElement(Parent, { count = 2 })) - end) + return React.createElement( + React.Fragment, + nil, + children, + React.createElement(MemoizedChild, { duration = 1 }) + ) + end - act(function() - return containerB:render(React.createElement(Parent, { count = 1 })) - end) - act(function() - return store._profilerStore:startProfiling() - end) - act(function() - return containerA:render(React.createElement(Parent, { count = 3 })) - end) - act(function() - return containerC:render(React.createElement(Parent, { count = 1 })) - end) - act(function() - return containerA:render(React.createElement(Parent, { count = 1 })) - end) - act(function() - return containerB:render(nil) - end) - act(function() - return containerA:render(React.createElement(Parent, { count = 0 })) - end) - act(function() - return store._profilerStore:stopProfiling() - end) + local containerA = ReactRoblox.createRoot(Instance.new("Frame")) + local containerB = ReactRoblox.createRoot(Instance.new("Frame")) + local containerC = ReactRoblox.createRoot(Instance.new("Frame")) + act(function() + return containerA:render(React.createElement(Parent, { count = 2 })) + end) - local allProfilingDataForRoots = {} - local function Validator(rootID, previousProfilingDataForRoot) - local profilingDataForRoot = store._profilerStore:getDataForRoot(rootID) + act(function() + return containerB:render(React.createElement(Parent, { count = 1 })) + end) + act(function() + return store._profilerStore:startProfiling() + end) + act(function() + return containerA:render(React.createElement(Parent, { count = 3 })) + end) + act(function() + return containerC:render(React.createElement(Parent, { count = 1 })) + end) + act(function() + return containerA:render(React.createElement(Parent, { count = 1 })) + end) + act(function() + return containerB:render(nil) + end) + act(function() + return containerA:render(React.createElement(Parent, { count = 0 })) + end) + act(function() + return store._profilerStore:stopProfiling() + end) - if previousProfilingDataForRoot ~= nil then - jestExpect(profilingDataForRoot).toEqual(previousProfilingDataForRoot) - else - jestExpect(profilingDataForRoot).toMatchSnapshot( - string.format( - "Data for root %s", - tostring(profilingDataForRoot.displayName) - ) - ) - end - table.insert(allProfilingDataForRoots, profilingDataForRoot) + local allProfilingDataForRoots = {} + local function Validator(rootID, previousProfilingDataForRoot) + local profilingDataForRoot = store._profilerStore:getDataForRoot(rootID) + + if previousProfilingDataForRoot ~= nil then + jestExpect(profilingDataForRoot).toEqual(previousProfilingDataForRoot) + else + jestExpect(profilingDataForRoot).toMatchSnapshot( + string.format("Data for root %s", tostring(profilingDataForRoot.displayName)) + ) end + table.insert(allProfilingDataForRoots, profilingDataForRoot) + end - local profilingData = store._profilerStore:profilingData() - local dataForRoots = if profilingData ~= nil - then profilingData.dataForRoots - else nil - jestExpect(dataForRoots).never.toBeNull() + local profilingData = store._profilerStore:profilingData() + local dataForRoots = if profilingData ~= nil then profilingData.dataForRoots else nil + jestExpect(dataForRoots).never.toBeNull() - if dataForRoots ~= nil then - dataForRoots:forEach(function(dataForRoot) - Validator(dataForRoot.rootID, nil) - end) - end - jestExpect(#allProfilingDataForRoots).toBe(3) - utils.exportImportHelper(bridge, store) + if dataForRoots ~= nil then + dataForRoots:forEach(function(dataForRoot) + Validator(dataForRoot.rootID, nil) + end) + end + jestExpect(#allProfilingDataForRoots).toBe(3) + utils.exportImportHelper(bridge, store) - for _, profilingDataForRoot in allProfilingDataForRoots do - Validator(profilingDataForRoot.rootID, profilingDataForRoot) - end + for _, profilingDataForRoot in allProfilingDataForRoots do + Validator(profilingDataForRoot.rootID, profilingDataForRoot) end - ) + end) it("should collect data for each commit", function() local MemoizedChild, Child local function Parent(props) @@ -190,8 +181,7 @@ xdescribe("ProfilingCache", function() local count = props.count local children = table.create(count) :: any for index = 0, count - 1 do - children[index + 1] = - React.createElement(Child, { key = index, duration = index }) + children[index + 1] = React.createElement(Child, { key = index, duration = index }) end return React.createElement( @@ -232,16 +222,13 @@ xdescribe("ProfilingCache", function() end) local allCommitData = {} local function Validator(ref) - local commitIndex, previousCommitDetails, rootID = - ref.commitIndex, ref.previousCommitDetails, ref.rootID + local commitIndex, previousCommitDetails, rootID = ref.commitIndex, ref.previousCommitDetails, ref.rootID local commitData = store._profilerStore:getCommitData(rootID, commitIndex) if previousCommitDetails ~= nil then jestExpect(commitData).toEqual(previousCommitDetails) else table.insert(allCommitData, commitData) - jestExpect(commitData).toMatchSnapshot( - string.format("CommitDetails commitIndex: %d", commitIndex - 1) - ) + jestExpect(commitData).toMatchSnapshot(string.format("CommitDetails commitIndex: %d", commitIndex - 1)) end end local rootID = store:getRoots()[1] @@ -300,19 +287,13 @@ xdescribe("ProfilingCache", function() end ModernContextConsumer = React.Component:extend("ModernContextConsumer") function ModernContextConsumer:render() - return React.createElement( - FunctionComponentWithHooks, - { count = self.context } - ) + return React.createElement(FunctionComponentWithHooks, { count = self.context }) end LegacyContextConsumer = React.Component:extend("LegacyContextConsumer") function LegacyContextConsumer:render() instance = self - return React.createElement( - FunctionComponentWithHooks, - { count = self.context.count } - ) + return React.createElement(FunctionComponentWithHooks, { count = self.context.count }) end local container = ReactRoblox.createRoot(Instance.new("Frame")) @@ -327,14 +308,10 @@ xdescribe("ProfilingCache", function() return (instance :: any):setState({ count = 1 }) end) act(function() - return container:render( - React.createElement(LegacyContextProvider, { foo = 123 }) - ) + return container:render(React.createElement(LegacyContextProvider, { foo = 123 })) end) act(function() - return container:render( - React.createElement(LegacyContextProvider, { bar = "abc" }) - ) + return container:render(React.createElement(LegacyContextProvider, { bar = "abc" })) end) act(function() return container:render(React.createElement(LegacyContextProvider, nil)) @@ -344,16 +321,13 @@ xdescribe("ProfilingCache", function() end) local allCommitData = {} local function Validator(ref) - local commitIndex, previousCommitDetails, rootID = - ref.commitIndex, ref.previousCommitDetails, ref.rootID + local commitIndex, previousCommitDetails, rootID = ref.commitIndex, ref.previousCommitDetails, ref.rootID local commitData = store._profilerStore:getCommitData(rootID, commitIndex) if previousCommitDetails ~= nil then jestExpect(commitData).toEqual(previousCommitDetails) else table.insert(allCommitData, commitData) - jestExpect(commitData).toMatchSnapshot( - string.format("CommitDetails commitIndex: %d", commitIndex - 1) - ) + jestExpect(commitData).toMatchSnapshot(string.format("CommitDetails commitIndex: %d", commitIndex - 1)) end end local rootID = store:getRoots()[1] @@ -423,22 +397,14 @@ xdescribe("ProfilingCache", function() end) act(function() return container:render( - React.createElement( - Context.Provider, - { value = true }, - React.createElement(Component, { count = 1 }) - ) + React.createElement(Context.Provider, { value = true }, React.createElement(Component, { count = 1 })) ) end) -- Second render has no changed hooks, only changed props. act(function() return container:render( - React.createElement( - Context.Provider, - { value = true }, - React.createElement(Component, { count = 2 }) - ) + React.createElement(Context.Provider, { value = true }, React.createElement(Component, { count = 2 })) ) end) @@ -456,11 +422,7 @@ xdescribe("ProfilingCache", function() -- Technically, DevTools will miss this "context" change since it only tracks legacy context. act(function() return container:render( - React.createElement( - Context.Provider, - { value = false }, - React.createElement(Component, { count = 2 }) - ) + React.createElement(Context.Provider, { value = false }, React.createElement(Component, { count = 2 })) ) end) @@ -471,18 +433,14 @@ xdescribe("ProfilingCache", function() local allCommitData = {} local function Validator(ref) - local commitIndex, previousCommitDetails, rootID = - ref.commitIndex, ref.previousCommitDetails, ref.rootID + local commitIndex, previousCommitDetails, rootID = ref.commitIndex, ref.previousCommitDetails, ref.rootID local commitData = store._profilerStore:getCommitData(rootID, commitIndex) if previousCommitDetails ~= nil then jestExpect(commitData).toEqual(previousCommitDetails) else table.insert(allCommitData, commitData) jestExpect(commitData).toMatchSnapshot( - string.format( - "CommitDetails commitIndex: %s", - tostring(commitIndex - 1) - ) + string.format("CommitDetails commitIndex: %s", tostring(commitIndex - 1)) ) end return nil @@ -509,51 +467,45 @@ xdescribe("ProfilingCache", function() }) end end) - it( - "should calculate a self duration based on actual children (not filtered children)", - function() - local Parent, Child - store:setComponentFilters({ utils.createDisplayNameFilter("^Parent$") }) - local function Grandparent() - Scheduler.unstable_advanceTime(10) - return React.createElement( - React.Fragment, - nil, - React.createElement(Parent, { key = "one" }), - React.createElement(Parent, { key = "two" }) - ) - end - function Parent() - Scheduler.unstable_advanceTime(2) - return React.createElement(Child, nil) - end - function Child() - Scheduler.unstable_advanceTime(1) - return nil - end - act(function() - return store._profilerStore:startProfiling() - end) - act(function() - return ReactRoblox.createRoot(Instance.new("Frame")) - :render(React.createElement(Grandparent, nil)) - end) - act(function() - return store._profilerStore:stopProfiling() - end) - local commitData = nil - local function Validator(ref) - local commitIndex, rootID = ref.commitIndex, ref.rootID - commitData = store._profilerStore:getCommitData(rootID, commitIndex) - jestExpect(commitData).toMatchSnapshot( - "CommitDetails with filtered self durations" - ) - end - local rootID = store:getRoots()[1] - Validator({ commitIndex = 1, rootID = rootID }) - jestExpect(commitData).never.toBeNull() + it("should calculate a self duration based on actual children (not filtered children)", function() + local Parent, Child + store:setComponentFilters({ utils.createDisplayNameFilter("^Parent$") }) + local function Grandparent() + Scheduler.unstable_advanceTime(10) + return React.createElement( + React.Fragment, + nil, + React.createElement(Parent, { key = "one" }), + React.createElement(Parent, { key = "two" }) + ) + end + function Parent() + Scheduler.unstable_advanceTime(2) + return React.createElement(Child, nil) + end + function Child() + Scheduler.unstable_advanceTime(1) + return nil + end + act(function() + return store._profilerStore:startProfiling() + end) + act(function() + return ReactRoblox.createRoot(Instance.new("Frame")):render(React.createElement(Grandparent, nil)) + end) + act(function() + return store._profilerStore:stopProfiling() + end) + local commitData = nil + local function Validator(ref) + local commitIndex, rootID = ref.commitIndex, ref.rootID + commitData = store._profilerStore:getCommitData(rootID, commitIndex) + jestExpect(commitData).toMatchSnapshot("CommitDetails with filtered self durations") end - ) + local rootID = store:getRoots()[1] + Validator({ commitIndex = 1, rootID = rootID }) + jestExpect(commitData).never.toBeNull() + end) --[=[ xit("should calculate self duration correctly for suspended views", function(done) local Fallback, Async @@ -648,8 +600,7 @@ xdescribe("ProfilingCache", function() local count = props.count local children = table.create(count) :: any for index = 0, count - 1 do - children[index + 1] = - React.createElement(Child, { key = index, duration = index }) + children[index + 1] = React.createElement(Child, { key = index, duration = index }) end return React.createElement( @@ -685,8 +636,7 @@ xdescribe("ProfilingCache", function() local allFiberCommits = {} local function Validator(ref) - local fiberID, previousFiberCommits, rootID = - ref.fiberID, ref.previousFiberCommits, ref.rootID + local fiberID, previousFiberCommits, rootID = ref.fiberID, ref.previousFiberCommits, ref.rootID local fiberCommits = store._profilerStore:profilingCache():getFiberCommits({ fiberID = fiberID, rootID = rootID, @@ -695,9 +645,7 @@ xdescribe("ProfilingCache", function() jestExpect(fiberCommits).toEqual(previousFiberCommits) else table.insert(allFiberCommits, fiberCommits) - jestExpect(fiberCommits).toMatchSnapshot( - string.format("FiberCommits: element %d", fiberID) - ) + jestExpect(fiberCommits).toMatchSnapshot(string.format("FiberCommits: element %d", fiberID)) end end local rootID = store:getRoots()[1] @@ -705,12 +653,7 @@ xdescribe("ProfilingCache", function() for index = 0, store:getNumElements() - 1 do local fiberID = store:getElementIDAtIndex(index) if fiberID == nil then - error( - string.format( - "Unexpected null ID for element at index %s", - tostring(index) - ) - ) + error(string.format("Unexpected null ID for element at index %s", tostring(index))) end Validator({ @@ -751,8 +694,7 @@ xdescribe("ProfilingCache", function() local count = props.count local children = table.create(count) :: any for index = 0, count - 1 do - children[index + 1] = - React.createElement(Child, { key = index, duration = index }) + children[index + 1] = React.createElement(Child, { key = index, duration = index }) end return React.createElement( @@ -774,22 +716,14 @@ xdescribe("ProfilingCache", function() return store._profilerStore:startProfiling() end) act(function() - return SchedulerTracing.unstable_trace( - "mount: one child", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent, { count = 1 })) - end - ) + return SchedulerTracing.unstable_trace("mount: one child", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent, { count = 1 })) + end) end) act(function() - return SchedulerTracing.unstable_trace( - "update: two children", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent, { count = 2 })) - end - ) + return SchedulerTracing.unstable_trace("update: two children", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent, { count = 2 })) + end) end) act(function() return store._profilerStore:stopProfiling() @@ -797,10 +731,9 @@ xdescribe("ProfilingCache", function() local interactions = nil local function Validator(ref) local previousInteractions, rootID = ref.previousInteractions, ref.rootID - interactions = - store._profilerStore:profilingCache():getInteractionsChartData({ - rootID = rootID, - }).interactions + interactions = store._profilerStore:profilingCache():getInteractionsChartData({ + rootID = rootID, + }).interactions -- ROBLOX FIXME: interactions[0] supposed to have __count=1, but it's 0 once it gets to the ProfilerStore. it's correct in WorkLoop and Tracing. if previousInteractions ~= nil then jestExpect(interactions).toEqual(previousInteractions) @@ -846,16 +779,8 @@ xdescribe("ProfilingCache", function() React.createElement( Switch, nil, - React.createElement( - Route, - { path = "/" }, - React.createElement(Home, nil) - ), - React.createElement( - Route, - { path = "/about" }, - React.createElement(About, nil) - ) + React.createElement(Route, { path = "/" }, React.createElement(Home, nil)), + React.createElement(Route, { path = "/about" }, React.createElement(About, nil)) ) ) end @@ -863,11 +788,7 @@ xdescribe("ProfilingCache", function() return React.createElement( React.Suspense, nil, - React.createElement( - Link, - { path = "/about" }, - React.createElement("TextLabel", { Text = "Home" }) - ) + React.createElement(Link, { path = "/about" }, React.createElement("TextLabel", { Text = "Home" })) ) end function About() @@ -878,11 +799,7 @@ xdescribe("ProfilingCache", function() function Router(ref) local children = ref.children local path, setPath = React.useState("/") - return React.createElement( - RouterContext.Provider, - { value = { path = path, setPath = setPath } }, - children - ) + return React.createElement(RouterContext.Provider, { value = { path = path, setPath = setPath } }, children) end -- Mimics https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Switch.js diff --git a/modules/react-devtools-shared/src/__tests__/profilingCharts.spec.lua b/modules/react-devtools-shared/src/__tests__/profilingCharts.spec.lua index 8b486ab1..695caf73 100644 --- a/modules/react-devtools-shared/src/__tests__/profilingCharts.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/profilingCharts.spec.lua @@ -73,22 +73,14 @@ xdescribe("profiling charts", function() return store._profilerStore:startProfiling() end) utils.act(function() - return SchedulerTracing.unstable_trace( - "mount", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent)) - end - ) + return SchedulerTracing.unstable_trace("mount", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent)) + end) end) utils.act(function() - return SchedulerTracing.unstable_trace( - "update", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent)) - end - ) + return SchedulerTracing.unstable_trace("update", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent)) + end) end) utils.act(function() return store._profilerStore:stopProfiling() @@ -100,18 +92,13 @@ xdescribe("profiling charts", function() commitIndex = commitIndex, rootID = rootID, }) - local chartData = - store._profilerStore:profilingCache():getFlamegraphChartData({ - commitIndex = commitIndex, - commitTree = commitTree, - rootID = rootID, - }) - jestExpect(commitTree).toMatchSnapshot( - ("%s: CommitTree"):format(tostring(commitIndex - 1)) - ) - jestExpect(chartData).toMatchSnapshot( - ("%s: FlamegraphChartData"):format(tostring(commitIndex - 1)) - ) + local chartData = store._profilerStore:profilingCache():getFlamegraphChartData({ + commitIndex = commitIndex, + commitTree = commitTree, + rootID = rootID, + }) + jestExpect(commitTree).toMatchSnapshot(("%s: CommitTree"):format(tostring(commitIndex - 1))) + jestExpect(chartData).toMatchSnapshot(("%s: FlamegraphChartData"):format(tostring(commitIndex - 1))) renderFinished = true return nil end @@ -157,23 +144,15 @@ xdescribe("profiling charts", function() end) utils.act(function() - return SchedulerTracing.unstable_trace( - "mount", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent)) - end - ) + return SchedulerTracing.unstable_trace("mount", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent)) + end) end) utils.act(function() - return SchedulerTracing.unstable_trace( - "update", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent)) - end - ) + return SchedulerTracing.unstable_trace("update", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent)) + end) end) utils.act(function() @@ -186,18 +165,13 @@ xdescribe("profiling charts", function() commitIndex = commitIndex, rootID = rootID, }) - local chartData = - store._profilerStore:profilingCache():getRankedChartData({ - commitIndex = commitIndex, - commitTree = commitTree, - rootID = rootID, - }) - jestExpect(commitTree).toMatchSnapshot( - ("%s: CommitTree"):format(tostring(commitIndex - 1)) - ) - jestExpect(chartData).toMatchSnapshot( - ("%s: RankedChartData"):format(tostring(commitIndex - 1)) - ) + local chartData = store._profilerStore:profilingCache():getRankedChartData({ + commitIndex = commitIndex, + commitTree = commitTree, + rootID = rootID, + }) + jestExpect(commitTree).toMatchSnapshot(("%s: CommitTree"):format(tostring(commitIndex - 1))) + jestExpect(chartData).toMatchSnapshot(("%s: RankedChartData"):format(tostring(commitIndex - 1))) renderFinished = true return nil end @@ -241,23 +215,15 @@ xdescribe("profiling charts", function() return store._profilerStore:startProfiling() end) utils.act(function() - return SchedulerTracing.unstable_trace( - "mount", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent)) - end - ) + return SchedulerTracing.unstable_trace("mount", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent)) + end) end) utils.act(function() - return SchedulerTracing.unstable_trace( - "update", - Scheduler.unstable_now(), - function() - return container:render(React.createElement(Parent)) - end - ) + return SchedulerTracing.unstable_trace("update", Scheduler.unstable_now(), function() + return container:render(React.createElement(Parent)) + end) end) utils.act(function() return store._profilerStore:stopProfiling() @@ -265,10 +231,9 @@ xdescribe("profiling charts", function() local renderFinished = false local function Validator(ref) local _commitIndex, rootID = ref.commitIndex, ref.rootID - local chartData = - store._profilerStore:profilingCache():getInteractionsChartData({ - rootID = rootID, - }) + local chartData = store._profilerStore:profilingCache():getInteractionsChartData({ + rootID = rootID, + }) jestExpect(chartData).toMatchSnapshot("Interactions") renderFinished = true return nil diff --git a/modules/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder.spec.lua b/modules/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder.spec.lua index 0924d485..efab6d2c 100644 --- a/modules/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder.spec.lua @@ -91,9 +91,7 @@ xdescribe("commit tree", function() rootID = rootID, }) - jestExpect(commitTree).toMatchSnapshot( - string.format("%d: CommitTree", commitIndex - 1) - ) + jestExpect(commitTree).toMatchSnapshot(string.format("%d: CommitTree", commitIndex - 1)) end end) end) diff --git a/modules/react-devtools-shared/src/__tests__/store.spec.lua b/modules/react-devtools-shared/src/__tests__/store.spec.lua index bd8c63db..c49c3cb8 100644 --- a/modules/react-devtools-shared/src/__tests__/store.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/store.spec.lua @@ -301,9 +301,7 @@ describe("Store", function() -- ROBLOX deviation: Use Frame instance rather than DOM element local root = ReactRoblox.createRoot(Instance.new("Frame")) act(function() - return root:render( - React.createElement(Wrapper, { shouldSuspense = true }) - ) + return root:render(React.createElement(Wrapper, { shouldSuspense = true })) end) jestExpect(devtoolsUtils.printStore(store)).toBe([[[root] ▾ @@ -1035,9 +1033,7 @@ describe("Store", function() local root = ReactRoblox.createRoot(Instance.new("Frame")) act(function() - return root:render( - React.createElement(Wrapper, { shouldSuspense = true }) - ) + return root:render(React.createElement(Wrapper, { shouldSuspense = true })) end) jestExpect(devtoolsUtils.printStore(store)).toBe([[[root] ▸ ]]) @@ -1386,9 +1382,7 @@ describe("Store", function() end) for i = 0, store:getNumElements() - 1 do - jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe( - i - ) + jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe(i) end end) it("should support multiple roots with one children each", function() @@ -1410,9 +1404,7 @@ describe("Store", function() end) for i = 0, store:getNumElements() - 1 do - jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe( - i - ) + jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe(i) end end) it("should support a single root with multiple top level children", function() @@ -1441,9 +1433,7 @@ describe("Store", function() end) for i = 0, store:getNumElements() - 1 do - jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe( - i - ) + jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe(i) end end) it("should support multiple roots with multiple top level children", function() @@ -1472,9 +1462,7 @@ describe("Store", function() end) for i = 0, store:getNumElements() - 1 do - jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe( - i - ) + jestExpect(store:getIndexOfElementID(store:getElementIDAtIndex(i))).toBe(i) end end) end) @@ -1557,8 +1545,7 @@ describe("Store", function() -- FakeHigherOrderComponent.displayName = 'withFoo(withBar(Baz))' local MemoizedFakeHigherOrderComponent = React.memo(FakeHigherOrderComponent) - local ForwardRefFakeHigherOrderComponent = - React.forwardRef(FakeHigherOrderComponent) + local ForwardRefFakeHigherOrderComponent = React.forwardRef(FakeHigherOrderComponent) local function App() return React.createElement( React.Fragment, diff --git a/modules/react-devtools-shared/src/__tests__/storeComponentFilters.spec.lua b/modules/react-devtools-shared/src/__tests__/storeComponentFilters.spec.lua index d8299c53..58ebcd46 100644 --- a/modules/react-devtools-shared/src/__tests__/storeComponentFilters.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/storeComponentFilters.spec.lua @@ -80,9 +80,7 @@ describe("Store component filters", function() end act(function() local root = ReactRoblox.createRoot(Instance.new("Frame")) - return root:render( - React.createElement(Root, {}, React.createElement(Component)) - ) + return root:render(React.createElement(Root, {}, React.createElement(Component))) end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("1: mount") @@ -94,9 +92,7 @@ describe("Store component filters", function() end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead -- ROBLOX FIXME: still shows the Frame and TextLabel, upstream only has [root] ▾ - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "2: hide host components" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("2: hide host components") act(function() store:setComponentFilters({ utils.createElementTypeFilter(Types.ElementTypeClass), @@ -105,9 +101,7 @@ describe("Store component filters", function() end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead -- ROBLOX FIXME: supposed to hide Root, but doesn't - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "3: hide class components" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("3: hide class components") act(function() store:setComponentFilters({ utils.createElementTypeFilter(Types.ElementTypeClass), @@ -117,9 +111,7 @@ describe("Store component filters", function() end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead -- ROBLOX FIXME: should only show Frame and TextLabel - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "4: hide class and function components" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("4: hide class and function components") act(function() store:setComponentFilters({ utils.createElementTypeFilter(Types.ElementTypeClass, false), @@ -128,9 +120,7 @@ describe("Store component filters", function() return store:getComponentFilters() end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "5: disable all filters" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("5: disable all filters") end) it("should ignore invalid ElementTypeRoot filter", function() local function Root(props) @@ -148,9 +138,7 @@ describe("Store component filters", function() }) end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "2: add invalid filter" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("2: add invalid filter") end) it("should filter by display name", function() local function Text(props) @@ -229,9 +217,7 @@ describe("Store component filters", function() return store:getComponentFilters() end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "3: hide components in a made up fake path" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("3: hide components in a made up fake path") end) it("should filter HOCs", function() local function Component() @@ -266,41 +252,36 @@ describe("Store component filters", function() return store:getComponentFilters() end) -- ROBLOX deviation: we use devtoolsUtils.printStore, upstream uses a jest serializer (storeSerializer) instead - jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot( - "3: disable HOC filter" - ) + jestExpect(devtoolsUtils.printStore(store)).toMatchSnapshot("3: disable HOC filter") + end) + it("should not send a bridge update if the set of enabled filters has not changed", function() + act(function() + store:setComponentFilters({ utils.createHOCFilter(true) }) + return store:getComponentFilters() + end) + bridge:addListener("updateComponentFilters", function(componentFilters) + error("Unexpected component update") + end) + act(function() + store:setComponentFilters({ + utils.createHOCFilter(false), + utils.createHOCFilter(true), + }) + return store:getComponentFilters() + end) + act(function() + store:setComponentFilters({ + utils.createHOCFilter(true), + utils.createLocationFilter("abc", false), + }) + return store:getComponentFilters() + end) + act(function() + store:setComponentFilters({ + utils.createHOCFilter(true), + utils.createElementTypeFilter(Types.ElementTypeHostComponent, false), + }) + return store:getComponentFilters() + end) end) - it( - "should not send a bridge update if the set of enabled filters has not changed", - function() - act(function() - store:setComponentFilters({ utils.createHOCFilter(true) }) - return store:getComponentFilters() - end) - bridge:addListener("updateComponentFilters", function(componentFilters) - error("Unexpected component update") - end) - act(function() - store:setComponentFilters({ - utils.createHOCFilter(false), - utils.createHOCFilter(true), - }) - return store:getComponentFilters() - end) - act(function() - store:setComponentFilters({ - utils.createHOCFilter(true), - utils.createLocationFilter("abc", false), - }) - return store:getComponentFilters() - end) - act(function() - store:setComponentFilters({ - utils.createHOCFilter(true), - utils.createElementTypeFilter(Types.ElementTypeHostComponent, false), - }) - return store:getComponentFilters() - end) - end - ) end) diff --git a/modules/react-devtools-shared/src/__tests__/storeOwners.spec.lua b/modules/react-devtools-shared/src/__tests__/storeOwners.spec.lua index 662960e9..59c38f1f 100644 --- a/modules/react-devtools-shared/src/__tests__/storeOwners.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/storeOwners.spec.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/__tests__/storeOwners-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/__tests__/storeOwners-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -26,6 +26,7 @@ describeIfDev("Store owners list", function() local LuauPolyfill local Boolean local act + local legacyRender local store local devtoolsUtils local printOwnersList diff --git a/modules/react-devtools-shared/src/__tests__/utils.lua b/modules/react-devtools-shared/src/__tests__/utils.lua index 90068540..a7c51b30 100644 --- a/modules/react-devtools-shared/src/__tests__/utils.lua +++ b/modules/react-devtools-shared/src/__tests__/utils.lua @@ -200,8 +200,7 @@ end exports.exportImportHelper = function(bridge: FrontendBridge, store: Store): () local utils = require("./devtools/views/Profiler/utils") local prepareProfilingDataExport = utils.prepareProfilingDataExport - local prepareProfilingDataFrontendFromExport = - utils.prepareProfilingDataFrontendFromExport + local prepareProfilingDataFrontendFromExport = utils.prepareProfilingDataFrontendFromExport local profilerStore = store._profilerStore local profilingDataFrontendInitial = profilerStore:profilingData() @@ -215,17 +214,13 @@ exports.exportImportHelper = function(bridge: FrontendBridge, store: Store): () -- Simulate writing/reading to disk. local serializedProfilingDataExport = HttpService:JSONEncode(profilingDataExport) - local parsedProfilingDataExport = - HttpService:JSONDecode(serializedProfilingDataExport) + local parsedProfilingDataExport = HttpService:JSONDecode(serializedProfilingDataExport) - local profilingDataFrontend = - prepareProfilingDataFrontendFromExport(parsedProfilingDataExport) + local profilingDataFrontend = prepareProfilingDataFrontendFromExport(parsedProfilingDataExport) jestExpect(profilingDataFrontend.imported).toBe(true) -- Sanity check that profiling snapshots are serialized correctly. - jestExpect(profilingDataFrontendInitial.dataForRoots).toEqual( - profilingDataFrontend.dataForRoots - ) + jestExpect(profilingDataFrontendInitial.dataForRoots).toEqual(profilingDataFrontend.dataForRoots) -- Snapshot the JSON-parsed object, rather than the raw string, because Jest formats the diff nicer. jestExpect(parsedProfilingDataExport).toMatchSnapshot("imported data") diff --git a/modules/react-devtools-shared/src/__tests__/utils.spec.lua b/modules/react-devtools-shared/src/__tests__/utils.spec.lua index a7e0cde8..dcf5cf6d 100644 --- a/modules/react-devtools-shared/src/__tests__/utils.spec.lua +++ b/modules/react-devtools-shared/src/__tests__/utils.spec.lua @@ -51,12 +51,9 @@ describe("utils", function() jestExpect(getDisplayName(function() end, "Fallback")).toEqual("Fallback") end) - it( - "should return Anonymous for anonymous functions without a fallback", - function() - jestExpect(getDisplayName(function() end)).toEqual("Anonymous") - end - ) + it("should return Anonymous for anonymous functions without a fallback", function() + jestExpect(getDisplayName(function() end)).toEqual("Anonymous") + end) -- Simulate a reported bug: -- https://github.com/facebook/react/issues/16685 @@ -67,74 +64,53 @@ describe("utils", function() end) describe("getDisplayNameForReactElement", function() -- ROBLOX deviation: Lua can't put fields on functions - xit( - "should return correct display name for an element with function type", - function() - local function FauxComponent() end - - -- FauxComponent.displayName = 'OverrideDisplayName' - - local element = createElement(FauxComponent) - - jestExpect(getDisplayNameForReactElement(element)).toEqual( - "OverrideDisplayName" - ) - end - ) - - it( - "should return correct display name for an element with a type of StrictMode", - function() - local element = createElement(StrictMode) - - jestExpect(getDisplayNameForReactElement(element)).toEqual("StrictMode") - end - ) - - it( - "should return correct display name for an element with a type of SuspenseList", - function() - local element - -- ROBLOX Test Noise: This warning shouldn't be triggered - jestExpect(function() - element = createElement(SuspenseList) - end).toErrorDev("type is invalid", { withoutStack = true }) - - jestExpect(getDisplayNameForReactElement(element)).toEqual("SuspenseList") - end - ) - - it( - "should return NotImplementedInDevtools for an element with invalid symbol type", - function() - local element - -- ROBLOX Test Noise: This warning isn't captured in - -- upstream, so it may rely on test setup to suppress it - jestExpect(function() - element = (createElement :: any)(Symbol("foo")) - end).toErrorDev("type is invalid", { withoutStack = true }) - - jestExpect(getDisplayNameForReactElement(element)).toEqual( - "NotImplementedInDevtools" - ) - end - ) - - it( - "should return NotImplementedInDevtools for an element with invalid type", - function() - local element - -- ROBLOX Test Noise: This warning isn't captured in - -- upstream, so it may rely on test setup to suppress it - jestExpect(function() - element = (createElement :: any)(true) - end).toErrorDev("type is invalid", { withoutStack = true }) - - jestExpect(getDisplayNameForReactElement(element)).toEqual( - "NotImplementedInDevtools" - ) - end - ) + xit("should return correct display name for an element with function type", function() + local function FauxComponent() end + + -- FauxComponent.displayName = 'OverrideDisplayName' + + local element = createElement(FauxComponent) + + jestExpect(getDisplayNameForReactElement(element)).toEqual("OverrideDisplayName") + end) + + it("should return correct display name for an element with a type of StrictMode", function() + local element = createElement(StrictMode) + + jestExpect(getDisplayNameForReactElement(element)).toEqual("StrictMode") + end) + + it("should return correct display name for an element with a type of SuspenseList", function() + local element + -- ROBLOX Test Noise: This warning shouldn't be triggered + jestExpect(function() + element = createElement(SuspenseList) + end).toErrorDev("type is invalid", { withoutStack = true }) + + jestExpect(getDisplayNameForReactElement(element)).toEqual("SuspenseList") + end) + + it("should return NotImplementedInDevtools for an element with invalid symbol type", function() + local element + -- ROBLOX Test Noise: This warning isn't captured in + -- upstream, so it may rely on test setup to suppress it + jestExpect(function() + element = (createElement :: any)(Symbol("foo")) + end).toErrorDev("type is invalid", { withoutStack = true }) + + jestExpect(getDisplayNameForReactElement(element)).toEqual("NotImplementedInDevtools") + end) + + it("should return NotImplementedInDevtools for an element with invalid type", function() + local element + -- ROBLOX Test Noise: This warning isn't captured in + -- upstream, so it may rely on test setup to suppress it + jestExpect(function() + element = (createElement :: any)(true) + end).toErrorDev("type is invalid", { withoutStack = true }) + + jestExpect(getDisplayNameForReactElement(element)).toEqual("NotImplementedInDevtools") + end) it("should return Element for null type", function() local element diff --git a/modules/react-devtools-shared/src/backend/ReactSymbols.lua b/modules/react-devtools-shared/src/backend/ReactSymbols.lua index d7f86f2e..11ded61a 100644 --- a/modules/react-devtools-shared/src/backend/ReactSymbols.lua +++ b/modules/react-devtools-shared/src/backend/ReactSymbols.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/backend/ReactSymbols.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/backend/ReactSymbols.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-devtools-shared/src/backend/agent.lua b/modules/react-devtools-shared/src/backend/agent.lua index 730f70a8..d05fe37d 100644 --- a/modules/react-devtools-shared/src/backend/agent.lua +++ b/modules/react-devtools-shared/src/backend/agent.lua @@ -24,10 +24,8 @@ local throttle = function(fn: Function, _limit: number): Function end local constants = require("../constants") local SESSION_STORAGE_LAST_SELECTION_KEY = constants.SESSION_STORAGE_LAST_SELECTION_KEY -local SESSION_STORAGE_RELOAD_AND_PROFILE_KEY = - constants.SESSION_STORAGE_RELOAD_AND_PROFILE_KEY -local SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = - constants.SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY +local SESSION_STORAGE_RELOAD_AND_PROFILE_KEY = constants.SESSION_STORAGE_RELOAD_AND_PROFILE_KEY +local SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = constants.SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY local __DEBUG__ = constants.__DEBUG__ local storage = require("../storage") local sessionStorageGetItem = storage.sessionStorageGetItem @@ -161,10 +159,7 @@ export type Agent = EventEmitter<{ getRendererInterfaces: (self: Agent) -> { [RendererID]: RendererInterface }, copyElementPath: (self: Agent, copyElementParams: CopyElementParams) -> (), deletePath: (self: Agent, deletePathParams: DeletePathParams) -> (), - getInstanceAndStyle: ( - self: Agent, - elementAndRendererId: ElementAndRendererID - ) -> InstanceAndStyle | nil, + getInstanceAndStyle: (self: Agent, elementAndRendererId: ElementAndRendererID) -> InstanceAndStyle | nil, getIDForNode: (self: Agent, node: Object) -> number | nil, getProfilingData: (self: Agent, rendererIdObject: { rendererID: RendererID }) -> (), getProfilingStatus: (self: Agent) -> (), @@ -172,10 +167,7 @@ export type Agent = EventEmitter<{ inspectElement: (self: Agent, inspectElementParams: InspectElementParams) -> (), logElementToConsole: (self: Agent, elementAndRendererID: ElementAndRendererID) -> (), overrideSuspense: (self: Agent, overrideSuspenseParams: OverrideSuspenseParams) -> (), - overrideValueAtPath: ( - self: Agent, - overrideValueAtPathParams: OverrideValueAtPathParams - ) -> (), + overrideValueAtPath: (self: Agent, overrideValueAtPathParams: OverrideValueAtPathParams) -> (), overrideContext: (self: Agent, setInParams: SetInParams) -> (), overrideHookState: (self: Agent, overrideHookParams: OverrideHookParams) -> (), overrideProps: (self: Agent, setInParams: SetInParams) -> (), @@ -183,11 +175,7 @@ export type Agent = EventEmitter<{ reloadAndProfile: (self: Agent, recordChangeDescriptions: boolean) -> (), renamePath: (self: Agent, renamePathParams: RenamePathParams) -> (), selectNode: (self: Agent, target: Object) -> (), - setRendererInterface: ( - self: Agent, - rendererID: number, - rendererInterface: RendererInterface - ) -> (), + setRendererInterface: (self: Agent, rendererID: number, rendererInterface: RendererInterface) -> (), setTraceUpdatesEnabled: (self: Agent, traceUpdatesEnabled: boolean) -> (), syncSelectionFromNativeElementsPanel: (self: Agent) -> (), shutdown: (self: Agent) -> (), @@ -230,17 +218,14 @@ function Agent.new(bridge: BackendBridge) self._traceUpdatesEnabled = false if sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) == "true" then - self._recordChangeDescriptions = sessionStorageGetItem( - SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY - ) == "true" + self._recordChangeDescriptions = sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) == "true" self._isProfiling = true sessionStorageRemoveItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) sessionStorageRemoveItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) end - local persistedSelectionString = - sessionStorageGetItem(SESSION_STORAGE_LAST_SELECTION_KEY) + local persistedSelectionString = sessionStorageGetItem(SESSION_STORAGE_LAST_SELECTION_KEY) if persistedSelectionString ~= nil then self._persistedSelection = JSON.JSONDecode(persistedSelectionString) @@ -267,15 +252,9 @@ function Agent.new(bridge: BackendBridge) bridge:addListener("startProfiling", wrapSelf(self.startProfiling)) bridge:addListener("stopProfiling", wrapSelf(self.stopProfiling)) bridge:addListener("storeAsGlobal", wrapSelf(self.storeAsGlobal)) - bridge:addListener( - "syncSelectionFromNativeElementsPanel", - wrapSelf(self.syncSelectionFromNativeElementsPanel) - ) + bridge:addListener("syncSelectionFromNativeElementsPanel", wrapSelf(self.syncSelectionFromNativeElementsPanel)) bridge:addListener("shutdown", wrapSelf(self.shutdown)) - bridge:addListener( - "updateConsolePatchSettings", - wrapSelf(self.updateConsolePatchSettings) - ) + bridge:addListener("updateConsolePatchSettings", wrapSelf(self.updateConsolePatchSettings)) bridge:addListener("updateComponentFilters", wrapSelf(self.updateComponentFilters)) bridge:addListener("viewAttributeSource", wrapSelf(self.viewAttributeSource)) bridge:addListener("viewElementSource", wrapSelf(self.viewElementSource)) @@ -311,14 +290,11 @@ function Agent:getRendererInterfaces() end function Agent:copyElementPath(copyElementParams: CopyElementParams): () - local id, path, rendererID = - copyElementParams.id, copyElementParams.path, copyElementParams.rendererID + local id, path, rendererID = copyElementParams.id, copyElementParams.path, copyElementParams.rendererID local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).copyElementPath(id, path) end @@ -333,16 +309,12 @@ function Agent:deletePath(deletePathParams: DeletePathParams): () local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).deletePath(type_, id, hookID, path) end end -function Agent:getInstanceAndStyle( - elementAndRendererId: ElementAndRendererID -): InstanceAndStyle | nil +function Agent:getInstanceAndStyle(elementAndRendererId: ElementAndRendererID): InstanceAndStyle | nil local id, rendererID = elementAndRendererId.id, elementAndRendererId.rendererID local renderer = self._rendererInterfaces[rendererID] @@ -383,9 +355,7 @@ function Agent:getOwnersList(elementAndRendererID: ElementAndRendererID) local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else local owners = (renderer :: RendererInterface).getOwnersList(id) @@ -396,21 +366,13 @@ function Agent:getOwnersList(elementAndRendererID: ElementAndRendererID) end end function Agent:inspectElement(inspectElementParams: InspectElementParams) - local id, path, rendererID = - inspectElementParams.id, - inspectElementParams.path, - inspectElementParams.rendererID + local id, path, rendererID = inspectElementParams.id, inspectElementParams.path, inspectElementParams.rendererID local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else - self._bridge:send( - "inspectedElement", - (renderer :: RendererInterface).inspectElement(id, path) - ) + self._bridge:send("inspectedElement", (renderer :: RendererInterface).inspectElement(id, path)) -- When user selects an element, stop trying to restore the selection, -- and instead remember the current selection for the next reload. @@ -437,24 +399,18 @@ function Agent:logElementToConsole(elementAndRendererID: ElementAndRendererID) local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).logElementToConsole(id) end end function Agent:overrideSuspense(overrideSuspenseParams: OverrideSuspenseParams) local id, rendererID, forceFallback = - overrideSuspenseParams.id, - overrideSuspenseParams.rendererID, - overrideSuspenseParams.forceFallback + overrideSuspenseParams.id, overrideSuspenseParams.rendererID, overrideSuspenseParams.forceFallback local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).overrideSuspense(id, forceFallback) end @@ -470,17 +426,9 @@ function Agent:overrideValueAtPath(overrideValueAtPathParams: OverrideValueAtPat local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else - (renderer :: RendererInterface).overrideValueAtPath( - type_, - id, - hookID, - path, - value - ) + (renderer :: RendererInterface).overrideValueAtPath(type_, id, hookID, path, value) end end @@ -488,11 +436,7 @@ end -- to the new "overrideValueAtPath" command the backend is now listening to. function Agent:overrideContext(setInParams: SetInParams) local id, path, rendererID, wasForwarded, value = - setInParams.id, - setInParams.path, - setInParams.rendererID, - setInParams.wasForwarded, - setInParams.value + setInParams.id, setInParams.path, setInParams.rendererID, setInParams.wasForwarded, setInParams.value -- Don't forward a message that's already been forwarded by the front-end Bridge. -- We only need to process the override command once! @@ -535,11 +479,7 @@ end -- to the new "overrideValueAtPath" command the backend is now listening to. function Agent:overrideProps(setInParams: SetInParams) local id, path, rendererID, wasForwarded, value = - setInParams.id, - setInParams.path, - setInParams.rendererID, - setInParams.wasForwarded, - setInParams.value + setInParams.id, setInParams.path, setInParams.rendererID, setInParams.wasForwarded, setInParams.value -- Don't forward a message that's already been forwarded by the front-end Bridge. -- We only need to process the override command once! @@ -558,11 +498,7 @@ end -- to the new "overrideValueAtPath" command the backend is now listening to. function Agent:overrideState(setInParams: SetInParams) local id, path, rendererID, wasForwarded, value = - setInParams.id, - setInParams.path, - setInParams.rendererID, - setInParams.wasForwarded, - setInParams.value + setInParams.id, setInParams.path, setInParams.rendererID, setInParams.wasForwarded, setInParams.value -- Don't forward a message that's already been forwarded by the front-end Bridge. -- We only need to process the override command once! @@ -605,9 +541,7 @@ function Agent:renamePath(renamePathParams: RenamePathParams) local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).renamePath(type_, id, hookID, oldPath, newPath) end @@ -619,10 +553,7 @@ function Agent:selectNode(target: Object): () self._bridge:send("selectFiber", id) end end -function Agent:setRendererInterface( - rendererID: number, - rendererInterface: RendererInterface -) +function Agent:setRendererInterface(rendererID: number, rendererInterface: RendererInterface) self._rendererInterfaces[rendererID] = rendererInterface if self._isProfiling then @@ -636,10 +567,7 @@ function Agent:setRendererInterface( -- It'll start tracking mounts for matches to the last selection path. local selection: PersistedSelection? = self._persistedSelection - if - selection ~= nil - and (selection :: PersistedSelection).rendererID == rendererID - then + if selection ~= nil and (selection :: PersistedSelection).rendererID == rendererID then rendererInterface.setTrackedPath((selection :: PersistedSelection).path) end end @@ -688,16 +616,11 @@ end function Agent:storeAsGlobal(storeAsGlobalParams: StoreAsGlobalParams) local count, id, path, rendererID = - storeAsGlobalParams.count, - storeAsGlobalParams.id, - storeAsGlobalParams.path, - storeAsGlobalParams.rendererID + storeAsGlobalParams.count, storeAsGlobalParams.id, storeAsGlobalParams.path, storeAsGlobalParams.rendererID local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).storeAsGlobal(id, path, count) end @@ -707,8 +630,7 @@ function Agent:updateConsolePatchSettings(_ref16: { appendComponentStack: boolean, breakOnConsoleErrors: boolean, }) - local appendComponentStack, breakOnConsoleErrors = - _ref16.appendComponentStack, _ref16.breakOnConsoleErrors + local appendComponentStack, breakOnConsoleErrors = _ref16.appendComponentStack, _ref16.breakOnConsoleErrors -- If the frontend preference has change, -- or in the case of React Native- if the backend is just finding out the preference- @@ -729,14 +651,11 @@ function Agent:updateComponentFilters(componentFilters: Array) end end function Agent:viewAttributeSource(copyElementParams: CopyElementParams) - local id, path, rendererID = - copyElementParams.id, copyElementParams.path, copyElementParams.rendererID + local id, path, rendererID = copyElementParams.id, copyElementParams.path, copyElementParams.rendererID local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).prepareViewAttributeSource(id, path) end @@ -746,9 +665,7 @@ function Agent:viewElementSource(elementAndRendererID: ElementAndRendererID) local renderer = self._rendererInterfaces[rendererID] if renderer == nil then - console.warn( - string.format('Invalid renderer id "%d" for element "%d"', rendererID, id) - ) + console.warn(string.format('Invalid renderer id "%d" for element "%d"', rendererID, id)) else (renderer :: RendererInterface).prepareViewElementSource(id) end diff --git a/modules/react-devtools-shared/src/backend/renderer.lua b/modules/react-devtools-shared/src/backend/renderer.lua index 82d84f10..2d317fd4 100644 --- a/modules/react-devtools-shared/src/backend/renderer.lua +++ b/modules/react-devtools-shared/src/backend/renderer.lua @@ -70,15 +70,12 @@ local copyWithRename = backendUtils.copyWithRename local copyWithSet = backendUtils.copyWithSet local constants = require("../constants") local __DEBUG__ = constants.__DEBUG__ -local SESSION_STORAGE_RELOAD_AND_PROFILE_KEY = - constants.SESSION_STORAGE_RELOAD_AND_PROFILE_KEY -local SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = - constants.SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY +local SESSION_STORAGE_RELOAD_AND_PROFILE_KEY = constants.SESSION_STORAGE_RELOAD_AND_PROFILE_KEY +local SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = constants.SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY local TREE_OPERATION_ADD = constants.TREE_OPERATION_ADD local TREE_OPERATION_REMOVE = constants.TREE_OPERATION_REMOVE local TREE_OPERATION_REORDER_CHILDREN = constants.TREE_OPERATION_REORDER_CHILDREN -local TREE_OPERATION_UPDATE_TREE_BASE_DURATION = - constants.TREE_OPERATION_UPDATE_TREE_BASE_DURATION +local TREE_OPERATION_UPDATE_TREE_BASE_DURATION = constants.TREE_OPERATION_UPDATE_TREE_BASE_DURATION local ReactDebugTools = require("@pkg/@jsdotlua/react-debug-tools") local inspectHooksOfFiber = ReactDebugTools.inspectHooksOfFiber local Console = require("./console") @@ -87,8 +84,7 @@ local registerRendererWithConsole = Console.registerRenderer local ReactSymbols = require("./ReactSymbols") local CONCURRENT_MODE_NUMBER = ReactSymbols.CONCURRENT_MODE_NUMBER local CONCURRENT_MODE_SYMBOL_STRING = ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING -local DEPRECATED_ASYNC_MODE_SYMBOL_STRING = - ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING +local DEPRECATED_ASYNC_MODE_SYMBOL_STRING = ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING local PROVIDER_NUMBER = ReactSymbols.PROVIDER_NUMBER local PROVIDER_SYMBOL_STRING = ReactSymbols.PROVIDER_SYMBOL_STRING local CONTEXT_NUMBER = ReactSymbols.CONTEXT_NUMBER @@ -320,14 +316,10 @@ exports.getInternalReactConstants = function(version: string): { -- // ********************************************************** local function getTypeSymbol(type_: any): Symbol | number - local symbolOrNumber = if typeof(type_) == "table" - then type_["$$typeof"] - else type_ + local symbolOrNumber = if typeof(type_) == "table" then type_["$$typeof"] else type_ -- ROBLOX deviation: symbol is not a native Luau type - return if typeof(symbolOrNumber) == "table" - then tostring(symbolOrNumber) - else symbolOrNumber + return if typeof(symbolOrNumber) == "table" then tostring(symbolOrNumber) else symbolOrNumber end local ClassComponent, IncompleteClassComponent, FunctionComponent, IndeterminateComponent, ForwardRef, HostRoot, HostComponent, HostPortal, HostText, Fragment, MemoComponent, SimpleMemoComponent, SuspenseComponent, SuspenseListComponent = @@ -351,10 +343,7 @@ exports.getInternalReactConstants = function(version: string): { if typeSymbol == MEMO_NUMBER or typeSymbol == MEMO_SYMBOL_STRING then -- recursively resolving memo type in case of memo(forwardRef(Component)) return resolveFiberType(type_.type) - elseif - typeSymbol == FORWARD_REF_NUMBER - or typeSymbol == FORWARD_REF_SYMBOL_STRING - then + elseif typeSymbol == FORWARD_REF_NUMBER or typeSymbol == FORWARD_REF_SYMBOL_STRING then return type_.render else return type_ @@ -377,8 +366,7 @@ exports.getInternalReactConstants = function(version: string): { return getDisplayName(resolvedType) elseif tag == ForwardRef then -- Mirror https://github.com/facebook/react/blob/7c21bf72ace77094fd1910cc350a548287ef8350/packages/shared/getComponentName.js#L27-L37 - return (type_ and type_.displayName) - or getDisplayName(resolvedType, "Anonymous") + return (type_ and type_.displayName) or getDisplayName(resolvedType, "Anonymous") elseif tag == HostRoot then return nil elseif tag == HostComponent then @@ -399,20 +387,13 @@ exports.getInternalReactConstants = function(version: string): { or typeSymbol == DEPRECATED_ASYNC_MODE_SYMBOL_STRING then return nil - elseif - typeSymbol == PROVIDER_NUMBER or typeSymbol == PROVIDER_SYMBOL_STRING - then + elseif typeSymbol == PROVIDER_NUMBER or typeSymbol == PROVIDER_SYMBOL_STRING then -- 16.3.0 exposed the context object as "context" -- PR #12501 changed it to "_context" for 16.3.1+ -- NOTE Keep in sync with inspectElementRaw() resolvedContext = fiber.type._context or fiber.type.context - return string.format( - "%s.Provider", - resolvedContext.displayName or "Context" - ) - elseif - typeSymbol == CONTEXT_NUMBER or typeSymbol == CONTEXT_SYMBOL_STRING - then + return string.format("%s.Provider", resolvedContext.displayName or "Context") + elseif typeSymbol == CONTEXT_NUMBER or typeSymbol == CONTEXT_SYMBOL_STRING then -- 16.3-16.5 read from "type" because the Consumer is the actual context object. -- 16.6+ should read from "type._context" because Consumer can be different (in DEV). -- NOTE Keep in sync with inspectElementRaw() @@ -420,18 +401,10 @@ exports.getInternalReactConstants = function(version: string): { -- NOTE: TraceUpdatesBackendManager depends on the name ending in '.Consumer' -- If you change the name, figure out a more resilient way to detect it. - return string.format( - "%s.Consumer", - resolvedContext.displayName or "Context" - ) - elseif - typeSymbol == STRICT_MODE_NUMBER - or typeSymbol == STRICT_MODE_SYMBOL_STRING - then + return string.format("%s.Consumer", resolvedContext.displayName or "Context") + elseif typeSymbol == STRICT_MODE_NUMBER or typeSymbol == STRICT_MODE_SYMBOL_STRING then return nil - elseif - typeSymbol == PROFILER_NUMBER or typeSymbol == PROFILER_SYMBOL_STRING - then + elseif typeSymbol == PROFILER_NUMBER or typeSymbol == PROFILER_SYMBOL_STRING then return string.format("Profiler(%s)", fiber.memoizedProps.id) elseif typeSymbol == SCOPE_NUMBER or typeSymbol == SCOPE_SYMBOL_STRING then return "Scope" @@ -545,8 +518,7 @@ exports.attach = function( return renderer.scheduleUpdate(...) end - local supportsTogglingSuspense = typeof(setSuspenseHandler) == "function" - and typeof(scheduleUpdate) == "function" + local supportsTogglingSuspense = typeof(setSuspenseHandler) == "function" and typeof(scheduleUpdate) == "function" -- Patching the console enables DevTools to do a few useful things: -- * Append component stacks to warnings and error messages @@ -561,10 +533,8 @@ exports.attach = function( -- The renderer interface can't read these preferences directly, -- because it is stored in localStorage within the context of the extension. -- It relies on the extension to pass the preference through via the global. - local appendComponentStack = window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ - ~= false - local breakOnConsoleErrors = window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ - == true + local appendComponentStack = window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ ~= false + local breakOnConsoleErrors = window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ == true if appendComponentStack or breakOnConsoleErrors then patchConsole({ @@ -579,9 +549,7 @@ exports.attach = function( -- ROBLOX deviation: Use string nil rather than null as it is Roblox convenion local displayName = getDisplayNameForFiber(fiber) or "nil" local id = getFiberID(fiber) - local parentDisplayName = if parentFiber ~= nil - then getDisplayNameForFiber(parentFiber :: Fiber) - else "nil" + local parentDisplayName = if parentFiber ~= nil then getDisplayNameForFiber(parentFiber :: Fiber) else "nil" local parentID = if parentFiber then getFiberID(parentFiber :: Fiber) else "" -- NOTE: calling getFiberID or getPrimaryFiber is unsafe here -- because it will put them in the map. For now, we'll omit them. @@ -594,11 +562,7 @@ exports.attach = function( displayName, id, if parentFiber - then string.format( - "%s (%s)", - tostring(parentDisplayName), - tostring(parentID) - ) + then string.format("%s (%s)", tostring(parentDisplayName), tostring(parentID)) else "" ) ) @@ -671,32 +635,21 @@ exports.attach = function( end if componentFilter.type == ComponentFilterDisplayName then -- ROBLOX deviation: use value directly as pattern rather than creating a RegExp - hideElementsWithDisplayNames:add( - (componentFilter :: RegExpComponentFilter).value - ) + hideElementsWithDisplayNames:add((componentFilter :: RegExpComponentFilter).value) elseif componentFilter.type == ComponentFilterElementType then - hideElementsWithTypes:add( - (componentFilter :: ElementTypeComponentFilter).value - ) + hideElementsWithTypes:add((componentFilter :: ElementTypeComponentFilter).value) elseif componentFilter.type == ComponentFilterLocation then if (componentFilter :: RegExpComponentFilter).isValid and (componentFilter :: RegExpComponentFilter).value ~= "" then -- ROBLOX deviation: use value directly as pattern rather than creating a RegExp - hideElementsWithPaths:add( - (componentFilter :: RegExpComponentFilter).value - ) + hideElementsWithPaths:add((componentFilter :: RegExpComponentFilter).value) end elseif componentFilter.type == ComponentFilterHOC then hideElementsWithDisplayNames:add("%(") else - console.warn( - string.format( - 'Invalid component filter type "%d"', - componentFilter.type - ) - ) + console.warn(string.format('Invalid component filter type "%d"', componentFilter.type)) end end end @@ -762,12 +715,7 @@ exports.attach = function( -- For now, ignore it, and only show it once it gets hydrated. -- https://github.com/bvaughn/react-devtools-experimental/issues/197 return true - elseif - tag == HostPortal - or tag == HostText - or tag == Fragment - or tag == OffscreenComponent - then + elseif tag == HostPortal or tag == HostText or tag == Fragment or tag == OffscreenComponent then return true elseif tag == HostRoot then -- It is never valid to filter the root element. @@ -847,22 +795,13 @@ exports.attach = function( or typeSymbol == DEPRECATED_ASYNC_MODE_SYMBOL_STRING then return ElementTypeContext - elseif - typeSymbol == PROVIDER_NUMBER or typeSymbol == PROVIDER_SYMBOL_STRING - then + elseif typeSymbol == PROVIDER_NUMBER or typeSymbol == PROVIDER_SYMBOL_STRING then return ElementTypeContext - elseif - typeSymbol == CONTEXT_NUMBER or typeSymbol == CONTEXT_SYMBOL_STRING - then + elseif typeSymbol == CONTEXT_NUMBER or typeSymbol == CONTEXT_SYMBOL_STRING then return ElementTypeContext - elseif - typeSymbol == STRICT_MODE_NUMBER - or typeSymbol == STRICT_MODE_SYMBOL_STRING - then + elseif typeSymbol == STRICT_MODE_NUMBER or typeSymbol == STRICT_MODE_SYMBOL_STRING then return ElementTypeOtherOrUnknown - elseif - typeSymbol == PROFILER_NUMBER or typeSymbol == PROFILER_SYMBOL_STRING - then + elseif typeSymbol == PROFILER_NUMBER or typeSymbol == PROFILER_SYMBOL_STRING then return ElementTypeProfiler else return ElementTypeOtherOrUnknown @@ -890,10 +829,7 @@ exports.attach = function( return fiber end - local function getChangeDescription( - prevFiber: Fiber | nil, - nextFiber: Fiber - ): ChangeDescription | nil + local function getChangeDescription(prevFiber: Fiber | nil, nextFiber: Fiber): ChangeDescription | nil local fiberType = getElementTypeForFiber(nextFiber) if fiberType == ElementTypeClass @@ -914,19 +850,10 @@ exports.attach = function( else return { context = getContextChangedKeys(nextFiber), - didHooksChange = didHooksChange( - (prevFiber :: Fiber).memoizedState, - nextFiber.memoizedState - ), + didHooksChange = didHooksChange((prevFiber :: Fiber).memoizedState, nextFiber.memoizedState), isFirstMount = false, - props = getChangedKeys( - (prevFiber :: Fiber).memoizedProps, - nextFiber.memoizedProps - ), - state = getChangedKeys( - (prevFiber :: Fiber).memoizedState, - nextFiber.memoizedState - ), + props = getChangedKeys((prevFiber :: Fiber).memoizedProps, nextFiber.memoizedProps), + state = getChangedKeys((prevFiber :: Fiber).memoizedState, nextFiber.memoizedState), } end else @@ -987,17 +914,14 @@ exports.attach = function( if idToContextsMap ~= nil then local id = getFiberID(getPrimaryFiber(fiber)) -- ROBLOX TODO? optimize this pattern into just the get - local prevContexts = if idToContextsMap:has(id) - then idToContextsMap:get(id) - else nil + local prevContexts = if idToContextsMap:has(id) then idToContextsMap:get(id) else nil local nextContexts = getContextsForFiber(fiber) if prevContexts == nil or nextContexts == nil then return nil end - local prevLegacyContext, prevModernContext = - prevContexts[1], prevContexts[2] + local prevLegacyContext, prevModernContext = prevContexts[1], prevContexts[2] local nextLegacyContext, nextModernContext = (nextContexts :: Array)[1], (nextContexts :: Array)[2] @@ -1065,12 +989,7 @@ exports.attach = function( end -- We can't report anything meaningful for hooks changes. -- ROBLOX deviation: hasOwnProperty doesn't exist - if - next_["baseState"] - and next_["memoizedState"] - and next_["next"] - and next_["queue"] - then + if next_["baseState"] and next_["memoizedState"] and next_["next"] and next_["queue"] then while next_ ~= nil do -- ROBLOX deviation START: use didHookChange instead of equality check if didHookChange(prev, next_) then @@ -1149,10 +1068,7 @@ exports.attach = function( -- ROBLOX deviation: Use global if global.__DEV__ then if not Number.isInteger(op) then - console.error( - "pushOperation() was called but the value is not an integer.", - op - ) + console.error("pushOperation() was called but the value is not an integer.", op) end end table.insert(pendingOperations, op) @@ -1340,10 +1256,7 @@ exports.attach = function( if isProfiling then if displayNamesByRootID ~= nil then - (displayNamesByRootID :: Map):set( - id, - getDisplayNameForRoot(fiber) - ) + (displayNamesByRootID :: Map):set(id, getDisplayNameForRoot(fiber)) end end else @@ -1351,9 +1264,7 @@ exports.attach = function( local displayName = getDisplayNameForFiber(fiber) local elementType = getElementTypeForFiber(fiber) local _debugOwner = fiber._debugOwner - local ownerID = if _debugOwner ~= nil - then getFiberID(getPrimaryFiber(_debugOwner :: Fiber)) - else 0 + local ownerID = if _debugOwner ~= nil then getFiberID(getPrimaryFiber(_debugOwner :: Fiber)) else 0 local parentID = if Boolean.toJSBoolean(parentFiber) then getFiberID(getPrimaryFiber(parentFiber :: Fiber)) else 0 @@ -1388,10 +1299,7 @@ exports.attach = function( -- We're in the process of trying to restore previous selection. -- If this fiber matched but is being unmounted, there's no use trying. -- Reset the state so we don't keep holding onto it. - if - fiber == trackedPathMatchFiber - or fiber == (trackedPathMatchFiber :: Fiber).alternate - then + if fiber == trackedPathMatchFiber or fiber == (trackedPathMatchFiber :: Fiber).alternate then setTrackedPath(nil) end end @@ -1481,12 +1389,8 @@ exports.attach = function( -- get the fallback child from the inner fragment and mount -- it as if it was our own child. Updates handle this too. local primaryChildFragment = fiber.child - local fallbackChildFragment = if primaryChildFragment - then primaryChildFragment.sibling - else nil - local fallbackChild = if fallbackChildFragment - then fallbackChildFragment.child - else nil + local fallbackChildFragment = if primaryChildFragment then primaryChildFragment.sibling else nil + local fallbackChild = if fallbackChildFragment then fallbackChildFragment.child else nil if fallbackChild ~= nil then mountFiberRecursively( @@ -1530,12 +1434,7 @@ exports.attach = function( updateTrackedPathStateAfterMount(mightSiblingsBeOnTrackedPath) if traverseSiblings and fiber.sibling ~= nil then - mountFiberRecursively( - fiber.sibling, - parentFiber :: Fiber, - true, - traceNearestHostComponentUpdate - ) + mountFiberRecursively(fiber.sibling, parentFiber :: Fiber, true, traceNearestHostComponentUpdate) end end @@ -1548,16 +1447,13 @@ exports.attach = function( end -- We might meet a nested Suspense on our way. - local isTimedOutSuspense = fiber.tag == ReactTypeOfWork.SuspenseComponent - and fiber.memoizedState ~= nil + local isTimedOutSuspense = fiber.tag == ReactTypeOfWork.SuspenseComponent and fiber.memoizedState ~= nil local child = fiber.child if isTimedOutSuspense then -- If it's showing fallback tree, let's traverse it instead. local primaryChildFragment = fiber.child - local fallbackChildFragment = if primaryChildFragment - then primaryChildFragment.sibling - else nil + local fallbackChildFragment = if primaryChildFragment then primaryChildFragment.sibling else nil -- Skip over to the real Fiber child. child = if fallbackChildFragment then fallbackChildFragment.child else nil @@ -1576,8 +1472,7 @@ exports.attach = function( end recordProfilingDurations = function(fiber: Fiber) local id = getFiberID(getPrimaryFiber(fiber)) - local actualDuration, treeBaseDuration = - fiber.actualDuration, fiber.treeBaseDuration + local actualDuration, treeBaseDuration = fiber.actualDuration, fiber.treeBaseDuration idToTreeBaseDurationMap:set(id, treeBaseDuration or 0) @@ -1586,12 +1481,8 @@ exports.attach = function( -- It's important to update treeBaseDuration even if the current Fiber did not render, -- because it's possible that one of its descendants did. - if - alternate == nil - or treeBaseDuration ~= (alternate :: Fiber).treeBaseDuration - then - local convertedTreeBaseDuration = - math.floor((treeBaseDuration or 0) * 1000) + if alternate == nil or treeBaseDuration ~= (alternate :: Fiber).treeBaseDuration then + local convertedTreeBaseDuration = math.floor((treeBaseDuration or 0) * 1000) pushOperation(TREE_OPERATION_UPDATE_TREE_BASE_DURATION) pushOperation(id) @@ -1609,8 +1500,7 @@ exports.attach = function( local child = fiber.child while child ~= nil do - selfDuration = selfDuration - - ((child :: Fiber).actualDuration or 0) + selfDuration = selfDuration - ((child :: Fiber).actualDuration or 0) child = (child :: Fiber).sibling end @@ -1622,19 +1512,16 @@ exports.attach = function( table.insert(metadata.durations, id) table.insert(metadata.durations, actualDuration :: number) table.insert(metadata.durations, selfDuration) - metadata.maxActualDuration = - math.max(metadata.maxActualDuration, actualDuration :: number) + metadata.maxActualDuration = math.max(metadata.maxActualDuration, actualDuration :: number) if recordChangeDescriptions then local changeDescription = getChangeDescription(alternate, fiber) if changeDescription ~= nil then if metadata.changeDescriptions ~= nil then - ( - metadata.changeDescriptions :: Map< - number, - ChangeDescription - > - ):set(id, changeDescription :: ChangeDescription) + (metadata.changeDescriptions :: Map):set( + id, + changeDescription :: ChangeDescription + ) end end @@ -1722,9 +1609,7 @@ exports.attach = function( end if mostRecentlyInspectedElement ~= nil - and (mostRecentlyInspectedElement :: InspectedElement).id == getFiberID( - getPrimaryFiber(nextFiber) - ) + and (mostRecentlyInspectedElement :: InspectedElement).id == getFiberID(getPrimaryFiber(nextFiber)) and didFiberRender(prevFiber, nextFiber) then -- If this Fiber has updated, clear cached inspected data. @@ -1752,15 +1637,11 @@ exports.attach = function( -- Fallback -> Fallback: -- 1. Reconcile fallback set. local nextFiberChild = nextFiber.child - local nextFallbackChildSet = if nextFiberChild - then nextFiberChild.sibling - else nil + local nextFallbackChildSet = if nextFiberChild then nextFiberChild.sibling else nil -- Note: We can't use nextFiber.child.sibling.alternate -- because the set is special and alternate may not exist. local prevFiberChild = prevFiber.child - local prevFallbackChildSet = if prevFiberChild - then prevFiberChild.sibling - else nil + local prevFallbackChildSet = if prevFiberChild then prevFiberChild.sibling else nil if nextFallbackChildSet ~= nil @@ -1800,17 +1681,10 @@ exports.attach = function( -- 2. Mount fallback set local nextFiberChild = nextFiber.child - local nextFallbackChildSet = if nextFiberChild - then nextFiberChild.sibling - else nil + local nextFallbackChildSet = if nextFiberChild then nextFiberChild.sibling else nil if nextFallbackChildSet ~= nil then - mountFiberRecursively( - nextFallbackChildSet, - nextFiber, - true, - traceNearestHostComponentUpdate - ) + mountFiberRecursively(nextFallbackChildSet, nextFiber, true, traceNearestHostComponentUpdate) shouldResetChildren = true end @@ -1835,9 +1709,7 @@ exports.attach = function( updateFiberRecursively( nextChild :: Fiber, prevChild :: Fiber, - if shouldIncludeInTree - then nextFiber - else parentFiber :: Fiber, + if shouldIncludeInTree then nextFiber else parentFiber :: Fiber, traceNearestHostComponentUpdate ) then @@ -1882,9 +1754,7 @@ exports.attach = function( -- If we're tracing updates and we've bailed out before reaching a host node, -- we should fall back to recursively marking the nearest host descendants for highlight. if traceNearestHostComponentUpdate then - local hostFibers = findAllCurrentHostFibers( - getFiberID(getPrimaryFiber(nextFiber)) - ) + local hostFibers = findAllCurrentHostFibers(getFiberID(getPrimaryFiber(nextFiber))) for _, hostFiber in hostFibers do traceUpdatesForNodes:add(hostFiber.stateNode) @@ -1938,10 +1808,7 @@ exports.attach = function( pendingOperationsQueue = nil - if - localPendingOperationsQueue ~= nil - and #(localPendingOperationsQueue :: Array>) > 0 - then + if localPendingOperationsQueue ~= nil and #(localPendingOperationsQueue :: Array>) > 0 then for _, operations in localPendingOperationsQueue :: Array> do hook.emit("operations", operations) end @@ -1966,9 +1833,7 @@ exports.attach = function( currentCommitProfilingMetadata = { -- ROBLOX deviation: use bare table instead of Map type - changeDescriptions = if recordChangeDescriptions - then Map.new() - else nil, + changeDescriptions = if recordChangeDescriptions then Map.new() else nil, durations = {}, commitTime = getCurrentTime() - profilingStartTime, -- ROBLOX TODO: Work out how to deviate this assignment, it's messy @@ -1976,8 +1841,7 @@ exports.attach = function( Array.from(root.memoizedInteractions), function(interaction: Interaction) local tmp2 = Object.assign({}, interaction, { - timestamp = interaction.timestamp - - profilingStartTime, + timestamp = interaction.timestamp - profilingStartTime, }) return tmp2 end @@ -2060,17 +1924,14 @@ exports.attach = function( end ), maxActualDuration = 0, - priorityLevel = if priorityLevel == nil - then nil - else formatPriorityLevel(priorityLevel), + priorityLevel = if priorityLevel == nil then nil else formatPriorityLevel(priorityLevel), } end if alternate then -- TODO: relying on this seems a bit fishy. local wasMounted = (alternate :: Fiber).memoizedState ~= nil and (alternate :: Fiber).memoizedState.element ~= nil - local isMounted = current.memoizedState ~= nil - and current.memoizedState.element ~= nil + local isMounted = current.memoizedState ~= nil and current.memoizedState.element ~= nil if not wasMounted and isMounted then -- Mount a new root. @@ -2090,22 +1951,16 @@ exports.attach = function( mountFiberRecursively(current :: Fiber, nil, false, false) end if isProfiling and isProfilingSupported then - local commitProfilingMetadata = ( - (rootToCommitProfilingMetadataMap :: any) :: CommitProfilingMetadataMap - ):get(currentRootID) + local commitProfilingMetadata = ((rootToCommitProfilingMetadataMap :: any) :: CommitProfilingMetadataMap):get( + currentRootID + ) if commitProfilingMetadata ~= nil then - table.insert( - commitProfilingMetadata, - (currentCommitProfilingMetadata :: any) :: CommitProfilingData - ) + table.insert(commitProfilingMetadata, (currentCommitProfilingMetadata :: any) :: CommitProfilingData) else - ((rootToCommitProfilingMetadataMap :: any) :: CommitProfilingMetadataMap):set( - currentRootID, - { - (currentCommitProfilingMetadata :: any) :: CommitProfilingData, - } - ) + ((rootToCommitProfilingMetadataMap :: any) :: CommitProfilingMetadataMap):set(currentRootID, { + (currentCommitProfilingMetadata :: any) :: CommitProfilingData, + }) end end @@ -2171,8 +2026,7 @@ exports.attach = function( if isTimedOutSuspense then -- A timed-out Suspense's findDOMNode is useless. -- Try our best to find the fallback directly. - local maybeFallbackFiber = (fiber :: Fiber).child - and ((fiber :: Fiber).child :: Fiber).sibling + local maybeFallbackFiber = (fiber :: Fiber).child and ((fiber :: Fiber).child :: Fiber).sibling if maybeFallbackFiber ~= nil then fiber = maybeFallbackFiber :: Fiber end @@ -2197,10 +2051,7 @@ exports.attach = function( return if fiber ~= nil then getDisplayNameForFiber(fiber) else nil end - local function getFiberIDForNative( - hostInstance, - findNearestUnfilteredAncestor: boolean? - ): number? + local function getFiberIDForNative(hostInstance, findNearestUnfilteredAncestor: boolean?): number? findNearestUnfilteredAncestor = findNearestUnfilteredAncestor or false local fiber = renderer.findFiberByHostInstance(hostInstance) @@ -2225,10 +2076,7 @@ exports.attach = function( -- ROBLOX NOTE: Copied these supporting functions from ReactFiberTreeReflection local function assertIsMounted(fiber) - invariant( - getNearestMountedFiber(fiber) == fiber, - "Unable to find node on an unmounted component." - ) + invariant(getNearestMountedFiber(fiber) == fiber, "Unable to find node on an unmounted component.") end findCurrentFiberUsingSlowPathById = function(id: number): Fiber | nil @@ -2244,10 +2092,7 @@ exports.attach = function( if not alternate then -- If there is no alternate, then we only need to check if it is mounted. local nearestMounted = getNearestMountedFiber(fiber :: Fiber) - invariant( - nearestMounted ~= nil, - "Unable to find node on an unmounted component." - ) + invariant(nearestMounted ~= nil, "Unable to find node on an unmounted component.") if nearestMounted ~= (fiber :: Fiber) then return nil end @@ -2377,10 +2222,7 @@ exports.attach = function( end -- END copied code - local function prepareViewAttributeSource( - id: number, - path: Array - ): () + local function prepareViewAttributeSource(id: number, path: Array): () local isCurrent = isMostRecentlyInspectedElementCurrent(id) if isCurrent then @@ -2395,8 +2237,7 @@ exports.attach = function( return end - local elementType, tag, type_ = - (fiber :: Fiber).elementType, (fiber :: Fiber).tag, (fiber :: Fiber).type + local elementType, tag, type_ = (fiber :: Fiber).elementType, (fiber :: Fiber).tag, (fiber :: Fiber).type if tag == ClassComponent @@ -2408,10 +2249,7 @@ exports.attach = function( elseif tag == ForwardRef then global["$type"] = type_.render elseif tag == MemoComponent or tag == SimpleMemoComponent then - global["$type"] = elementType ~= nil - and elementType.type ~= nil - and elementType.type - or type_ + global["$type"] = elementType ~= nil and elementType.type ~= nil and elementType.type or type_ else global["$type"] = nil end @@ -2492,11 +2330,8 @@ exports.attach = function( local elementType = getElementTypeForFiber(fiber :: Fiber) - local usesHooks = ( - tag == FunctionComponent - or tag == SimpleMemoComponent - or tag == ForwardRef - ) and (not not memoizedState or not not dependencies) + local usesHooks = (tag == FunctionComponent or tag == SimpleMemoComponent or tag == ForwardRef) + and (not not memoizedState or not not dependencies) local typeSymbol = getTypeSymbol(type_) local canViewSource = false @@ -2538,15 +2373,11 @@ exports.attach = function( local currentType = (current :: Fiber).type local currentTypeSymbol = getTypeSymbol(currentType) - if - currentTypeSymbol == PROVIDER_NUMBER - or currentTypeSymbol == PROVIDER_SYMBOL_STRING - then + if currentTypeSymbol == PROVIDER_NUMBER or currentTypeSymbol == PROVIDER_SYMBOL_STRING then -- 16.3.0 exposed the context object as "context" -- PR #12501 changed it to "_context" for 16.3.1+ -- NOTE Keep in sync with getDisplayNameForFiber() - local providerResolvedContext = currentType._context - or currentType.context + local providerResolvedContext = currentType._context or currentType.context if providerResolvedContext == consumerResolvedContext then context = (current :: Fiber).memoizedProps.value @@ -2627,14 +2458,10 @@ exports.attach = function( canEditHooks = typeof(overrideHookState) == "function", canEditFunctionProps = typeof(overrideProps) == "function", -- Does the current renderer support advanced editing interface? - canEditHooksAndDeletePaths = typeof(overrideHookStateDeletePath) - == "function", - canEditHooksAndRenamePaths = typeof(overrideHookStateRenamePath) - == "function", - canEditFunctionPropsDeletePaths = typeof(overridePropsDeletePath) - == "function", - canEditFunctionPropsRenamePaths = typeof(overridePropsRenamePath) - == "function", + canEditHooksAndDeletePaths = typeof(overrideHookStateDeletePath) == "function", + canEditHooksAndRenamePaths = typeof(overrideHookStateRenamePath) == "function", + canEditFunctionPropsDeletePaths = typeof(overridePropsDeletePath) == "function", + canEditFunctionPropsRenamePaths = typeof(overridePropsRenamePath) == "function", canToggleSuspense = supportsTogglingSuspense -- If it's showing the real content, we can always flip fallback. and ( @@ -2713,9 +2540,7 @@ exports.attach = function( end end - local current = if key == nil - then currentlyInspectedPaths - else currentlyInspectedPaths[key] + local current = if key == nil then currentlyInspectedPaths else currentlyInspectedPaths[key] if not Boolean.toJSBoolean(current) then return false @@ -2732,8 +2557,7 @@ exports.attach = function( end local function updateSelectedElement(inspectedElement: InspectedElement): () - local hooks, id, props = - inspectedElement.hooks, inspectedElement.id, inspectedElement.props + local hooks, id, props = inspectedElement.hooks, inspectedElement.id, inspectedElement.props local fiber: Fiber? = idToFiberMap:get(id) if fiber == nil then @@ -2743,16 +2567,9 @@ exports.attach = function( end local elementType, stateNode, tag, type_ = - (fiber :: Fiber).elementType, - (fiber :: Fiber).stateNode, - (fiber :: Fiber).tag, - (fiber :: Fiber).type + (fiber :: Fiber).elementType, (fiber :: Fiber).stateNode, (fiber :: Fiber).tag, (fiber :: Fiber).type - if - tag == ClassComponent - or tag == IncompleteClassComponent - or tag == IndeterminateComponent - then + if tag == ClassComponent or tag == IncompleteClassComponent or tag == IndeterminateComponent then global["$r"] = stateNode elseif tag == FunctionComponent then global["$r"] = { @@ -2768,21 +2585,14 @@ exports.attach = function( elseif tag == MemoComponent or tag == SimpleMemoComponent then global["$r"] = { props = props, - type = elementType ~= nil - and elementType.type ~= nil - and elementType.type - or type_, + type = elementType ~= nil and elementType.type ~= nil and elementType.type or type_, } else global["$r"] = nil end end - local function storeAsGlobal( - id: number, - path: Array, - count: number - ): () + local function storeAsGlobal(id: number, path: Array, count: number): () local isCurrent = isMostRecentlyInspectedElementCurrent(id) if isCurrent then @@ -2804,10 +2614,7 @@ exports.attach = function( end end - local function inspectElement( - id: number, - path: Array? - ): InspectedElementPayload + local function inspectElement(id: number, path: Array?): InspectedElementPayload local isCurrent = isMostRecentlyInspectedElementCurrent(id) if isCurrent then @@ -2843,10 +2650,7 @@ exports.attach = function( else hasElementUpdatedSinceLastInspected = false - if - mostRecentlyInspectedElement == nil - or (mostRecentlyInspectedElement :: InspectedElement).id ~= id - then + if mostRecentlyInspectedElement == nil or (mostRecentlyInspectedElement :: InspectedElement).id ~= id then currentlyInspectedPaths = {} end @@ -2870,25 +2674,16 @@ exports.attach = function( -- Clone before cleaning so that we preserve the full data. -- This will enable us to send patches without re-inspecting if hydrated paths are requested. -- (Reducing how often we shallow-render is a better DX for function components that use hooks.) - local cleanedInspectedElement = - Object.assign({}, mostRecentlyInspectedElement) + local cleanedInspectedElement = Object.assign({}, mostRecentlyInspectedElement) - cleanedInspectedElement.context = cleanForBridge( - cleanedInspectedElement.context, - createIsPathAllowed("context", nil) - ) - cleanedInspectedElement.hooks = cleanForBridge( - cleanedInspectedElement.hooks, - createIsPathAllowed("hooks", "hooks") - ) - cleanedInspectedElement.props = cleanForBridge( - cleanedInspectedElement.props, - createIsPathAllowed("props", nil) - ) - cleanedInspectedElement.state = cleanForBridge( - cleanedInspectedElement.state, - createIsPathAllowed("state", nil) - ) + cleanedInspectedElement.context = + cleanForBridge(cleanedInspectedElement.context, createIsPathAllowed("context", nil)) + cleanedInspectedElement.hooks = + cleanForBridge(cleanedInspectedElement.hooks, createIsPathAllowed("hooks", "hooks")) + cleanedInspectedElement.props = + cleanForBridge(cleanedInspectedElement.props, createIsPathAllowed("props", nil)) + cleanedInspectedElement.state = + cleanForBridge(cleanedInspectedElement.state, createIsPathAllowed("state", nil)) return { id = id, @@ -3033,8 +2828,7 @@ exports.attach = function( overridePropsRenamePath(fiber, oldPath, newPath) end else - (fiber :: Fiber).pendingProps = - copyWithRename(instance.props, oldPath, newPath) + (fiber :: Fiber).pendingProps = copyWithRename(instance.props, oldPath, newPath) instance:forceUpdate() end elseif type_ == "state" then @@ -3084,8 +2878,7 @@ exports.attach = function( overrideProps(fiber :: Fiber, path, value) end else - (fiber :: Fiber).pendingProps = - copyWithSet(instance.props, path, value) + (fiber :: Fiber).pendingProps = copyWithSet(instance.props, path, value) instance:forceUpdate() end elseif type_ == "state" then @@ -3129,15 +2922,11 @@ exports.attach = function( initialTreeBaseDurationsMap:forEach(function(treeBaseDuration, id) if initialIDToRootMap ~= nil - and (initialIDToRootMap :: Map):get(id) - == rootID + and (initialIDToRootMap :: Map):get(id) == rootID then -- We don't need to convert milliseconds to microseconds in this case, -- because the profiling summary is JSON serialized. - table.insert( - initialTreeBaseDurations, - { id, treeBaseDuration } - ) + table.insert(initialTreeBaseDurations, { id, treeBaseDuration }) end end) end @@ -3228,10 +3017,7 @@ exports.attach = function( hook.getFiberRoots(rendererID):forEach(function(root) local rootID = getFiberID(getPrimaryFiber(root.current)); - ((displayNamesByRootID :: any) :: DisplayNamesByRootID):set( - rootID, - getDisplayNameForRoot(root.current) - ) + ((displayNamesByRootID :: any) :: DisplayNamesByRootID):set(rootID, getDisplayNameForRoot(root.current)) if shouldRecordChangeDescriptions then -- Record all contexts at the time profiling is started. @@ -3253,10 +3039,7 @@ exports.attach = function( -- Automatically start profiling so that we don't miss timing info from initial "mount". if sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) == "true" then - startProfiling( - sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) - == "true" - ) + startProfiling(sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) == "true") end -- React will switch between these implementations depending on whether @@ -3271,13 +3054,8 @@ exports.attach = function( end -- ROBLOX FIXME Luau: infers this as (number, a) -> (), but it doesn't later normalize to (number, boolean) -> () local function overrideSuspense(id: number, forceFallback: boolean): () - if - typeof(setSuspenseHandler) ~= "function" - or typeof(scheduleUpdate) ~= "function" - then - error( - "Expected overrideSuspense() to not get called for earlier React versions." - ) + if typeof(setSuspenseHandler) ~= "function" or typeof(scheduleUpdate) ~= "function" then + error("Expected overrideSuspense() to not get called for earlier React versions.") end if forceFallback then forceFallbackForSuspenseIDs:add(id) @@ -3340,8 +3118,7 @@ exports.attach = function( if actualFrame.index == (expectedFrame :: PathFrame).index and actualFrame.key == (expectedFrame :: PathFrame).key - and actualFrame.displayName - == (expectedFrame :: PathFrame).displayName + and actualFrame.displayName == (expectedFrame :: PathFrame).displayName then -- We have our next match. trackedPathMatchFiber = fiber @@ -3390,11 +3167,7 @@ exports.attach = function( end -- Luau FIXME: `pseudoKey == nil` above should narrow pseudoKey from string? to string - local name = string.sub( - pseudoKey :: string, - 1, - String.lastIndexOf(pseudoKey :: string, ":") - 1 - ) + local name = string.sub(pseudoKey :: string, 1, String.lastIndexOf(pseudoKey :: string, ":") - 1) local counter = rootDisplayNameCounter:get(name) -- ROBLOX FIXME Luau: needs type states to know past this branch count is non-nil diff --git a/modules/react-devtools-shared/src/backend/types.lua b/modules/react-devtools-shared/src/backend/types.lua index 821ea615..d8c1abe2 100644 --- a/modules/react-devtools-shared/src/backend/types.lua +++ b/modules/react-devtools-shared/src/backend/types.lua @@ -103,47 +103,31 @@ export type ReactRenderer = { rendererPackageName: string, bundleType: BundleType, -- 16.9+ - overrideHookState: (( - self: ReactRenderer, - Object, - number, - Array, - any - ) -> ()), + overrideHookState: (self: ReactRenderer, Object, number, Array, any) -> (), -- 17+ - overrideHookStateDeletePath: (( - self: ReactRenderer, - Object, - number, - Array - ) -> ()), + overrideHookStateDeletePath: (self: ReactRenderer, Object, number, Array) -> (), -- 17+ - overrideHookStateRenamePath: (( + overrideHookStateRenamePath: ( self: ReactRenderer, Object, number, Array, Array - ) -> ()), + ) -> (), -- 16.7+ - overrideProps: ((self: ReactRenderer, Object, Array, any) -> ()), + overrideProps: (self: ReactRenderer, Object, Array, any) -> (), -- 17+ - overridePropsDeletePath: ( - (self: ReactRenderer, Object, Array) -> () - ), + overridePropsDeletePath: (self: ReactRenderer, Object, Array) -> (), -- 17+ - overridePropsRenamePath: (( + overridePropsRenamePath: ( self: ReactRenderer, Object, Array, Array - ) -> ()), - -- 16.9+ - scheduleUpdate: ((self: ReactRenderer, Object) -> ()), - setSuspenseHandler: ( - self: ReactRenderer, - shouldSuspend: (fiber: Object) -> boolean ) -> (), + -- 16.9+ + scheduleUpdate: (self: ReactRenderer, Object) -> (), + setSuspenseHandler: (self: ReactRenderer, shouldSuspend: (fiber: Object) -> boolean) -> (), -- Only injected by React v16.8+ in order to support hooks inspection. currentDispatcherRef: CurrentDispatcherRef?, -- Only injected by React v16.9+ in DEV mode. @@ -332,13 +316,7 @@ export type RendererInterface = { overrideValueAtPath: (Type, number, number?, Array, any) -> (), prepareViewAttributeSource: (number, Array) -> (), prepareViewElementSource: (number) -> (), - renamePath: ( - Type, - number, - number?, - Array, - Array - ) -> (), + renamePath: (Type, number, number?, Array, Array) -> (), renderer: ReactRenderer | nil, setTraceUpdatesEnabled: (boolean) -> (), setTrackedPath: (Array | nil) -> (), @@ -368,7 +346,7 @@ export type DevToolsHook = { on: (string, Handler) -> (), off: (string, Handler) -> (), reactDevtoolsAgent: Object?, - sub: (string, Handler) -> (() -> ()), + sub: (string, Handler) -> () -> (), -- Used by react-native-web and Flipper/Inspector resolveRNStyle: ResolveNativeStyle?, diff --git a/modules/react-devtools-shared/src/backend/utils.lua b/modules/react-devtools-shared/src/backend/utils.lua index 960d00cb..9a32170b 100644 --- a/modules/react-devtools-shared/src/backend/utils.lua +++ b/modules/react-devtools-shared/src/backend/utils.lua @@ -31,13 +31,8 @@ exports.cleanForBridge = function( if data ~= nil then local cleanedPaths: Array> = {} local unserializablePaths: Array> = {} - local cleanedData = dehydrate( - data :: Object, - cleanedPaths, - unserializablePaths, - path :: Array, - isPathAllowed - ) + local cleanedData = + dehydrate(data :: Object, cleanedPaths, unserializablePaths, path :: Array, isPathAllowed) return { data = cleanedData, cleaned = cleanedPaths, diff --git a/modules/react-devtools-shared/src/bridge.lua b/modules/react-devtools-shared/src/bridge.lua index f0adb1f6..696f5e7f 100644 --- a/modules/react-devtools-shared/src/bridge.lua +++ b/modules/react-devtools-shared/src/bridge.lua @@ -51,9 +51,7 @@ type OverrideHookState = OverrideValue & { hookID: number } type PathType = string -- ROBLOX deviation: Luau can't use ...type, use intersection instead -type DeletePath = - ElementAndRendererID - & { type: PathType, hookID: number?, path: Array } +type DeletePath = ElementAndRendererID & { type: PathType, hookID: number?, path: Array } -- ROBLOX deviation: Luau can't use ...type, use intersection instead type RenamePath = ElementAndRendererID & { @@ -84,9 +82,7 @@ type ViewAttributeSourceParams = ElementAndRendererID & { path: Array? } -- ROBLOX deviation: Luau can't use ...type, use intersection instead -type StoreAsGlobalParams = - ElementAndRendererID - & { count: number, path: Array } +type StoreAsGlobalParams = ElementAndRendererID & { count: number, path: Array } -- ROBLOX deviation: Luau can't use ...type, use intersection instead type NativeStyleEditor_RenameAttributeParams = ElementAndRendererID & { @@ -96,9 +92,7 @@ type NativeStyleEditor_RenameAttributeParams = ElementAndRendererID & { } -- ROBLOX deviation: Luau can't use ...type, use intersection instead -type NativeStyleEditor_SetValueParams = - ElementAndRendererID - & { name: string, value: string } +type NativeStyleEditor_SetValueParams = ElementAndRendererID & { name: string, value: string } type UpdateConsolePatchSettingsParams = { appendComponentStack: boolean, @@ -203,10 +197,7 @@ export type Bridge< ) -> (), shutdown: (self: Bridge) -> (), _flush: (self: Bridge) -> (), - overrideValueAtPath: ( - self: Bridge, - _ref: OverrideValueAtPath - ) -> (), + overrideValueAtPath: (self: Bridge, _ref: OverrideValueAtPath) -> (), } type Bridge_Statics = { @@ -215,10 +206,7 @@ type Bridge_Statics = { -- ROBLOX deviation: not sure where TimeoutID comes from in upstream type TimeoutID = any -local Bridge: Bridge & Bridge_Statics = setmetatable( - {}, - { __index = EventEmitter } -) :: any +local Bridge: Bridge & Bridge_Statics = setmetatable({}, { __index = EventEmitter }) :: any local BridgeMetatable = { __index = Bridge } function Bridge.new(wall: Wall) @@ -250,12 +238,7 @@ end function Bridge:send(event: EventName, ...: ElementType) local payload = { ... } if self._isShutdown then - console.warn( - string.format( - 'Cannot send message "%s" through a Bridge that has been shutdown.', - event - ) - ) + console.warn(string.format('Cannot send message "%s" through a Bridge that has been shutdown.', event)) return end @@ -334,10 +317,7 @@ function Bridge:_flush(): () -- ROBLOX deviation: Use a while loop instead of for loop to handle new insertions during the loop local i = 1 while i < #self._messageQueue do - self._wall.send( - self._messageQueue[i], - table.unpack(self._messageQueue[i + 1]) - ) + self._wall.send(self._messageQueue[i], table.unpack(self._messageQueue[i + 1])) i += 2 end table.clear(self._messageQueue) @@ -354,8 +334,7 @@ end -- Temporarily support older standalone backends by forwarding "overrideValueAtPath" commands -- to the older message types they may be listening to. function Bridge:overrideValueAtPath(_ref: OverrideValueAtPath) - local id, path, rendererID, type_, value = - _ref.id, _ref.path, _ref.rendererID, _ref.type, _ref.value + local id, path, rendererID, type_, value = _ref.id, _ref.path, _ref.rendererID, _ref.type, _ref.value if type_ == "context" then self:send("overrideContext", { id = id, diff --git a/modules/react-devtools-shared/src/constants.lua b/modules/react-devtools-shared/src/constants.lua index f0a243ae..56d60e22 100644 --- a/modules/react-devtools-shared/src/constants.lua +++ b/modules/react-devtools-shared/src/constants.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/constants.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/constants.js -- /** -- * Copyright (c) Facebook, Inc. and its affiliates. -- * @@ -44,7 +44,6 @@ exports.UNSUPPORTED_VERSION_URL = -- HACK -- --- Extracting during build time avoids a temporarily invalid state for the inline target. -- Sometimes the inline target is rendered before root styles are applied, -- which would result in e.g. NaN itemSize being passed to react-window list. -- diff --git a/modules/react-devtools-shared/src/devtools/cache.lua b/modules/react-devtools-shared/src/devtools/cache.lua index 2e16ea45..f78e940c 100644 --- a/modules/react-devtools-shared/src/devtools/cache.lua +++ b/modules/react-devtools-shared/src/devtools/cache.lua @@ -32,11 +32,7 @@ local createContext = React.createContext -- ROBLOX deviation START: Suspender needs a generic param to be type compatible with Thenable export type Suspender = { - andThen: ( - self: Thenable, - onFulfill: (R) -> () | U, - onReject: (error: any) -> () | U - ) -> (), + andThen: (self: Thenable, onFulfill: (R) -> () | U, onReject: (error: any) -> () | U) -> (), } -- ROBLOX deviation END @@ -69,8 +65,7 @@ local Pending = 0 local Resolved = 1 local Rejected = 2 -local ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher +local ReactCurrentDispatcher = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher local function readContext(Context, observedBits: boolean?) local dispatcher = ReactCurrentDispatcher.current @@ -110,12 +105,7 @@ local function getEntriesForResource(resource: any): WeakMap end -- ROBLOX deviation END -local function accessResult( - resource: any, - fetch: (Input) -> Thenable, - input: Input, - key: Key -): Result +local function accessResult(resource: any, fetch: (Input) -> Thenable, input: Input, key: Key): Result local entriesForResource = getEntriesForResource(resource) local entry = entriesForResource:get(key) diff --git a/modules/react-devtools-shared/src/devtools/store.lua b/modules/react-devtools-shared/src/devtools/store.lua index 0464f809..eb294120 100644 --- a/modules/react-devtools-shared/src/devtools/store.lua +++ b/modules/react-devtools-shared/src/devtools/store.lua @@ -27,8 +27,7 @@ local constants = require("../constants") local TREE_OPERATION_ADD = constants.TREE_OPERATION_ADD local TREE_OPERATION_REMOVE = constants.TREE_OPERATION_REMOVE local TREE_OPERATION_REORDER_CHILDREN = constants.TREE_OPERATION_REORDER_CHILDREN -local TREE_OPERATION_UPDATE_TREE_BASE_DURATION = - constants.TREE_OPERATION_UPDATE_TREE_BASE_DURATION +local TREE_OPERATION_UPDATE_TREE_BASE_DURATION = constants.TREE_OPERATION_UPDATE_TREE_BASE_DURATION local types = require("../types") local ElementTypeRoot = types.ElementTypeRoot local utils = require("../utils") @@ -64,10 +63,8 @@ local debug_ = function(methodName, ...) end end -local LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY = - "React::DevTools::collapseNodesByDefault" -local LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = - "React::DevTools::recordChangeDescriptions" +local LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY = "React::DevTools::collapseNodesByDefault" +local LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = "React::DevTools::recordChangeDescriptions" type Config = { isProfiling: boolean?, @@ -86,9 +83,7 @@ type Config = { type Store_static = { new: (bridge: FrontendBridge, config: Config?) -> Store, } -local Store: Store & Store_static = ( - setmetatable({}, { __index = EventEmitter }) :: any -) :: Store & Store_static +local Store: Store & Store_static = (setmetatable({}, { __index = EventEmitter }) :: any) :: Store & Store_static local StoreMetatable = { __index = Store } function Store.new(bridge: FrontendBridge, config: Config?): Store @@ -156,13 +151,9 @@ function Store.new(bridge: FrontendBridge, config: Config?): Store debug_("constructor", "subscribing to Bridge") end - self._collapseNodesByDefault = localStorageGetItem( - LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY - ) == "true" + self._collapseNodesByDefault = localStorageGetItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY) == "true" - self._recordChangeDescriptions = localStorageGetItem( - LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY - ) == "true" + self._recordChangeDescriptions = localStorageGetItem(LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) == "true" self._componentFilters = getSavedComponentFilters() @@ -219,14 +210,8 @@ function Store.new(bridge: FrontendBridge, config: Config?): Store bridge:addListener("overrideComponentFilters", self.onBridgeOverrideComponentFilters) bridge:addListener("shutdown", self.onBridgeShutdown) bridge:addListener("isBackendStorageAPISupported", self.onBridgeStorageSupported) - bridge:addListener( - "isNativeStyleEditorSupported", - self.onBridgeNativeStyleEditorSupported - ) - bridge:addListener( - "unsupportedRendererVersion", - self.onBridgeUnsupportedRendererVersion - ) + bridge:addListener("isNativeStyleEditorSupported", self.onBridgeNativeStyleEditorSupported) + bridge:addListener("unsupportedRendererVersion", self.onBridgeUnsupportedRendererVersion) return self end @@ -240,10 +225,7 @@ function Store:assertExpectedRootMapSizes() end -- These maps should always be the same size as the number of roots - self:assertMapSizeMatchesRootCount( - self._rootIDToCapabilities, - "_rootIDToCapabilities" - ) + self:assertMapSizeMatchesRootCount(self._rootIDToCapabilities, "_rootIDToCapabilities") self:assertMapSizeMatchesRootCount(self._rootIDToRendererID, "_rootIDToRendererID") end @@ -273,10 +255,7 @@ end function Store:setCollapseNodesByDefault(value: boolean) self._collapseNodesByDefault = value - localStorageSetItem( - LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY, - if value then "true" else "false" - ) + localStorageSetItem(LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY, if value then "true" else "false") self:emit("collapseNodesByDefault") end function Store:getComponentFilters(): Array @@ -292,17 +271,13 @@ function Store:setComponentFilters(value: Array): () -- Filter updates are expensive to apply (since they impact the entire tree). -- Let's determine if they've changed and avoid doing this work if they haven't. - local prevEnabledComponentFilters = Array.filter( - self._componentFilters, - function(filter) - return filter.isEnabled - end - ) + local prevEnabledComponentFilters = Array.filter(self._componentFilters, function(filter) + return filter.isEnabled + end) local nextEnabledComponentFilters = Array.filter(value, function(filter) return filter.isEnabled end) - local haveEnabledFiltersChanged = #prevEnabledComponentFilters - ~= #nextEnabledComponentFilters + local haveEnabledFiltersChanged = #prevEnabledComponentFilters ~= #nextEnabledComponentFilters if not haveEnabledFiltersChanged then -- ROBLOX deviation: 1-indexing use 1 not 0 @@ -349,10 +324,7 @@ end function Store:setRecordChangeDescriptions(value: boolean): () self._recordChangeDescriptions = value - localStorageSetItem( - LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, - if value then "true" else "false" - ) + localStorageSetItem(LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, if value then "true" else "false") self:emit("recordChangeDescriptions") end function Store:getRevision(): number @@ -388,11 +360,7 @@ end function Store:getElementAtIndex(index: number): Element? if index < 0 or index >= self:getNumElements() then console.warn( - string.format( - "Invalid index %d specified; store contains %d items.", - index, - self:getNumElements() - ) + string.format("Invalid index %d specified; store contains %d items.", index, self:getNumElements()) ) return nil end @@ -535,8 +503,7 @@ function Store:getOwnersListForElement(ownerID: number): Array Array.from(unsortedIDs), -- ROBLOX FIXME Luau: shouldn't need this annotation? function(idA: number, idB: number) - return (self:getIndexOfElementID(idA) or 0) - - (self:getIndexOfElementID(idB) or 0) + return (self:getIndexOfElementID(idA) or 0) - (self:getIndexOfElementID(idB) or 0) end ) @@ -641,9 +608,7 @@ function Store:toggleIsCollapsed(id: number, isCollapsed: boolean): () local weightDelta = 1 - (element :: Element).weight -- ROBLOX FIXME Luau: shouldn't need this annoatation, should infer correctly - local parentElement: Element? = ( - self._idToElement:get(element.parentID) :: any - ) :: Element + local parentElement: Element? = (self._idToElement:get(element.parentID) :: any) :: Element while parentElement ~= nil do -- We don't need to break on a collapsed parent in the same way as the expand case below. -- That's because collapsing a node doesn't "bubble" and affect its parents. @@ -656,9 +621,7 @@ function Store:toggleIsCollapsed(id: number, isCollapsed: boolean): () local currentElement: Element? = element while currentElement ~= nil do - local oldWeight = if (currentElement :: Element).isCollapsed - then 1 - else currentElement.weight + local oldWeight = if (currentElement :: Element).isCollapsed then 1 else currentElement.weight if (currentElement :: Element).isCollapsed then didMutate = true; @@ -669,9 +632,7 @@ function Store:toggleIsCollapsed(id: number, isCollapsed: boolean): () else (currentElement :: Element).weight local weightDelta = newWeight - oldWeight -- ROBLOX FIXME Luau: shouldn't need this annoatation, should infer correctly - local parentElement: Element? = ( - self._idToElement:get(currentElement.parentID) :: any - ) :: Element + local parentElement: Element? = (self._idToElement:get(currentElement.parentID) :: any) :: Element while parentElement ~= nil do parentElement.weight += weightDelta @@ -801,9 +762,7 @@ function Store:onBridgeOperations(operations: Array): () if self._idToElement:has(id) then error( Error.new( - ("Cannot add node %s because a node with that id is already in the Store."):format( - tostring(id) - ) + ("Cannot add node %s because a node with that id is already in the Store."):format(tostring(id)) ) ) end @@ -888,8 +847,7 @@ function Store:onBridgeOperations(operations: Array): () table.insert(parentElement.children, id) - local displayNameWithoutHOCs, hocDisplayNames = - separateDisplayNameAndHOCs(displayName, type_) + local displayNameWithoutHOCs, hocDisplayNames = separateDisplayNameAndHOCs(displayName, type_) local element = { children = {}, @@ -945,14 +903,7 @@ function Store:onBridgeOperations(operations: Array): () element.children, element.ownerID, element.parentID, element.weight if #children > 0 then - error( - Error.new( - string.format( - "Node %s was removed before its children.", - tostring(id) - ) - ) - ) + error(Error.new(string.format("Node %s was removed before its children.", tostring(id)))) end self._idToElement:delete(id) @@ -974,14 +925,7 @@ function Store:onBridgeOperations(operations: Array): () haveRootsChanged = true else if __DEBUG__ then - debug_( - "Remove", - string.format( - "node %s from parent %s", - tostring(id), - tostring(parentID) - ) - ) + debug_("Remove", string.format("node %s from parent %s", tostring(id), tostring(parentID))) end parentElement = (self._idToElement:get(parentID) :: any) :: Element @@ -1043,13 +987,8 @@ function Store:onBridgeOperations(operations: Array): () if _G.__DEV__ then local childElement: Element? = self._idToElement:get(childID) - if - childElement == nil - or (childElement :: Element).parentID ~= id - then - console.error( - "Children cannot be added or removed during a reorder operation." - ) + if childElement == nil or (childElement :: Element).parentID ~= id then + console.error("Children cannot be added or removed during a reorder operation.") end end end @@ -1057,14 +996,7 @@ function Store:onBridgeOperations(operations: Array): () i = i + numChildren if _G.__DEBUG__ then - debug_( - "Re-order", - string.format( - "Node %s children %s", - tostring(id), - Array.join(children, ",") - ) - ) + debug_("Re-order", string.format("Node %s children %s", tostring(id), Array.join(children, ","))) end elseif operation == TREE_OPERATION_UPDATE_TREE_BASE_DURATION then -- Base duration updates are only sent while profiling is in progress. @@ -1085,8 +1017,7 @@ function Store:onBridgeOperations(operations: Array): () self._supportsProfiling = false for _, capabilities in self._rootIDToCapabilities do - local hasOwnerMetadata, supportsProfiling = - capabilities.hasOwnerMetadata, capabilities.supportsProfiling + local hasOwnerMetadata, supportsProfiling = capabilities.hasOwnerMetadata, capabilities.supportsProfiling if hasOwnerMetadata then self._hasOwnerMetadata = true @@ -1112,9 +1043,7 @@ function Store:onBridgeOperations(operations: Array): () self:emit("mutated", { addedElementIDs, removedElementIDs }) end -function Store:onBridgeOverrideComponentFilters( - componentFilters: Array -): () +function Store:onBridgeOverrideComponentFilters(componentFilters: Array): () self._componentFilters = componentFilters saveComponentFilters(componentFilters) @@ -1127,10 +1056,7 @@ function Store:onBridgeShutdown(): () self._bridge:removeListener("operations", self.onBridgeOperations) self._bridge:removeListener("shutdown", self.onBridgeShutdown) - self._bridge:removeListener( - "isBackendStorageAPISupported", - self.onBridgeStorageSupported - ) + self._bridge:removeListener("isBackendStorageAPISupported", self.onBridgeStorageSupported) end function Store:onBridgeStorageSupported(isBackendStorageAPISupported: boolean): () diff --git a/modules/react-devtools-shared/src/devtools/utils.lua b/modules/react-devtools-shared/src/devtools/utils.lua index 1e3a5e38..07853d92 100644 --- a/modules/react-devtools-shared/src/devtools/utils.lua +++ b/modules/react-devtools-shared/src/devtools/utils.lua @@ -40,16 +40,11 @@ exports.printElement = function(element: Element, includeWeight: boolean?) hocDisplayNames = table.clone(element.hocDisplayNames) end - local hocs = if hocDisplayNames == nil - then "" - else string.format(" [%s]", table.concat(hocDisplayNames, "][")) + local hocs = if hocDisplayNames == nil then "" else string.format(" [%s]", table.concat(hocDisplayNames, "][")) local suffix = "" if includeWeight then - suffix = string.format( - " (%s)", - if element.isCollapsed then "1" else tostring(element.weight) - ) + suffix = string.format(" (%s)", if element.isCollapsed then "1" else tostring(element.weight)) end return string.format( "%s%s <%s%s>%s%s", @@ -80,10 +75,7 @@ exports.printStore = function(store: Store, includeWeight: boolean?) Array.forEach(store:getRoots(), function(rootID) local weight = ((store:getElementByID(rootID) :: any) :: Element).weight - table.insert( - snapshotLines, - "[root]" .. (if includeWeight then string.format(" (%d)", weight) else "") - ) + table.insert(snapshotLines, "[root]" .. (if includeWeight then string.format(" (%d)", weight) else "")) for i = rootWeight, rootWeight + weight - 1 do local element: Element? = store:getElementAtIndex(i) @@ -91,10 +83,7 @@ exports.printStore = function(store: Store, includeWeight: boolean?) error(string.format("Could not find element at index %d", i)) end - table.insert( - snapshotLines, - exports.printElement(element :: Element, includeWeight :: boolean) - ) + table.insert(snapshotLines, exports.printElement(element :: Element, includeWeight :: boolean)) end rootWeight += weight end) @@ -121,11 +110,7 @@ end -- so this method replaces e.g. 'foo' with "foo" exports.sanitizeForParse = function(value) if typeof(value) == "string" then - if - #value >= 2 - and string.sub(value, 1, 1) == "'" - and string.sub(value, #value) == "'" - then + if #value >= 2 and string.sub(value, 1, 1) == "'" and string.sub(value, #value) == "'" then return '"' .. string.sub(value, 1, #value - 2) .. '"' end end diff --git a/modules/react-devtools-shared/src/events.lua b/modules/react-devtools-shared/src/events.lua index bec7309c..ab71d278 100644 --- a/modules/react-devtools-shared/src/events.lua +++ b/modules/react-devtools-shared/src/events.lua @@ -20,11 +20,7 @@ type EventListener = (...ElementType) -> ...any export type EventEmitter = { listenersMap: Map>, -- ROBLOX TODO: function generics >( - addListener: ( - self: EventEmitter, - event: string, - listener: EventListener - ) -> (), + addListener: (self: EventEmitter, event: string, listener: EventListener) -> (), -- ROBLOX TODO: function generics >( emit: (EventEmitter, string, ...ElementType) -> (), removeAllListeners: (EventEmitter) -> (), @@ -34,9 +30,7 @@ export type EventEmitter = { type EventEmitter_statics = { new: () -> EventEmitter, } -local EventEmitter: EventEmitter & EventEmitter_statics = ( - {} :: any -) :: EventEmitter & EventEmitter_statics +local EventEmitter: EventEmitter & EventEmitter_statics = ({} :: any) :: EventEmitter & EventEmitter_statics local EventEmitterMetatable = { __index = EventEmitter } function EventEmitter.new(): EventEmitter diff --git a/modules/react-devtools-shared/src/hydration.lua b/modules/react-devtools-shared/src/hydration.lua index 0236f2d0..ec553586 100644 --- a/modules/react-devtools-shared/src/hydration.lua +++ b/modules/react-devtools-shared/src/hydration.lua @@ -325,12 +325,7 @@ exports.dehydrate = function( return data end -exports.fillInPath = function( - object: Object, - data: DehydratedData, - path: Array, - value: any -): () +exports.fillInPath = function(object: Object, data: DehydratedData, path: Array, value: any): () unimplemented("fillInPath") end diff --git a/modules/react-devtools-shared/src/storage.lua b/modules/react-devtools-shared/src/storage.lua index c6a7c483..74f4a8c8 100644 --- a/modules/react-devtools-shared/src/storage.lua +++ b/modules/react-devtools-shared/src/storage.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react-devtools-shared/src/storage.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-devtools-shared/src/storage.js -- /* -- * Copyright (c) Facebook, Inc. and its affiliates. -- * @@ -6,7 +6,6 @@ -- * LICENSE file in the root directory of this source tree. -- * -- */ - local exports = {} if _G.__LOCALSTORAGE__ == nil then _G.__LOCALSTORAGE__ = {} diff --git a/modules/react-devtools-shared/src/types.lua b/modules/react-devtools-shared/src/types.lua index 37f2e37c..5a71a42f 100644 --- a/modules/react-devtools-shared/src/types.lua +++ b/modules/react-devtools-shared/src/types.lua @@ -80,9 +80,6 @@ export type BooleanComponentFilter = { type: number, } -export type ComponentFilter = - BooleanComponentFilter - | ElementTypeComponentFilter - | RegExpComponentFilter +export type ComponentFilter = BooleanComponentFilter | ElementTypeComponentFilter | RegExpComponentFilter return exports diff --git a/modules/react-devtools-shared/src/utils.lua b/modules/react-devtools-shared/src/utils.lua index fd98b385..fa1004af 100644 --- a/modules/react-devtools-shared/src/utils.lua +++ b/modules/react-devtools-shared/src/utils.lua @@ -45,16 +45,12 @@ local constants = require("./constants") local TREE_OPERATION_ADD = constants.TREE_OPERATION_ADD local TREE_OPERATION_REMOVE = constants.TREE_OPERATION_REMOVE local TREE_OPERATION_REORDER_CHILDREN = constants.TREE_OPERATION_REORDER_CHILDREN -local TREE_OPERATION_UPDATE_TREE_BASE_DURATION = - constants.TREE_OPERATION_UPDATE_TREE_BASE_DURATION +local TREE_OPERATION_UPDATE_TREE_BASE_DURATION = constants.TREE_OPERATION_UPDATE_TREE_BASE_DURATION local types = require("./types") local ElementTypeRoot = types.ElementTypeRoot -local LOCAL_STORAGE_FILTER_PREFERENCES_KEY = - constants.LOCAL_STORAGE_FILTER_PREFERENCES_KEY -local LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS = - constants.LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS -local LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY = - constants.LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY +local LOCAL_STORAGE_FILTER_PREFERENCES_KEY = constants.LOCAL_STORAGE_FILTER_PREFERENCES_KEY +local LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS = constants.LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS +local LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY = constants.LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY local ComponentFilterElementType = types.ComponentFilterElementType local ElementTypeHostComponent = types.ElementTypeHostComponent local ElementTypeClass = types.ElementTypeClass @@ -107,11 +103,7 @@ exports.getDisplayName = function(type_: any, fallbackName: string?): string if typeof(type_) == "table" and typeof(type_.__componentName) == "string" then displayName = type_.__componentName -- ROBLOX deviation END - elseif - typeof(type_) == "table" - and typeof(type_.name) == "string" - and type_.name ~= "" - then + elseif typeof(type_) == "table" and typeof(type_.name) == "string" and type_.name ~= "" then displayName = type_.name -- ROBLOX deviation: use the Lua logic in getComponentName to extract names of function components elseif typeof(type_) == "function" then @@ -142,11 +134,7 @@ exports.printOperationsArray = function(operations: Array) local rendererID = operations[1] :: number local rootID = operations[2] :: number local logs = { - string.format( - "operations for renderer:%s and root:%s", - tostring(rendererID), - tostring(rootID) - ), + string.format("operations for renderer:%s and root:%s", tostring(rendererID), tostring(rootID)), } -- ROBLOX deviation: 1-indexing so start at 3 @@ -204,12 +192,7 @@ exports.printOperationsArray = function(operations: Array) table.insert( logs, - string.format( - "Add node %d (%s) as child of %d", - id, - displayName or "null", - parentID - ) + string.format("Add node %d (%s) as child of %d", id, displayName or "null", parentID) ) end elseif operation == TREE_OPERATION_REMOVE then @@ -229,14 +212,7 @@ exports.printOperationsArray = function(operations: Array) local children = Array.slice(operations, i, i + numChildren) i += numChildren - table.insert( - logs, - string.format( - "Re-order node %d children %s", - id, - Array.join(children, ",") - ) - ) + table.insert(logs, string.format("Re-order node %d children %s", id, Array.join(children, ","))) elseif operation == TREE_OPERATION_UPDATE_TREE_BASE_DURATION then -- Base duration updates are only sent while profiling is in progress. -- We can ignore them at this point. @@ -274,10 +250,7 @@ exports.getSavedComponentFilters = function(): Array return result end exports.saveComponentFilters = function(componentFilters: Array): () - localStorageSetItem( - LOCAL_STORAGE_FILTER_PREFERENCES_KEY, - JSON:JSONEncode(componentFilters) - ) + localStorageSetItem(LOCAL_STORAGE_FILTER_PREFERENCES_KEY, JSON:JSONEncode(componentFilters)) end exports.getAppendComponentStack = function(): boolean local ok, result = pcall(function() @@ -311,10 +284,7 @@ exports.getBreakOnConsoleErrors = function(): boolean end exports.setBreakOnConsoleErrors = function(value: boolean): () - localStorageSetItem( - LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS, - JSON:JSONEncode(value) - ) + localStorageSetItem(LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS, JSON:JSONEncode(value)) end exports.separateDisplayNameAndHOCs = function( displayName: string | nil, @@ -408,16 +378,11 @@ exports.deletePathInObject = function(object: Object?, path: Array, - newPath: Array -) +exports.renamePathInObject = function(object: Object?, oldPath: Array, newPath: Array) local length = #oldPath if object ~= nil then - local parent = - exports.getInObject(object :: Object, Array.slice(oldPath, 1, length)) + local parent = exports.getInObject(object :: Object, Array.slice(oldPath, 1, length)) if parent then local lastOld = oldPath[length] :: number @@ -662,10 +627,7 @@ function exports.formatDataForPreview(data: Object, showFormattedValue: boolean) -- elseif type_ == 'regexp' then -- elseif type_ == 'symbol' then elseif type_ == "react_element" then - return string.format( - "<%s />", - truncateForDisplay(exports.getDisplayNameForReactElement(data) or "Unknown") - ) + return string.format("<%s />", truncateForDisplay(exports.getDisplayNameForReactElement(data) or "Unknown")) -- elseif type_ == 'array_buffer' then -- elseif type_ == 'data_view' then elseif type_ == "array" then @@ -710,11 +672,7 @@ function exports.formatDataForPreview(data: Object, showFormattedValue: boolean) formatted = formatted .. ", " end formatted = formatted - .. string.format( - "%s: %s", - tostring(key), - exports.formatDataForPreview(data[key], false) - ) + .. string.format("%s: %s", tostring(key), exports.formatDataForPreview(data[key], false)) if string.len(formatted) > MAX_PREVIEW_STRING_LENGTH then -- Prevent doing a lot of unnecessary iteration... break diff --git a/modules/react-is/src/__tests__/ReactIs.spec.lua b/modules/react-is/src/__tests__/ReactIs.spec.lua index 6ec03e01..77d065ab 100644 --- a/modules/react-is/src/__tests__/ReactIs.spec.lua +++ b/modules/react-is/src/__tests__/ReactIs.spec.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.2/packages/react-is/src/__tests__/ReactIs-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-is/src/__tests__/ReactIs-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -30,6 +30,7 @@ type React_Component = ReactTypes.React_Component local React local ReactDOM local ReactIs +local SuspenseList describe("ReactIs", function() beforeEach(function() jest.resetModules() @@ -45,6 +46,11 @@ describe("ReactIs", function() ReactIs = require("@pkg/@jsdotlua/react-is") ReactDOM = require("@pkg/@jsdotlua/react-roblox") -- ROBLOX deviation END + if Boolean.toJSBoolean(gate(function(flags) + return flags.enableSuspenseList + end)) then + SuspenseList = React.SuspenseList + end end) it("should return undefined for unknown/invalid types", function() expect(ReactIs.typeOf("abc")).toBe(nil) @@ -55,6 +61,9 @@ describe("ReactIs", function() -- ROBLOX deviation START: no undefined in Lua, we only support nil -- expect(ReactIs.typeOf(nil)).toBe(nil) -- ROBLOX deviation END + expect(ReactIs.typeOf(nil)).toBe(nil) + expect(ReactIs.typeOf(0 / 0)).toBe(nil) + expect(ReactIs.typeOf(Symbol("def"))).toBe(nil) end) it("identifies valid element types", function() type Component = React_Component & {} @@ -67,7 +76,12 @@ describe("ReactIs", function() return React.createElement("TextLabel") -- ROBLOX deviation END end - + type PureComponent = React_Component & {} + type PureComponent_statics = {} + local PureComponent = React.PureComponent:extend("PureComponent") :: PureComponent & PureComponent_statics + function PureComponent.render(self: PureComponent) + return React.createElement("div") + end local function FunctionComponent() -- ROBLOX deviation START: replace div with TextLabel -- return React.createElement("div") @@ -97,6 +111,7 @@ describe("ReactIs", function() local Context = React.createContext(false) expect(ReactIs.isValidElementType("div")).toEqual(true) expect(ReactIs.isValidElementType(Component)).toEqual(true) + expect(ReactIs.isValidElementType(PureComponent)).toEqual(true) expect(ReactIs.isValidElementType(FunctionComponent)).toEqual(true) expect(ReactIs.isValidElementType(ForwardRefComponent)).toEqual(true) expect(ReactIs.isValidElementType(LazyComponent)).toEqual(true) @@ -269,6 +284,14 @@ describe("ReactIs", function() expect(ReactIs.isSuspense({ type = ReactIs.Suspense })).toBe(false) expect(ReactIs.isSuspense("React.Suspense")).toBe(false) expect(ReactIs.isSuspense(React.createElement("div", nil))).toBe(false) + end) -- @gate enableSuspenseList + it("should identify suspense list", function() + expect(ReactIs.isValidElementType(SuspenseList)).toBe(true) + expect(ReactIs.typeOf(React.createElement(SuspenseList, nil))).toBe(ReactIs.SuspenseList) + expect(ReactIs.isSuspenseList(React.createElement(SuspenseList, nil))).toBe(true) + expect(ReactIs.isSuspenseList({ type = ReactIs.SuspenseList })).toBe(false) + expect(ReactIs.isSuspenseList("React.SuspenseList")).toBe(false) + expect(ReactIs.isSuspenseList(React.createElement("div", nil))).toBe(false) end) it("should identify profile root", function() expect(ReactIs.isValidElementType(React.Profiler)).toBe(true) diff --git a/modules/react-is/src/init.lua b/modules/react-is/src/init.lua index 9d02f5ed..59a5193f 100644 --- a/modules/react-is/src/init.lua +++ b/modules/react-is/src/init.lua @@ -103,9 +103,7 @@ local function typeOf(object: any) else -- ROBLOX note: We need to check that __type is a table before we -- index into it, or Luau will throw errors - local __typeofType = __type - and typeof(__type) == "table" - and __type["$$typeof"] + local __typeofType = __type and typeof(__type) == "table" and __type["$$typeof"] if __typeofType == REACT_CONTEXT_TYPE @@ -168,8 +166,7 @@ local function isAsyncMode(object: any) -- ROBLOX deviation END hasWarnedAboutDeprecatedIsAsyncMode = true -- Using console['warn'] to evade Babel and ESLint console["warn"]( - "The ReactIs.isAsyncMode() alias has been deprecated, " - .. "and will be removed in React 18+." + "The ReactIs.isAsyncMode() alias has been deprecated, " .. "and will be removed in React 18+." ) end end @@ -185,8 +182,7 @@ local function isConcurrentMode(object: any) -- ROBLOX deviation END hasWarnedAboutDeprecatedIsConcurrentMode = true -- Using console['warn'] to evade Babel and ESLint console["warn"]( - "The ReactIs.isConcurrentMode() alias has been deprecated, " - .. "and will be removed in React 18+." + "The ReactIs.isConcurrentMode() alias has been deprecated, " .. "and will be removed in React 18+." ) end end @@ -202,9 +198,7 @@ local function isContextProvider(object: any) end exports.isContextProvider = isContextProvider local function isElement(object: any) - return typeof(object) == "table" - and object ~= nil - and object["$$typeof"] == REACT_ELEMENT_TYPE + return typeof(object) == "table" and object ~= nil and object["$$typeof"] == REACT_ELEMENT_TYPE end exports.isElement = isElement local function isForwardRef(object: any) diff --git a/modules/react-noop-renderer/src/ReactNoop.lua b/modules/react-noop-renderer/src/ReactNoop.lua index 212ebe76..a4cac8d4 100644 --- a/modules/react-noop-renderer/src/ReactNoop.lua +++ b/modules/react-noop-renderer/src/ReactNoop.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/faa697f4f9afe9f1c98e315b2a9e70f5a74a7a74/packages/react-noop-renderer/src/ReactNoop.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-noop-renderer/src/ReactNoop.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-noop-renderer/src/createReactNoop.lua b/modules/react-noop-renderer/src/createReactNoop.lua index ad7ead6b..fed6d114 100644 --- a/modules/react-noop-renderer/src/createReactNoop.lua +++ b/modules/react-noop-renderer/src/createReactNoop.lua @@ -113,10 +113,7 @@ local function createReactNoop(reconciler, useMutation: boolean) table.insert(parentInstance.children, child) end - local function appendChildToContainer( - parentInstance: Container, - child: Instance | TextInstance - ): () + local function appendChildToContainer(parentInstance: Container, child: Instance | TextInstance): () if typeof(parentInstance.rootID) ~= "string" then -- Some calls to this aren't typesafe. -- This helps surface mistakes in tests. @@ -125,10 +122,7 @@ local function createReactNoop(reconciler, useMutation: boolean) appendChildToContainerOrInstance(parentInstance, child) end - local function appendChild( - parentInstance: Instance, - child: Instance | TextInstance - ): () + local function appendChild(parentInstance: Instance, child: Instance | TextInstance): () if typeof((parentInstance :: any).rootID) == "string" then -- Some calls to this aren't typesafe. -- This helps surface mistakes in tests. @@ -194,14 +188,8 @@ local function createReactNoop(reconciler, useMutation: boolean) Array.splice(parentInstance.children, index, 1) end - local function removeChildFromContainer( - parentInstance: Container, - child: Instance | TextInstance - ) - if - typeof(parentInstance) == "table" - and typeof(parentInstance.rootID) ~= "string" - then + local function removeChildFromContainer(parentInstance: Container, child: Instance | TextInstance) + if typeof(parentInstance) == "table" and typeof(parentInstance.rootID) ~= "string" then -- Some calls to this aren't typesafe. -- This helps surface mistakes in tests. error(Error("removeChildFromContainer() first argument is not a container.")) @@ -276,11 +264,7 @@ local function createReactNoop(reconciler, useMutation: boolean) return NO_CONTEXT end, - getChildHostContext = function( - parentHostContext: HostContext, - type: string, - rootcontainerInstance: Container - ) + getChildHostContext = function(parentHostContext: HostContext, type: string, rootcontainerInstance: Container) if type == "uppercase" then return UPPERCASE_CONTEXT end @@ -315,8 +299,7 @@ local function createReactNoop(reconciler, useMutation: boolean) -- text: shouldSetTextContent(type, props) -- ? computeText((props.children: any) + '', hostContext) -- : null, - text = shouldSetTextContent(type, props) - and computeText(tostring(props.children), hostContext) + text = shouldSetTextContent(type, props) and computeText(tostring(props.children), hostContext) or nil, context = hostContext, }, @@ -329,20 +312,11 @@ local function createReactNoop(reconciler, useMutation: boolean) table.insert(parentInstance.children, child) end, - finalizeInitialChildren = function( - _domElement: Instance, - _type: string, - _props: Props - ): boolean + finalizeInitialChildren = function(_domElement: Instance, _type: string, _props: Props): boolean return false end, - prepareUpdate = function( - instanceH: Instance, - type: string, - oldProps: Props, - newProps: Props - ): Object? + prepareUpdate = function(instanceH: Instance, type: string, oldProps: Props, newProps: Props): Object? if type == "errorInCompletePhase" then error(Error("Error in host config.")) end @@ -518,16 +492,11 @@ local function createReactNoop(reconciler, useMutation: boolean) instance.hidden = not not newProps.hidden if shouldSetTextContent(type, newProps) then -- deviation: Not sure about this one - instance.text = - computeText(tostring(newProps.children), instance.context) + instance.text = computeText(tostring(newProps.children), instance.context) end end, - commitTextUpdate = function( - textInstance: TextInstance, - oldText: string, - newText: string - ) + commitTextUpdate = function(textInstance: TextInstance, oldText: string, newText: string) hostUpdateCounter += 1 textInstance.text = computeText(newText, textInstance.context) end, @@ -570,9 +539,7 @@ local function createReactNoop(reconciler, useMutation: boolean) cloneInstance = cloneInstance, clearContainer = clearContainer, - createContainerChildSet = function( - container: Container - ): Array + createContainerChildSet = function(container: Container): Array return {} end, @@ -583,24 +550,15 @@ local function createReactNoop(reconciler, useMutation: boolean) table.insert(childSet, child) end, - finalizeContainerChildren = function( - container: Container, - newChildren: Array - ) + finalizeContainerChildren = function(container: Container, newChildren: Array) container.pendingChildren = newChildren - if - #newChildren == 1 - and newChildren[1].text == "Error when completing root" - then + if #newChildren == 1 and newChildren[1].text == "Error when completing root" then -- Trigger an error for testing purposes error(Error("Error when completing root")) end end, - replaceContainerChildren = function( - container: Container, - newChildren: Array - ) + replaceContainerChildren = function(container: Container, newChildren: Array) container.children = newChildren end, @@ -610,25 +568,12 @@ local function createReactNoop(reconciler, useMutation: boolean) props: Props, internalInstanceHandle: Object ) - local clone = cloneInstance( - instance, - nil, - type, - props, - props, - internalInstanceHandle, - true, - nil - ) + local clone = cloneInstance(instance, nil, type, props, props, internalInstanceHandle, true, nil) clone.hidden = true return clone end, - cloneHiddenTextInstance = function( - instance: TextInstance, - text: string, - internalInstanceHandle: Object - ) + cloneHiddenTextInstance = function(instance: TextInstance, text: string, internalInstanceHandle: Object) -- deviation: use metatable to define non-enumerable properties local clone = setmetatable({ text = instance.text, @@ -678,11 +623,9 @@ local function createReactNoop(reconciler, useMutation: boolean) local children = Array.map(child, function(c) return childToJSX(c) end) - if - Array.every(children, function(c) - return typeof(c) == "string" or typeof(c) == "number" - end) - then + if Array.every(children, function(c) + return typeof(c) == "string" or typeof(c) == "number" + end) then return Array.join(children, "") end return children @@ -828,8 +771,7 @@ local function createReactNoop(reconciler, useMutation: boolean) children = {}, } idCounter += 1 - local fiberRoot = - NoopRenderer.createContainer(container, ConcurrentRoot, false, nil) + local fiberRoot = NoopRenderer.createContainer(container, ConcurrentRoot, false, nil) return { _Scheduler = Scheduler, render = function(children) @@ -851,8 +793,7 @@ local function createReactNoop(reconciler, useMutation: boolean) children = {}, } idCounter += 1 - local fiberRoot = - NoopRenderer.createContainer(container, BlockingRoot, false, nil) + local fiberRoot = NoopRenderer.createContainer(container, BlockingRoot, false, nil) return { _Scheduler = Scheduler, render = function(children) @@ -874,8 +815,7 @@ local function createReactNoop(reconciler, useMutation: boolean) children = {}, } idCounter += 1 - local fiberRoot = - NoopRenderer.createContainer(container, LegacyRoot, false, nil) + local fiberRoot = NoopRenderer.createContainer(container, LegacyRoot, false, nil) return { _Scheduler = Scheduler, render = function(children) @@ -1040,10 +980,7 @@ local function createReactNoop(reconciler, useMutation: boolean) -- ROBLOX FIXME: This likely needs to be adopted to Roblox -- Instance structure as opposed to HTML DOM nodes - local function logHostInstances( - children: Array, - depth: number - ) + local function logHostInstances(children: Array, depth: number) -- ROBLOX deviation: May not be able to assume children is an array in -- Roblox (we use keys as names), so iterate with `pairs` @@ -1055,13 +992,7 @@ local function createReactNoop(reconciler, useMutation: boolean) log(indent .. "- " .. (child :: TextInstance).text) else -- $FlowFixMe - The child should've been refined now. - log( - indent - .. "- " - .. (child :: Instance).type - .. "#" - .. tostring(child.id) - ) + log(indent .. "- " .. (child :: Instance).type .. "#" .. tostring(child.id)) -- $FlowFixMe - The child should've been refined now. logHostInstances((child :: Instance).children, depth + 1) end @@ -1082,7 +1013,9 @@ local function createReactNoop(reconciler, useMutation: boolean) log( string.rep(" ", depth + 1) .. "~", -- ROBLOX TODO: this is a bogus field, even in upstream - "[" .. tostring((update :: any).expirationTime) .. "]" + "[" + .. tostring((update :: any).expirationTime) + .. "]" ) until update == nil end @@ -1096,7 +1029,9 @@ local function createReactNoop(reconciler, useMutation: boolean) log( string.rep(" ", depth + 1) .. "~", -- ROBLOX TODO: this is a bogus field, even in upstream - "[" .. tostring((update :: any).expirationTime) .. "]" + "[" + .. tostring((update :: any).expirationTime) + .. "]" ) until pendingUpdate == nil or pendingUpdate == firstPending end @@ -1108,9 +1043,7 @@ local function createReactNoop(reconciler, useMutation: boolean) string.rep(" ", depth) .. "- " -- need to explicitly coerce Symbol to a string - .. if fiber.type - then (fiber.type.name or tostring(fiber.type)) - else "[root]", + .. if fiber.type then (fiber.type.name or tostring(fiber.type)) else "[root]", "[" -- ROBLOX TODO: this field is bogus even in upstream, will always be nil .. tostring((fiber :: any).childExpirationTime) @@ -1170,17 +1103,10 @@ local function createReactNoop(reconciler, useMutation: boolean) local function noopAct(scope: (() -> Thenable) | () -> ()) if Scheduler.unstable_flushAllWithoutAsserting == nil then - error( - Error("This version of `act` requires a special mock build of Scheduler.") - ) + error(Error("This version of `act` requires a special mock build of Scheduler.")) end if typeof(setTimeout) == "table" and setTimeout._isMockFunction ~= true then - error( - Error( - "This version of `act` requires Jest's timer mocks " - .. "(i.e. jest.useFakeTimers)." - ) - ) + error(Error("This version of `act` requires Jest's timer mocks " .. "(i.e. jest.useFakeTimers).")) end local previousActingUpdatesScopeDepth = actingUpdatesScopeDepth diff --git a/modules/react-reconciler/src/DebugTracing.lua b/modules/react-reconciler/src/DebugTracing.lua index 59f6489c..9e09517d 100644 --- a/modules/react-reconciler/src/DebugTracing.lua +++ b/modules/react-reconciler/src/DebugTracing.lua @@ -25,8 +25,7 @@ type Lane = ReactFiberLaneModule.Lane type Lanes = ReactFiberLaneModule.Lanes type Wakeable = Shared.Wakeable -local enableDebugTracing = - require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableDebugTracing +local enableDebugTracing = require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableDebugTracing local nativeConsole: Object = console local nativeConsoleLog: nil | Function = nil @@ -263,11 +262,7 @@ local function logForceUpdateScheduled(componentName: string, lane: Lane): () end exports.logForceUpdateScheduled = logForceUpdateScheduled -local function logStateUpdateScheduled( - componentName: string, - lane: Lane, - payloadOrAction: any -): () +local function logStateUpdateScheduled(componentName: string, lane: Lane, payloadOrAction: any): () if _G.__DEV__ then if enableDebugTracing then log( diff --git a/modules/react-reconciler/src/MaxInts.lua b/modules/react-reconciler/src/MaxInts.lua index 7e12f309..72494b7e 100644 --- a/modules/react-reconciler/src/MaxInts.lua +++ b/modules/react-reconciler/src/MaxInts.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/MaxInts.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/MaxInts.js -- /** -- * Copyright (c) Facebook, Inc. and its affiliates. -- * diff --git a/modules/react-reconciler/src/ReactCapturedValue.lua b/modules/react-reconciler/src/ReactCapturedValue.lua index dc01805e..d16761b0 100644 --- a/modules/react-reconciler/src/ReactCapturedValue.lua +++ b/modules/react-reconciler/src/ReactCapturedValue.lua @@ -12,8 +12,7 @@ local ReactInternalTypes = require("./ReactInternalTypes") type Fiber = ReactInternalTypes.Fiber -local getStackByFiberInDevAndProd = - require("./ReactFiberComponentStack").getStackByFiberInDevAndProd +local getStackByFiberInDevAndProd = require("./ReactFiberComponentStack").getStackByFiberInDevAndProd export type CapturedValue = { value: T, diff --git a/modules/react-reconciler/src/ReactChildFiber.new.lua b/modules/react-reconciler/src/ReactChildFiber.new.lua index 97411dba..ec5cd8a1 100644 --- a/modules/react-reconciler/src/ReactChildFiber.new.lua +++ b/modules/react-reconciler/src/ReactChildFiber.new.lua @@ -144,11 +144,7 @@ function coerceRef(returnFiber: Fiber, current: Fiber | nil, element: ReactEleme local mixedRef = element.ref if mixedRef ~= nil and type(mixedRef) == "string" then -- ROBLOX deviation: we do not support string refs, and will not coerce - if - not element._owner - or not element._self - or element._owner.stateNode == element._self - then + if not element._owner or not element._self or element._owner.stateNode == element._self then -- ROBLOX performance: don't get component name unless we have to use it local componentName if __DEV__ then @@ -173,9 +169,7 @@ function coerceRef(returnFiber: Fiber, current: Fiber | nil, element: ReactEleme end if not element._owner then - error( - "Expected ref to be a function or an object returned by React.createRef(), or nil." - ) + error("Expected ref to be a function or an object returned by React.createRef(), or nil.") end -- if __DEV__ then @@ -365,10 +359,7 @@ local function ChildReconciler(shouldTrackSideEffects) end end - local function deleteRemainingChildren( - returnFiber: Fiber, - currentFirstChild: Fiber | nil - ) + local function deleteRemainingChildren(returnFiber: Fiber, currentFirstChild: Fiber | nil) if not shouldTrackSideEffects then -- Noop. return nil @@ -384,10 +375,7 @@ local function ChildReconciler(shouldTrackSideEffects) return nil end - local function mapRemainingChildren( - returnFiber: Fiber, - currentFirstChild: Fiber - ): { [string | number]: Fiber } + local function mapRemainingChildren(returnFiber: Fiber, currentFirstChild: Fiber): { [string | number]: Fiber } -- Add the remaining children to a temporary map so that we can find them by -- keys quickly. Implicit (null) keys get added to this set with their index -- instead. @@ -416,11 +404,7 @@ local function ChildReconciler(shouldTrackSideEffects) return clone end - local function placeChild( - newFiber: Fiber, - lastPlacedIndex: number, - newIndex: number - ): number + local function placeChild(newFiber: Fiber, lastPlacedIndex: number, newIndex: number): number newFiber.index = newIndex if not shouldTrackSideEffects then -- Noop. @@ -453,12 +437,7 @@ local function ChildReconciler(shouldTrackSideEffects) return newFiber end - local function updateTextNode( - returnFiber: Fiber, - current: Fiber | nil, - textContent: string, - lanes: Lanes - ) + local function updateTextNode(returnFiber: Fiber, current: Fiber | nil, textContent: string, lanes: Lanes) -- ROBLOX FIXME: Luau narrowing issue if current == nil or (current :: Fiber).tag ~= HostText then -- Insert @@ -473,12 +452,7 @@ local function ChildReconciler(shouldTrackSideEffects) end end - local function updateElement( - returnFiber: Fiber, - current: Fiber | nil, - element: ReactElement, - lanes: Lanes - ): Fiber + local function updateElement(returnFiber: Fiber, current: Fiber | nil, element: ReactElement, lanes: Lanes): Fiber if current ~= nil then if (current :: Fiber).elementType == element.type @@ -503,10 +477,7 @@ local function ChildReconciler(shouldTrackSideEffects) if type(type_) == "table" and type_["$$typeof"] == REACT_LAZY_TYPE then type_ = resolveLazyType(type_) :: LazyComponent end - if - type_["$$typeof"] == REACT_BLOCK_TYPE - and type_._render == (current :: Fiber).type._render - then + if type_["$$typeof"] == REACT_BLOCK_TYPE and type_._render == (current :: Fiber).type._render then -- Same as above but also update the .type field. local existing = useFiber(current :: Fiber, element.props) existing.return_ = returnFiber @@ -526,12 +497,7 @@ local function ChildReconciler(shouldTrackSideEffects) return created end - local function updatePortal( - returnFiber: Fiber, - current: Fiber | nil, - portal: ReactPortal, - lanes: Lanes - ): Fiber + local function updatePortal(returnFiber: Fiber, current: Fiber | nil, portal: ReactPortal, lanes: Lanes): Fiber -- ROBLOX FIXME: type narrowing. if current == nil @@ -562,8 +528,7 @@ local function ChildReconciler(shouldTrackSideEffects) ): Fiber if current == nil or (current :: Fiber).tag ~= Fragment then -- Insert - local created = - createFiberFromFragment(fragment, returnFiber.mode, lanes, key) + local created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key) created.return_ = returnFiber return created else @@ -640,8 +605,7 @@ local function ChildReconciler(shouldTrackSideEffects) -- ROBLOX deviation peformance: this is the equiv of checking for a table, and we already know typeof(newChild) is a table in this branch -- if isArray(newChild) or getIteratorFn(newChild) then - local created = - createFiberFromFragment(newChild, returnFiber.mode, lanes, nil) + local created = createFiberFromFragment(newChild, returnFiber.mode, lanes, nil) created.return_ = returnFiber return created @@ -653,8 +617,7 @@ local function ChildReconciler(shouldTrackSideEffects) -- Text nodes don't have keys. If the previous node is implicitly keyed -- we can continue to replace it without aborting even if it is not a text -- node. - local created = - createFiberFromText(tostring(newChild), returnFiber.mode, lanes) + local created = createFiberFromText(tostring(newChild), returnFiber.mode, lanes) created.return_ = returnFiber return created end @@ -696,13 +659,7 @@ local function ChildReconciler(shouldTrackSideEffects) if newChildTypeof == REACT_ELEMENT_TYPE then if newChild.key == key then if newChild.type == REACT_FRAGMENT_TYPE then - return updateFragment( - returnFiber, - oldFiber, - newChild.props.children, - lanes, - key :: string? - ) + return updateFragment(returnFiber, oldFiber, newChild.props.children, lanes, key :: string?) end return updateElement(returnFiber, oldFiber, newChild, lanes) else @@ -788,13 +745,7 @@ local function ChildReconciler(shouldTrackSideEffects) end local matchedFiber = existingChildren[existingChildrenKey] if newChild.type == REACT_FRAGMENT_TYPE then - return updateFragment( - returnFiber, - matchedFiber, - newChild.props.children, - lanes, - newChild.key - ) + return updateFragment(returnFiber, matchedFiber, newChild.props.children, lanes, newChild.key) end return updateElement(returnFiber, matchedFiber, newChild, lanes) elseif newChildTypeof == REACT_PORTAL_TYPE then @@ -811,13 +762,7 @@ local function ChildReconciler(shouldTrackSideEffects) local init = newChild._init -- ROBLOX deviation: Roact stable keys - Since the table key was -- already applied to `newChild` above, we don't need to pass it along - return updateFromMap( - existingChildren, - returnFiber, - newIdx, - init(payload), - lanes - ) + return updateFromMap(existingChildren, returnFiber, newIdx, init(payload), lanes) end end @@ -850,11 +795,7 @@ local function ChildReconciler(shouldTrackSideEffects) --[[ Warns if there is a duplicate or missing key ]] - local function warnOnInvalidKey( - child: any, - knownKeys: Set | nil, - returnFiber: Fiber - ): Set | nil + local function warnOnInvalidKey(child: any, knownKeys: Set | nil, returnFiber: Fiber): Set | nil if __DEV__ then if child == nil or type(child) ~= "table" then return knownKeys @@ -951,13 +892,8 @@ local function ChildReconciler(shouldTrackSideEffects) local newFiber -- ROBLOX performance: avoid repeated indexing of newChildren to newIdx local newChildNewIdx = newChildren[newIdx] - if - newChildNewIdx ~= nil - and type(newChildNewIdx) == "table" - and newChildNewIdx["$$typeof"] ~= nil - then - newFiber = - updateSlot(returnFiber, oldFiber, newChildNewIdx, lanes, newIdx) + if newChildNewIdx ~= nil and type(newChildNewIdx) == "table" and newChildNewIdx["$$typeof"] ~= nil then + newFiber = updateSlot(returnFiber, oldFiber, newChildNewIdx, lanes, newIdx) else newFiber = updateSlot(returnFiber, oldFiber, newChildNewIdx, lanes) end @@ -1017,11 +953,7 @@ local function ChildReconciler(shouldTrackSideEffects) local newFiber -- ROBLOX performance: avoid repeated indexing of newChildren to newIdx local newChildNewIdx = newChildren[newIdx] - if - newChildNewIdx ~= nil - and type(newChildNewIdx) == "table" - and newChildNewIdx["$$typeof"] ~= nil - then + if newChildNewIdx ~= nil and type(newChildNewIdx) == "table" and newChildNewIdx["$$typeof"] ~= nil then newFiber = createChild(returnFiber, newChildNewIdx, lanes, newIdx) else newFiber = createChild(returnFiber, newChildNewIdx, lanes) @@ -1069,10 +1001,7 @@ local function ChildReconciler(shouldTrackSideEffects) -- current, that means that we reused the fiber. We need to delete -- it from the child list so that we don't add it to the deletion -- list. - existingChildren[if newFiber.key == nil - then newIdx - else newFiber.key] = - nil + existingChildren[if newFiber.key == nil then newIdx else newFiber.key] = nil end end lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx) @@ -1145,8 +1074,7 @@ local function ChildReconciler(shouldTrackSideEffects) if newChildrenIterable.entries == iteratorFn then if not didWarnAboutMaps then console.error( - "Using Maps as children is not supported. " - .. "Use an array of keyed ReactElements instead." + "Using Maps as children is not supported. " .. "Use an array of keyed ReactElements instead." ) end didWarnAboutMaps = true @@ -1186,8 +1114,7 @@ local function ChildReconciler(shouldTrackSideEffects) else nextOldFiber = oldFiber.sibling end - local newFiber = - updateSlot(returnFiber, oldFiber, step.value, lanes, step.key) + local newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes, step.key) if newFiber == nil then -- TODO: This breaks on empty slots like nil children. That's -- unfortunate because it triggers the slow path all the time. We need @@ -1266,14 +1193,7 @@ local function ChildReconciler(shouldTrackSideEffects) -- ROBLOX FIXME LUau: need type states to understand the guard+return above existingChildren = mapRemainingChildren(returnFiber, oldFiber :: Fiber) end - local newFiber = updateFromMap( - existingChildren, - returnFiber, - newIdx, - step.value, - lanes, - step.key - ) + local newFiber = updateFromMap(existingChildren, returnFiber, newIdx, step.value, lanes, step.key) if newFiber ~= nil then if shouldTrackSideEffects then if newFiber.alternate ~= nil then @@ -1505,63 +1425,34 @@ local function ChildReconciler(shouldTrackSideEffects) -- Handle object types -- ROBLOX deviation: upstream checks for `object`, but we need to manually exclude array - local isObject = newChild ~= nil - and typeOfNewChild == "table" - and not newChildIsArray + local isObject = newChild ~= nil and typeOfNewChild == "table" and not newChildIsArray if isObject then -- ROBLOX performance: avoid repeated indexing of $$typeof local newChildTypeof = newChild["$$typeof"] if newChildTypeof == REACT_ELEMENT_TYPE then - return placeSingleChild( - reconcileSingleElement( - returnFiber, - currentFirstChild, - newChild, - lanes - ) - ) + return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes)) elseif newChildTypeof == REACT_PORTAL_TYPE then - return placeSingleChild( - reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes) - ) + return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes)) elseif newChildTypeof == REACT_LAZY_TYPE then if enableLazyElements then local payload = newChild._payload local init = newChild._init -- TODO: This function is supposed to be non-recursive. - return reconcileChildFibers( - returnFiber, - currentFirstChild, - init(payload), - lanes - ) + return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes) end end -- ROBLOX performance: make these next blocks `elseif`, as they're mutually exclusive to `isObject` above elseif newChildIsArray then return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes) elseif typeOfNewChild == "string" or typeOfNewChild == "number" then - return placeSingleChild( - reconcileSingleTextNode( - returnFiber, - currentFirstChild, - tostring(newChild), - lanes - ) - ) + return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, tostring(newChild), lanes)) end -- ROBLOX performance? only call getIteratorFn once, pass in the value local newChildIteratorFn = getIteratorFn(newChild) if newChildIteratorFn then - return reconcileChildrenIterator( - returnFiber, - currentFirstChild, - newChild, - lanes, - newChildIteratorFn - ) + return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, lanes, newChildIteratorFn) end -- ROBLOX performance? eliminate a cmp in hot path for something unimplemented anyway diff --git a/modules/react-reconciler/src/ReactCurrentFiber.lua b/modules/react-reconciler/src/ReactCurrentFiber.lua index 5eac5c95..a7ee2551 100644 --- a/modules/react-reconciler/src/ReactCurrentFiber.lua +++ b/modules/react-reconciler/src/ReactCurrentFiber.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/b0cb137bcbd3a11d8eff3c2229cd6b8379d29785/packages/react-reconciler/src/ReactCurrentFiber.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactCurrentFiber.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiber.new.lua b/modules/react-reconciler/src/ReactFiber.new.lua index e781b93f..72a82f28 100644 --- a/modules/react-reconciler/src/ReactFiber.new.lua +++ b/modules/react-reconciler/src/ReactFiber.new.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactFiber.new.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiber.new.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -320,7 +320,6 @@ local function createWorkInProgress(current: Fiber, pendingProps: any): Fiber if __DEV__ then -- DEV-only fields - workInProgress._debugID = current._debugID workInProgress._debugSource = current._debugSource workInProgress._debugOwner = current._debugOwner workInProgress._debugHookTypes = current._debugHookTypes @@ -447,7 +446,7 @@ local function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) workInProgress.lanes = current.lanes workInProgress.child = current.child - workInProgress.subtreeFlags = current.subtreeFlags + workInProgress.subtreeFlags = NoFlags workInProgress.deletions = nil workInProgress.memoizedProps = current.memoizedProps workInProgress.memoizedState = current.memoizedState @@ -744,7 +743,10 @@ function createFiberFromProfiler( ): Fiber if __DEV__ then if typeof(pendingProps.id) ~= "string" then - console.error('Profiler must specify an "id" as a prop') + console.error( + 'Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.', + typeof(pendingProps.id) + ) end end @@ -988,7 +990,6 @@ local function assignFiberPropertiesInDEV(target: Fiber, source: Fiber): Fiber target.selfBaseDuration = source.selfBaseDuration target.treeBaseDuration = source.treeBaseDuration end - target._debugID = source._debugID target._debugSource = source._debugSource target._debugOwner = source._debugOwner target._debugNeedsRemount = source._debugNeedsRemount diff --git a/modules/react-reconciler/src/ReactFiberBeginWork.new.lua b/modules/react-reconciler/src/ReactFiberBeginWork.new.lua index 8e01fa23..5541bc39 100644 --- a/modules/react-reconciler/src/ReactFiberBeginWork.new.lua +++ b/modules/react-reconciler/src/ReactFiberBeginWork.new.lua @@ -19,8 +19,7 @@ local function unimplemented(message: string) end local __DEV__ = _G.__DEV__ :: boolean -local __DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ = - _G.__DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ :: boolean +local __DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ = _G.__DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ :: boolean local __COMPAT_WARNINGS__ = _G.__COMPAT_WARNINGS__ :: boolean -- ROBLOX: use patched console from Shared @@ -98,16 +97,14 @@ local Deletion = ReactFiberFlags.Deletion local ForceUpdateForLegacySuspense = ReactFiberFlags.ForceUpdateForLegacySuspense local ReactSharedInternals = require("@pkg/@jsdotlua/shared").ReactSharedInternals local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags -local debugRenderPhaseSideEffectsForStrictMode = - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode +local debugRenderPhaseSideEffectsForStrictMode = ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode local disableLegacyContext = ReactFeatureFlags.disableLegacyContext local disableModulePatternComponents = ReactFeatureFlags.disableModulePatternComponents local enableProfilerTimer = ReactFeatureFlags.enableProfilerTimer local enableSchedulerTracing = ReactFeatureFlags.enableSchedulerTracing local enableSuspenseServerRenderer = ReactFeatureFlags.enableSuspenseServerRenderer -- local enableFundamentalAPI = ReactFeatureFlags.enableFundamentalAPI -local warnAboutDefaultPropsOnFunctionComponents = - ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents +local warnAboutDefaultPropsOnFunctionComponents = ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents -- local enableScopeAPI = ReactFeatureFlags.enableScopeAPI local invariant = require("@pkg/@jsdotlua/shared").invariant local describeError = require("@pkg/@jsdotlua/shared").describeError @@ -118,16 +115,12 @@ local REACT_LAZY_TYPE = ReactSymbols.REACT_LAZY_TYPE local _getIteratorFn = ReactSymbols.getIteratorFn local ReactStrictModeWarnings = require("./ReactStrictModeWarnings.new.lua") local ReactCurrentFiber = require("./ReactCurrentFiber") -local getCurrentFiberOwnerNameInDevOrNull = - ReactCurrentFiber.getCurrentFiberOwnerNameInDevOrNull +local getCurrentFiberOwnerNameInDevOrNull = ReactCurrentFiber.getCurrentFiberOwnerNameInDevOrNull local setIsRendering = ReactCurrentFiber.setIsRendering local ReactFiberHotReloadingModule = require("./ReactFiberHotReloading.new.lua") -local resolveFunctionForHotReloading = - ReactFiberHotReloadingModule.resolveFunctionForHotReloading -local resolveForwardRefForHotReloading = - ReactFiberHotReloadingModule.resolveForwardRefForHotReloading -local resolveClassForHotReloading = - ReactFiberHotReloadingModule.resolveClassForHotReloading +local resolveFunctionForHotReloading = ReactFiberHotReloadingModule.resolveFunctionForHotReloading +local resolveForwardRefForHotReloading = ReactFiberHotReloadingModule.resolveForwardRefForHotReloading +local resolveClassForHotReloading = ReactFiberHotReloadingModule.resolveClassForHotReloading local ReactChildFiber = require("./ReactChildFiber.new.lua") :: any local mountChildFibers = ReactChildFiber.mountChildFibers @@ -158,12 +151,10 @@ local suspenseStackCursor = ReactFiberSuspenseContext.suspenseStackCursor local hasSuspenseContext = ReactFiberSuspenseContext.hasSuspenseContext local ForceSuspenseFallback = ReactFiberSuspenseContext.ForceSuspenseFallback local addSubtreeSuspenseContext = ReactFiberSuspenseContext.addSubtreeSuspenseContext -local InvisibleParentSuspenseContext = - ReactFiberSuspenseContext.InvisibleParentSuspenseContext +local InvisibleParentSuspenseContext = ReactFiberSuspenseContext.InvisibleParentSuspenseContext local pushSuspenseContext = ReactFiberSuspenseContext.pushSuspenseContext -- local setShallowSuspenseContext = ReactFiberSuspenseContext.setShallowSuspenseContext -local setDefaultShallowSuspenseContext = - ReactFiberSuspenseContext.setDefaultShallowSuspenseContext +local setDefaultShallowSuspenseContext = ReactFiberSuspenseContext.setDefaultShallowSuspenseContext -- local {findFirstSuspended} = require("./ReactFiberSuspenseComponent/new") -- local { -- , @@ -212,8 +203,7 @@ local function bailoutHooks(...) return lazyRefs.bailoutHooksRef(...) end -local stopProfilerTimerIfRunning = - require("./ReactProfilerTimer.new.lua").stopProfilerTimerIfRunning +local stopProfilerTimerIfRunning = require("./ReactProfilerTimer.new.lua").stopProfilerTimerIfRunning local ReactFiberContext = require("./ReactFiberContext.new.lua") local getMaskedContext = ReactFiberContext.getMaskedContext local getUnmaskedContext = ReactFiberContext.getUnmaskedContext @@ -228,8 +218,7 @@ local resetHydrationState = ReactFiberHydrationContext.resetHydrationState local enterHydrationState = ReactFiberHydrationContext.enterHydrationState local reenterHydrationStateFromDehydratedSuspenseInstance = ReactFiberHydrationContext.reenterHydrationStateFromDehydratedSuspenseInstance -local tryToClaimNextHydratableInstance = - ReactFiberHydrationContext.tryToClaimNextHydratableInstance +local tryToClaimNextHydratableInstance = ReactFiberHydrationContext.tryToClaimNextHydratableInstance local warnIfHydrating = ReactFiberHydrationContext.warnIfHydrating local ReactFiberClassComponent = require("./ReactFiberClassComponent.new.lua") :: any local adoptClassInstance = ReactFiberClassComponent.adoptClassInstance @@ -239,8 +228,7 @@ local mountClassInstance = ReactFiberClassComponent.mountClassInstance local resumeMountClassInstance = ReactFiberClassComponent.resumeMountClassInstance local updateClassInstance = ReactFiberClassComponent.updateClassInstance -local resolveDefaultProps = - require("./ReactFiberLazyComponent.new.lua").resolveDefaultProps +local resolveDefaultProps = require("./ReactFiberLazyComponent.new.lua").resolveDefaultProps local ReactFiber = require("./ReactFiber.new.lua") local resolveLazyComponentTag = ReactFiber.resolveLazyComponentTag local createFiberFromFragment = ReactFiber.createFiberFromFragment @@ -261,10 +249,8 @@ local RetryAfterError = ReactFiberWorkLoop.RetryAfterError local NoContext = ReactFiberWorkLoop.NoContext local Schedule_tracing_wrap -local setWorkInProgressVersion = - require("./ReactMutableSource.new.lua").setWorkInProgressVersion -local markSkippedUpdateLanes = - require("./ReactFiberWorkInProgress").markSkippedUpdateLanes +local setWorkInProgressVersion = require("./ReactMutableSource.new.lua").setWorkInProgressVersion +local markSkippedUpdateLanes = require("./ReactFiberWorkInProgress").markSkippedUpdateLanes local ConsolePatchingDev = require("@pkg/@jsdotlua/shared").ConsolePatchingDev local disableLogs = ConsolePatchingDev.disableLogs local reenableLogs = ConsolePatchingDev.reenableLogs @@ -304,19 +290,13 @@ if __DEV__ then DidWarn.didWarnAboutDefaultPropsOnFunctionComponent = {} end -local function reconcileChildren( - current: Fiber | nil, - workInProgress: Fiber, - nextChildren: any, - renderLanes: Lanes -) +local function reconcileChildren(current: Fiber | nil, workInProgress: Fiber, nextChildren: any, renderLanes: Lanes) if current == nil then -- If this is a fresh new component that hasn't been rendered yet, we -- won't update its child set by applying minimal side-effects. Instead, -- we will add them all to the child before it gets rendered. That means -- we can optimize this reconciliation pass by not tracking side-effects. - workInProgress.child = - mountChildFibers(workInProgress, nil, nextChildren, renderLanes) + workInProgress.child = mountChildFibers(workInProgress, nil, nextChildren, renderLanes) else -- If the current child is the same as the work in progress, it means that -- we haven't yet started any work on these children. Therefore, we use @@ -324,12 +304,7 @@ local function reconcileChildren( -- If we had any progressed work already, that is invalid at this point so -- let's throw it out. - workInProgress.child = reconcileChildFibers( - workInProgress, - (current :: Fiber).child, - nextChildren, - renderLanes - ) + workInProgress.child = reconcileChildFibers(workInProgress, (current :: Fiber).child, nextChildren, renderLanes) end end @@ -347,14 +322,12 @@ local function forceUnmountCurrentAndReconcile( -- To do this, we're going to go through the reconcile algorithm twice. In -- the first pass, we schedule a deletion for all the current children by -- passing nil. - workInProgress.child = - reconcileChildFibers(workInProgress, current.child, nil, renderLanes) + workInProgress.child = reconcileChildFibers(workInProgress, current.child, nil, renderLanes) -- In the second pass, we mount the new children. The trick here is that we -- pass nil in place of where we usually pass the current child set. This has -- the effect of remounting all children regardless of whether their -- identities match. - workInProgress.child = - reconcileChildFibers(workInProgress, nil, nextChildren, renderLanes) + workInProgress.child = reconcileChildFibers(workInProgress, nil, nextChildren, renderLanes) end local function updateForwardRef( @@ -392,31 +365,15 @@ local function updateForwardRef( -- The rest is a fork of updateFunctionComponent local nextChildren - prepareToReadContext( - workInProgress, - renderLanes, - exports.markWorkInProgressReceivedUpdate - ) + prepareToReadContext(workInProgress, renderLanes, exports.markWorkInProgressReceivedUpdate) if __DEV__ then ReactCurrentOwner.current = workInProgress setIsRendering(true) - nextChildren = - renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes) - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes) + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() - local ok, result = xpcall( - renderWithHooks, - describeError, - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ) + local ok, result = + xpcall(renderWithHooks, describeError, current, workInProgress, render, nextProps, ref, renderLanes) if ok then nextChildren = result end @@ -429,8 +386,7 @@ local function updateForwardRef( end setIsRendering(false) else - nextChildren = - renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes) + nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes) end if current ~= nil and not didReceiveUpdate then @@ -472,14 +428,7 @@ local function updateMemoComponent( if __DEV__ then validateFunctionComponentInDev(workInProgress, type_) end - return updateSimpleMemoComponent( - current, - workInProgress, - resolvedType, - nextProps, - updateLanes, - renderLanes - ) + return updateSimpleMemoComponent(current, workInProgress, resolvedType, nextProps, updateLanes, renderLanes) end if __DEV__ or __DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ then -- ROBLOX deviation: adds support for legacy Roact's validateProps() @@ -658,20 +607,10 @@ function updateSimpleMemoComponent( end end end - return updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes - ) + return updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes) end -local function updateOffscreenComponent( - current: Fiber?, - workInProgress: Fiber, - renderLanes: Lanes -): Fiber | nil +local function updateOffscreenComponent(current: Fiber?, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil local nextProps: OffscreenProps = workInProgress.pendingProps local nextChildren = nextProps.children @@ -681,10 +620,7 @@ local function updateOffscreenComponent( prevState = (current :: Fiber).memoizedState end - if - nextProps.mode == "hidden" - or nextProps.mode == "unstable-defer-without-hiding" - then + if nextProps.mode == "hidden" or nextProps.mode == "unstable-defer-without-hiding" then if bit32.band(workInProgress.mode, ConcurrentMode) == NoMode then -- In legacy sync mode, don't defer the subtree. Render it now. -- TODO: Figure out what we should do in Blocking mode. @@ -694,9 +630,7 @@ local function updateOffscreenComponent( workInProgress.memoizedState = nextState pushRenderLanes(workInProgress, renderLanes) -- ROBLOX TODO: recast ReactFiberLane.OffscreenLane to type Lane - elseif - not ReactFiberLane.includesSomeLane(renderLanes, ReactFiberLane.OffscreenLane) - then + elseif not ReactFiberLane.includesSomeLane(renderLanes, ReactFiberLane.OffscreenLane) then local nextBaseLanes if prevState ~= nil then -- ROBLOX FIXME: remove :: recast once Luau understands if-statement nil check @@ -712,8 +646,7 @@ local function updateOffscreenComponent( end -- deviation: unchain multiple assignment into two discrete assignments. - workInProgress.childLanes = - ReactFiberLane.laneToLanes(ReactFiberLane.OffscreenLane) + workInProgress.childLanes = ReactFiberLane.laneToLanes(ReactFiberLane.OffscreenLane) workInProgress.lanes = workInProgress.childLanes local nextState: OffscreenState = { @@ -745,10 +678,7 @@ local function updateOffscreenComponent( local subtreeRenderLanes if prevState ~= nil then -- ROBLOX FIXME: remove :: recast once Luau understands if-statement nil check - subtreeRenderLanes = ReactFiberLane.mergeLanes( - (prevState :: OffscreenState).baseLanes, - renderLanes - ) + subtreeRenderLanes = ReactFiberLane.mergeLanes((prevState :: OffscreenState).baseLanes, renderLanes) -- Since we're not hidden anymore, reset the state workInProgress.memoizedState = nil else @@ -797,28 +727,16 @@ end local function markRef(current: Fiber | nil, workInProgress: Fiber) local ref = workInProgress.ref - if - (current == nil and ref ~= nil) - or (current ~= nil and (current :: Fiber).ref ~= ref) - then + if (current == nil and ref ~= nil) or (current ~= nil and (current :: Fiber).ref ~= ref) then -- Schedule a Ref effect workInProgress.flags = bit32.bor(workInProgress.flags, Ref) end end -function updateFunctionComponent( - current, - workInProgress, - Component, - nextProps: any, - renderLanes -) +function updateFunctionComponent(current, workInProgress, Component, nextProps: any, renderLanes) if __DEV__ or __DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ then -- ROBLOX deviation: function components can't have props in Lua - if - type(Component) ~= "function" - and (workInProgress.type ~= workInProgress.elementType) - then + if type(Component) ~= "function" and (workInProgress.type ~= workInProgress.elementType) then -- Lazy component props can't be validated in createElement -- because they're only guaranteed to be resolved here. -- ROBLOX deviation: adds support for legacy Roact's validateProps() @@ -849,26 +767,12 @@ function updateFunctionComponent( end local nextChildren - prepareToReadContext( - workInProgress, - renderLanes, - exports.markWorkInProgressReceivedUpdate - ) + prepareToReadContext(workInProgress, renderLanes, exports.markWorkInProgressReceivedUpdate) if __DEV__ then ReactCurrentOwner.current = workInProgress setIsRendering(true) - nextChildren = renderWithHooks( - current, - workInProgress, - Component, - nextProps, - context, - renderLanes - ) - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes) + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() local ok, result = xpcall( renderWithHooks, @@ -890,14 +794,7 @@ function updateFunctionComponent( end setIsRendering(false) else - nextChildren = renderWithHooks( - current, - workInProgress, - Component, - nextProps, - context, - renderLanes - ) + nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes) end if current ~= nil and not didReceiveUpdate then @@ -1022,11 +919,7 @@ local function updateClassComponent( hasContext = false end -- ROBLOX deviation: pass in function to break cyclic require dependency - prepareToReadContext( - workInProgress, - renderLanes, - exports.markWorkInProgressReceivedUpdate - ) + prepareToReadContext(workInProgress, renderLanes, exports.markWorkInProgressReceivedUpdate) local instance = workInProgress.stateNode local shouldUpdate @@ -1047,25 +940,12 @@ local function updateClassComponent( shouldUpdate = true elseif current == nil then -- In a resume, we'll already have an instance we can reuse. - shouldUpdate = - resumeMountClassInstance(workInProgress, Component, nextProps, renderLanes) + shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderLanes) else - shouldUpdate = updateClassInstance( - current, - workInProgress, - Component, - nextProps, - renderLanes - ) + shouldUpdate = updateClassInstance(current, workInProgress, Component, nextProps, renderLanes) end - local nextUnitOfWork = finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes - ) + local nextUnitOfWork = + finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes) if __DEV__ then local inst = workInProgress.stateNode if shouldUpdate and inst.props ~= nextProps then @@ -1111,10 +991,7 @@ function finishClassComponent( local nextChildren if didCaptureError - and ( - Component.getDerivedStateFromError == nil - or type(Component.getDerivedStateFromError) ~= "function" - ) + and (Component.getDerivedStateFromError == nil or type(Component.getDerivedStateFromError) ~= "function") then -- If we captured an error, but getDerivedStateFromError is not defined, -- unmount all the children. componentDidCatch will schedule an update to @@ -1131,10 +1008,7 @@ function finishClassComponent( setIsRendering(true) -- deviation: Call with ':' instead of '.' so that render can access self nextChildren = instance:render() - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() -- deviation: Pass instance so that render can access self local ok, result = xpcall(instance.render, describeError, instance) @@ -1158,12 +1032,7 @@ function finishClassComponent( -- the existing children. Conceptually, the normal children and the children -- that are shown on error are two different sets, so we shouldn't reuse -- normal children even if their identities match. - forceUnmountCurrentAndReconcile( - current :: Fiber, - workInProgress, - nextChildren, - renderLanes - ) + forceUnmountCurrentAndReconcile(current :: Fiber, workInProgress, nextChildren, renderLanes) else reconcileChildren(current, workInProgress, nextChildren, renderLanes) end @@ -1184,11 +1053,7 @@ local function pushHostRootContext(workInProgress) -- FIXME (roblox): type refinement '(workInProgress.stateNode: FiberRoot)' local root = workInProgress.stateNode if root.pendingContext then - pushTopLevelContextObject( - workInProgress, - root.pendingContext, - root.pendingContext ~= root.context - ) + pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext ~= root.context) elseif root.context then -- Should always be set pushTopLevelContextObject(workInProgress, root.context, false) @@ -1254,8 +1119,7 @@ local function updateHostRoot(current, workInProgress, renderLanes) -- Conceptually this is similar to Placement in that a new subtree is -- inserted into the React tree here. It just happens to not need DOM -- mutations because it already exists. - node.flags = - bit32.bor(bit32.band(node.flags, bit32.bnot(Placement)), Hydrating) + node.flags = bit32.bor(bit32.band(node.flags, bit32.bnot(Placement)), Hydrating) node = node.sibling end else @@ -1273,11 +1137,7 @@ end -- workInProgress: Fiber, -- renderLanes: Lanes -- ) -local function updateHostComponent( - current: any, - workInProgress: Fiber, - renderLanes: Lanes -) +local function updateHostComponent(current: any, workInProgress: Fiber, renderLanes: Lanes) pushHostContext(workInProgress) if current == nil then @@ -1323,13 +1183,7 @@ local function updateHostText(current, workInProgress) return nil end -local function mountLazyComponent( - _current, - workInProgress, - elementType, - updateLanes, - renderLanes -) +local function mountLazyComponent(_current, workInProgress, elementType, updateLanes, renderLanes) if _current ~= nil then -- A lazy component only mounts if it suspended inside a non- -- concurrent tree, in an inconsistent state. We want to treat it like @@ -1358,34 +1212,21 @@ local function mountLazyComponent( Component = resolveFunctionForHotReloading(Component) workInProgress.type = Component end - child = updateFunctionComponent( - nil, - workInProgress, - Component, - resolvedProps, - renderLanes - ) + child = updateFunctionComponent(nil, workInProgress, Component, resolvedProps, renderLanes) return child elseif resolvedTag == ClassComponent then if __DEV__ then Component = resolveClassForHotReloading(Component) workInProgress.type = Component end - child = updateClassComponent( - nil, - workInProgress, - Component, - resolvedProps, - renderLanes - ) + child = updateClassComponent(nil, workInProgress, Component, resolvedProps, renderLanes) return child elseif resolvedTag == ForwardRef then if __DEV__ then Component = resolveForwardRefForHotReloading(Component) workInProgress.type = Component end - child = - updateForwardRef(nil, workInProgress, Component, resolvedProps, renderLanes) + child = updateForwardRef(nil, workInProgress, Component, resolvedProps, renderLanes) return child elseif resolvedTag == MemoComponent then if __DEV__ or __DISABLE_ALL_WARNINGS_EXCEPT_PROP_VALIDATION__ then @@ -1430,11 +1271,7 @@ local function mountLazyComponent( end local hint = "" if __DEV__ then - if - Component ~= nil - and type(Component) == "table" - and Component["$$typeof"] == REACT_LAZY_TYPE - then + if Component ~= nil and type(Component) == "table" and Component["$$typeof"] == REACT_LAZY_TYPE then hint = " Did you wrap a component in React.lazy() more than once?" elseif type(Component) == "table" and Component["$$typeof"] == nil then hint = "\n" .. inspect(Component) @@ -1454,13 +1291,7 @@ local function mountLazyComponent( return nil end -function mountIncompleteClassComponent( - _current, - workInProgress, - Component, - nextProps, - renderLanes -) +function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderLanes) if _current ~= nil then -- An incomplete component only mounts if it suspended inside a non- -- concurrent tree, in an inconsistent state. We want to treat it like @@ -1487,31 +1318,15 @@ function mountIncompleteClassComponent( else hasContext = false end - prepareToReadContext( - workInProgress, - renderLanes, - exports.markWorkInProgressReceivedUpdate - ) + prepareToReadContext(workInProgress, renderLanes, exports.markWorkInProgressReceivedUpdate) constructClassInstance(workInProgress, Component, nextProps) mountClassInstance(workInProgress, Component, nextProps, renderLanes) - return finishClassComponent( - nil, - workInProgress, - Component, - true, - hasContext, - renderLanes - ) + return finishClassComponent(nil, workInProgress, Component, true, hasContext, renderLanes) end -local function mountIndeterminateComponent( - current, - workInProgress, - Component, - renderLanes -) +local function mountIndeterminateComponent(current, workInProgress, Component, renderLanes) if current ~= nil then -- An indeterminate component only mounts if it suspended inside a non- -- concurrent tree, in an inconsistent state. We want to treat it like @@ -1530,11 +1345,7 @@ local function mountIndeterminateComponent( context = getMaskedContext(workInProgress, unmaskedContext) end - prepareToReadContext( - workInProgress, - renderLanes, - exports.markWorkInProgressReceivedUpdate - ) + prepareToReadContext(workInProgress, renderLanes, exports.markWorkInProgressReceivedUpdate) local value if __DEV__ then @@ -1562,12 +1373,10 @@ local function mountIndeterminateComponent( setIsRendering(true) ReactCurrentOwner.current = workInProgress - value = - renderWithHooks(nil, workInProgress, Component, props, context, renderLanes) + value = renderWithHooks(nil, workInProgress, Component, props, context, renderLanes) setIsRendering(false) else - value = - renderWithHooks(nil, workInProgress, Component, props, context, renderLanes) + value = renderWithHooks(nil, workInProgress, Component, props, context, renderLanes) end -- React DevTools reads this flag. workInProgress.flags = bit32.bor(workInProgress.flags, PerformedWork) @@ -1657,28 +1466,13 @@ local function mountIndeterminateComponent( if type(Component) ~= "function" then getDerivedStateFromProps = (Component :: React_Component).getDerivedStateFromProps end - if - getDerivedStateFromProps ~= nil - and type(getDerivedStateFromProps) == "function" - then - applyDerivedStateFromProps( - workInProgress, - Component, - getDerivedStateFromProps, - props - ) + if getDerivedStateFromProps ~= nil and type(getDerivedStateFromProps) == "function" then + applyDerivedStateFromProps(workInProgress, Component, getDerivedStateFromProps, props) end adoptClassInstance(workInProgress, value) mountClassInstance(workInProgress, Component, props, renderLanes) - return finishClassComponent( - nil, - workInProgress, - Component, - true, - hasContext, - renderLanes - ) + return finishClassComponent(nil, workInProgress, Component, true, hasContext, renderLanes) else -- Proceed under the assumption that this is a function component workInProgress.tag = FunctionComponent @@ -1691,21 +1485,10 @@ local function mountIndeterminateComponent( ) end - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() - local ok, result = xpcall( - renderWithHooks, - describeError, - nil, - workInProgress, - Component, - props, - context, - renderLanes - ) + local ok, result = + xpcall(renderWithHooks, describeError, nil, workInProgress, Component, props, context, renderLanes) -- finally reenableLogs() if ok then @@ -1784,15 +1567,9 @@ function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) then local componentName = getComponentName(Component) or "Unknown" - if - not DidWarn.didWarnAboutGetDerivedStateOnFunctionComponent[componentName] - then - console.error( - "%s: Function components do not support getDerivedStateFromProps.", - componentName - ) - DidWarn.didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = - true + if not DidWarn.didWarnAboutGetDerivedStateOnFunctionComponent[componentName] then + console.error("%s: Function components do not support getDerivedStateFromProps.", componentName) + DidWarn.didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true end end @@ -1805,10 +1582,7 @@ function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) local componentName = getComponentName(Component) or "Unknown" if not DidWarn.didWarnAboutContextTypeOnFunctionComponent[componentName] then - console.error( - "%s: Function components do not support contextType.", - componentName - ) + console.error("%s: Function components do not support contextType.", componentName) DidWarn.didWarnAboutContextTypeOnFunctionComponent[componentName] = true end end @@ -1826,10 +1600,7 @@ local function mountSuspenseOffscreenState(renderLanes: Lanes): OffscreenState } end -local function updateSuspenseOffscreenState( - prevOffscreenState: OffscreenState, - renderLanes: Lanes -): OffscreenState +local function updateSuspenseOffscreenState(prevOffscreenState: OffscreenState, renderLanes: Lanes): OffscreenState return { baseLanes = ReactFiberLane.mergeLanes(prevOffscreenState.baseLanes, renderLanes), } @@ -1887,10 +1658,7 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) local showFallback = false local didSuspend = bit32.band(workInProgress.flags, DidCapture) ~= NoFlags - if - didSuspend - or shouldRemainOnFallback(suspenseContext, current, workInProgress, renderLanes) - then + if didSuspend or shouldRemainOnFallback(suspenseContext, current, workInProgress, renderLanes) then -- Something in this boundary's subtree already suspended. Switch to -- rendering the fallback children. showFallback = true @@ -1903,14 +1671,8 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) -- handle the fallback state. -- Boundaries without fallbacks or should be avoided are not considered since -- they cannot handle preferred fallback states. - if - nextProps.fallback ~= nil - and nextProps.unstable_avoidThisFallback ~= true - then - suspenseContext = addSubtreeSuspenseContext( - suspenseContext, - InvisibleParentSuspenseContext - ) + if nextProps.fallback ~= nil and nextProps.unstable_avoidThisFallback ~= true then + suspenseContext = addSubtreeSuspenseContext(suspenseContext, InvisibleParentSuspenseContext) end end end @@ -1953,11 +1715,7 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) if suspenseState ~= nil then local dehydrated = (suspenseState :: SuspenseState).dehydrated if dehydrated ~= nil then - return mountDehydratedSuspenseComponent( - workInProgress, - dehydrated, - renderLanes - ) + return mountDehydratedSuspenseComponent(workInProgress, dehydrated, renderLanes) end end end @@ -1966,29 +1724,18 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) local nextPrimaryChildren = nextProps.children local nextFallbackChildren = nextProps.fallback if showFallback then - local fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ) + local fallbackFragment = + mountSuspenseFallbackChildren(workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes) local primaryChildFragment: Fiber = workInProgress.child :: any primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes) workInProgress.memoizedState = SUSPENDED_MARKER return fallbackFragment - elseif - nextProps.unstable_expectedLoadTime ~= nil - and type(nextProps.unstable_expectedLoadTime) == "number" - then + elseif nextProps.unstable_expectedLoadTime ~= nil and type(nextProps.unstable_expectedLoadTime) == "number" then -- This is a CPU-bound tree. Skip this tree and show a placeholder to -- unblock the surrounding content. Then immediately retry after the -- initial commit. - local fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ) + local fallbackFragment = + mountSuspenseFallbackChildren(workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes) local primaryChildFragment: Fiber = workInProgress.child :: any primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes) workInProgress.memoizedState = SUSPENDED_MARKER @@ -2007,11 +1754,7 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) end return fallbackFragment else - return mountSuspensePrimaryChildren( - workInProgress, - nextPrimaryChildren, - renderLanes - ) + return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren, renderLanes) end else -- This is an update. @@ -2049,17 +1792,15 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) -- Therefore we now have to render the fallback. local nextPrimaryChildren = nextProps.children local nextFallbackChildren = nextProps.fallback - local fallbackChildFragment = - mountSuspenseFallbackAfterRetryWithoutHydrating( - current, - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ) + local fallbackChildFragment = mountSuspenseFallbackAfterRetryWithoutHydrating( + current, + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ) local primaryChildFragment: Fiber = workInProgress.child :: any - primaryChildFragment.memoizedState = - mountSuspenseOffscreenState(renderLanes) + primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes) workInProgress.memoizedState = SUSPENDED_MARKER return fallbackChildFragment end @@ -2081,28 +1822,20 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) -- ROBLOX deviation: if/else in place of ternary if prevOffscreenState == nil then - primaryChildFragment.memoizedState = - mountSuspenseOffscreenState(renderLanes) + primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes) else -- ROBLOX FIXME: remove :: when Luau understands ~= nil - primaryChildFragment.memoizedState = updateSuspenseOffscreenState( - prevOffscreenState :: OffscreenState, - renderLanes - ) + primaryChildFragment.memoizedState = + updateSuspenseOffscreenState(prevOffscreenState :: OffscreenState, renderLanes) end - primaryChildFragment.childLanes = - getRemainingWorkInPrimaryTree(current, renderLanes) + primaryChildFragment.childLanes = getRemainingWorkInPrimaryTree(current, renderLanes) workInProgress.memoizedState = SUSPENDED_MARKER return fallbackChildFragment else local nextPrimaryChildren = nextProps.children - local primaryChildFragment = updateSuspensePrimaryChildren( - current, - workInProgress, - nextPrimaryChildren, - renderLanes - ) + local primaryChildFragment = + updateSuspensePrimaryChildren(current, workInProgress, nextPrimaryChildren, renderLanes) workInProgress.memoizedState = nil return primaryChildFragment end @@ -2124,18 +1857,14 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) -- ROBLOX deviation: if/else in place of ternary if prevOffscreenState == nil then - primaryChildFragment.memoizedState = - mountSuspenseOffscreenState(renderLanes) + primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes) else -- ROBLOX FIXME: remove :: once Luau understands nil check - primaryChildFragment.memoizedState = updateSuspenseOffscreenState( - prevOffscreenState :: OffscreenState, - renderLanes - ) + primaryChildFragment.memoizedState = + updateSuspenseOffscreenState(prevOffscreenState :: OffscreenState, renderLanes) end - primaryChildFragment.childLanes = - getRemainingWorkInPrimaryTree(current, renderLanes) + primaryChildFragment.childLanes = getRemainingWorkInPrimaryTree(current, renderLanes) -- Skip the primary children, and continue working on the -- fallback children. workInProgress.memoizedState = SUSPENDED_MARKER @@ -2144,12 +1873,8 @@ local function updateSuspenseComponent(current, workInProgress, renderLanes) -- Still haven't timed out. Continue rendering the children, like we -- normally do. local nextPrimaryChildren = nextProps.children - local primaryChildFragment = updateSuspensePrimaryChildren( - current, - workInProgress, - nextPrimaryChildren, - renderLanes - ) + local primaryChildFragment = + updateSuspensePrimaryChildren(current, workInProgress, nextPrimaryChildren, renderLanes) workInProgress.memoizedState = nil return primaryChildFragment end @@ -2163,19 +1888,13 @@ function mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLan mode = "visible", children = primaryChildren, } - local primaryChildFragment = - createFiberFromOffscreen(primaryChildProps, mode, renderLanes, nil) + local primaryChildFragment = createFiberFromOffscreen(primaryChildProps, mode, renderLanes, nil) primaryChildFragment.return_ = workInProgress workInProgress.child = primaryChildFragment return primaryChildFragment end -function mountSuspenseFallbackChildren( - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) +function mountSuspenseFallbackChildren(workInProgress, primaryChildren, fallbackChildren, renderLanes) local mode = workInProgress.mode local progressedPrimaryFragment: Fiber | nil = workInProgress.child @@ -2204,13 +1923,10 @@ function mountSuspenseFallbackChildren( primaryChildFragment.treeBaseDuration = 0 end - fallbackChildFragment = - createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) + fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) else - primaryChildFragment = - createFiberFromOffscreen(primaryChildProps, mode, ReactFiberLane.NoLanes, nil) - fallbackChildFragment = - createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) + primaryChildFragment = createFiberFromOffscreen(primaryChildProps, mode, ReactFiberLane.NoLanes, nil) + fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) end primaryChildFragment.return_ = workInProgress @@ -2220,29 +1936,20 @@ function mountSuspenseFallbackChildren( return fallbackChildFragment end -local function createWorkInProgressOffscreenFiber( - current: Fiber, - offscreenProps: OffscreenProps -) +local function createWorkInProgressOffscreenFiber(current: Fiber, offscreenProps: OffscreenProps) -- The props argument to `createWorkInProgress` is `any` typed, so we use this -- wrapper function to constrain it. return createWorkInProgress(current, offscreenProps) end -function updateSuspensePrimaryChildren( - current, - workInProgress, - primaryChildren, - renderLanes -) +function updateSuspensePrimaryChildren(current, workInProgress, primaryChildren, renderLanes) local currentPrimaryChildFragment: Fiber = current.child :: any local currentFallbackChildFragment: Fiber | nil = currentPrimaryChildFragment.sibling - local primaryChildFragment = - createWorkInProgressOffscreenFiber(currentPrimaryChildFragment, { - mode = "visible", - children = primaryChildren, - }) + local primaryChildFragment = createWorkInProgressOffscreenFiber(currentPrimaryChildFragment, { + mode = "visible", + children = primaryChildren, + }) if bit32.band(workInProgress.mode, BlockingMode) == NoMode then primaryChildFragment.lanes = renderLanes end @@ -2264,13 +1971,7 @@ function updateSuspensePrimaryChildren( return primaryChildFragment end -function updateSuspenseFallbackChildren( - current, - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) +function updateSuspenseFallbackChildren(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) local mode = workInProgress.mode local currentPrimaryChildFragment: Fiber = current.child :: any local currentFallbackChildFragment: Fiber | nil = currentPrimaryChildFragment.sibling @@ -2305,10 +2006,8 @@ function updateSuspenseFallbackChildren( -- do in Concurrent Mode. primaryChildFragment.actualDuration = 0 primaryChildFragment.actualStartTime = -1 - primaryChildFragment.selfBaseDuration = - currentPrimaryChildFragment.selfBaseDuration - primaryChildFragment.treeBaseDuration = - currentPrimaryChildFragment.treeBaseDuration + primaryChildFragment.selfBaseDuration = currentPrimaryChildFragment.selfBaseDuration + primaryChildFragment.treeBaseDuration = currentPrimaryChildFragment.treeBaseDuration end -- The fallback fiber was added as a deletion effect during the first pass. @@ -2316,24 +2015,18 @@ function updateSuspenseFallbackChildren( -- to delete it. workInProgress.deletions = nil else - primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - primaryChildProps - ) + primaryChildFragment = createWorkInProgressOffscreenFiber(currentPrimaryChildFragment, primaryChildProps) -- Since we're reusing a current tree, we need to reuse the flags, too. -- (We don't do this in legacy mode, because in legacy mode we don't re-use -- the current tree; see previous branch.) - primaryChildFragment.subtreeFlags = - bit32.band(currentPrimaryChildFragment.subtreeFlags, StaticMask) + primaryChildFragment.subtreeFlags = bit32.band(currentPrimaryChildFragment.subtreeFlags, StaticMask) end local fallbackChildFragment if currentFallbackChildFragment ~= nil then - fallbackChildFragment = - createWorkInProgress(currentFallbackChildFragment, fallbackChildren) + fallbackChildFragment = createWorkInProgress(currentFallbackChildFragment, fallbackChildren) else - fallbackChildFragment = - createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) + fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) -- Needs a placement effect because the parent (the Suspense boundary) already -- mounted but this is a new fiber. fallbackChildFragment.flags = bit32.bor(fallbackChildFragment.flags, Placement) @@ -2347,19 +2040,14 @@ function updateSuspenseFallbackChildren( return fallbackChildFragment end -local function retrySuspenseComponentWithoutHydrating( - current: Fiber, - workInProgress: Fiber, - renderLanes: Lanes -) +local function retrySuspenseComponentWithoutHydrating(current: Fiber, workInProgress: Fiber, renderLanes: Lanes) -- This will add the old fiber to the deletion list reconcileChildFibers(workInProgress, current.child, nil, renderLanes) -- We're now not suspended nor dehydrated. local nextProps = workInProgress.pendingProps local primaryChildren = nextProps.children - local primaryChildFragment = - mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) + local primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) -- Needs a placement effect because the parent (the Suspense boundary) already -- mounted but this is a new fiber. primaryChildFragment.flags = bit32.bor(primaryChildFragment.flags, Placement) @@ -2376,10 +2064,8 @@ function mountSuspenseFallbackAfterRetryWithoutHydrating( renderLanes ) local mode = workInProgress.mode - local primaryChildFragment = - createFiberFromOffscreen(primaryChildren, mode, ReactFiberLane.NoLanes, nil) - local fallbackChildFragment = - createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) + local primaryChildFragment = createFiberFromOffscreen(primaryChildren, mode, ReactFiberLane.NoLanes, nil) + local fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, nil) -- Needs a placement effect because the parent (the Suspense -- boundary) already mounted but this is a new fiber. fallbackChildFragment.flags = bit32.bor(fallbackChildFragment.flags, Placement) @@ -2432,8 +2118,7 @@ function mountDehydratedSuspenseComponent( if enableSchedulerTracing then markSpawnedWork(ReactFiberLane.DefaultHydrationLane) end - workInProgress.lanes = - ReactFiberLane.laneToLanes(ReactFiberLane.DefaultHydrationLane) + workInProgress.lanes = ReactFiberLane.laneToLanes(ReactFiberLane.DefaultHydrationLane) else -- We'll continue hydrating the rest at offscreen priority since we'll already -- be showing the right content coming from the server, it is no rush. @@ -2457,42 +2142,28 @@ function updateDehydratedSuspenseComponent( warnIfHydrating() if bit32.band(getExecutionContext(), RetryAfterError) ~= NoContext then - return retrySuspenseComponentWithoutHydrating( - current, - workInProgress, - renderLanes - ) + return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes) end if bit32.band(workInProgress.mode, BlockingMode) == NoMode then - return retrySuspenseComponentWithoutHydrating( - current, - workInProgress, - renderLanes - ) + return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes) end if isSuspenseInstanceFallback(suspenseInstance) then -- This boundary is in a permanent fallback state. In this case, we'll never -- get an update and we'll never be able to hydrate the final content. Let's just try the -- client side render instead. - return retrySuspenseComponentWithoutHydrating( - current, - workInProgress, - renderLanes - ) + return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes) end -- We use lanes to indicate that a child might depend on context, so if -- any context has changed, we need to treat is as if the input might have changed. - local hasContextChanged = - ReactFiberLane.includesSomeLane(renderLanes, current.childLanes) + local hasContextChanged = ReactFiberLane.includesSomeLane(renderLanes, current.childLanes) if didReceiveUpdate or hasContextChanged then -- This boundary has changed since the first render. This means that we are now unable to -- hydrate it. We might still be able to hydrate it using a higher priority lane. local root = getWorkInProgressRoot() if root ~= nil then - local attemptHydrationAtLane = - ReactFiberLane.getBumpedLaneForHydration(root, renderLanes) + local attemptHydrationAtLane = ReactFiberLane.getBumpedLaneForHydration(root, renderLanes) if attemptHydrationAtLane ~= ReactFiberLane.NoLane and attemptHydrationAtLane ~= suspenseState.retryLane @@ -2520,11 +2191,7 @@ function updateDehydratedSuspenseComponent( -- skip hydration. -- Delay having to do this as long as the suspense timeout allows us. renderDidSuspendDelayIfPossible() - return retrySuspenseComponentWithoutHydrating( - current, - workInProgress, - renderLanes - ) + return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes) elseif isSuspenseInstancePending(suspenseInstance) then -- This component is still pending more data from the server, so we can't hydrate its -- content. We treat it as if this component suspended itself. It might seem as if @@ -2547,8 +2214,7 @@ function updateDehydratedSuspenseComponent( if enableSchedulerTracing then if Schedule_tracing_wrap == nil then - Schedule_tracing_wrap = - require("@pkg/@jsdotlua/scheduler").tracing.unstable_wrap + Schedule_tracing_wrap = require("@pkg/@jsdotlua/scheduler").tracing.unstable_wrap end retry = Schedule_tracing_wrap(retry) end @@ -2557,14 +2223,10 @@ function updateDehydratedSuspenseComponent( return nil else -- This is the first attempt. - reenterHydrationStateFromDehydratedSuspenseInstance( - workInProgress, - suspenseInstance - ) + reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress, suspenseInstance) local nextProps = workInProgress.pendingProps local primaryChildren = nextProps.children - local primaryChildFragment = - mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) + local primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) -- Mark the children as hydrating. This is a fast path to know whether this -- tree is part of a hydrating tree. This is used to determine if a child -- node has fully mounted yet, and for scheduling event replaying. @@ -2955,11 +2617,7 @@ end -- return workInProgress.child -- end -function updatePortalComponent( - current: Fiber | nil, - workInProgress: Fiber, - renderLanes: Lanes -) +function updatePortalComponent(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes) pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo) local nextChildren = workInProgress.pendingProps if current == nil then @@ -2968,8 +2626,7 @@ function updatePortalComponent( -- flow doesn't do during mount. This doesn't happen at the root because -- the root always starts with a "current" with a nil child. -- TODO: Consider unifying this with how the root works. - workInProgress.child = - reconcileChildFibers(workInProgress, nil, nextChildren, renderLanes) + workInProgress.child = reconcileChildFibers(workInProgress, nil, nextChildren, renderLanes) else reconcileChildren(current, workInProgress, nextChildren, renderLanes) end @@ -2978,11 +2635,7 @@ end local hasWarnedAboutUsingNoValuePropOnContextProvider = false -local function updateContextProvider( - current: Fiber | nil, - workInProgress: Fiber, - renderLanes: Lanes -) +local function updateContextProvider(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes) local providerType: ReactProviderType = workInProgress.type local context: ReactContext = providerType._context @@ -3005,13 +2658,7 @@ local function updateContextProvider( local validateProps = workInProgress.type.validateProps if providerPropTypes or validateProps then - checkPropTypes( - providerPropTypes, - validateProps, - newProps, - "prop", - "Context.Provider" - ) + checkPropTypes(providerPropTypes, validateProps, newProps, "prop", "Context.Provider") end end @@ -3022,10 +2669,7 @@ local function updateContextProvider( local changedBits = calculateChangedBits(context, newValue, oldValue) if changedBits == 0 then -- No change. Bailout early if children are the same. - if - oldProps.children == newProps.children - and not hasLegacyContextChanged() - then + if oldProps.children == newProps.children and not hasLegacyContextChanged() then return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) end else @@ -3046,11 +2690,7 @@ local hasWarnedAbout = { usingLegacyConsumer = false, } -function updateContextConsumer( - current: Fiber | nil, - workInProgress: Fiber, - renderLanes: Lanes -) +function updateContextConsumer(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes) local context: ReactContext = workInProgress.type -- The logic below for Context differs depending on PROD or DEV mode. In -- DEV mode, we create a separate object for Context.Consumer that acts @@ -3112,11 +2752,7 @@ function updateContextConsumer( end end - prepareToReadContext( - workInProgress, - renderLanes, - exports.markWorkInProgressReceivedUpdate - ) + prepareToReadContext(workInProgress, renderLanes, exports.markWorkInProgressReceivedUpdate) local newValue = readContext(context, newProps.unstable_observedBits) local newChildren if __DEV__ then @@ -3160,11 +2796,7 @@ exports.markWorkInProgressReceivedUpdate = function() didReceiveUpdate = true end -function bailoutOnAlreadyFinishedWork( - current: Fiber | nil, - workInProgress: Fiber, - renderLanes: Lanes -): Fiber | nil +function bailoutOnAlreadyFinishedWork(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil if current then -- Reuse previous dependencies workInProgress.dependencies = current.dependencies @@ -3191,11 +2823,7 @@ function bailoutOnAlreadyFinishedWork( end end -function remountFiber( - current: Fiber, - oldWorkInProgress: Fiber, - newWorkInProgress: Fiber -): Fiber | nil +function remountFiber(current: Fiber, oldWorkInProgress: Fiber, newWorkInProgress: Fiber): Fiber | nil if __DEV__ then local returnFiber = oldWorkInProgress.return_ if returnFiber == nil then @@ -3251,10 +2879,7 @@ function remountFiber( -- Restart work from the new fiber. return newWorkInProgress else - error( - "Did not expect this call in production. " - .. "This is a bug in React. Please file an issue." - ) + error("Did not expect this call in production. " .. "This is a bug in React. Please file an issue.") end end @@ -3330,15 +2955,12 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes if (state :: SuspenseState).dehydrated ~= nil then pushSuspenseContext( workInProgress, - setDefaultShallowSuspenseContext( - suspenseStackCursor.current - ) + setDefaultShallowSuspenseContext(suspenseStackCursor.current) ) -- We know that this component will suspend again because if it has -- been unsuspended it has committed as a resolved Suspense component. -- If it needs to be retried, it should have work scheduled on it. - workInProgress.flags = - bit32.bor(workInProgress.flags, DidCapture) + workInProgress.flags = bit32.bor(workInProgress.flags, DidCapture) -- We should never render the children of a dehydrated boundary until we -- upgrade it. We return nil instead of bailoutOnAlreadyFinishedWork. return nil @@ -3351,16 +2973,10 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes -- child fragment. local primaryChildFragment: Fiber = workInProgress.child :: any local primaryChildLanes = primaryChildFragment.childLanes - if - ReactFiberLane.includesSomeLane(renderLanes, primaryChildLanes) - then + if ReactFiberLane.includesSomeLane(renderLanes, primaryChildLanes) then -- The primary children have pending work. Use the normal path -- to attempt to render the primary children again. - return updateSuspenseComponent( - current, - workInProgress, - renderLanes - ) + return updateSuspenseComponent(current, workInProgress, renderLanes) else -- The primary child fragment does not have pending work marked -- on it @@ -3370,11 +2986,7 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes ) -- The primary children do not have pending work with sufficient -- priority. Bailout. - local child = bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ) + local child = bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) if child ~= nil then -- The fallback children have pending work. Skip over the -- primary children and work on the fallback. @@ -3384,10 +2996,7 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes end end else - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ) + pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)) end elseif workInProgress.tag == SuspenseListComponent then unimplemented("beginWork: SuspenseListComponent") @@ -3435,10 +3044,7 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes -- -- as before. We can fast bail out. -- return nil -- end - elseif - workInProgress.tag == OffscreenComponent - or workInProgress.tag == LegacyHiddenComponent - then + elseif workInProgress.tag == OffscreenComponent or workInProgress.tag == LegacyHiddenComponent then -- Need to check if the tree still needs to be deferred. This is -- almost identical to the logic used in the normal update path, -- so we'll just enter that. The only difference is we'll bail out @@ -3476,21 +3082,10 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes workInProgress.lanes = ReactFiberLane.NoLanes if workInProgress.tag == ReactWorkTags.IndeterminateComponent then - return mountIndeterminateComponent( - current, - workInProgress, - workInProgress.type, - renderLanes - ) + return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes) elseif workInProgress.tag == LazyComponent then local elementType = workInProgress.elementType - return mountLazyComponent( - current, - workInProgress, - elementType, - updateLanes, - renderLanes - ) + return mountLazyComponent(current, workInProgress, elementType, updateLanes, renderLanes) elseif workInProgress.tag == FunctionComponent then local Component = workInProgress.type local unresolvedProps = workInProgress.pendingProps @@ -3500,25 +3095,13 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes else resolvedProps = resolveDefaultProps(Component, unresolvedProps) end - return updateFunctionComponent( - current, - workInProgress, - Component, - resolvedProps, - renderLanes - ) + return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes) elseif workInProgress.tag == ClassComponent then local Component = workInProgress.type local unresolvedProps = workInProgress.pendingProps local resolvedProps = workInProgress.elementType == Component and unresolvedProps or resolveDefaultProps(Component, unresolvedProps) - return updateClassComponent( - current, - workInProgress, - Component, - resolvedProps, - renderLanes - ) + return updateClassComponent(current, workInProgress, Component, resolvedProps, renderLanes) elseif workInProgress.tag == HostRoot then return updateHostRoot(current, workInProgress, renderLanes) elseif workInProgress.tag == HostComponent then @@ -3574,14 +3157,7 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes end end resolvedProps = resolveDefaultProps(type_.type, resolvedProps) - return updateMemoComponent( - current, - workInProgress, - type_, - resolvedProps, - updateLanes, - renderLanes - ) + return updateMemoComponent(current, workInProgress, type_, resolvedProps, updateLanes, renderLanes) elseif workInProgress.tag == SimpleMemoComponent then return updateSimpleMemoComponent( current, @@ -3596,13 +3172,7 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes local unresolvedProps = workInProgress.pendingProps local resolvedProps = workInProgress.elementType == Component and unresolvedProps or resolveDefaultProps(Component, unresolvedProps) - return mountIncompleteClassComponent( - current, - workInProgress, - Component, - resolvedProps, - renderLanes - ) + return mountIncompleteClassComponent(current, workInProgress, Component, resolvedProps, renderLanes) -- elseif workInProgress.tag == SuspenseListComponent then -- unimplemented("beginWork: SuspenseListComponent") -- return updateSuspenseListComponent(current, workInProgress, renderLanes) @@ -3621,8 +3191,7 @@ local function beginWork(current: any, workInProgress: Fiber, renderLanes: Lanes end invariant( false, - "Unknown unit of work tag (%s). This error is likely caused by a bug in " - .. "React. Please file an issue.", + "Unknown unit of work tag (%s). This error is likely caused by a bug in " .. "React. Please file an issue.", tostring(workInProgress.tag) ) return nil diff --git a/modules/react-reconciler/src/ReactFiberClassComponent.new.lua b/modules/react-reconciler/src/ReactFiberClassComponent.new.lua index 66ba52f6..b89aec68 100644 --- a/modules/react-reconciler/src/ReactFiberClassComponent.new.lua +++ b/modules/react-reconciler/src/ReactFiberClassComponent.new.lua @@ -33,8 +33,7 @@ local Snapshot = ReactFiberFlags.Snapshot local MountLayoutDev = ReactFiberFlags.MountLayoutDev local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags -local debugRenderPhaseSideEffectsForStrictMode = - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode +local debugRenderPhaseSideEffectsForStrictMode = ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode local disableLegacyContext = ReactFeatureFlags.disableLegacyContext local enableDebugTracing = ReactFeatureFlags.enableDebugTracing local enableSchedulingProfiler = ReactFeatureFlags.enableSchedulingProfiler @@ -55,18 +54,15 @@ local ReactSymbols = require("@pkg/@jsdotlua/shared").ReactSymbols local REACT_CONTEXT_TYPE = ReactSymbols.REACT_CONTEXT_TYPE local REACT_PROVIDER_TYPE = ReactSymbols.REACT_PROVIDER_TYPE -local resolveDefaultProps = - require("./ReactFiberLazyComponent.new.lua").resolveDefaultProps +local resolveDefaultProps = require("./ReactFiberLazyComponent.new.lua").resolveDefaultProps local ReactTypeOfMode = require("./ReactTypeOfMode") local DebugTracingMode = ReactTypeOfMode.DebugTracingMode local StrictMode = ReactTypeOfMode.StrictMode local enqueueUpdate = ReactUpdateQueue.enqueueUpdate local processUpdateQueue = ReactUpdateQueue.processUpdateQueue -local checkHasForceUpdateAfterProcessing = - ReactUpdateQueue.checkHasForceUpdateAfterProcessing -local resetHasForceUpdateBeforeProcessing = - ReactUpdateQueue.resetHasForceUpdateBeforeProcessing +local checkHasForceUpdateAfterProcessing = ReactUpdateQueue.checkHasForceUpdateAfterProcessing +local resetHasForceUpdateBeforeProcessing = ReactUpdateQueue.resetHasForceUpdateBeforeProcessing local createUpdate = ReactUpdateQueue.createUpdate local ReplaceState = ReactUpdateQueue.ReplaceState local ForceUpdate = ReactUpdateQueue.ForceUpdate @@ -142,8 +138,7 @@ if __DEV__ then if not didWarnOnInvalidCallback[key] then didWarnOnInvalidCallback[key] = true console.error( - "%s(...): Expected the last optional `callback` argument to be a " - .. "function. Instead received: %s.", + "%s(...): Expected the last optional `callback` argument to be a " .. "function. Instead received: %s.", callerName, tostring(callback) ) @@ -202,14 +197,10 @@ local function applyDerivedStateFromProps( local prevState = workInProgress.memoizedState if __DEV__ then - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() -- Invoke the function an extra time to help detect side-effects. - local ok, result = - xpcall(getDerivedStateFromProps, describeError, nextProps, prevState) + local ok, result = xpcall(getDerivedStateFromProps, describeError, nextProps, prevState) reenableLogs() @@ -225,9 +216,7 @@ local function applyDerivedStateFromProps( warnOnUndefinedDerivedState(ctor, partialState) end -- Merge the partial state and the previous state. - local memoizedState = if partialState == nil - then prevState - else Object.assign({}, prevState, partialState) + local memoizedState = if partialState == nil then prevState else Object.assign({}, prevState, partialState) workInProgress.memoizedState = memoizedState -- Once the update queue is empty, persist the derived state onto the @@ -249,7 +238,7 @@ local function initializeClassComponentUpdater() classComponentUpdater = { isMounted = isMounted, - enqueueSetState = function(inst, payload, callback: (() -> (...any))?) + enqueueSetState = function(inst, payload, callback: (() -> ...any)?) local fiber = getInstance(inst) local eventTime = requestEventTime() local lane = requestUpdateLane(fiber) @@ -352,36 +341,16 @@ local function getClassComponentUpdater() return classComponentUpdater end -function checkShouldComponentUpdate( - workInProgress, - ctor, - oldProps, - newProps, - oldState, - newState, - nextContext -) +function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) local instance = workInProgress.stateNode - if - instance.shouldComponentUpdate ~= nil - and type(instance.shouldComponentUpdate) == "function" - then + if instance.shouldComponentUpdate ~= nil and type(instance.shouldComponentUpdate) == "function" then if __DEV__ then - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() -- deviation: Pass instance so that the method receives self -- Invoke the function an extra time to help detect side-effects. - local ok, result = xpcall( - instance.shouldComponentUpdate, - describeError, - instance, - newProps, - newState, - nextContext - ) + local ok, result = + xpcall(instance.shouldComponentUpdate, describeError, instance, newProps, newState, nextContext) -- finally reenableLogs() if not ok then @@ -390,8 +359,7 @@ function checkShouldComponentUpdate( end end -- deviation: Call with ":" so that the method receives self - local shouldUpdate = - instance:shouldComponentUpdate(newProps, newState, nextContext) + local shouldUpdate = instance:shouldComponentUpdate(newProps, newState, nextContext) if __DEV__ then if shouldUpdate == nil then @@ -409,9 +377,7 @@ function checkShouldComponentUpdate( -- ROBLOX deviation: for us, the isPureReactComponent flag will be visible as a -- direct member of the 'ctor', which in reality is the component definition if type(ctor) == "table" and ctor.isPureReactComponent then - return ( - not shallowEqual(oldProps, newProps) or not shallowEqual(oldState, newState) - ) + return (not shallowEqual(oldProps, newProps) or not shallowEqual(oldState, newState)) end return true @@ -441,11 +407,7 @@ local function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: an end end - if - instance.getInitialState - and not instance.getInitialState.isReactClassApproved - and not instance.state - then + if instance.getInitialState and not instance.getInitialState.isReactClassApproved and not instance.state then console.error( "getInitialState was defined on %s, a plain JavaScript class. " .. "This is only supported for classes created using React.createClass. " @@ -453,10 +415,7 @@ local function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: an name ) end - if - instance.getDefaultProps - and not instance.getDefaultProps.isReactClassApproved - then + if instance.getDefaultProps and not instance.getDefaultProps.isReactClassApproved then console.error( "getDefaultProps was defined on %s, a plain JavaScript class. " .. "This is only supported for classes created using React.createClass. " @@ -532,11 +491,7 @@ local function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: an ) end -- ROBLOX deviation: don't access fields on a function - if - type(ctor) == "table" - and ctor.isPureReactComponent - and instance.shouldComponentUpdate ~= nil - then + if type(ctor) == "table" and ctor.isPureReactComponent and instance.shouldComponentUpdate ~= nil then console.error( "%s has a method called shouldComponentUpdate(). " .. "shouldComponentUpdate should not be used when extending React.PureComponent. " @@ -564,8 +519,7 @@ local function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: an end if type(instance.componentWillRecieveProps) == "function" then console.error( - "%s has a method called " - .. "componentWillRecieveProps(). Did you mean componentWillReceiveProps()?", + "%s has a method called " .. "componentWillRecieveProps(). Did you mean componentWillReceiveProps()?", name ) end @@ -643,8 +597,7 @@ local function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: an and type(ctor.childContextTypes) ~= "table" then console.error( - "%s.getChildContext(): childContextTypes must be defined in order to " - .. "use getChildContext().", + "%s.getChildContext(): childContextTypes must be defined in order to " .. "use getChildContext().", name ) end @@ -715,17 +668,12 @@ local function constructClassInstance(workInProgress: Fiber, ctor: any, props: a unmaskedContext = getUnmaskedContext(workInProgress, ctor, true) local contextTypes = ctor.contextTypes isLegacyContextConsumer = contextTypes ~= nil - context = isLegacyContextConsumer - and getMaskedContext(workInProgress, unmaskedContext) - or emptyContextObject + context = isLegacyContextConsumer and getMaskedContext(workInProgress, unmaskedContext) or emptyContextObject end -- Instantiate twice to help detect side-effects. if __DEV__ then - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() -- deviation: ctor will actually refer to a class component, we use the -- `__ctor` function that it exposes @@ -754,10 +702,7 @@ local function constructClassInstance(workInProgress: Fiber, ctor: any, props: a if __DEV__ then -- ROBLOX deviation: Instead of checking if state is nil, we check if it is our -- UninitializedState singleton. - if - type(ctor.getDerivedStateFromProps) == "function" - and state == UninitializedState - then + if type(ctor.getDerivedStateFromProps) == "function" and state == UninitializedState then local componentName = getComponentName(ctor) or "Component" if not didWarnAboutUninitializedState[componentName] then didWarnAboutUninitializedState[componentName] = true @@ -812,11 +757,7 @@ local function constructClassInstance(workInProgress: Fiber, ctor: any, props: a elseif type(instance.UNSAFE_componentWillUpdate) == "function" then foundWillUpdateName = "UNSAFE_componentWillUpdate" end - if - foundWillMountName ~= nil - or foundWillReceivePropsName ~= nil - or foundWillUpdateName ~= nil - then + if foundWillMountName ~= nil or foundWillReceivePropsName ~= nil or foundWillUpdateName ~= nil then local componentName = getComponentName(ctor) or "Component" local newApiName if type(ctor.getDerivedStateFromProps) == "function" then @@ -834,9 +775,7 @@ local function constructClassInstance(workInProgress: Fiber, ctor: any, props: a local willReceievePropsName if foundWillReceivePropsName ~= nil then - willReceievePropsName = ( - "\n " .. tostring(foundWillReceivePropsName) - ) + willReceievePropsName = ("\n " .. tostring(foundWillReceivePropsName)) else willReceievePropsName = "" end @@ -878,18 +817,12 @@ end local function callComponentWillMount(workInProgress, instance) local oldState = instance.state - if - instance.componentWillMount ~= nil - and type(instance.componentWillMount) == "function" - then + if instance.componentWillMount ~= nil and type(instance.componentWillMount) == "function" then -- deviation: Call with ":" so that the method receives self instance:componentWillMount() end -- ROBLOX TODO: Should we really run both of these? - if - instance.UNSAFE_componentWillMount ~= nil - and type(instance.UNSAFE_componentWillMount) == "function" - then + if instance.UNSAFE_componentWillMount ~= nil and type(instance.UNSAFE_componentWillMount) == "function" then -- deviation: Call with ":" so that the method receives self instance:UNSAFE_componentWillMount() end @@ -909,10 +842,7 @@ end function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) local oldState = instance.state - if - instance.componentWillReceiveProps ~= nil - and type(instance.componentWillReceiveProps) == "function" - then + if instance.componentWillReceiveProps ~= nil and type(instance.componentWillReceiveProps) == "function" then -- deviation: Call with ":" so that the method receives self instance:componentWillReceiveProps(newProps, nextContext) end @@ -942,12 +872,7 @@ function callComponentWillReceiveProps(workInProgress, instance, newProps, nextC end -- Invokes the mount life-cycles on a previously never rendered instance. -local function mountClassInstance( - workInProgress: Fiber, - ctor: any, - newProps: any, - renderLanes: Lanes -) +local function mountClassInstance(workInProgress: Fiber, ctor: any, newProps: any, renderLanes: Lanes) if __DEV__ then checkClassInstance(workInProgress, ctor, newProps) end @@ -995,10 +920,7 @@ local function mountClassInstance( end if warnAboutDeprecatedLifecycles then - ReactStrictModeWarnings.recordUnsafeLifecycleWarnings( - workInProgress, - instance - ) + ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(workInProgress, instance) end end @@ -1011,16 +933,8 @@ local function mountClassInstance( if type(ctor) == "table" then getDerivedStateFromProps = ctor.getDerivedStateFromProps end - if - getDerivedStateFromProps ~= nil - and type(getDerivedStateFromProps) == "function" - then - applyDerivedStateFromProps( - workInProgress, - ctor, - getDerivedStateFromProps, - newProps - ) + if getDerivedStateFromProps ~= nil and type(getDerivedStateFromProps) == "function" then + applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps) instance.state = workInProgress.memoizedState end @@ -1031,10 +945,7 @@ local function mountClassInstance( typeofCtor == "table" and type(ctor.getDerivedStateFromProps) ~= "function" and type(instance.getSnapshotBeforeUpdate) ~= "function" - and ( - type(instance.UNSAFE_componentWillMount) == "function" - or type(instance.componentWillMount) == "function" - ) + and (type(instance.UNSAFE_componentWillMount) == "function" or type(instance.componentWillMount) == "function") then callComponentWillMount(workInProgress, instance) -- If we had additional state updates during this life-cycle, let's @@ -1045,20 +956,14 @@ local function mountClassInstance( if type(instance.componentDidMount) == "function" then if __DEV__ and enableDoubleInvokingEffects then - workInProgress.flags = - bit32.bor(workInProgress.flags, bit32.bor(MountLayoutDev, Update)) + workInProgress.flags = bit32.bor(workInProgress.flags, bit32.bor(MountLayoutDev, Update)) else workInProgress.flags = bit32.bor(workInProgress.flags, Update) end end end -function resumeMountClassInstance( - workInProgress: Fiber, - ctor: any, - newProps: any, - renderLanes: Lanes -): boolean +function resumeMountClassInstance(workInProgress: Fiber, ctor: any, newProps: any, renderLanes: Lanes): boolean local instance = workInProgress.stateNode local oldProps = workInProgress.memoizedProps @@ -1115,8 +1020,7 @@ function resumeMountClassInstance( -- effect even though we're bailing out, so that cWU/cDU are called. if type(instance.componentDidMount) == "function" then if __DEV__ and enableDoubleInvokingEffects then - workInProgress.flags = - bit32.bor(workInProgress.flags, MountLayoutDev, Update) + workInProgress.flags = bit32.bor(workInProgress.flags, MountLayoutDev, Update) else workInProgress.flags = bit32.bor(workInProgress.flags, Update) end @@ -1124,29 +1028,13 @@ function resumeMountClassInstance( return false end - if - getDerivedStateFromProps ~= nil - and type(getDerivedStateFromProps) == "function" - then - applyDerivedStateFromProps( - workInProgress, - ctor, - getDerivedStateFromProps, - newProps - ) + if getDerivedStateFromProps ~= nil and type(getDerivedStateFromProps) == "function" then + applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps) newState = workInProgress.memoizedState end local shouldUpdate = checkHasForceUpdateAfterProcessing() - or checkShouldComponentUpdate( - workInProgress, - ctor, - oldProps, - newProps, - oldState, - newState, - nextContext - ) + or checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) if shouldUpdate then -- In order to support react-lifecycles-compat polyfilled components, @@ -1167,8 +1055,7 @@ function resumeMountClassInstance( end if type(instance.componentDidMount) == "function" then if __DEV__ and enableDoubleInvokingEffects then - workInProgress.flags = - bit32.bor(workInProgress.flags, MountLayoutDev, Update) + workInProgress.flags = bit32.bor(workInProgress.flags, MountLayoutDev, Update) else workInProgress.flags = bit32.bor(workInProgress.flags, Update) end @@ -1178,8 +1065,7 @@ function resumeMountClassInstance( -- effect even though we're bailing out, so that cWU/cDU are called. if type(instance.componentDidMount) == "function" then if __DEV__ and enableDoubleInvokingEffects then - workInProgress.flags = - bit32.bor(workInProgress.flags, MountLayoutDev, Update) + workInProgress.flags = bit32.bor(workInProgress.flags, MountLayoutDev, Update) else workInProgress.flags = bit32.bor(workInProgress.flags, Update) end @@ -1235,14 +1121,8 @@ local function updateClassInstance( nextContext = getMaskedContext(workInProgress, nextUnmaskedContext) end - local hasNewLifecycles = ( - getDerivedStateFromProps ~= nil - and type(getDerivedStateFromProps) == "function" - ) - or ( - instance.getSnapshotBeforeUpdate ~= nil - and type(instance.getSnapshotBeforeUpdate) == "function" - ) + local hasNewLifecycles = (getDerivedStateFromProps ~= nil and type(getDerivedStateFromProps) == "function") + or (instance.getSnapshotBeforeUpdate ~= nil and type(instance.getSnapshotBeforeUpdate) == "function") -- Note: During these life-cycles, instance.props/instance.state are what -- ever the previously attempted to render - not the "current". However, @@ -1257,10 +1137,7 @@ local function updateClassInstance( instance.UNSAFE_componentWillReceiveProps ~= nil and type(instance.UNSAFE_componentWillReceiveProps) == "function" ) - or ( - instance.componentWillReceiveProps ~= nil - and type(instance.componentWillReceiveProps) == "function" - ) + or (instance.componentWillReceiveProps ~= nil and type(instance.componentWillReceiveProps) == "function") ) then if unresolvedOldProps ~= unresolvedNewProps or oldContext ~= nextContext then @@ -1284,54 +1161,26 @@ local function updateClassInstance( then -- If an update was already in progress, we should schedule an Update -- effect even though we're bailing out, so that cWU/cDU are called. - if - instance.componentDidUpdate ~= nil - and type(instance.componentDidUpdate) == "function" - then - if - unresolvedOldProps ~= current.memoizedProps - or oldState ~= current.memoizedState - then + if instance.componentDidUpdate ~= nil and type(instance.componentDidUpdate) == "function" then + if unresolvedOldProps ~= current.memoizedProps or oldState ~= current.memoizedState then workInProgress.flags = bit32.bor(workInProgress.flags, Update) end end - if - instance.getSnapshotBeforeUpdate ~= nil - and type(instance.getSnapshotBeforeUpdate) == "function" - then - if - unresolvedOldProps ~= current.memoizedProps - or oldState ~= current.memoizedState - then + if instance.getSnapshotBeforeUpdate ~= nil and type(instance.getSnapshotBeforeUpdate) == "function" then + if unresolvedOldProps ~= current.memoizedProps or oldState ~= current.memoizedState then workInProgress.flags = bit32.bor(workInProgress.flags, Snapshot) end end return false end - if - getDerivedStateFromProps ~= nil - and type(getDerivedStateFromProps) == "function" - then - applyDerivedStateFromProps( - workInProgress, - ctor, - getDerivedStateFromProps, - newProps - ) + if getDerivedStateFromProps ~= nil and type(getDerivedStateFromProps) == "function" then + applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps) newState = workInProgress.memoizedState end local shouldUpdate = checkHasForceUpdateAfterProcessing() - or checkShouldComponentUpdate( - workInProgress, - ctor, - oldProps, - newProps, - oldState, - newState, - nextContext - ) + or checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) if shouldUpdate then -- In order to support react-lifecycles-compat polyfilled components, @@ -1339,20 +1188,11 @@ local function updateClassInstance( if not hasNewLifecycles and ( - ( - instance.UNSAFE_componentWillUpdate ~= nil - and type(instance.UNSAFE_componentWillUpdate) == "function" - ) - or ( - instance.componentWillUpdate ~= nil - and type(instance.componentWillUpdate) == "function" - ) + (instance.UNSAFE_componentWillUpdate ~= nil and type(instance.UNSAFE_componentWillUpdate) == "function") + or (instance.componentWillUpdate ~= nil and type(instance.componentWillUpdate) == "function") ) then - if - instance.componentWillUpdate ~= nil - and type(instance.componentWillUpdate) == "function" - then + if instance.componentWillUpdate ~= nil and type(instance.componentWillUpdate) == "function" then -- deviation: Call with ":" so that the method receives self instance:componentWillUpdate(newProps, newState, nextContext) end @@ -1364,40 +1204,22 @@ local function updateClassInstance( instance:UNSAFE_componentWillUpdate(newProps, newState, nextContext) end end - if - instance.componentDidUpdate ~= nil - and type(instance.componentDidUpdate) == "function" - then + if instance.componentDidUpdate ~= nil and type(instance.componentDidUpdate) == "function" then workInProgress.flags = bit32.bor(workInProgress.flags, Update) end - if - instance.getSnapshotBeforeUpdate ~= nil - and type(instance.getSnapshotBeforeUpdate) == "function" - then + if instance.getSnapshotBeforeUpdate ~= nil and type(instance.getSnapshotBeforeUpdate) == "function" then workInProgress.flags = bit32.bor(workInProgress.flags, Snapshot) end else -- If an update was already in progress, we should schedule an Update -- effect even though we're bailing out, so that cWU/cDU are called. - if - instance.componentDidUpdate ~= nil - and type(instance.componentDidUpdate) == "function" - then - if - unresolvedOldProps ~= current.memoizedProps - or oldState ~= current.memoizedState - then + if instance.componentDidUpdate ~= nil and type(instance.componentDidUpdate) == "function" then + if unresolvedOldProps ~= current.memoizedProps or oldState ~= current.memoizedState then workInProgress.flags = bit32.bor(workInProgress.flags, Update) end end - if - instance.getSnapshotBeforeUpdate ~= nil - and type(instance.getSnapshotBeforeUpdate) == "function" - then - if - unresolvedOldProps ~= current.memoizedProps - or oldState ~= current.memoizedState - then + if instance.getSnapshotBeforeUpdate ~= nil and type(instance.getSnapshotBeforeUpdate) == "function" then + if unresolvedOldProps ~= current.memoizedProps or oldState ~= current.memoizedState then workInProgress.flags = bit32.bor(workInProgress.flags, Snapshot) end end diff --git a/modules/react-reconciler/src/ReactFiberCommitWork.new.lua b/modules/react-reconciler/src/ReactFiberCommitWork.new.lua index e5d09a08..2e695ba9 100644 --- a/modules/react-reconciler/src/ReactFiberCommitWork.new.lua +++ b/modules/react-reconciler/src/ReactFiberCommitWork.new.lua @@ -145,8 +145,7 @@ local currentDebugFiberInDEV = ReactCurrentFiber.current local resetCurrentDebugFiberInDEV = ReactCurrentFiber.resetCurrentFiber local setCurrentDebugFiberInDEV = ReactCurrentFiber.setCurrentFiber local onCommitUnmount = require("./ReactFiberDevToolsHook.new.lua").onCommitUnmount -local resolveDefaultProps = - require("./ReactFiberLazyComponent.new.lua").resolveDefaultProps +local resolveDefaultProps = require("./ReactFiberLazyComponent.new.lua").resolveDefaultProps local ReactProfilerTimer = require("./ReactProfilerTimer.new.lua") local startLayoutEffectTimer = ReactProfilerTimer.startLayoutEffectTimer local recordPassiveEffectDuration = ReactProfilerTimer.recordPassiveEffectDuration @@ -203,21 +202,12 @@ end -- deviation: stub to allow dependency injection that breaks circular dependency local function schedulePassiveEffectCallback(): () - console.warn( - "ReactFiberCommitWork: schedulePassiveEffectCallback causes a dependency cycle\n" - .. debug.traceback() - ) + console.warn("ReactFiberCommitWork: schedulePassiveEffectCallback causes a dependency cycle\n" .. debug.traceback()) end -- deviation: stub to allow dependency injection that breaks circular dependency -local function captureCommitPhaseError( - rootFiber: Fiber, - sourceFiber: Fiber | nil, - error_: any? -): () - console.warn( - "ReactFiberCommitWork: captureCommitPhaseError causes a dependency cycle" - ) +local function captureCommitPhaseError(rootFiber: Fiber, sourceFiber: Fiber | nil, error_: any?): () + console.warn("ReactFiberCommitWork: captureCommitPhaseError causes a dependency cycle") error(error_) end @@ -230,8 +220,7 @@ local HookPassive = ReactHookEffectTags.Passive local didWarnAboutReassigningPropsRef local didWarnAboutReassigningProps = function() if not didWarnAboutReassigningPropsRef then - didWarnAboutReassigningPropsRef = - require("./ReactFiberBeginWork.new.lua").didWarnAboutReassigningProps + didWarnAboutReassigningPropsRef = require("./ReactFiberBeginWork.new.lua").didWarnAboutReassigningProps end return didWarnAboutReassigningPropsRef end @@ -256,11 +245,7 @@ local nearestProfilerOnStack: Fiber | nil = nil local function callComponentWillUnmountWithTimer(current, instance) instance.props = current.memoizedProps instance.state = current.memoizedState - if - enableProfilerTimer - and enableProfilerCommitHooks - and bit32.band(current.mode, ProfileMode) ~= 0 - then + if enableProfilerTimer and enableProfilerCommitHooks and bit32.band(current.mode, ProfileMode) ~= 0 then -- ROBLOX performance? we could hoist start...Timer() out and eliminate the anon function, but then the timer would incldue the pcall overhead local ok, exception = xpcall(function() startLayoutEffectTimer() @@ -280,14 +265,9 @@ local function callComponentWillUnmountWithTimer(current, instance) end -- Capture errors so they don't interrupt unmounting. -function safelyCallComponentWillUnmount( - current: Fiber, - instance: any, - nearestMountedAncestor -): () +function safelyCallComponentWillUnmount(current: Fiber, instance: any, nearestMountedAncestor): () -- ROBLOX performance: eliminate the __DEV__ and invokeGuardedCallback, like React 18 has done - local ok, error_ = - xpcall(callComponentWillUnmountWithTimer, describeError, current, instance) + local ok, error_ = xpcall(callComponentWillUnmountWithTimer, describeError, current, instance) if not ok then captureCommitPhaseError(current, nearestMountedAncestor, error_) @@ -310,11 +290,7 @@ local function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber): ( end end -local function safelyCallDestroy( - current: Fiber, - nearestMountedAncestor: Fiber | nil, - destroy: () -> () -): () +local function safelyCallDestroy(current: Fiber, nearestMountedAncestor: Fiber | nil, destroy: () -> ()): () -- ROBLOX performance: eliminate the __DEV__ and invokeGuardedCallback, like React 18 has done local ok, error_ = xpcall(destroy, describeError) if not ok then @@ -322,10 +298,7 @@ local function safelyCallDestroy( end end -local function commitBeforeMutationLifeCycles( - current: Fiber | nil, - finishedWork: Fiber -): () +local function commitBeforeMutationLifeCycles(current: Fiber | nil, finishedWork: Fiber): () if finishedWork.tag == FunctionComponent or finishedWork.tag == ForwardRef @@ -343,10 +316,7 @@ local function commitBeforeMutationLifeCycles( -- but instead we rely on them being set during last render. -- TODO: revisit this when we implement resuming. if __DEV__ then - if - finishedWork.type == finishedWork.elementType - and not didWarnAboutReassigningProps - then + if finishedWork.type == finishedWork.elementType and not didWarnAboutReassigningProps then if instance.props ~= finishedWork.memoizedProps then console.error( "Expected %s props to match memoized props before " @@ -415,11 +385,7 @@ local function commitBeforeMutationLifeCycles( ) end -local function commitHookEffectListUnmount( - flags: HookFlags, - finishedWork: Fiber, - nearestMountedAncestor: Fiber? -) +local function commitHookEffectListUnmount(flags: HookFlags, finishedWork: Fiber, nearestMountedAncestor: Fiber?) local updateQueue: FunctionComponentUpdateQueue | nil = finishedWork.updateQueue local lastEffect if updateQueue ~= nil then @@ -444,8 +410,7 @@ local function commitHookEffectListUnmount( end local function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) - local updateQueue: FunctionComponentUpdateQueue | nil = - finishedWork.updateQueue :: any + local updateQueue: FunctionComponentUpdateQueue | nil = finishedWork.updateQueue :: any local lastEffect = if updateQueue ~= nil then updateQueue.lastEffect else nil if lastEffect ~= nil then local firstEffect = lastEffect.next @@ -487,8 +452,7 @@ function commitProfilerPassiveEffect(finishedRoot: FiberRoot, finishedWork: Fibe if enableProfilerTimer and enableProfilerCommitHooks then if finishedWork.tag == Profiler then local passiveEffectDuration = finishedWork.stateNode.passiveEffectDuration - local id, onPostCommit = - finishedWork.memoizedProps.id, finishedWork.memoizedProps.onPostCommit + local id, onPostCommit = finishedWork.memoizedProps.id, finishedWork.memoizedProps.onPostCommit -- This value will still reflect the previous commit phase. -- It does not get reset until the start of the next commit phase. @@ -592,20 +556,10 @@ local function recursivelyCommitLayoutEffects( if __DEV__ then local prevCurrentFiberInDEV = currentDebugFiberInDEV setCurrentDebugFiberInDEV(finishedWork) - invokeGuardedCallback( - nil, - commitLayoutEffectsForProfiler, - nil, - finishedWork, - finishedRoot - ) + invokeGuardedCallback(nil, commitLayoutEffectsForProfiler, nil, finishedWork, finishedRoot) if hasCaughtError() then local error_ = clearCaughtError() - captureCommitPhaseError( - finishedWork, - finishedWork.return_, - error_ - ) + captureCommitPhaseError(finishedWork, finishedWork.return_, error_) end if prevCurrentFiberInDEV ~= nil then setCurrentDebugFiberInDEV(prevCurrentFiberInDEV) @@ -614,18 +568,9 @@ local function recursivelyCommitLayoutEffects( end else -- ROBLOX TODO? pass in captureCommitPhaseError? - local ok, error_ = xpcall( - commitLayoutEffectsForProfiler, - describeError, - finishedWork, - finishedRoot - ) + local ok, error_ = xpcall(commitLayoutEffectsForProfiler, describeError, finishedWork, finishedRoot) if not ok then - captureCommitPhaseError( - finishedWork, - finishedWork.return_, - error_ - ) + captureCommitPhaseError(finishedWork, finishedWork.return_, error_) end end end @@ -726,12 +671,7 @@ local function recursivelyCommitLayoutEffects( local primaryFlags = bit32.band(flags, bit32.bor(Update, Callback)) if primaryFlags ~= NoFlags then - if - tag == FunctionComponent - or tag == ForwardRef - or tag == SimpleMemoComponent - or tag == Block - then + if tag == FunctionComponent or tag == ForwardRef or tag == SimpleMemoComponent or tag == Block then if enableProfilerTimer and enableProfilerCommitHooks @@ -740,10 +680,7 @@ local function recursivelyCommitLayoutEffects( -- ROBLOX try local ok, error_ = xpcall(function() startLayoutEffectTimer() - commitHookEffectListMount( - bit32.bor(HookLayout, HookHasEffect), - finishedWork - ) + commitHookEffectListMount(bit32.bor(HookLayout, HookHasEffect), finishedWork) end, describeError) -- ROBLOX finally recordLayoutEffectDuration(finishedWork) @@ -751,10 +688,7 @@ local function recursivelyCommitLayoutEffects( error(error_) end else - commitHookEffectListMount( - bit32.bor(HookLayout, HookHasEffect), - finishedWork - ) + commitHookEffectListMount(bit32.bor(HookLayout, HookHasEffect), finishedWork) end if bit32.band(finishedWork.subtreeFlags, PassiveMask) ~= NoFlags then @@ -808,8 +742,7 @@ function commitLayoutEffectsForProfiler(finishedWork: Fiber, finishedRoot: Fiber local flags = finishedWork.flags local current = finishedWork.alternate - local onCommit, onRender = - finishedWork.memoizedProps.onCommit, finishedWork.memoizedProps.onRender + local onCommit, onRender = finishedWork.memoizedProps.onCommit, finishedWork.memoizedProps.onRender local effectDuration = finishedWork.stateNode.effectDuration local commitTime = getCommitTime() @@ -880,10 +813,7 @@ function commitLayoutEffectsForClassComponent(finishedWork: Fiber) -- but instead we rely on them being set during last render. -- TODO: revisit this when we implement resuming. if __DEV__ then - if - finishedWork.type == finishedWork.elementType - and not didWarnAboutReassigningProps - then + if finishedWork.type == finishedWork.elementType and not didWarnAboutReassigningProps then if instance.props ~= finishedWork.memoizedProps then console.error( "Expected %s props to match memoized props before " @@ -926,18 +856,14 @@ function commitLayoutEffectsForClassComponent(finishedWork: Fiber) instance:componentDidMount() end else - local prevProps = finishedWork.elementType == finishedWork.type - and current.memoizedProps + local prevProps = finishedWork.elementType == finishedWork.type and current.memoizedProps or resolveDefaultProps(finishedWork.type, current.memoizedProps) local prevState = current.memoizedState -- We could update instance props and state here, -- but instead we rely on them being set during last render. -- TODO: revisit this when we implement resuming. if __DEV__ then - if - finishedWork.type == finishedWork.elementType - and not didWarnAboutReassigningProps - then + if finishedWork.type == finishedWork.elementType and not didWarnAboutReassigningProps then if instance.props ~= finishedWork.memoizedProps then console.error( "Expected %s props to match memoized props before " @@ -968,11 +894,7 @@ function commitLayoutEffectsForClassComponent(finishedWork: Fiber) local ok, result = xpcall(function() startLayoutEffectTimer() -- deviation: Call with ":" so that the method receives self - instance:componentDidUpdate( - prevProps, - prevState, - instance.__reactInternalSnapshotBeforeUpdate - ) + instance:componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate) end, describeError) -- finally recordLayoutEffectDuration(finishedWork) @@ -981,11 +903,7 @@ function commitLayoutEffectsForClassComponent(finishedWork: Fiber) end else -- deviation: Call with ":" so that the method receives self - instance:componentDidUpdate( - prevProps, - prevState, - instance.__reactInternalSnapshotBeforeUpdate - ) + instance:componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate) end end end @@ -995,10 +913,7 @@ function commitLayoutEffectsForClassComponent(finishedWork: Fiber) local updateQueue: UpdateQueue | nil = finishedWork.updateQueue if updateQueue ~= nil then if __DEV__ then - if - finishedWork.type == finishedWork.elementType - and not didWarnAboutReassigningProps - then + if finishedWork.type == finishedWork.elementType and not didWarnAboutReassigningProps then if instance.props ~= finishedWork.memoizedProps then console.error( "Expected %s props to match memoized props before " @@ -1196,18 +1111,10 @@ function commitUnmount( and bit32.band(current.mode, ProfileMode) ~= 0 then startLayoutEffectTimer() - safelyCallDestroy( - current, - nearestMountedAncestor, - effect.destroy - ) + safelyCallDestroy(current, nearestMountedAncestor, effect.destroy) recordLayoutEffectDuration(current) else - safelyCallDestroy( - current, - nearestMountedAncestor, - effect.destroy - ) + safelyCallDestroy(current, nearestMountedAncestor, effect.destroy) end end end @@ -1231,12 +1138,7 @@ function commitUnmount( -- We are also not using this parent because -- the portal will get pushed immediately. if supportsMutation then - unmountHostComponents( - finishedRoot, - current, - nearestMountedAncestor, - renderPriorityLevel - ) + unmountHostComponents(finishedRoot, current, nearestMountedAncestor, renderPriorityLevel) elseif supportsPersistence then unimplemented("emptyPortalContainer") -- emptyPortalContainer(current) @@ -1390,8 +1292,7 @@ local function getHostParentFiber(fiber: Fiber): Fiber -- ROBLOX deviation START: use React 18 approach, which Luau understands better than invariant error( Error.new( - "Expected to find a host parent. This error is likely caused by a bug " - .. "in React. Please file an issue." + "Expected to find a host parent. This error is likely caused by a bug " .. "in React. Please file an issue." ) ) -- ROBLOX deviation END @@ -1422,11 +1323,7 @@ function getHostSibling(fiber: Fiber): Instance? end (node.sibling :: Fiber).return_ = node.return_ :: Fiber node = node.sibling :: Fiber - while - node.tag ~= HostComponent - and node.tag ~= HostText - and node.tag ~= DehydratedFragment - do + while node.tag ~= HostComponent and node.tag ~= HostText and node.tag ~= DehydratedFragment do -- If it is not host node and, we might have a host node inside it. -- Try to search down until we find one. if bit32.band(node.flags, Placement) ~= 0 then @@ -1485,8 +1382,7 @@ local function commitPlacement(finishedWork: Fiber) -- eslint-disable-next-line-no-fallthrough invariant( false, - "Invalid host parent fiber. This error is likely caused by a bug " - .. "in React. Please file an issue." + "Invalid host parent fiber. This error is likely caused by a bug " .. "in React. Please file an issue." ) end if bit32.band(parentFiber.flags, ContentReset) ~= 0 then @@ -1506,11 +1402,7 @@ local function commitPlacement(finishedWork: Fiber) end end -function insertOrAppendPlacementNodeIntoContainer( - node: Fiber, - before: Instance?, - parent: Container -) +function insertOrAppendPlacementNodeIntoContainer(node: Fiber, before: Instance?, parent: Container) local tag = node.tag local isHost = tag == HostComponent or tag == HostText -- ROBLOX performance: avoid always-false compare for Roblox renderer in hot path @@ -1626,12 +1518,7 @@ function unmountHostComponents( end if node.tag == HostComponent or node.tag == HostText then - commitNestedUnmounts( - finishedRoot, - node, - nearestMountedAncestor, - renderPriorityLevel - ) + commitNestedUnmounts(finishedRoot, node, nearestMountedAncestor, renderPriorityLevel) -- After all the children have unmounted, it is now safe to remove the -- node from the tree. if currentParentIsContainer then @@ -1754,12 +1641,7 @@ local function commitDeletion( -- if supportsMutation then -- Recursively delete all host nodes from the parent. -- Detach refs and call componentWillUnmount() on the whole subtree. - unmountHostComponents( - finishedRoot, - current, - nearestMountedAncestor, - renderPriorityLevel - ) + unmountHostComponents(finishedRoot, current, nearestMountedAncestor, renderPriorityLevel) -- else -- -- Detach refs and call componentWillUnmount() on the whole subtree. -- commitNestedUnmounts( @@ -1859,19 +1741,11 @@ local function commitWork(current: Fiber | nil, finishedWork: Fiber) -- This prevents sibling component effects from interfering with each other, -- e.g. a destroy function in one component should never override a ref set -- by a create function in another component during the same commit. - if - enableProfilerTimer - and enableProfilerCommitHooks - and bit32.band(finishedWork.mode, ProfileMode) ~= 0 - then + if enableProfilerTimer and enableProfilerCommitHooks and bit32.band(finishedWork.mode, ProfileMode) ~= 0 then -- ROBLOX try local ok, result = xpcall(function() startLayoutEffectTimer() - commitHookEffectListUnmount( - bit32.bor(HookLayout, HookHasEffect), - finishedWork, - finishedWork.return_ - ) + commitHookEffectListUnmount(bit32.bor(HookLayout, HookHasEffect), finishedWork, finishedWork.return_) end, describeError) -- ROBLOX finally recordLayoutEffectDuration(finishedWork) @@ -1879,11 +1753,7 @@ local function commitWork(current: Fiber | nil, finishedWork: Fiber) error(result) end else - commitHookEffectListUnmount( - bit32.bor(HookLayout, HookHasEffect), - finishedWork, - finishedWork.return_ - ) + commitHookEffectListUnmount(bit32.bor(HookLayout, HookHasEffect), finishedWork, finishedWork.return_) end return elseif finishedWork.tag == ClassComponent then @@ -1907,14 +1777,7 @@ local function commitWork(current: Fiber | nil, finishedWork: Fiber) local updatePayload: nil | UpdatePayload = finishedWork.updateQueue :: any finishedWork.updateQueue = nil if updatePayload ~= nil then - commitUpdate( - instance, - updatePayload, - type, - oldProps, - newProps, - finishedWork - ) + commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork) end end return @@ -1975,10 +1838,7 @@ local function commitWork(current: Fiber | nil, finishedWork: Fiber) -- return -- end -- break - elseif - finishedWork.tag == OffscreenComponent - or finishedWork.tag == LegacyHiddenComponent - then + elseif finishedWork.tag == OffscreenComponent or finishedWork.tag == LegacyHiddenComponent then local newState: OffscreenState | nil = finishedWork.memoizedState local isHidden = newState ~= nil hideOrUnhideAllChildren(finishedWork, isHidden) @@ -2021,10 +1881,7 @@ function commitSuspenseComponent(finishedWork: Fiber) end elseif __DEV__ then if suspenseCallback ~= nil then - console.error( - "Unexpected type for suspenseCallback: %s", - tostring(suspenseCallback) - ) + console.error("Unexpected type for suspenseCallback: %s", tostring(suspenseCallback)) end end end @@ -2122,43 +1979,24 @@ local function commitPassiveUnmount(finishedWork: Fiber): () or finishedWork.tag == SimpleMemoComponent or finishedWork.tag == Block then - if - enableProfilerTimer - and enableProfilerCommitHooks - and bit32.band(finishedWork.mode, ProfileMode) ~= 0 - then + if enableProfilerTimer and enableProfilerCommitHooks and bit32.band(finishedWork.mode, ProfileMode) ~= 0 then startPassiveEffectTimer() - commitHookEffectListUnmount( - bit32.bor(HookPassive, HookHasEffect), - finishedWork, - finishedWork.return_ - ) + commitHookEffectListUnmount(bit32.bor(HookPassive, HookHasEffect), finishedWork, finishedWork.return_) recordPassiveEffectDuration(finishedWork) else - commitHookEffectListUnmount( - bit32.bor(HookPassive, HookHasEffect), - finishedWork, - finishedWork.return_ - ) + commitHookEffectListUnmount(bit32.bor(HookPassive, HookHasEffect), finishedWork, finishedWork.return_) end end end -local function commitPassiveUnmountInsideDeletedTree( - current: Fiber, - nearestMountedAncestor: Fiber | nil -): () +local function commitPassiveUnmountInsideDeletedTree(current: Fiber, nearestMountedAncestor: Fiber | nil): () if current.tag == FunctionComponent or current.tag == ForwardRef or current.tag == SimpleMemoComponent or current.tag == Block then - if - enableProfilerTimer - and enableProfilerCommitHooks - and bit32.band(current.mode, ProfileMode) ~= 0 - then + if enableProfilerTimer and enableProfilerCommitHooks and bit32.band(current.mode, ProfileMode) ~= 0 then startPassiveEffectTimer() commitHookEffectListUnmount(HookPassive, current, nearestMountedAncestor) recordPassiveEffectDuration(current) @@ -2175,19 +2013,11 @@ local function commitPassiveMount(finishedRoot: FiberRoot, finishedWork: Fiber): or finishedWork.tag == SimpleMemoComponent or finishedWork.tag == Block then - if - enableProfilerTimer - and enableProfilerCommitHooks - and bit32.band(finishedWork.mode, ProfileMode) ~= 0 - then + if enableProfilerTimer and enableProfilerCommitHooks and bit32.band(finishedWork.mode, ProfileMode) ~= 0 then startPassiveEffectTimer() -- ROBLOX try - local ok, error_ = xpcall( - commitHookEffectListMount, - describeError, - bit32.bor(HookPassive, HookHasEffect), - finishedWork - ) + local ok, error_ = + xpcall(commitHookEffectListMount, describeError, bit32.bor(HookPassive, HookHasEffect), finishedWork) -- ROBLOX finally recordPassiveEffectDuration(finishedWork) if not ok then @@ -2209,13 +2039,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): () or fiber.tag == SimpleMemoComponent or fiber.tag == Block then - invokeGuardedCallback( - nil, - commitHookEffectListMount, - nil, - bit32.bor(HookLayout, HookHasEffect), - fiber - ) + invokeGuardedCallback(nil, commitHookEffectListMount, nil, bit32.bor(HookLayout, HookHasEffect), fiber) if hasCaughtError() then local mountError = clearCaughtError() captureCommitPhaseError(fiber, fiber.return_, mountError) @@ -2241,13 +2065,7 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): () or fiber.tag == SimpleMemoComponent or fiber.tag == Block then - invokeGuardedCallback( - nil, - commitHookEffectListMount, - nil, - bit32.bor(HookPassive, HookHasEffect), - fiber - ) + invokeGuardedCallback(nil, commitHookEffectListMount, nil, bit32.bor(HookPassive, HookHasEffect), fiber) if hasCaughtError() then local mountError = clearCaughtError() captureCommitPhaseError(fiber, fiber.return_, mountError) diff --git a/modules/react-reconciler/src/ReactFiberCompleteWork.new.lua b/modules/react-reconciler/src/ReactFiberCompleteWork.new.lua index 8aa40116..d1a98dc8 100644 --- a/modules/react-reconciler/src/ReactFiberCompleteWork.new.lua +++ b/modules/react-reconciler/src/ReactFiberCompleteWork.new.lua @@ -40,8 +40,7 @@ local ReactFiberOffscreenComponent = require("./ReactFiberOffscreenComponent") type OffscreenState = ReactFiberOffscreenComponent.OffscreenState local ReactMutableSource = require("./ReactMutableSource.new.lua") -local resetMutableSourceWorkInProgressVersions = - ReactMutableSource.resetWorkInProgressVersions +local resetMutableSourceWorkInProgressVersions = ReactMutableSource.resetWorkInProgressVersions -- local {now} = require("./SchedulerWithReactIntegration/new") @@ -125,8 +124,7 @@ local popHostContainer = ReactFiberHostContext.popHostContainer local ReactFiberSuspenseContext = require("./ReactFiberSuspenseContext.new.lua") local popSuspenseContext = ReactFiberSuspenseContext.popSuspenseContext local suspenseStackCursor = ReactFiberSuspenseContext.suspenseStackCursor -local InvisibleParentSuspenseContext = - ReactFiberSuspenseContext.InvisibleParentSuspenseContext +local InvisibleParentSuspenseContext = ReactFiberSuspenseContext.InvisibleParentSuspenseContext local hasSuspenseContext = ReactFiberSuspenseContext.hasSuspenseContext type SuspenseContext = ReactFiberSuspenseContext.SuspenseContext -- local pushSuspenseContext = ReactFiberSuspenseContext.pushSuspenseContext @@ -142,15 +140,12 @@ local popTopLevelLegacyContextObject = ReactFiberContext.popTopLevelContextObjec local popProvider = require("./ReactFiberNewContext.new.lua").popProvider local ReactFiberHydrationContext = require("./ReactFiberHydrationContext.new.lua") -local prepareToHydrateHostSuspenseInstance = - ReactFiberHydrationContext.prepareToHydrateHostSuspenseInstance +local prepareToHydrateHostSuspenseInstance = ReactFiberHydrationContext.prepareToHydrateHostSuspenseInstance local popHydrationState = ReactFiberHydrationContext.popHydrationState local resetHydrationState = ReactFiberHydrationContext.resetHydrationState -- local getIsHydrating = ReactFiberHydrationContext.getIsHydrating -local prepareToHydrateHostInstance = - ReactFiberHydrationContext.prepareToHydrateHostInstance -local prepareToHydrateHostTextInstance = - ReactFiberHydrationContext.prepareToHydrateHostTextInstance +local prepareToHydrateHostInstance = ReactFiberHydrationContext.prepareToHydrateHostInstance +local prepareToHydrateHostTextInstance = ReactFiberHydrationContext.prepareToHydrateHostTextInstance local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags local enableSchedulerTracing = ReactFeatureFlags.enableSchedulerTracing local enableSuspenseCallback = ReactFeatureFlags.enableSuspenseCallback @@ -286,14 +281,8 @@ if supportsMutation then -- TODO: Experiencing an error where oldProps is nil. Suggests a host -- component is hitting the resume path. Figure out why. Possibly -- related to `hidden`. - local updatePayload = prepareUpdate( - instance, - type, - oldProps, - newProps, - rootContainerInstance, - currentHostContext - ) + local updatePayload = + prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext) -- TODO: Type this specific to this type of component. workInProgress.updateQueue = updatePayload -- If the update payload indicates that there is a change or if there @@ -302,12 +291,7 @@ if supportsMutation then markUpdate(workInProgress) end end - function updateHostText( - current: Fiber, - workInProgress: Fiber, - oldText: string, - newText: string - ) + function updateHostText(current: Fiber, workInProgress: Fiber, oldText: string, newText: string) -- If the text differs, mark it as an update. All the work in done in commitWork. if oldText ~= newText then markUpdate(workInProgress) @@ -722,10 +706,7 @@ local function bubbleProperties(completedWork: Fiber) if not didBailout then -- Bubble up the earliest expiration time. - if - enableProfilerTimer - and bit32.band(completedWork.mode, ProfileMode) ~= NoMode - then + if enableProfilerTimer and bit32.band(completedWork.mode, ProfileMode) ~= NoMode then -- In profiling mode, resetChildExpirationTime is also used to reset -- profiler durations. local actualDuration = completedWork.actualDuration @@ -733,8 +714,7 @@ local function bubbleProperties(completedWork: Fiber) local child = completedWork.child while child ~= nil do - newChildLanes = - mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes)) + newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes)) subtreeFlags = bit32.bor(subtreeFlags, child.subtreeFlags) subtreeFlags = bit32.bor(subtreeFlags, child.flags) @@ -762,8 +742,7 @@ local function bubbleProperties(completedWork: Fiber) -- newChildLanes, -- mergeLanes(child.lanes, child.childLanes) -- ) - newChildLanes = - bit32.bor(newChildLanes, bit32.bor(child.lanes, child.childLanes)) + newChildLanes = bit32.bor(newChildLanes, bit32.bor(child.lanes, child.childLanes)) subtreeFlags = bit32.bor(subtreeFlags, child.subtreeFlags) subtreeFlags = bit32.bor(subtreeFlags, child.flags) @@ -781,27 +760,21 @@ local function bubbleProperties(completedWork: Fiber) completedWork.subtreeFlags = bit32.bor(completedWork.subtreeFlags, subtreeFlags) else -- Bubble up the earliest expiration time. - if - enableProfilerTimer - and bit32.band(completedWork.mode, ProfileMode) ~= NoMode - then + if enableProfilerTimer and bit32.band(completedWork.mode, ProfileMode) ~= NoMode then -- In profiling mode, resetChildExpirationTime is also used to reset -- profiler durations. local treeBaseDuration = completedWork.selfBaseDuration local child = completedWork.child while child ~= nil do - newChildLanes = - mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes)) + newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes)) -- "Static" flags share the lifetime of the fiber/hook they belong to, -- so we should bubble those up even during a bailout. All the other -- flags have a lifetime only of a single render + commit, so we should -- ignore them. - subtreeFlags = - bit32.bor(subtreeFlags, bit32.band(child.subtreeFlags, StaticMask)) - subtreeFlags = - bit32.bor(subtreeFlags, bit32.band(child.flags, StaticMask)) + subtreeFlags = bit32.bor(subtreeFlags, bit32.band(child.subtreeFlags, StaticMask)) + subtreeFlags = bit32.bor(subtreeFlags, bit32.band(child.flags, StaticMask)) treeBaseDuration += child.treeBaseDuration child = child.sibling @@ -816,17 +789,14 @@ local function bubbleProperties(completedWork: Fiber) -- newChildLanes, -- mergeLanes(child.lanes, child.childLanes) -- ) - newChildLanes = - bit32.bor(newChildLanes, bit32.bor(child.lanes, child.childLanes)) + newChildLanes = bit32.bor(newChildLanes, bit32.bor(child.lanes, child.childLanes)) -- "Static" flags share the lifetime of the fiber/hook they belong to, -- so we should bubble those up even during a bailout. All the other -- flags have a lifetime only of a single render + commit, so we should -- ignore them. - subtreeFlags = - bit32.bor(subtreeFlags, bit32.band(child.subtreeFlags, StaticMask)) - subtreeFlags = - bit32.bor(subtreeFlags, bit32.band(child.flags, StaticMask)) + subtreeFlags = bit32.bor(subtreeFlags, bit32.band(child.subtreeFlags, StaticMask)) + subtreeFlags = bit32.bor(subtreeFlags, bit32.band(child.flags, StaticMask)) -- ROBLOX note: this was missed in the "new" version of the file in React 17, but is fixed in React 18 -- Update the return pointer so the tree is consistent. This is a code @@ -852,11 +822,7 @@ end -- workInProgress: Fiber, -- renderLanes: Lanes -- ): Fiber | nil -local function completeWork( - current, - workInProgress: Fiber, - renderLanes: Lanes -): Fiber | nil +local function completeWork(current, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil local newProps = workInProgress.pendingProps if @@ -914,13 +880,7 @@ local function completeWork( local rootContainerInstance = getRootHostContainer() local type = workInProgress.type if current ~= nil and workInProgress.stateNode ~= nil then - updateHostComponent( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ) + updateHostComponent(current, workInProgress, type, newProps, rootContainerInstance) if current.ref ~= workInProgress.ref then markRef(workInProgress) @@ -946,25 +906,14 @@ local function completeWork( if wasHydrated then -- TODO: Move this and createInstance step into the beginPhase -- to consolidate. - if - prepareToHydrateHostInstance( - workInProgress, - rootContainerInstance, - currentHostContext - ) - then + if prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext) then -- If changes to the hydrated node need to be applied at the -- commit-phase we mark this as such. markUpdate(workInProgress) end else - local instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress - ) + local instance = + createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress) appendAllChildren(instance, workInProgress, false, false) @@ -973,15 +922,7 @@ local function completeWork( -- Certain renderers require commit-time effects for initial mount. -- (eg DOM renderer supports auto-focus for certain elements). -- Make sure such renderers get scheduled for later work. - if - finalizeInitialChildren( - instance, - type, - newProps, - rootContainerInstance, - currentHostContext - ) - then + if finalizeInitialChildren(instance, type, newProps, rootContainerInstance, currentHostContext) then markUpdate(workInProgress) end end @@ -1017,12 +958,8 @@ local function completeWork( markUpdate(workInProgress) end else - workInProgress.stateNode = createTextInstance( - newText, - rootContainerInstance, - currentHostContext, - workInProgress - ) + workInProgress.stateNode = + createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress) end end bubbleProperties(workInProgress) @@ -1043,10 +980,7 @@ local function completeWork( local newFlags = flags -- Call onRender any time this fiber or its subtree are worked on. - if - bit32.band(flags, PerformedWork) ~= NoFlags - or bit32.band(subtreeFlags, PerformedWork) ~= NoFlags - then + if bit32.band(flags, PerformedWork) ~= NoFlags or bit32.band(subtreeFlags, PerformedWork) ~= NoFlags then newFlags = bit32.bor(newFlags, OnRenderFlag) end @@ -1057,8 +991,7 @@ local function completeWork( -- contains layout effects, like we do for passive effects. if bit32.band(flags, bit32.bor(LayoutMask, Deletion)) ~= NoFlags - or bit32.band(subtreeFlags, bit32.bor(LayoutMask, Deletion)) - ~= NoFlags + or bit32.band(subtreeFlags, bit32.bor(LayoutMask, Deletion)) ~= NoFlags then newFlags = bit32.bor(newFlags, OnCommitFlag) end @@ -1066,10 +999,7 @@ local function completeWork( -- Call onPostCommit only if the subtree contains passive work. -- Don't have to check for deletions, because Deletion is already -- a passive flag. - if - bit32.band(flags, PassiveMask) ~= NoFlags - or bit32.band(subtreeFlags, PassiveMask) ~= NoFlags - then + if bit32.band(flags, PassiveMask) ~= NoFlags or bit32.band(subtreeFlags, PassiveMask) ~= NoFlags then newFlags = bit32.bor(newFlags, OnPostCommitFlag) end workInProgress.flags = newFlags @@ -1152,10 +1082,7 @@ local function completeWork( -- Something suspended. Re-render with the fallback children. workInProgress.lanes = renderLanes -- Do not reset the effect list. - if - enableProfilerTimer - and bit32.band(workInProgress.mode, ProfileMode) ~= NoMode - then + if enableProfilerTimer and bit32.band(workInProgress.mode, ProfileMode) ~= NoMode then transferActualDuration(workInProgress) end -- Don't bubble properties in this case. @@ -1188,8 +1115,7 @@ local function completeWork( -- and this is the first time we know we're going to suspend we -- should be able to immediately restart from within throwException. local hasInvisibleChildContext = current == nil - and workInProgress.memoizedProps.unstable_avoidThisFallback - ~= true + and workInProgress.memoizedProps.unstable_avoidThisFallback ~= true if hasInvisibleChildContext or hasSuspenseContext( @@ -1565,10 +1491,7 @@ local function completeWork( -- bubbleProperties(workInProgress) -- return nil -- end - elseif - workInProgress.tag == OffscreenComponent - or workInProgress.tag == LegacyHiddenComponent - then + elseif workInProgress.tag == OffscreenComponent or workInProgress.tag == LegacyHiddenComponent then popRenderLanes(workInProgress) local nextState: OffscreenState | nil = workInProgress.memoizedState local nextIsHidden = nextState ~= nil @@ -1577,10 +1500,7 @@ local function completeWork( local prevState: OffscreenState | nil = current.memoizedState local prevIsHidden = prevState ~= nil - if - prevIsHidden ~= nextIsHidden - and newProps.mode ~= "unstable-defer-without-hiding" - then + if prevIsHidden ~= nextIsHidden and newProps.mode ~= "unstable-defer-without-hiding" then workInProgress.flags = bit32.bor(workInProgress.flags, Update) end end @@ -1588,10 +1508,7 @@ local function completeWork( -- Don't bubble properties for hidden children. if not nextIsHidden - or includesSomeLane( - ReactFiberWorkLoop.subtreeRenderLanes, - OffscreenLane :: Lane - ) + or includesSomeLane(ReactFiberWorkLoop.subtreeRenderLanes, OffscreenLane :: Lane) or bit32.band(workInProgress.mode, ConcurrentMode) == NoMode then bubbleProperties(workInProgress) @@ -1601,8 +1518,7 @@ local function completeWork( end invariant( false, - "Unknown unit of work tag (%s). This error is likely caused by a bug in " - .. "React. Please file an issue.", + "Unknown unit of work tag (%s). This error is likely caused by a bug in " .. "React. Please file an issue.", tostring(workInProgress.tag) ) return nil diff --git a/modules/react-reconciler/src/ReactFiberComponentStack.lua b/modules/react-reconciler/src/ReactFiberComponentStack.lua index 25b12992..0f646f49 100644 --- a/modules/react-reconciler/src/ReactFiberComponentStack.lua +++ b/modules/react-reconciler/src/ReactFiberComponentStack.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/72d00ab623502983ebd7ac0756cf2787df109811/packages/react-reconciler/src/ReactFiberComponentStack.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberComponentStack.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiberContext.new.lua b/modules/react-reconciler/src/ReactFiberContext.new.lua index 20e308db..d51f3f28 100644 --- a/modules/react-reconciler/src/ReactFiberContext.new.lua +++ b/modules/react-reconciler/src/ReactFiberContext.new.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/a724a3b578dce77d427bef313102a4d0e978d9b4/packages/react-reconciler/src/ReactFiberContext.new.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberContext.new.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/react-reconciler/src/ReactFiberDevToolsHook.new.lua b/modules/react-reconciler/src/ReactFiberDevToolsHook.new.lua index a3477e0d..74fefb6f 100644 --- a/modules/react-reconciler/src/ReactFiberDevToolsHook.new.lua +++ b/modules/react-reconciler/src/ReactFiberDevToolsHook.new.lua @@ -40,8 +40,7 @@ local function isCallable(value) return false end -local enableProfilerTimer = - require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableProfilerTimer +local enableProfilerTimer = require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableProfilerTimer local ReactInternalTypes = require("./ReactInternalTypes") type Fiber = ReactInternalTypes.Fiber @@ -110,8 +109,7 @@ exports.onScheduleRoot = function(root: FiberRoot, children: ReactNodeList) -- ROBLOX deviation: our mocked functions are tables with __call, since they have fields and isCallable(injectedHook.onScheduleFiberRoot) then - local ok, err = - pcall(injectedHook.onScheduleFiberRoot, rendererID, root, children) + local ok, err = pcall(injectedHook.onScheduleFiberRoot, rendererID, root, children) if not ok then if _G.__DEV__ and not hasLoggedError then diff --git a/modules/react-reconciler/src/ReactFiberErrorDialog.lua b/modules/react-reconciler/src/ReactFiberErrorDialog.lua index 5636e466..5ebe42dd 100644 --- a/modules/react-reconciler/src/ReactFiberErrorDialog.lua +++ b/modules/react-reconciler/src/ReactFiberErrorDialog.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactFiberErrorDialog.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberErrorDialog.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiberErrorLogger.lua b/modules/react-reconciler/src/ReactFiberErrorLogger.lua index fa9b486a..a70d33fd 100644 --- a/modules/react-reconciler/src/ReactFiberErrorLogger.lua +++ b/modules/react-reconciler/src/ReactFiberErrorLogger.lua @@ -73,12 +73,9 @@ exports.logCapturedError = function(boundary: Fiber, errorInfo: CapturedValue component:" + componentNameMessage = "The above error occurred in the <" .. tostring(componentName) .. "> component:" else - componentNameMessage = - "The above error occurred in one of your React components:" + componentNameMessage = "The above error occurred in one of your React components:" end local errorBoundaryMessage @@ -92,11 +89,7 @@ exports.logCapturedError = function(boundary: Fiber, errorInfo: CapturedValue = ReactTypes.ReactContext type ReactBinding = ReactTypes.ReactBinding type ReactBindingUpdater = ReactTypes.ReactBindingUpdater type MutableSource = ReactTypes.MutableSource -type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn< - Source, - Snapshot -> -type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn< - Source, - Snapshot -> +type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn +type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn local ReactInternalTypes = require("./ReactInternalTypes") type Fiber = ReactInternalTypes.Fiber @@ -91,16 +85,14 @@ local HookHasEffect = ReactHookEffectTags.HasEffect local HookLayout = ReactHookEffectTags.Layout local HookPassive = ReactHookEffectTags.Passive local ReactFiberWorkLoop = require("./ReactFiberWorkLoop.new.lua") :: any -local warnIfNotCurrentlyActingUpdatesInDEV = - ReactFiberWorkLoop.warnIfNotCurrentlyActingUpdatesInDEV +local warnIfNotCurrentlyActingUpdatesInDEV = ReactFiberWorkLoop.warnIfNotCurrentlyActingUpdatesInDEV local scheduleUpdateOnFiber = ReactFiberWorkLoop.scheduleUpdateOnFiber local warnIfNotScopedWithMatchingAct = ReactFiberWorkLoop.warnIfNotScopedWithMatchingAct local requestEventTime = ReactFiberWorkLoop.requestEventTime local requestUpdateLane = ReactFiberWorkLoop.requestUpdateLane local markSkippedUpdateLanes = ReactFiberWorkLoop.markSkippedUpdateLanes local getWorkInProgressRoot = ReactFiberWorkLoop.getWorkInProgressRoot -local warnIfNotCurrentlyActingEffectsInDEV = - ReactFiberWorkLoop.warnIfNotCurrentlyActingEffectsInDEV +local warnIfNotCurrentlyActingEffectsInDEV = ReactFiberWorkLoop.warnIfNotCurrentlyActingEffectsInDEV -- local { -- getWorkInProgressRoot, -- requestUpdateLane, @@ -182,7 +174,7 @@ export type Hook = { export type Effect = { tag: HookFlags, -- ROBLOX TODO: this needs Luau type pack support to express accurately - create: (() -> (() -> ())) | () -> (), + create: (() -> () -> ()) | () -> (), destroy: (() -> ())?, deps: Array | nil, next: Effect, @@ -320,8 +312,7 @@ end function warnOnHookMismatchInDev(currentHookName: HookType) if __DEV__ then -- ROBLOX deviation: getComponentName will return nil in most Hook cases, use same fallback as elsewhere - local componentName = getComponentName(currentlyRenderingFiber.type) - or "Component" + local componentName = getComponentName(currentlyRenderingFiber.type) or "Component" if not didWarnAboutMismatchedHooksForComponent[componentName] then didWarnAboutMismatchedHooksForComponent[componentName] = true @@ -454,20 +445,10 @@ exports.bailoutHooks = function(current: Fiber, workInProgress: Fiber, lanes: La if __DEV__ and enableDoubleInvokingEffects then workInProgress.flags = bit32.band( workInProgress.flags, - bit32.bnot( - bit32.bor( - MountPassiveDevEffect, - PassiveEffect, - MountLayoutDevEffect, - UpdateEffect - ) - ) + bit32.bnot(bit32.bor(MountPassiveDevEffect, PassiveEffect, MountLayoutDevEffect, UpdateEffect)) ) else - workInProgress.flags = bit32.band( - workInProgress.flags, - bit32.bnot(bit32.bor(PassiveEffect, UpdateEffect)) - ) + workInProgress.flags = bit32.band(workInProgress.flags, bit32.bnot(bit32.bor(PassiveEffect, UpdateEffect))) end current.lanes = removeLanes(current.lanes, lanes) end @@ -625,11 +606,7 @@ function basicStateReducer(state: S, action: BasicStateAction): S end end -function mountReducer( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? -): (S, Dispatch) +function mountReducer(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) local hook = mountWorkInProgressHook() local initialState if init ~= nil then @@ -662,18 +639,11 @@ function mountReducer( -- ROBLOX deviation END: Lua version of useState and useReducer return two items, not list like upstream end -function updateReducer( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? -): (S, Dispatch) +function updateReducer(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) local hook = updateWorkInProgressHook() local queue = hook.queue -- ROBLOX deviation: change from invariant to avoid funtion call in hot path - assert( - queue ~= nil, - "Should have a queue. This is likely a bug in React. Please file an issue." - ) + assert(queue ~= nil, "Should have a queue. This is likely a bug in React. Please file an issue.") queue.lastRenderedReducer = reducer @@ -745,8 +715,7 @@ function updateReducer( -- Update the remaining priority in the queue. -- TODO: Don't need to accumulate this. Instead, we can remove -- renderLanes from the original lanes. - currentlyRenderingFiber.lanes = - mergeLanes(currentlyRenderingFiber.lanes, updateLane) + currentlyRenderingFiber.lanes = mergeLanes(currentlyRenderingFiber.lanes, updateLane) markSkippedUpdateLanes(updateLane) else -- This update does have sufficient priority. @@ -803,18 +772,11 @@ function updateReducer( return hook.memoizedState, dispatch end -function rerenderReducer( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? -): (S, Dispatch) +function rerenderReducer(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) local hook = updateWorkInProgressHook() local queue = hook.queue -- ROBLOX performance: changed from invariant to avoid function call in hot path - assert( - queue ~= nil, - "Should have a queue. This is likely a bug in React. Please file an issue." - ) + assert(queue ~= nil, "Should have a queue. This is likely a bug in React. Please file an issue.") queue.lastRenderedReducer = reducer @@ -871,10 +833,7 @@ type MutableSourceMemoizedState = { function readFromUnsubcribedMutableSource( root: FiberRoot, source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn ): Snapshot if __DEV__ then warnAboutMultipleRenderersDEV(source) @@ -954,20 +913,11 @@ end function useMutableSource( hook: Hook, source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot local root: FiberRoot = getWorkInProgressRoot() - invariant( - root ~= nil, - "Expected a work-in-progress root. This is a bug in React. Please file an issue." - ) + invariant(root ~= nil, "Expected a work-in-progress root. This is a bug in React. Please file an issue.") local getVersion = source._getVersion local version_ = getVersion(source._source) @@ -1072,9 +1022,7 @@ function useMutableSource( local unsubscribe = subscribe(source._source, handleChange) if __DEV__ then if type(unsubscribe) ~= "function" then - console.error( - "Mutable source subscribe function must return an unsubscribe function." - ) + console.error("Mutable source subscribe function must return an unsubscribe function.") end end @@ -1093,11 +1041,7 @@ function useMutableSource( -- -- In both cases, we need to throw away pending updates (since they are no longer relevant) -- and treat reading from the source as we do in the mount case. - if - not is(prevGetSnapshot, getSnapshot) - or not is(prevSource, source) - or not is(prevSubscribe, subscribe) - then + if not is(prevGetSnapshot, getSnapshot) or not is(prevSource, source) or not is(prevSubscribe, subscribe) then -- Create a new queue and setState method, -- So if there are interleaved updates, they get pushed to the older queue. -- When this becomes current, the previous queue and dispatch method will be discarded, @@ -1128,14 +1072,8 @@ end function mountMutableSource( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot local hook = mountWorkInProgressHook() hook.memoizedState = { @@ -1151,14 +1089,8 @@ end function updateMutableSource( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot local hook = updateWorkInProgressHook() return useMutableSource(hook, source, getSnapshot, subscribe) @@ -1212,8 +1144,7 @@ local function pushEffect(tag, create, destroy, deps) -- Circular next = nil :: any, } - local componentUpdateQueue: FunctionComponentUpdateQueue = - currentlyRenderingFiber.updateQueue :: any + local componentUpdateQueue: FunctionComponentUpdateQueue = currentlyRenderingFiber.updateQueue :: any if componentUpdateQueue == nil then -- ROBLOX performance: inline simple function in hot path -- componentUpdateQueue = createFunctionComponentUpdateQueue() @@ -1280,8 +1211,7 @@ local function mountEffectImpl(fiberFlags, hookFlags, create, deps): () local nextDeps = deps currentlyRenderingFiber.flags = bit32.bor(currentlyRenderingFiber.flags, fiberFlags) - hook.memoizedState = - pushEffect(bit32.bor(HookHasEffect, hookFlags), create, nil, nextDeps) + hook.memoizedState = pushEffect(bit32.bor(HookHasEffect, hookFlags), create, nil, nextDeps) end -- ROBLOX deviation START: must explicitly mark deps argument as optional/nil-able @@ -1307,13 +1237,12 @@ function updateEffectImpl(fiberFlags, hookFlags, create, deps: Array?): () currentlyRenderingFiber.flags = bit32.bor(currentlyRenderingFiber.flags, fiberFlags) - hook.memoizedState = - pushEffect(bit32.bor(HookHasEffect, hookFlags), create, destroy, nextDeps) + hook.memoizedState = pushEffect(bit32.bor(HookHasEffect, hookFlags), create, destroy, nextDeps) end local function mountEffect( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () if __DEV__ then @@ -1325,25 +1254,15 @@ local function mountEffect( end if __DEV__ and enableDoubleInvokingEffects then - mountEffectImpl( - bit32.bor(MountPassiveDevEffect, PassiveEffect, PassiveStaticEffect), - HookPassive, - create, - deps - ) + mountEffectImpl(bit32.bor(MountPassiveDevEffect, PassiveEffect, PassiveStaticEffect), HookPassive, create, deps) else - mountEffectImpl( - bit32.bor(PassiveEffect, PassiveStaticEffect), - HookPassive, - create, - deps - ) + mountEffectImpl(bit32.bor(PassiveEffect, PassiveStaticEffect), HookPassive, create, deps) end end local function updateEffect( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () if __DEV__ then @@ -1358,16 +1277,11 @@ end local function mountLayoutEffect( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () if __DEV__ and enableDoubleInvokingEffects then - mountEffectImpl( - bit32.bor(MountLayoutDevEffect, UpdateEffect), - HookLayout, - create, - deps - ) + mountEffectImpl(bit32.bor(MountLayoutDevEffect, UpdateEffect), HookLayout, create, deps) else mountEffectImpl(UpdateEffect, HookLayout, create, deps) end @@ -1375,7 +1289,7 @@ end local function updateLayoutEffect( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () updateEffectImpl(UpdateEffect, HookLayout, create, deps) @@ -1401,15 +1315,12 @@ function imperativeHandleEffect( -- explicit way to know that something is a ref object; instead, we check -- that it's an empty object with a metatable, which is what Roact refs -- look like since they indirect to bindings via their metatable - local isRefObject = getmetatable(refObject) ~= nil - and #Object.keys(refObject) == 0 + local isRefObject = getmetatable(refObject) ~= nil and #Object.keys(refObject) == 0 if not isRefObject then console.error( "Expected useImperativeHandle() first argument to either be a " .. "ref callback or React.createRef() object. Instead received: %s.", - "an object with keys {" - .. Array.join(Object.keys(refObject), ", ") - .. "}" + "an object with keys {" .. Array.join(Object.keys(refObject), ", ") .. "}" ) end end @@ -1444,14 +1355,9 @@ function mountImperativeHandle( local effectDeps = if deps ~= nil then Array.concat(deps, { ref }) else nil if __DEV__ and enableDoubleInvokingEffects then - return mountEffectImpl( - bit32.bor(MountLayoutDevEffect, UpdateEffect), - HookLayout, - function() - return imperativeHandleEffect(create, ref) - end, - effectDeps - ) + return mountEffectImpl(bit32.bor(MountLayoutDevEffect, UpdateEffect), HookLayout, function() + return imperativeHandleEffect(create, ref) + end, effectDeps) else return mountEffectImpl(UpdateEffect, HookLayout, function() return imperativeHandleEffect(create, ref) @@ -1826,19 +1732,14 @@ function dispatchAction(fiber: Fiber, queue: UpdateQueue, action: A, queue.pending = update local alternate = fiber.alternate - if - fiber == currentlyRenderingFiber - or (alternate ~= nil and alternate == currentlyRenderingFiber) - then + if fiber == currentlyRenderingFiber or (alternate ~= nil and alternate == currentlyRenderingFiber) then -- This is a render phase update. Stash it in a lazily-created map of -- queue -> linked list of updates. After this render pass, we'll restart -- and apply the stashed updates on top of the work-in-progress hook. didScheduleRenderPhaseUpdate = true didScheduleRenderPhaseUpdateDuringThisPass = true else - if - fiber.lanes == NoLanes and (alternate == nil or alternate.lanes == NoLanes) - then + if fiber.lanes == NoLanes and (alternate == nil or alternate.lanes == NoLanes) then -- The queue is currently empty, which means we can eagerly compute the -- next state before entering the render phase. If the new state is the -- same as the current state, we may be able to bail out entirely. @@ -1847,8 +1748,7 @@ function dispatchAction(fiber: Fiber, queue: UpdateQueue, action: A, local prevDispatcher if __DEV__ then prevDispatcher = ReactCurrentDispatcher.current - ReactCurrentDispatcher.current = - InvalidNestedHooksDispatcherOnUpdateInDEV + ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV end -- ROBLOX try local currentState: S = queue.lastRenderedState :: any @@ -2037,7 +1937,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useEffect" @@ -2057,7 +1957,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useLayoutEffect" @@ -2084,11 +1984,7 @@ if __DEV__ then -- ROBLOX FIXME Luau: TypeError: Type 'boolean' could not be converted into 'T' return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" mountHookTypesDev() local prevDispatcher = ReactCurrentDispatcher.current @@ -2145,14 +2041,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" mountHookTypesDev() @@ -2184,7 +2074,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useEffect" @@ -2202,7 +2092,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useLayoutEffect" @@ -2226,11 +2116,7 @@ if __DEV__ then end return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" updateHookTypesDev() local prevDispatcher = ReactCurrentDispatcher.current @@ -2287,14 +2173,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" updateHookTypesDev() @@ -2325,7 +2205,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useEffect" @@ -2343,7 +2223,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useLayoutEffect" @@ -2367,11 +2247,7 @@ if __DEV__ then end return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" updateHookTypesDev() local prevDispatcher = ReactCurrentDispatcher.current @@ -2428,14 +2304,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" updateHookTypesDev() @@ -2466,7 +2336,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () currentHookNameInDev = "useEffect" @@ -2484,7 +2354,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useLayoutEffect" @@ -2508,17 +2378,12 @@ if __DEV__ then end return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" updateHookTypesDev() local prevDispatcher = ReactCurrentDispatcher.current ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV - local ok, result, setResult = - pcall(rerenderReducer, reducer, initialArg, init) + local ok, result, setResult = pcall(rerenderReducer, reducer, initialArg, init) -- ROBLOX finally ReactCurrentDispatcher.current = prevDispatcher if not ok then @@ -2570,14 +2435,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" updateHookTypesDev() @@ -2611,7 +2470,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () currentHookNameInDev = "useEffect" @@ -2631,7 +2490,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () currentHookNameInDev = "useLayoutEffect" @@ -2657,11 +2516,7 @@ if __DEV__ then end return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" warnInvalidHookAccess() mountHookTypesDev() @@ -2725,14 +2580,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" warnInvalidHookAccess() @@ -2768,7 +2617,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () currentHookNameInDev = "useEffect" @@ -2788,7 +2637,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useLayoutEffect" @@ -2814,11 +2663,7 @@ if __DEV__ then end return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" warnInvalidHookAccess() updateHookTypesDev() @@ -2883,14 +2728,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" warnInvalidHookAccess() @@ -2926,7 +2765,7 @@ if __DEV__ then end, useEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () currentHookNameInDev = "useEffect" @@ -2946,7 +2785,7 @@ if __DEV__ then end, useLayoutEffect = function( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array? ): () currentHookNameInDev = "useLayoutEffect" @@ -2972,18 +2811,13 @@ if __DEV__ then end return unpack(results, 2) end :: any, - useReducer = function( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ): (S, Dispatch) + useReducer = function(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) currentHookNameInDev = "useReducer" warnInvalidHookAccess() updateHookTypesDev() local prevDispatcher = ReactCurrentDispatcher.current ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV - local ok, result, setResult = - pcall(rerenderReducer, reducer, initialArg, init) + local ok, result, setResult = pcall(rerenderReducer, reducer, initialArg, init) -- ROBLOX finally ReactCurrentDispatcher.current = prevDispatcher if not ok then @@ -3041,14 +2875,8 @@ if __DEV__ then -- }, useMutableSource = function( source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn< - Source, - Snapshot - >, - subscribe: MutableSourceSubscribeFn< - Source, - Snapshot - > + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn ): Snapshot currentHookNameInDev = "useMutableSource" warnInvalidHookAccess() @@ -3078,9 +2906,7 @@ local function renderWithHooks( currentlyRenderingFiber = workInProgress if __DEV__ then - hookTypesDev = if current ~= nil - then (current._debugHookTypes :: any) :: Array - else nil + hookTypesDev = if current ~= nil then (current._debugHookTypes :: any) :: Array else nil -- ROBLOX deviation START: index variable offset by one for Lua hookTypesUpdateIndexDev = 0 -- ROBLOX deviation END @@ -3122,8 +2948,7 @@ local function renderWithHooks( ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV end else - ReactCurrentDispatcher.current = (current == nil or current.memoizedState == nil) - and HooksDispatcherOnMount + ReactCurrentDispatcher.current = (current == nil or current.memoizedState == nil) and HooksDispatcherOnMount or HooksDispatcherOnUpdate end @@ -3140,8 +2965,7 @@ local function renderWithHooks( if numberOfReRenders >= RE_RENDER_LIMIT then error( Error.new( - "Too many re-renders. React limits the number of renders to prevent " - .. "an infinite loop." + "Too many re-renders. React limits the number of renders to prevent " .. "an infinite loop." ) ) end @@ -3166,8 +2990,7 @@ local function renderWithHooks( hookTypesUpdateIndexDev = 0 end - ReactCurrentDispatcher.current = __DEV__ and HooksDispatcherOnRerenderInDEV - or HooksDispatcherOnRerender + ReactCurrentDispatcher.current = __DEV__ and HooksDispatcherOnRerenderInDEV or HooksDispatcherOnRerender children = Component(props, secondArg) until not didScheduleRenderPhaseUpdateDuringThisPass @@ -3203,8 +3026,7 @@ local function renderWithHooks( if didRenderTooFewHooks then error( Error.new( - "Rendered fewer hooks than expected. This may be caused by an accidental " - .. "early return statement." + "Rendered fewer hooks than expected. This may be caused by an accidental " .. "early return statement." ) ) end diff --git a/modules/react-reconciler/src/ReactFiberHostConfig.lua b/modules/react-reconciler/src/ReactFiberHostConfig.lua index 4b163658..a8157436 100644 --- a/modules/react-reconciler/src/ReactFiberHostConfig.lua +++ b/modules/react-reconciler/src/ReactFiberHostConfig.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/9ac42dd074c42b66ecc0334b75200b1d2989f892/packages/react-reconciler/src/ReactFiberHostConfig.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHostConfig.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiberHostContext.new.lua b/modules/react-reconciler/src/ReactFiberHostContext.new.lua index 094a43ff..d56a60d5 100644 --- a/modules/react-reconciler/src/ReactFiberHostContext.new.lua +++ b/modules/react-reconciler/src/ReactFiberHostContext.new.lua @@ -34,8 +34,7 @@ local NO_CONTEXT: NoContextT = {} local contextStackCursor: StackCursor = createCursor(NO_CONTEXT) local contextFiberStackCursor: StackCursor = createCursor(NO_CONTEXT) -local rootInstanceStackCursor: StackCursor = - createCursor(NO_CONTEXT) +local rootInstanceStackCursor: StackCursor = createCursor(NO_CONTEXT) function requiredContext(c: Value | NoContextT): Value -- ROBLOX performance: eliminate expensive optional cmp in hot path diff --git a/modules/react-reconciler/src/ReactFiberHotReloading.new.lua b/modules/react-reconciler/src/ReactFiberHotReloading.new.lua index 320a4dc4..dd117fef 100644 --- a/modules/react-reconciler/src/ReactFiberHotReloading.new.lua +++ b/modules/react-reconciler/src/ReactFiberHotReloading.new.lua @@ -53,7 +53,7 @@ export type Family = { -- |} -- Resolves type to a family. -type RefreshHandler = (any) -> (Family?) +type RefreshHandler = (any) -> Family? -- -- Used by React Refresh runtime through DevTools Global Hook. -- export type SetRefreshHandler = (handler: RefreshHandler | nil) => void @@ -140,86 +140,85 @@ local function resolveForwardRefForHotReloading(type: any): any end exports.resolveForwardRefForHotReloading = resolveForwardRefForHotReloading -exports.isCompatibleFamilyForHotReloading = - function(fiber: Fiber, element: ReactElement): boolean - warn("isCompatibleFamilyForHotReloading is stubbed (returns false)") - return false - -- if _G.__DEV__ then - -- if resolveFamily == nil then - -- -- Hot reloading is disabled. - -- return false - -- end - - -- local prevType = fiber.elementType - -- local nextType = element.type - - -- -- If we got here, we know types aren't == equal. - -- local needsCompareFamilies = false - - -- local $$typeofNextType = - -- typeof nextType == 'table’' and nextType ~= nil - -- ? nextType.$$typeof - -- : nil - - -- switch (fiber.tag) - -- case ClassComponent: { - -- if typeof nextType == 'function') - -- needsCompareFamilies = true - -- end - -- break - -- end - -- case FunctionComponent: { - -- if typeof nextType == 'function') - -- needsCompareFamilies = true - -- } else if $$typeofNextType == REACT_LAZY_TYPE) - -- -- We don't know the inner type yet. - -- -- We're going to assume that the lazy inner type is stable, - -- -- and so it is sufficient to avoid reconciling it away. - -- -- We're not going to unwrap or actually use the new lazy type. - -- needsCompareFamilies = true - -- end - -- break - -- end - -- case ForwardRef: { - -- if $$typeofNextType == REACT_FORWARD_REF_TYPE) - -- needsCompareFamilies = true - -- } else if $$typeofNextType == REACT_LAZY_TYPE) - -- needsCompareFamilies = true - -- end - -- break - -- end - -- case MemoComponent: - -- case SimpleMemoComponent: { - -- if $$typeofNextType == REACT_MEMO_TYPE) - -- -- TODO: if it was but can no longer be simple, - -- -- we shouldn't set this. - -- needsCompareFamilies = true - -- } else if $$typeofNextType == REACT_LAZY_TYPE) - -- needsCompareFamilies = true - -- end - -- break - -- end - -- default: - -- return false - -- end - - -- -- Check if both types have a family and it's the same one. - -- if needsCompareFamilies) - -- -- Note: memo() and forwardRef() we'll compare outer rather than inner type. - -- -- This means both of them need to be registered to preserve state. - -- -- If we unwrapped and compared the inner types for wrappers instead, - -- -- then we would risk falsely saying two separate memo(Foo) - -- -- calls are equivalent because they wrap the same Foo function. - -- local prevFamily = resolveFamily(prevType) - -- if prevFamily ~= undefined and prevFamily == resolveFamily(nextType)) - -- return true - -- end - -- end - -- return false - -- } else { - -- return false - -- end - end +exports.isCompatibleFamilyForHotReloading = function(fiber: Fiber, element: ReactElement): boolean + warn("isCompatibleFamilyForHotReloading is stubbed (returns false)") + return false + -- if _G.__DEV__ then + -- if resolveFamily == nil then + -- -- Hot reloading is disabled. + -- return false + -- end + + -- local prevType = fiber.elementType + -- local nextType = element.type + + -- -- If we got here, we know types aren't == equal. + -- local needsCompareFamilies = false + + -- local $$typeofNextType = + -- typeof nextType == 'table’' and nextType ~= nil + -- ? nextType.$$typeof + -- : nil + + -- switch (fiber.tag) + -- case ClassComponent: { + -- if typeof nextType == 'function') + -- needsCompareFamilies = true + -- end + -- break + -- end + -- case FunctionComponent: { + -- if typeof nextType == 'function') + -- needsCompareFamilies = true + -- } else if $$typeofNextType == REACT_LAZY_TYPE) + -- -- We don't know the inner type yet. + -- -- We're going to assume that the lazy inner type is stable, + -- -- and so it is sufficient to avoid reconciling it away. + -- -- We're not going to unwrap or actually use the new lazy type. + -- needsCompareFamilies = true + -- end + -- break + -- end + -- case ForwardRef: { + -- if $$typeofNextType == REACT_FORWARD_REF_TYPE) + -- needsCompareFamilies = true + -- } else if $$typeofNextType == REACT_LAZY_TYPE) + -- needsCompareFamilies = true + -- end + -- break + -- end + -- case MemoComponent: + -- case SimpleMemoComponent: { + -- if $$typeofNextType == REACT_MEMO_TYPE) + -- -- TODO: if it was but can no longer be simple, + -- -- we shouldn't set this. + -- needsCompareFamilies = true + -- } else if $$typeofNextType == REACT_LAZY_TYPE) + -- needsCompareFamilies = true + -- end + -- break + -- end + -- default: + -- return false + -- end + + -- -- Check if both types have a family and it's the same one. + -- if needsCompareFamilies) + -- -- Note: memo() and forwardRef() we'll compare outer rather than inner type. + -- -- This means both of them need to be registered to preserve state. + -- -- If we unwrapped and compared the inner types for wrappers instead, + -- -- then we would risk falsely saying two separate memo(Foo) + -- -- calls are equivalent because they wrap the same Foo function. + -- local prevFamily = resolveFamily(prevType) + -- if prevFamily ~= undefined and prevFamily == resolveFamily(nextType)) + -- return true + -- end + -- end + -- return false + -- } else { + -- return false + -- end +end exports.markFailedErrorBoundaryForHotReloading = function(fiber: Fiber) if _G.__DEV__ then diff --git a/modules/react-reconciler/src/ReactFiberHydrationContext.new.lua b/modules/react-reconciler/src/ReactFiberHydrationContext.new.lua index a2a64b19..21ae365f 100644 --- a/modules/react-reconciler/src/ReactFiberHydrationContext.new.lua +++ b/modules/react-reconciler/src/ReactFiberHydrationContext.new.lua @@ -60,10 +60,8 @@ local hydrateTextInstance = ReactFiberHostConfig.hydrateTextInstance local hydrateSuspenseInstance = ReactFiberHostConfig.hydrateSuspenseInstance local getNextHydratableInstanceAfterSuspenseInstance = ReactFiberHostConfig.getNextHydratableInstanceAfterSuspenseInstance -local didNotMatchHydratedContainerTextInstance = - ReactFiberHostConfig.didNotMatchHydratedContainerTextInstance -local didNotMatchHydratedTextInstance = - ReactFiberHostConfig.didNotMatchHydratedTextInstance +local didNotMatchHydratedContainerTextInstance = ReactFiberHostConfig.didNotMatchHydratedContainerTextInstance +local didNotMatchHydratedTextInstance = ReactFiberHostConfig.didNotMatchHydratedTextInstance local shouldSetTextContent = ReactFiberHostConfig.shouldSetTextContent -- local { @@ -76,8 +74,7 @@ local shouldSetTextContent = ReactFiberHostConfig.shouldSetTextContent -- didNotFindHydratableTextInstance, -- didNotFindHydratableSuspenseInstance, -- } = require(Packages../ReactFiberHostConfig' -local enableSuspenseServerRenderer = - require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableSuspenseServerRenderer +local enableSuspenseServerRenderer = require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableSuspenseServerRenderer local OffscreenLane = require("./ReactFiberLane").OffscreenLane -- The deepest Fiber on the stack involved in a hydration context. @@ -89,9 +86,7 @@ local isHydrating: boolean = false function warnIfHydrating() if _G.__DEV__ then if isHydrating then - console.error( - "We should not be hydrating here. This is a bug in React. Please file a bug." - ) + console.error("We should not be hydrating here. This is a bug in React. Please file a bug.") end end end @@ -108,10 +103,7 @@ function enterHydrationState(fiber: Fiber): boolean return true end -function reenterHydrationStateFromDehydratedSuspenseInstance( - fiber: Fiber, - suspenseInstance: SuspenseInstance -): boolean +function reenterHydrationStateFromDehydratedSuspenseInstance(fiber: Fiber, suspenseInstance: SuspenseInstance): boolean if not supportsHydration then return false end @@ -241,8 +233,7 @@ function tryHydrate(fiber, nextInstance) return false elseif fiber.tag == SuspenseComponent then if enableSuspenseServerRenderer then - local suspenseInstance: nil | SuspenseInstance = - canHydrateSuspenseInstance(nextInstance) + local suspenseInstance: nil | SuspenseInstance = canHydrateSuspenseInstance(nextInstance) if suspenseInstance ~= nil then local suspenseState: SuspenseState = { dehydrated = suspenseInstance, @@ -253,8 +244,7 @@ function tryHydrate(fiber, nextInstance) -- This simplifies the code for getHostSibling and deleting nodes, -- since it doesn't have to consider all Suspense boundaries and -- check if they're dehydrated ones or not. - local dehydratedFragment = - createFiberFromDehydratedFragment(suspenseInstance) + local dehydratedFragment = createFiberFromDehydratedFragment(suspenseInstance) dehydratedFragment.return_ = fiber fiber.child = dehydratedFragment return true @@ -302,11 +292,7 @@ function tryToClaimNextHydratableInstance(fiber: Fiber) nextHydratableInstance = getFirstHydratableChild(nextInstance) end -function prepareToHydrateHostInstance( - fiber: Fiber, - rootContainerInstance: Container, - hostContext: HostContext -): boolean +function prepareToHydrateHostInstance(fiber: Fiber, rootContainerInstance: Container, hostContext: HostContext): boolean if not supportsHydration then invariant( false, @@ -316,14 +302,8 @@ function prepareToHydrateHostInstance( end local instance: Instance = fiber.stateNode - local updatePayload = hydrateInstance( - instance, - fiber.type, - fiber.memoizedProps, - rootContainerInstance, - hostContext, - fiber - ) + local updatePayload = + hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber) -- TODO: Type this specific to this type of component. fiber.updateQueue = updatePayload -- If the update payload indicates that there is a change or if there @@ -354,22 +334,12 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean if returnFiber ~= nil then if returnFiber.tag == HostRoot then local parentContainer = returnFiber.stateNode.containerInfo - didNotMatchHydratedContainerTextInstance( - parentContainer, - textInstance, - textContent - ) + didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent) elseif returnFiber.tag == HostComponent then local parentType = returnFiber.type local parentProps = returnFiber.memoizedProps local parentInstance = returnFiber.stateNode - didNotMatchHydratedTextInstance( - parentType, - parentProps, - parentInstance, - textInstance, - textContent - ) + didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent) end end end @@ -465,11 +435,7 @@ function popHydrationState(fiber: Fiber): boolean -- TODO: Better heuristic. if fiber.tag ~= HostComponent - or ( - type_ ~= "head" - and type_ ~= "body" - and not shouldSetTextContent(type_, fiber.memoizedProps) - ) + or (type_ ~= "head" and type_ ~= "body" and not shouldSetTextContent(type_, fiber.memoizedProps)) then local nextInstance = nextHydratableInstance while nextInstance do diff --git a/modules/react-reconciler/src/ReactFiberLane.lua b/modules/react-reconciler/src/ReactFiberLane.lua index 1fb71a22..656845e9 100644 --- a/modules/react-reconciler/src/ReactFiberLane.lua +++ b/modules/react-reconciler/src/ReactFiberLane.lua @@ -13,8 +13,7 @@ type FiberRoot = ReactInternalTypes.FiberRoot type ReactPriorityLevel = ReactInternalTypes.ReactPriorityLevel local console = require("@pkg/@jsdotlua/shared").console -local ReactFiberSchedulerPriorities = - require("./ReactFiberSchedulerPriorities.roblox.lua") +local ReactFiberSchedulerPriorities = require("./ReactFiberSchedulerPriorities.roblox.lua") -- deviation: Instead of defining these here, and and re-exporting in -- `ReactInternalTypes`, we depend on and re-export them here to avoid cyclical @@ -225,17 +224,12 @@ local function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes return lanes end -local function schedulerPriorityToLanePriority( - schedulerPriorityLevel: ReactPriorityLevel -): LanePriority +local function schedulerPriorityToLanePriority(schedulerPriorityLevel: ReactPriorityLevel): LanePriority if schedulerPriorityLevel == ImmediateSchedulerPriority then return SyncLanePriority elseif schedulerPriorityLevel == UserBlockingSchedulerPriority then return InputContinuousLanePriority - elseif - schedulerPriorityLevel == NormalSchedulerPriority - or schedulerPriorityLevel == LowSchedulerPriority - then + elseif schedulerPriorityLevel == NormalSchedulerPriority or schedulerPriorityLevel == LowSchedulerPriority then -- // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration. return DefaultLanePriority elseif schedulerPriorityLevel == IdleSchedulerPriority then @@ -246,9 +240,7 @@ local function schedulerPriorityToLanePriority( end exports.schedulerPriorityToLanePriority = schedulerPriorityToLanePriority -local function lanePriorityToSchedulerPriority( - lanePriority: LanePriority -): ReactPriorityLevel +local function lanePriorityToSchedulerPriority(lanePriority: LanePriority): ReactPriorityLevel if lanePriority == SyncLanePriority or lanePriority == SyncBatchedLanePriority then return ImmediateSchedulerPriority elseif @@ -276,11 +268,7 @@ local function lanePriorityToSchedulerPriority( elseif lanePriority == NoLanePriority then return NoSchedulerPriority else - invariant( - false, - "Invalid update priority: %s. This is a bug in React.", - lanePriority - ) + invariant(false, "Invalid update priority: %s. This is a bug in React.", lanePriority) -- deviation: luau doesn't know that invariant throws, so we error error("unreachable") end @@ -315,8 +303,7 @@ local function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes -- // even if the work is suspended. local nonIdlePendingLanes = bit32.band(pendingLanes, NonIdleLanes) if nonIdlePendingLanes ~= NoLanes then - local nonIdleUnblockedLanes = - bit32.band(nonIdlePendingLanes, bit32.bnot(suspendedLanes)) + local nonIdleUnblockedLanes = bit32.band(nonIdlePendingLanes, bit32.bnot(suspendedLanes)) if nonIdleUnblockedLanes ~= NoLanes then nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes) nextLanePriority = return_highestLanePriority @@ -352,8 +339,7 @@ local function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes -- // are suspended. -- ROBLOX performance: inline getEqualOrHigherPriorityLanes to avoid function call overhead in hot path -- nextLanes = bit32.band(pendingLanes, getEqualOrHigherPriorityLanes(nextLanes)) - nextLanes = - bit32.band(pendingLanes, bit32.lshift(getLowestPriorityLane(nextLanes), 1) - 1) + nextLanes = bit32.band(pendingLanes, bit32.lshift(getLowestPriorityLane(nextLanes), 1) - 1) -- // If we're already in the middle of a render, switching lanes will interrupt -- // it and we'll lose our progress. We should only do this if the new lanes are @@ -482,10 +468,7 @@ local function markStarvedLanesAsExpired(root: FiberRoot, currentTime: number) -- // Found a pending lane with no expiration time. If it's not suspended, or -- // if it's pinged, assume it's CPU-bound. Compute a new expiration time -- // using the current time. - if - bit32.band(lane, suspendedLanes) == NoLanes - or bit32.band(lane, pingedLanes) ~= NoLanes - then + if bit32.band(lane, suspendedLanes) == NoLanes or bit32.band(lane, pingedLanes) ~= NoLanes then -- // Assumes timestamps are monotonically increasing. expirationTimes[index] = computeExpirationTime(lane, currentTime) end @@ -507,8 +490,7 @@ end exports.getHighestPriorityPendingLanes = getHighestPriorityPendingLanes local function getLanesToRetrySynchronouslyOnError(root: FiberRoot): Lanes - local everythingButOffscreen = - bit32.band(root.pendingLanes, bit32.bnot(OffscreenLane)) + local everythingButOffscreen = bit32.band(root.pendingLanes, bit32.bnot(OffscreenLane)) if everythingButOffscreen ~= NoLanes then return everythingButOffscreen end @@ -552,16 +534,14 @@ local function findUpdateLane(lanePriority: LanePriority, wipLanes: Lanes): Lane elseif lanePriority == SyncBatchedLanePriority then return SyncBatchedLane elseif lanePriority == InputDiscreteLanePriority then - local lane = - pickArbitraryLane(bit32.band(InputDiscreteLanes, bit32.bnot(wipLanes))) + local lane = pickArbitraryLane(bit32.band(InputDiscreteLanes, bit32.bnot(wipLanes))) if lane == NoLane then -- // Shift to the next priority level return findUpdateLane(InputContinuousLanePriority, wipLanes) end return lane elseif lanePriority == InputContinuousLanePriority then - local lane = - pickArbitraryLane(bit32.band(InputContinuousLanes, bit32.bnot(wipLanes))) + local lane = pickArbitraryLane(bit32.band(InputContinuousLanes, bit32.bnot(wipLanes))) if lane == NoLane then -- // Shift to the next priority level return findUpdateLane(DefaultLanePriority, wipLanes) @@ -817,20 +797,17 @@ end exports.markRootSuspended = markRootSuspended local function markRootPinged(root: FiberRoot, pingedLanes: Lanes, eventTime: number) - root.pingedLanes = - bit32.bor(root.pingedLanes, bit32.band(root.suspendedLanes, pingedLanes)) + root.pingedLanes = bit32.bor(root.pingedLanes, bit32.band(root.suspendedLanes, pingedLanes)) end exports.markRootPinged = markRootPinged local function markRootExpired(root: FiberRoot, expiredLanes: Lanes) - root.expiredLanes = - bit32.bor(root.expiredLanes, bit32.band(expiredLanes, root.pendingLanes)) + root.expiredLanes = bit32.bor(root.expiredLanes, bit32.band(expiredLanes, root.pendingLanes)) end exports.markRootExpired = markRootExpired local function markDiscreteUpdatesExpired(root: FiberRoot) - root.expiredLanes = - bit32.bor(root.expiredLanes, bit32.band(InputDiscreteLanes, root.pendingLanes)) + root.expiredLanes = bit32.bor(root.expiredLanes, bit32.band(InputDiscreteLanes, root.pendingLanes)) end exports.markDiscreteUpdatesExpired = markDiscreteUpdatesExpired @@ -840,8 +817,7 @@ end exports.hasDiscreteLanes = hasDiscreteLanes local function markRootMutableRead(root: FiberRoot, updateLane: Lane) - root.mutableReadLanes = - bit32.bor(root.mutableReadLanes, bit32.band(updateLane, root.pendingLanes)) + root.mutableReadLanes = bit32.bor(root.mutableReadLanes, bit32.band(updateLane, root.pendingLanes)) end exports.markRootMutableRead = markRootMutableRead @@ -900,10 +876,7 @@ local function getBumpedLaneForHydration(root: FiberRoot, renderLanes: Lanes): L local lane - if - highestLanePriority == SyncLanePriority - or highestLanePriority == SyncBatchedLanePriority - then + if highestLanePriority == SyncLanePriority or highestLanePriority == SyncBatchedLanePriority then lane = NoLane elseif highestLanePriority == InputDiscreteHydrationLanePriority @@ -915,15 +888,9 @@ local function getBumpedLaneForHydration(root: FiberRoot, renderLanes: Lanes): L or highestLanePriority == InputContinuousLanePriority then lane = InputContinuousHydrationLane - elseif - highestLanePriority == DefaultHydrationLanePriority - or highestLanePriority == DefaultLanePriority - then + elseif highestLanePriority == DefaultHydrationLanePriority or highestLanePriority == DefaultLanePriority then lane = DefaultHydrationLane - elseif - highestLanePriority == TransitionHydrationPriority - or highestLanePriority == TransitionPriority - then + elseif highestLanePriority == TransitionHydrationPriority or highestLanePriority == TransitionPriority then lane = TransitionHydrationLane elseif highestLanePriority == RetryLanePriority then -- // Shouldn't be reachable under normal circumstances, so there's no @@ -931,15 +898,9 @@ local function getBumpedLaneForHydration(root: FiberRoot, renderLanes: Lanes): L lane = TransitionHydrationLane elseif highestLanePriority == SelectiveHydrationLanePriority then lane = SelectiveHydrationLane - elseif - highestLanePriority == IdleHydrationLanePriority - or highestLanePriority == IdleLanePriority - then + elseif highestLanePriority == IdleHydrationLanePriority or highestLanePriority == IdleLanePriority then lane = IdleHydrationLane - elseif - highestLanePriority == OffscreenLanePriority - or highestLanePriority == NoLanePriority - then + elseif highestLanePriority == OffscreenLanePriority or highestLanePriority == NoLanePriority then lane = NoLane else invariant(false, "Invalid lane: %s. This is a bug in React.", tostring(lane)) diff --git a/modules/react-reconciler/src/ReactFiberLazyComponent.new.lua b/modules/react-reconciler/src/ReactFiberLazyComponent.new.lua index 98960f8e..0679c754 100644 --- a/modules/react-reconciler/src/ReactFiberLazyComponent.new.lua +++ b/modules/react-reconciler/src/ReactFiberLazyComponent.new.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/17f582e0453b808860be59ed3437c6a426ae52de/packages/react-reconciler/src/ReactFiberLazyComponent.new.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberLazyComponent.new.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiberNewContext.new.lua b/modules/react-reconciler/src/ReactFiberNewContext.new.lua index f53e36d2..f1da1864 100644 --- a/modules/react-reconciler/src/ReactFiberNewContext.new.lua +++ b/modules/react-reconciler/src/ReactFiberNewContext.new.lua @@ -100,10 +100,7 @@ exports.pushProvider = function(providerFiber: Fiber, nextValue: T): () context._currentValue = nextValue if _G.__DEV__ then - if - context._currentRenderer ~= nil - and context._currentRenderer ~= rendererSigil - then + if context._currentRenderer ~= nil and context._currentRenderer ~= rendererSigil then console.error( "Detected multiple renderers concurrently rendering the " .. "same context provider. This is currently unsupported." @@ -116,10 +113,7 @@ exports.pushProvider = function(providerFiber: Fiber, nextValue: T): () context._currentValue2 = nextValue if _G.__DEV__ then - if - context._currentRenderer2 ~= nil - and context._currentRenderer2 ~= rendererSigil - then + if context._currentRenderer2 ~= nil and context._currentRenderer2 ~= rendererSigil then console.error( "Detected multiple renderers concurrently rendering the " .. "same context provider. This is currently unsupported." @@ -143,33 +137,32 @@ exports.popProvider = function(providerFiber: Fiber) end end -exports.calculateChangedBits = - function(context: ReactContext, newValue: T, oldValue: T) - if is(oldValue, newValue) then - -- No change - return 0 - else - -- deviation: unravel ternary that's unsafe to translate - local changedBits = MAX_SIGNED_31_BIT_INT - if typeof(context._calculateChangedBits) == "function" then - changedBits = context._calculateChangedBits(oldValue, newValue) - end - - -- ROBLOX performance: eliminate nice-to-have compare in hot path that's removed in React 18 - -- if _G.__DEV__ then - -- if bit32.band(changedBits, MAX_SIGNED_31_BIT_INT) ~= changedBits then - -- console.error( - -- "calculateChangedBits: Expected the return value to be a " .. - -- "31-bit integer. Instead received: %s", - -- changedBits - -- ) - -- end - -- end - -- deviation: JS does a bitwise OR with 0 presumably to floor the value and - -- coerce to an int; we just use math.floor - return math.floor(changedBits) +exports.calculateChangedBits = function(context: ReactContext, newValue: T, oldValue: T) + if is(oldValue, newValue) then + -- No change + return 0 + else + -- deviation: unravel ternary that's unsafe to translate + local changedBits = MAX_SIGNED_31_BIT_INT + if typeof(context._calculateChangedBits) == "function" then + changedBits = context._calculateChangedBits(oldValue, newValue) end + + -- ROBLOX performance: eliminate nice-to-have compare in hot path that's removed in React 18 + -- if _G.__DEV__ then + -- if bit32.band(changedBits, MAX_SIGNED_31_BIT_INT) ~= changedBits then + -- console.error( + -- "calculateChangedBits: Expected the return value to be a " .. + -- "31-bit integer. Instead received: %s", + -- changedBits + -- ) + -- end + -- end + -- deviation: JS does a bitwise OR with 0 presumably to floor the value and + -- coerce to an int; we just use math.floor + return math.floor(changedBits) end +end exports.scheduleWorkOnParentPath = function(parent: Fiber | nil, renderLanes: Lanes) -- Update the child lanes of all the ancestors, including the alternates. @@ -181,9 +174,7 @@ exports.scheduleWorkOnParentPath = function(parent: Fiber | nil, renderLanes: La if alternate ~= nil then alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes) end - elseif - alternate ~= nil and not isSubsetOfLanes(alternate.childLanes, renderLanes) - then + elseif alternate ~= nil and not isSubsetOfLanes(alternate.childLanes, renderLanes) then alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes) else -- Neither alternate was updated, which means the rest of the @@ -225,8 +216,7 @@ exports.propagateContextChange = function( if fiber.tag == ClassComponent then -- Schedule a force update on the work-in-progress. - local update = - createUpdate(NoTimestamp, pickArbitraryLane(renderLanes)) + local update = createUpdate(NoTimestamp, pickArbitraryLane(renderLanes)) update.tag = ForceUpdate -- TODO: Because we don't have a work-in-progress, this will add the -- update to the current fiber, too, which means it will persist even if @@ -356,72 +346,68 @@ exports.prepareToReadContext = function( end end -exports.readContext = - function(context: ReactContext, observedBits: nil | number | boolean): T - if _G.__DEV__ then - -- This warning would fire if you read context inside a Hook like useMemo. - -- Unlike the class check below, it's not enforced in production for perf. - if isDisallowedContextReadInDEV then - console.error( - "Context can only be read while React is rendering. " - .. "In classes, you can read it in the render method or getDerivedStateFromProps. " - .. "In function components, you can read it directly in the function body, but not " - .. "inside Hooks like useReducer() or useMemo()." - ) - end +exports.readContext = function(context: ReactContext, observedBits: nil | number | boolean): T + if _G.__DEV__ then + -- This warning would fire if you read context inside a Hook like useMemo. + -- Unlike the class check below, it's not enforced in production for perf. + if isDisallowedContextReadInDEV then + console.error( + "Context can only be read while React is rendering. " + .. "In classes, you can read it in the render method or getDerivedStateFromProps. " + .. "In function components, you can read it directly in the function body, but not " + .. "inside Hooks like useReducer() or useMemo()." + ) end + end - if lastContextWithAllBitsObserved == context then + if lastContextWithAllBitsObserved == context then -- Nothing to do. We already observe everything in this context. - elseif observedBits == false or observedBits == 0 then + elseif observedBits == false or observedBits == 0 then -- Do not observe any updates. + else + local resolvedObservedBits -- Avoid deopting on observable arguments or heterogeneous types. + if typeof(observedBits) ~= "number" or observedBits == Number.MAX_SAFE_INTEGER then + -- Observe all updates. + -- lastContextWithAllBitsObserved = ((context: any): ReactContext) + lastContextWithAllBitsObserved = context + resolvedObservedBits = Number.MAX_SAFE_INTEGER else - local resolvedObservedBits -- Avoid deopting on observable arguments or heterogeneous types. - if - typeof(observedBits) ~= "number" - or observedBits == Number.MAX_SAFE_INTEGER - then - -- Observe all updates. - -- lastContextWithAllBitsObserved = ((context: any): ReactContext) - lastContextWithAllBitsObserved = context - resolvedObservedBits = Number.MAX_SAFE_INTEGER - else - resolvedObservedBits = observedBits - end - - local contextItem = { - -- context: ((context: any): ReactContext), - context = context, - observedBits = resolvedObservedBits, - next = nil, - } + resolvedObservedBits = observedBits + end - if lastContextDependency == nil then - if currentlyRenderingFiber == nil then - error( - Error.new( - "Context can only be read while React is rendering. " - .. "In classes, you can read it in the render method or getDerivedStateFromProps. " - .. "In function components, you can read it directly in the function body, but not " - .. "inside Hooks like useReducer() or useMemo()." - ) + local contextItem = { + -- context: ((context: any): ReactContext), + context = context, + observedBits = resolvedObservedBits, + next = nil, + } + + if lastContextDependency == nil then + if currentlyRenderingFiber == nil then + error( + Error.new( + "Context can only be read while React is rendering. " + .. "In classes, you can read it in the render method or getDerivedStateFromProps. " + .. "In function components, you can read it directly in the function body, but not " + .. "inside Hooks like useReducer() or useMemo()." ) - end - - -- This is the first dependency for this component. Create a new list. - lastContextDependency = contextItem; - (currentlyRenderingFiber :: Fiber).dependencies = { - lanes = NoLanes, - firstContext = contextItem, - responders = nil, - } - else - -- Append a new context item. - (lastContextDependency :: any).next = contextItem - lastContextDependency = contextItem + ) end + + -- This is the first dependency for this component. Create a new list. + lastContextDependency = contextItem; + (currentlyRenderingFiber :: Fiber).dependencies = { + lanes = NoLanes, + firstContext = contextItem, + responders = nil, + } + else + -- Append a new context item. + (lastContextDependency :: any).next = contextItem + lastContextDependency = contextItem end - return if isPrimaryRenderer then context._currentValue else context._currentValue2 end + return if isPrimaryRenderer then context._currentValue else context._currentValue2 +end return exports diff --git a/modules/react-reconciler/src/ReactFiberReconciler.lua b/modules/react-reconciler/src/ReactFiberReconciler.lua index e9619ded..22784934 100644 --- a/modules/react-reconciler/src/ReactFiberReconciler.lua +++ b/modules/react-reconciler/src/ReactFiberReconciler.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/faa697f4f9afe9f1c98e315b2a9e70f5a74a7a74/packages/react-reconciler/src/ReactFiberReconciler.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberReconciler.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiberReconciler.new.lua b/modules/react-reconciler/src/ReactFiberReconciler.new.lua index 785a51fb..8d761113 100644 --- a/modules/react-reconciler/src/ReactFiberReconciler.new.lua +++ b/modules/react-reconciler/src/ReactFiberReconciler.new.lua @@ -52,8 +52,7 @@ type SuspenseState = ReactFiberSuspenseComponent.SuspenseState local ReactFiberTreeReflection = require("./ReactFiberTreeReflection") local findCurrentHostFiber = ReactFiberTreeReflection.findCurrentHostFiber -local findCurrentHostFiberWithNoPortals = - ReactFiberTreeReflection.findCurrentHostFiberWithNoPortals +local findCurrentHostFiberWithNoPortals = ReactFiberTreeReflection.findCurrentHostFiberWithNoPortals local getInstance = require("@pkg/@jsdotlua/shared").ReactInstanceMap.get local HostComponent = ReactWorkTags.HostComponent local ClassComponent = ReactWorkTags.ClassComponent @@ -62,8 +61,7 @@ local SuspenseComponent = ReactWorkTags.SuspenseComponent local getComponentName = require("@pkg/@jsdotlua/shared").getComponentName local invariant = require("@pkg/@jsdotlua/shared").invariant local describeError = require("@pkg/@jsdotlua/shared").describeError -local enableSchedulingProfiler = - require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableSchedulingProfiler +local enableSchedulingProfiler = require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableSchedulingProfiler local ReactSharedInternals = require("@pkg/@jsdotlua/shared").ReactSharedInternals local getPublicInstance = require("./ReactFiberHostConfig").getPublicInstance local ReactFiberContext = require("./ReactFiberContext.new.lua") @@ -131,8 +129,7 @@ exports.ReactWorkTags = ReactWorkTags exports.ReactTypeOfMode = ReactTypeOfMode exports.ReactFiberFlags = ReactFiberFlags exports.getNearestMountedFiber = ReactFiberTreeReflection.getNearestMountedFiber -exports.findCurrentFiberUsingSlowPath = - ReactFiberTreeReflection.findCurrentFiberUsingSlowPath +exports.findCurrentFiberUsingSlowPath = ReactFiberTreeReflection.findCurrentFiberUsingSlowPath -- exports.registerMutableSourceForHydration = require("./ReactMutableSource.new.lua").registerMutableSourceForHydration exports.createPortal = require("./ReactPortal").createPortal @@ -212,10 +209,7 @@ local function findHostInstance(component: Object): PublicInstance | nil return hostFiber.stateNode end -local function findHostInstanceWithWarning( - component: Object, - methodName: string -): PublicInstance | nil +local function findHostInstanceWithWarning(component: Object, methodName: string): PublicInstance | nil if __DEV__ then local fiber = getInstance(component) if fiber == nil then @@ -327,11 +321,7 @@ exports.updateContainer = function( end if __DEV__ then - if - ReactCurrentFiberIsRendering - and ReactCurrentFiber.current ~= nil - and not didWarnAboutNestedUpdates - then + if ReactCurrentFiberIsRendering and ReactCurrentFiber.current ~= nil and not didWarnAboutNestedUpdates then didWarnAboutNestedUpdates = true console.error( "Render methods should be a pure function of props and state; " @@ -389,18 +379,17 @@ exports.flushPassiveEffects = flushPassiveEffects exports.IsThisRendererActing = IsThisRendererActing exports.act = act -exports.getPublicRootInstance = - function(container: OpaqueRoot): React_Component | PublicInstance | nil - local containerFiber = container.current - if not containerFiber.child then - return nil - end - if containerFiber.child.tag == HostComponent then - return getPublicInstance(containerFiber.child.stateNode) - else - return containerFiber.child.stateNode - end +exports.getPublicRootInstance = function(container: OpaqueRoot): React_Component | PublicInstance | nil + local containerFiber = container.current + if not containerFiber.child then + return nil + end + if containerFiber.child.tag == HostComponent then + return getPublicInstance(containerFiber.child.stateNode) + else + return containerFiber.child.stateNode end +end -- deviation: Declare function ahead of use local markRetryLaneIfNotHydrated @@ -430,8 +419,7 @@ local function markRetryLaneImpl(fiber: Fiber, retryLane: Lane) local suspenseState: SuspenseState? = fiber.memoizedState if suspenseState then if suspenseState ~= nil and suspenseState.dehydrated ~= nil then - suspenseState.retryLane = - higherPriorityLane(suspenseState.retryLane, retryLane) + suspenseState.retryLane = higherPriorityLane(suspenseState.retryLane, retryLane) end end end @@ -534,11 +522,7 @@ local setSuspenseHandler = nil if __DEV__ then -- deviation: FIXME: obj: `Object | Array`, narrowing not possible with `isArray` - local function copyWithDeleteImpl( - obj: Object, - path: Array, - index: number - ) + local function copyWithDeleteImpl(obj: Object, path: Array, index: number) local key = path[index] local updated if Array.isArray(obj) then @@ -562,10 +546,7 @@ if __DEV__ then end -- deviation: FIXME: obj: `Object | Array`, narrowing not possible with `isArray` - local function copyWithDelete( - obj: Object, - path: Array - ): Object | Array + local function copyWithDelete(obj: Object, path: Array): Object | Array return copyWithDeleteImpl(obj, path, 0) end @@ -617,9 +598,7 @@ if __DEV__ then else for i = 1, #newPath do if oldPath[i] ~= newPath[i] then - console.warn( - "copyWithRename() expects paths to be the same except for the deepest key" - ) + console.warn("copyWithRename() expects paths to be the same except for the deepest key") return nil end end @@ -628,12 +607,7 @@ if __DEV__ then end -- deviation: FIXME: obj: `Object | Array`, narrowing not possible with `isArray` - local function copyWithSetImpl( - obj: Object, - path: Array, - index: number, - value: any - ) + local function copyWithSetImpl(obj: Object, path: Array, index: number, value: any) if index >= (#path + 1) then return value end @@ -650,11 +624,7 @@ if __DEV__ then end -- deviation: FIXME: obj: `Object | Array`, narrowing not possible with `isArray` - local function copyWithSet( - obj: Object, - path: Array, - value: any - ): Object | Array + local function copyWithSet(obj: Object, path: Array, value: any): Object | Array return copyWithSetImpl(obj, path, 1, value) end @@ -670,42 +640,40 @@ if __DEV__ then end -- Support DevTools editable values for useState and useReducer. - overrideHookState = - function(fiber: Fiber, id: number, path: Array, value: any) - local hook = findHook(fiber, id) - if hook ~= nil then - local newState = copyWithSet(hook.memoizedState, path, value) - hook.memoizedState = newState - hook.baseState = newState - - -- We aren't actually adding an update to the queue, - -- because there is no update we can add for useReducer hooks that won't trigger an error. - -- (There's no appropriate action type for DevTools overrides.) - -- As a result though, React will see the scheduled update as a noop and bailout. - -- Shallow cloning props works as a workaround for now to bypass the bailout check. - fiber.memoizedProps = table.clone(fiber.memoizedProps) - - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp) - end + overrideHookState = function(fiber: Fiber, id: number, path: Array, value: any) + local hook = findHook(fiber, id) + if hook ~= nil then + local newState = copyWithSet(hook.memoizedState, path, value) + hook.memoizedState = newState + hook.baseState = newState + + -- We aren't actually adding an update to the queue, + -- because there is no update we can add for useReducer hooks that won't trigger an error. + -- (There's no appropriate action type for DevTools overrides.) + -- As a result though, React will see the scheduled update as a noop and bailout. + -- Shallow cloning props works as a workaround for now to bypass the bailout check. + fiber.memoizedProps = table.clone(fiber.memoizedProps) + + scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp) end - overrideHookStateDeletePath = - function(fiber: Fiber, id: number, path: Array) - local hook = findHook(fiber, id) - if hook ~= nil then - local newState = copyWithDelete(hook.memoizedState, path) - hook.memoizedState = newState - hook.baseState = newState - - -- We aren't actually adding an update to the queue, - -- because there is no update we can add for useReducer hooks that won't trigger an error. - -- (There's no appropriate action type for DevTools overrides.) - -- As a result though, React will see the scheduled update as a noop and bailout. - -- Shallow cloning props works as a workaround for now to bypass the bailout check. - fiber.memoizedProps = table.clone(fiber.memoizedProps) - - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp) - end + end + overrideHookStateDeletePath = function(fiber: Fiber, id: number, path: Array) + local hook = findHook(fiber, id) + if hook ~= nil then + local newState = copyWithDelete(hook.memoizedState, path) + hook.memoizedState = newState + hook.baseState = newState + + -- We aren't actually adding an update to the queue, + -- because there is no update we can add for useReducer hooks that won't trigger an error. + -- (There's no appropriate action type for DevTools overrides.) + -- As a result though, React will see the scheduled update as a noop and bailout. + -- Shallow cloning props works as a workaround for now to bypass the bailout check. + fiber.memoizedProps = table.clone(fiber.memoizedProps) + + scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp) end + end overrideHookStateRenamePath = function( fiber: Fiber, id: number, @@ -748,11 +716,7 @@ if __DEV__ then end scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp) end - overridePropsRenamePath = function( - fiber: Fiber, - oldPath: Array, - newPath: Array - ) + overridePropsRenamePath = function(fiber: Fiber, oldPath: Array, newPath: Array) fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath) -- ROBLOX TODO: grab local for this since Luau can't deal with nested type narrowing local alternate = fiber.alternate @@ -766,7 +730,7 @@ if __DEV__ then scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp) end - setSuspenseHandler = function(newShouldSuspendImpl: (Fiber) -> (boolean)) + setSuspenseHandler = function(newShouldSuspendImpl: (Fiber) -> boolean) shouldSuspendImpl = newShouldSuspendImpl end end diff --git a/modules/react-reconciler/src/ReactFiberSuspenseComponent.new.lua b/modules/react-reconciler/src/ReactFiberSuspenseComponent.new.lua index 9ce03567..0a76a384 100644 --- a/modules/react-reconciler/src/ReactFiberSuspenseComponent.new.lua +++ b/modules/react-reconciler/src/ReactFiberSuspenseComponent.new.lua @@ -79,35 +79,34 @@ export type SuspenseListRenderState = { local exports = {} -exports.shouldCaptureSuspense = - function(workInProgress: Fiber, hasInvisibleParent: boolean): boolean - -- If it was the primary children that just suspended, capture and render the - -- fallback. Otherwise, don't capture and bubble to the next boundary. - local nextState: SuspenseState? = workInProgress.memoizedState - if nextState then - if nextState.dehydrated ~= nil then - -- A dehydrated boundary always captures. - return true - end - return false - end - local props = workInProgress.memoizedProps - -- In order to capture, the Suspense component must have a fallback prop. - if props.fallback == nil then - return false - end - -- Regular boundaries always capture. - if props.unstable_avoidThisFallback ~= true then +exports.shouldCaptureSuspense = function(workInProgress: Fiber, hasInvisibleParent: boolean): boolean + -- If it was the primary children that just suspended, capture and render the + -- fallback. Otherwise, don't capture and bubble to the next boundary. + local nextState: SuspenseState? = workInProgress.memoizedState + if nextState then + if nextState.dehydrated ~= nil then + -- A dehydrated boundary always captures. return true end - -- If it's a boundary we should avoid, then we prefer to bubble up to the - -- parent boundary if it is currently invisible. - if hasInvisibleParent then - return false - end - -- If the parent is not able to handle it, we must handle it. + return false + end + local props = workInProgress.memoizedProps + -- In order to capture, the Suspense component must have a fallback prop. + if props.fallback == nil then + return false + end + -- Regular boundaries always capture. + if props.unstable_avoidThisFallback ~= true then return true end + -- If it's a boundary we should avoid, then we prefer to bubble up to the + -- parent boundary if it is currently invisible. + if hasInvisibleParent then + return false + end + -- If the parent is not able to handle it, we must handle it. + return true +end exports.findFirstSuspended = function(row: Fiber): Fiber? local node = row diff --git a/modules/react-reconciler/src/ReactFiberSuspenseContext.new.lua b/modules/react-reconciler/src/ReactFiberSuspenseContext.new.lua index 6e0d010c..cec5a8fd 100644 --- a/modules/react-reconciler/src/ReactFiberSuspenseContext.new.lua +++ b/modules/react-reconciler/src/ReactFiberSuspenseContext.new.lua @@ -52,20 +52,14 @@ exports.InvisibleParentSuspenseContext = InvisibleParentSuspenseContext local ForceSuspenseFallback: ShallowSuspenseContext = 0b10 exports.ForceSuspenseFallback = ForceSuspenseFallback -local suspenseStackCursor: StackCursor = - createCursor(DefaultSuspenseContext) +local suspenseStackCursor: StackCursor = createCursor(DefaultSuspenseContext) exports.suspenseStackCursor = suspenseStackCursor -function exports.hasSuspenseContext( - parentContext: SuspenseContext, - flag: SuspenseContext -): boolean +function exports.hasSuspenseContext(parentContext: SuspenseContext, flag: SuspenseContext): boolean return bit32.band(parentContext, flag) ~= 0 end -function exports.setDefaultShallowSuspenseContext( - parentContext: SuspenseContext -): SuspenseContext +function exports.setDefaultShallowSuspenseContext(parentContext: SuspenseContext): SuspenseContext return bit32.band(parentContext, SubtreeSuspenseContextMask) end @@ -73,10 +67,7 @@ function exports.setShallowSuspenseContext( parentContext: SuspenseContext, shallowContext: ShallowSuspenseContext ): SuspenseContext - return bit32.bor( - bit32.band(parentContext, SubtreeSuspenseContextMask), - shallowContext - ) + return bit32.bor(bit32.band(parentContext, SubtreeSuspenseContextMask), shallowContext) end function exports.addSubtreeSuspenseContext( diff --git a/modules/react-reconciler/src/ReactFiberThrow.new.lua b/modules/react-reconciler/src/ReactFiberThrow.new.lua index 8cedc971..eadc4678 100644 --- a/modules/react-reconciler/src/ReactFiberThrow.new.lua +++ b/modules/react-reconciler/src/ReactFiberThrow.new.lua @@ -53,8 +53,7 @@ local NoFlags = ReactFiberFlags.NoFlags local ShouldCapture = ReactFiberFlags.ShouldCapture local LifecycleEffectMask = ReactFiberFlags.LifecycleEffectMask local ForceUpdateForLegacySuspense = ReactFiberFlags.ForceUpdateForLegacySuspense -local shouldCaptureSuspense = - require("./ReactFiberSuspenseComponent.new.lua").shouldCaptureSuspense +local shouldCaptureSuspense = require("./ReactFiberSuspenseComponent.new.lua").shouldCaptureSuspense local ReactTypeOfMode = require("./ReactTypeOfMode") local NoMode = ReactTypeOfMode.NoMode local BlockingMode = ReactTypeOfMode.BlockingMode @@ -69,13 +68,11 @@ local createUpdate = ReactUpdateQueue.createUpdate local CaptureUpdate = ReactUpdateQueue.CaptureUpdate local ForceUpdate = ReactUpdateQueue.ForceUpdate local enqueueUpdate = ReactUpdateQueue.enqueueUpdate -local markFailedErrorBoundaryForHotReloading = require( - script.Parent["ReactFiberHotReloading.new"] -).markFailedErrorBoundaryForHotReloading +local markFailedErrorBoundaryForHotReloading = + require(script.Parent["ReactFiberHotReloading.new"]).markFailedErrorBoundaryForHotReloading local hasSuspenseContext = ReactFiberSuspenseContext.hasSuspenseContext -local InvisibleParentSuspenseContext = - ReactFiberSuspenseContext.InvisibleParentSuspenseContext +local InvisibleParentSuspenseContext = ReactFiberSuspenseContext.InvisibleParentSuspenseContext local suspenseStackCursor = ReactFiberSuspenseContext.suspenseStackCursor -- ROBLOX FIXME: these will incur a dependency cycle @@ -87,8 +84,7 @@ local markLegacyErrorBoundaryAsFailedRef, isAlreadyFailedLegacyErrorBoundaryRef, local markLegacyErrorBoundaryAsFailed = function(...) if not markLegacyErrorBoundaryAsFailedRef then ReactFiberWorkLoop = require("./ReactFiberWorkLoop.new.lua") - markLegacyErrorBoundaryAsFailedRef = - ReactFiberWorkLoop.markLegacyErrorBoundaryAsFailed + markLegacyErrorBoundaryAsFailedRef = ReactFiberWorkLoop.markLegacyErrorBoundaryAsFailed end return markLegacyErrorBoundaryAsFailedRef(...) end @@ -110,8 +106,7 @@ local isAlreadyFailedLegacyErrorBoundary = function(...) if ReactFiberWorkLoop == nil then ReactFiberWorkLoop = require("./ReactFiberWorkLoop.new.lua") end - isAlreadyFailedLegacyErrorBoundaryRef = - ReactFiberWorkLoop.isAlreadyFailedLegacyErrorBoundary + isAlreadyFailedLegacyErrorBoundaryRef = ReactFiberWorkLoop.isAlreadyFailedLegacyErrorBoundary return isAlreadyFailedLegacyErrorBoundaryRef(...) end @@ -150,11 +145,7 @@ function createRootErrorUpdate( return update end -function createClassErrorUpdate( - fiber: Fiber, - errorInfo: CapturedValue, - lane: Lane -): Update +function createClassErrorUpdate(fiber: Fiber, errorInfo: CapturedValue, lane: Lane): Update local update = createUpdate(NoTimestamp, lane) update.tag = CaptureUpdate local getDerivedStateFromError = (fiber.type :: React_Component).getDerivedStateFromError @@ -217,8 +208,7 @@ local function attachPingListener(root: FiberRoot, wakeable: Wakeable, lanes: La -- Attach a listener to the promise to "ping" the root and retry. But only if -- one does not already exist for the lanes we're currently rendering (which -- acts like a "thread ID" here). - local pingCache: Map | Map>)> | nil = - root.pingCache + local pingCache: Map | Map>)> | nil = root.pingCache local threadIDs if pingCache == nil then -- ROBLOX deviation: use table in place of WeakMap @@ -229,13 +219,10 @@ local function attachPingListener(root: FiberRoot, wakeable: Wakeable, lanes: La } :: Map | Map>)> pingCache = root.pingCache :: Map | Map>)> else - threadIDs = ( - pingCache :: Map | Map>)> - )[wakeable] :: Set + threadIDs = (pingCache :: Map | Map>)>)[wakeable] :: Set if threadIDs == nil then threadIDs = {} :: Set; - (pingCache :: Map | Map>)>)[wakeable] = - threadIDs + (pingCache :: Map | Map>)>)[wakeable] = threadIDs end end if not threadIDs[lanes] then @@ -260,11 +247,7 @@ function throwException( -- The source fiber did not complete. sourceFiber.flags = bit32.bor(sourceFiber.flags, Incomplete) - if - value ~= nil - and typeof(value) == "table" - and typeof(value.andThen) == "function" - then + if value ~= nil and typeof(value) == "table" and typeof(value.andThen) == "function" then -- This is a wakeable. local wakeable: Wakeable = value @@ -297,10 +280,8 @@ function throwException( end end - local hasInvisibleParentBoundary = hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext :: SuspenseContext - ) + local hasInvisibleParentBoundary = + hasSuspenseContext(suspenseStackCursor.current, InvisibleParentSuspenseContext :: SuspenseContext) -- Schedule the nearest Suspense to re-render the timed out view. local workInProgress = returnFiber @@ -334,16 +315,13 @@ function throwException( -- should *not* suspend the commit. if bit32.band(workInProgress.mode, BlockingMode) == NoMode then workInProgress.flags = bit32.bor(workInProgress.flags, DidCapture) - sourceFiber.flags = - bit32.bor(sourceFiber.flags, ForceUpdateForLegacySuspense) + sourceFiber.flags = bit32.bor(sourceFiber.flags, ForceUpdateForLegacySuspense) -- We're going to commit this fiber even though it didn't complete. -- But we shouldn't call any lifecycle methods or callbacks. Remove -- all lifecycle effect tags. - sourceFiber.flags = bit32.band( - sourceFiber.flags, - bit32.bnot(bit32.bor(LifecycleEffectMask, Incomplete)) - ) + sourceFiber.flags = + bit32.band(sourceFiber.flags, bit32.bnot(bit32.bor(LifecycleEffectMask, Incomplete))) if sourceFiber.tag == ClassComponent then local currentSourceFiber = sourceFiber.alternate @@ -447,8 +425,7 @@ function throwException( local lane = pickArbitraryLane(rootRenderLanes) workInProgress.lanes = mergeLanes(workInProgress.lanes, lane) -- ROBLOX deviation: parameterize method onUncaughtError to avoid circular dependency - local update = - createRootErrorUpdate(workInProgress, errorInfo, lane, onUncaughtError) + local update = createRootErrorUpdate(workInProgress, errorInfo, lane, onUncaughtError) enqueueCapturedUpdate(workInProgress, update) return elseif workInProgress.tag == ClassComponent then diff --git a/modules/react-reconciler/src/ReactFiberTransition.lua b/modules/react-reconciler/src/ReactFiberTransition.lua index 612ece43..471a9c48 100644 --- a/modules/react-reconciler/src/ReactFiberTransition.lua +++ b/modules/react-reconciler/src/ReactFiberTransition.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/ddd1faa1972b614dfbfae205f2aa4a6c0b39a759/packages/react-reconciler/src/ReactFiberTransition.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberTransition.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/ReactFiberTreeReflection.lua b/modules/react-reconciler/src/ReactFiberTreeReflection.lua index 0d62b991..c31a4364 100644 --- a/modules/react-reconciler/src/ReactFiberTreeReflection.lua +++ b/modules/react-reconciler/src/ReactFiberTreeReflection.lua @@ -37,8 +37,7 @@ local ReactFiberFlags = require("./ReactFiberFlags") local NoFlags = ReactFiberFlags.NoFlags local Placement = ReactFiberFlags.Placement local Hydrating = ReactFiberFlags.Hydrating -local enableFundamentalAPI = - require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableFundamentalAPI +local enableFundamentalAPI = require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableFundamentalAPI local ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner @@ -135,10 +134,7 @@ exports.isMounted = function(component): boolean end local function assertIsMounted(fiber) - invariant( - getNearestMountedFiber(fiber) == fiber, - "Unable to find node on an unmounted component." - ) + invariant(getNearestMountedFiber(fiber) == fiber, "Unable to find node on an unmounted component.") end local function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber? @@ -359,9 +355,7 @@ end exports.isFiberSuspenseAndTimedOut = function(fiber: Fiber): boolean local memoizedState = fiber.memoizedState - return fiber.tag == SuspenseComponent - and memoizedState ~= nil - and memoizedState.dehydrated == nil + return fiber.tag == SuspenseComponent and memoizedState ~= nil and memoizedState.dehydrated == nil end exports.doesFiberContain = function(parentFiber: Fiber, childFiber: Fiber): boolean diff --git a/modules/react-reconciler/src/ReactFiberUnwindWork.new.lua b/modules/react-reconciler/src/ReactFiberUnwindWork.new.lua index 6edad46b..e0950a75 100644 --- a/modules/react-reconciler/src/ReactFiberUnwindWork.new.lua +++ b/modules/react-reconciler/src/ReactFiberUnwindWork.new.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/16654436039dd8f16a63928e71081c7745872e8f/packages/react-reconciler/src/ReactFiberUnwindWork.new.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberUnwindWork.new.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -141,6 +141,13 @@ local function unwindWork(workInProgress: Fiber, renderLanes: Lanes): Fiber? or workInProgress.tag == ReactWorkTags.LegacyHiddenComponent then popRenderLanes(workInProgress) + popTransition(workInProgress, current) + return nil + elseif condition_ == CacheComponent then + if Boolean.toJSBoolean(enableCache) then + local cache: Cache = workInProgress.memoizedState.cache + popCacheProvider(workInProgress, cache) + end return nil else return nil diff --git a/modules/react-reconciler/src/ReactFiberWorkLoop.new.lua b/modules/react-reconciler/src/ReactFiberWorkLoop.new.lua index c9aaed58..840bc12b 100644 --- a/modules/react-reconciler/src/ReactFiberWorkLoop.new.lua +++ b/modules/react-reconciler/src/ReactFiberWorkLoop.new.lua @@ -98,8 +98,7 @@ local DebugTracing = require("./DebugTracing") local SchedulingProfiler = require("./SchedulingProfiler") local SchedulerTracing = require("@pkg/@jsdotlua/scheduler").tracing -local __interactionsRef, __subscriberRef = - SchedulerTracing.__interactionsRef, SchedulerTracing.__subscriberRef +local __interactionsRef, __subscriberRef = SchedulerTracing.__interactionsRef, SchedulerTracing.__subscriberRef local ReactFiberHostConfig = require("./ReactFiberHostConfig") -- deviation: Use properties directly instead of localizing to avoid 200 limit @@ -169,8 +168,7 @@ local returnNextLanesPriority = ReactFiberLane.returnNextLanesPriority local setCurrentUpdateLanePriority = ReactFiberLane.setCurrentUpdateLanePriority local getCurrentUpdateLanePriority = ReactFiberLane.getCurrentUpdateLanePriority local markStarvedLanesAsExpired = ReactFiberLane.markStarvedLanesAsExpired -local getLanesToRetrySynchronouslyOnError = - ReactFiberLane.getLanesToRetrySynchronouslyOnError +local getLanesToRetrySynchronouslyOnError = ReactFiberLane.getLanesToRetrySynchronouslyOnError local getMostRecentEventTime = ReactFiberLane.getMostRecentEventTime local markRootUpdated = ReactFiberLane.markRootUpdated local markRootSuspended_dontCallThisOneDirectly = ReactFiberLane.markRootSuspended @@ -193,14 +191,12 @@ local throwException = ReactFiberThrow.throwException local createRootErrorUpdate = ReactFiberThrow.createRootErrorUpdate local createClassErrorUpdate = ReactFiberThrow.createClassErrorUpdate local ReactFiberCommitWork = require("./ReactFiberCommitWork.new.lua") -local commitBeforeMutationEffectOnFiber = - ReactFiberCommitWork.commitBeforeMutationLifeCycles +local commitBeforeMutationEffectOnFiber = ReactFiberCommitWork.commitBeforeMutationLifeCycles local commitPlacement = ReactFiberCommitWork.commitPlacement local commitWork = ReactFiberCommitWork.commitWork local commitDeletion = ReactFiberCommitWork.commitDeletion local commitPassiveUnmountOnFiber = ReactFiberCommitWork.commitPassiveUnmount -local commitPassiveUnmountInsideDeletedTreeOnFiber = - ReactFiberCommitWork.commitPassiveUnmountInsideDeletedTree +local commitPassiveUnmountInsideDeletedTreeOnFiber = ReactFiberCommitWork.commitPassiveUnmountInsideDeletedTree local commitPassiveMountOnFiber = ReactFiberCommitWork.commitPassiveMount local commitDetachRef = ReactFiberCommitWork.commitDetachRef -- local commitAttachRef = ReactFiberCommitWork.commitAttachRef @@ -209,16 +205,14 @@ local commitDetachRef = ReactFiberCommitWork.commitDetachRef local invokeLayoutEffectMountInDEV = ReactFiberCommitWork.invokeLayoutEffectMountInDEV local invokePassiveEffectMountInDEV = ReactFiberCommitWork.invokePassiveEffectMountInDEV local invokeLayoutEffectUnmountInDEV = ReactFiberCommitWork.invokeLayoutEffectUnmountInDEV -local invokePassiveEffectUnmountInDEV = - ReactFiberCommitWork.invokePassiveEffectUnmountInDEV +local invokePassiveEffectUnmountInDEV = ReactFiberCommitWork.invokePassiveEffectUnmountInDEV local recursivelyCommitLayoutEffects = ReactFiberCommitWork.recursivelyCommitLayoutEffects local Promise = require("@pkg/@jsdotlua/promise") local enqueueUpdate = require("./ReactUpdateQueue.new.lua").enqueueUpdate -local resetContextDependencies = - require("./ReactFiberNewContext.new.lua").resetContextDependencies +local resetContextDependencies = require("./ReactFiberNewContext.new.lua").resetContextDependencies -- ROBLOX deviation: Pre-declare function local ensureRootIsScheduled @@ -233,23 +227,19 @@ local lazyInitRefs = { } -- ROBLOX deviation: lazy initialize beginwork to break cyclic dependencies -local originalBeginWork = - function(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil - if not lazyInitRefs.originalBeginWorkRef then - lazyInitRefs.originalBeginWorkRef = - require("./ReactFiberBeginWork.new.lua").beginWork - end - return lazyInitRefs.originalBeginWorkRef(current, workInProgress, renderLanes) +local originalBeginWork = function(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil + if not lazyInitRefs.originalBeginWorkRef then + lazyInitRefs.originalBeginWorkRef = require("./ReactFiberBeginWork.new.lua").beginWork end + return lazyInitRefs.originalBeginWorkRef(current, workInProgress, renderLanes) +end -local completeWork = - function(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil - if not lazyInitRefs.completeWorkRef then - lazyInitRefs.completeWorkRef = - require("./ReactFiberCompleteWork.new.lua").completeWork - end - return (lazyInitRefs.completeWorkRef :: any)(current, workInProgress, renderLanes) +local completeWork = function(current: Fiber | nil, workInProgress: Fiber, renderLanes: Lanes): Fiber | nil + if not lazyInitRefs.completeWorkRef then + lazyInitRefs.completeWorkRef = require("./ReactFiberCompleteWork.new.lua").completeWork end + return (lazyInitRefs.completeWorkRef :: any)(current, workInProgress, renderLanes) +end local ReactFiberHooks -- ROBLOX deviation: lazy init for functions from ReactFiberHooks @@ -389,8 +379,7 @@ local workInProgressRootIncludedLanes: Lanes = ReactFiberLane.NoLanes -- The work left over by components that were visited during this render. Only -- includes unprocessed updates, not work in bailed out children. local ReactFiberWorkInProgress = require("./ReactFiberWorkInProgress") -local workInProgressRootSkippedLanes: (value: Lanes?) -> Lanes = - ReactFiberWorkInProgress.workInProgressRootSkippedLanes --: Lanes = ReactFiberLane.NoLanes +local workInProgressRootSkippedLanes: (value: Lanes?) -> Lanes = ReactFiberWorkInProgress.workInProgressRootSkippedLanes --: Lanes = ReactFiberLane.NoLanes -- Lanes that were updated (in an interleaved event) during this render. local workInProgressRootUpdatedLanes: Lanes = ReactFiberLane.NoLanes -- Lanes that were pinged (in an interleaved event) during this render. @@ -462,10 +451,7 @@ exports.getWorkInProgressRoot = function(): FiberRoot? end exports.requestEventTime = function() - if - bit32.band(executionContext, bit32.bor(RenderContext, CommitContext)) - ~= NoContext - then + if bit32.band(executionContext, bit32.bor(RenderContext, CommitContext)) ~= NoContext then -- We're inside React, so it's fine to read the actual time. return now() end @@ -527,8 +513,7 @@ exports.requestUpdateLane = function(fiber: Fiber): Lane currentEventWipLanes = workInProgressRootIncludedLanes end - local isTransition = ReactFiberTransition.requestCurrentTransition() - ~= ReactFiberTransition.NoTransition + local isTransition = ReactFiberTransition.requestCurrentTransition() ~= ReactFiberTransition.NoTransition if isTransition then if currentEventPendingLanes ~= ReactFiberLane.NoLanes then if mostRecentlyUpdatedRoot ~= nil then @@ -556,8 +541,7 @@ exports.requestUpdateLane = function(fiber: Fiber): Lane bit32.band(executionContext, DiscreteEventContext) ~= NoContext and schedulerPriority == UserBlockingSchedulerPriority then - lane = - findUpdateLane(ReactFiberLane.InputDiscreteLanePriority, currentEventWipLanes) + lane = findUpdateLane(ReactFiberLane.InputDiscreteLanePriority, currentEventWipLanes) else local schedulerLanePriority = schedulerPriorityToLanePriority(schedulerPriority) @@ -609,11 +593,7 @@ function requestRetryLane(fiber: Fiber): Lane return findRetryLane(currentEventWipLanes) end -exports.scheduleUpdateOnFiber = function( - fiber: Fiber, - lane: Lane, - eventTime: number -): FiberRoot | nil +exports.scheduleUpdateOnFiber = function(fiber: Fiber, lane: Lane, eventTime: number): FiberRoot | nil mod.checkForNestedUpdates() local root = mod.markUpdateLaneFromFiberToRoot(fiber, lane) @@ -636,8 +616,7 @@ exports.scheduleUpdateOnFiber = function( ReactFeatureFlags.deferRenderPhaseUpdateToNextBatch or bit32.band(executionContext, RenderContext) == NoContext then - workInProgressRootUpdatedLanes = - mergeLanes(workInProgressRootUpdatedLanes, lane) + workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane) end if workInProgressRootExitStatus == RootExitStatus.SuspendedWithDelay then -- The root already suspended with a delay, which means this render @@ -659,8 +638,7 @@ exports.scheduleUpdateOnFiber = function( -- Check if we're inside unbatchedUpdates bit32.band(executionContext, LegacyUnbatchedContext) ~= NoContext -- Check if we're not already rendering - and bit32.band(executionContext, bit32.bor(RenderContext, CommitContext)) - == NoContext + and bit32.band(executionContext, bit32.bor(RenderContext, CommitContext)) == NoContext then -- Register pending interactions on the root to avoid losing traced interaction data. mod.schedulePendingInteractions(root, lane) @@ -688,10 +666,7 @@ exports.scheduleUpdateOnFiber = function( bit32.band(executionContext, DiscreteEventContext) ~= NoContext -- Only updates at user-blocking priority or greater are considered -- discrete, even inside a discrete event. - and ( - priorityLevel == UserBlockingSchedulerPriority - or priorityLevel == ImmediateSchedulerPriority - ) + and (priorityLevel == UserBlockingSchedulerPriority or priorityLevel == ImmediateSchedulerPriority) then -- This is the result of a discrete event. Track the lowest priority -- discrete update per root so we can flush them early, if needed. @@ -729,10 +704,7 @@ mod.markUpdateLaneFromFiberToRoot = function(sourceFiber: Fiber, lane: Lane): Fi if __DEV__ then if alternate == nil - and bit32.band( - sourceFiber.flags, - bit32.bor(ReactFiberFlags.Placement, ReactFiberFlags.Hydrating) - ) + and bit32.band(sourceFiber.flags, bit32.bor(ReactFiberFlags.Placement, ReactFiberFlags.Hydrating)) ~= ReactFiberFlags.NoFlags then mod.warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber) @@ -749,10 +721,8 @@ mod.markUpdateLaneFromFiberToRoot = function(sourceFiber: Fiber, lane: Lane): Fi else if __DEV__ then if - bit32.band( - parent.flags, - bit32.bor(ReactFiberFlags.Placement, ReactFiberFlags.Hydrating) - ) ~= ReactFiberFlags.NoFlags + bit32.band(parent.flags, bit32.bor(ReactFiberFlags.Placement, ReactFiberFlags.Hydrating)) + ~= ReactFiberFlags.NoFlags then mod.warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber) end @@ -827,8 +797,7 @@ ensureRootIsScheduled = function(root: FiberRoot, currentTime: number) return mod.performSyncWorkOnRoot(root) end) else - local schedulerPriorityLevel = - lanePriorityToSchedulerPriority(newCallbackPriority) + local schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority) newCallbackNode = scheduleCallback(schedulerPriorityLevel, function() return mod.performConcurrentWorkOnRoot(root) end) @@ -872,12 +841,8 @@ mod.performConcurrentWorkOnRoot = function(root): (() -> ...any) | nil -- Determine the next expiration time to work on, using the fields stored -- on the root. - local lanes = getNextLanes( - root, - if root == workInProgressRoot - then workInProgressRootRenderLanes - else ReactFiberLane.NoLanes - ) + local lanes = + getNextLanes(root, if root == workInProgressRoot then workInProgressRootRenderLanes else ReactFiberLane.NoLanes) if lanes == ReactFiberLane.NoLanes then -- Defensive coding. This is never expected to happen. return nil @@ -885,9 +850,7 @@ mod.performConcurrentWorkOnRoot = function(root): (() -> ...any) | nil local exitStatus = mod.renderRootConcurrent(root, lanes) - if - includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes) - then + if includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes) then -- The render included lanes that were updated during the render phase. -- For example, when unhiding a hidden tree, we include all the lanes -- that were previously skipped when the tree was hidden. That set of @@ -955,10 +918,7 @@ function shouldForceFlushFallbacksInDEV() end mod.finishConcurrentRender = function(root, exitStatus, lanes) - if - exitStatus == RootExitStatus.Incomplete - or exitStatus == RootExitStatus.FatalErrored - then + if exitStatus == RootExitStatus.Incomplete or exitStatus == RootExitStatus.FatalErrored then invariant(false, "Root did not complete. This is a bug in React.") -- Flow knows about invariant, so it complains if I add a break -- if I do. eslint-disable-next-line no-fallthrough @@ -979,9 +939,7 @@ mod.finishConcurrentRender = function(root, exitStatus, lanes) then -- This render only included retries, no updates. Throttle committing -- retries so that we don't show too many loading states too quickly. - local msUntilTimeout = globalMostRecentFallbackTime - + FALLBACK_THROTTLE_MS - - now() + local msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now() -- Don't bother with a very short suspense time. if msUntilTimeout > 10 then local nextLanes = getNextLanes(root, ReactFiberLane.NoLanes) @@ -1076,20 +1034,12 @@ mod.performSyncWorkOnRoot = function(root) local lanes local exitStatus - if - root == workInProgressRoot - and includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes) - then + if root == workInProgressRoot and includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes) then -- There's a partial tree, and at least one of its lanes has expired. Finish -- rendering it before rendering the rest of the expired work. lanes = workInProgressRootRenderLanes exitStatus = mod.renderRootSync(root, lanes) - if - includesSomeLane( - workInProgressRootIncludedLanes, - workInProgressRootUpdatedLanes - ) - then + if includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes) then -- The render included lanes that were updated during the render phase. -- For example, when unhiding a hidden tree, we include all the lanes -- that were previously skipped when the tree was hidden. That set of @@ -1151,10 +1101,7 @@ end exports.flushRoot = function(root: FiberRoot, lanes: Lanes) markRootExpired(root, lanes) ensureRootIsScheduled(root, now()) - if - bit32.band(executionContext, bit32.bor(RenderContext, CommitContext)) - == NoContext - then + if bit32.band(executionContext, bit32.bor(RenderContext, CommitContext)) == NoContext then resetRenderTimer() flushSyncCallbackQueue() end @@ -1169,17 +1116,11 @@ exports.flushDiscreteUpdates = function() -- However, `act` uses `batchedUpdates`, so there's no way to distinguish -- those two cases. Need to fix this before exposing flushDiscreteUpdates -- as a public API. - if - bit32.band( - executionContext, - bit32.bor(BatchedContext, RenderContext, CommitContext) - ) ~= NoContext - then + if bit32.band(executionContext, bit32.bor(BatchedContext, RenderContext, CommitContext)) ~= NoContext then if __DEV__ then if bit32.band(executionContext, RenderContext) ~= NoContext then console.error( - "unstable_flushDiscreteUpdates: Cannot flush updates when React is " - .. "already rendering." + "unstable_flushDiscreteUpdates: Cannot flush updates when React is " .. "already rendering." ) end end @@ -1202,8 +1143,7 @@ exports.deferredUpdates = function(fn: () -> A): A if not __YOLO__ then -- ROBLOX performance: hoist non-throwable out of try{} to eliminate anon function setCurrentUpdateLanePriority(ReactFiberLane.DefaultLanePriority) - ok, result = - xpcall(runWithPriority, describeError, NormalSchedulerPriority, fn) + ok, result = xpcall(runWithPriority, describeError, NormalSchedulerPriority, fn) else ok = true setCurrentUpdateLanePriority(ReactFiberLane.DefaultLanePriority) @@ -1294,63 +1234,52 @@ exports.batchedEventUpdates = function(fn: (A) -> R, a: A): R end end -exports.discreteUpdates = - function(fn: (A, B, C, D) -> R, a: A, b: B, c: C, d: D): R - local prevExecutionContext = executionContext - executionContext = bit32.bor(executionContext, DiscreteEventContext) +exports.discreteUpdates = function(fn: (A, B, C, D) -> R, a: A, b: B, c: C, d: D): R + local prevExecutionContext = executionContext + executionContext = bit32.bor(executionContext, DiscreteEventContext) - if ReactFeatureFlags.decoupleUpdatePriorityFromScheduler then - local previousLanePriority = getCurrentUpdateLanePriority() - -- ROBLOX performance: extract non-throwable fn call out of try{} so we can remove an anon function - setCurrentUpdateLanePriority(ReactFiberLane.InputDiscreteLanePriority) - local ok, result = xpcall( - runWithPriority, - describeError, - UserBlockingSchedulerPriority, - function() - return fn(a, b, c, d) - end - ) + if ReactFeatureFlags.decoupleUpdatePriorityFromScheduler then + local previousLanePriority = getCurrentUpdateLanePriority() + -- ROBLOX performance: extract non-throwable fn call out of try{} so we can remove an anon function + setCurrentUpdateLanePriority(ReactFiberLane.InputDiscreteLanePriority) + local ok, result = xpcall(runWithPriority, describeError, UserBlockingSchedulerPriority, function() + return fn(a, b, c, d) + end) - -- finally - setCurrentUpdateLanePriority(previousLanePriority) - executionContext = prevExecutionContext - if executionContext == NoContext then - -- Flush the immediate callbacks that were scheduled during this batch - resetRenderTimer() - flushSyncCallbackQueue() - end + -- finally + setCurrentUpdateLanePriority(previousLanePriority) + executionContext = prevExecutionContext + if executionContext == NoContext then + -- Flush the immediate callbacks that were scheduled during this batch + resetRenderTimer() + flushSyncCallbackQueue() + end - if ok then - return result - else - error(result) - end + if ok then + return result else - local ok, result = xpcall( - runWithPriority, - describeError, - UserBlockingSchedulerPriority, - function() - return fn(a, b, c, d) - end - ) + error(result) + end + else + local ok, result = xpcall(runWithPriority, describeError, UserBlockingSchedulerPriority, function() + return fn(a, b, c, d) + end) - -- finally - executionContext = prevExecutionContext - if executionContext == NoContext then - -- Flush the immediate callbacks that were scheduled during this batch - resetRenderTimer() - flushSyncCallbackQueue() - end + -- finally + executionContext = prevExecutionContext + if executionContext == NoContext then + -- Flush the immediate callbacks that were scheduled during this batch + resetRenderTimer() + flushSyncCallbackQueue() + end - if ok then - return result - else - error(result) - end + if ok then + return result + else + error(result) end end +end exports.unbatchedUpdates = function(fn: (A) -> R, a: A): R local prevExecutionContext = executionContext @@ -1382,10 +1311,7 @@ end exports.flushSync = function(fn: (A) -> R, a: A): R local prevExecutionContext = executionContext - if - (bit32.band(prevExecutionContext, bit32.bor(RenderContext, CommitContext))) - ~= NoContext - then + if (bit32.band(prevExecutionContext, bit32.bor(RenderContext, CommitContext))) ~= NoContext then if __DEV__ then console.error( "flushSync was called from inside a lifecycle method. React cannot " @@ -1406,14 +1332,9 @@ exports.flushSync = function(fn: (A) -> R, a: A): R local ok, result if not __YOLO__ then if fn then - ok, result = xpcall( - runWithPriority, - describeError, - ImmediateSchedulerPriority, - function() - return fn(a) - end - ) + ok, result = xpcall(runWithPriority, describeError, ImmediateSchedulerPriority, function() + return fn(a) + end) else ok = true -- ROBLOX note: return (undefined: $FlowFixMe) @@ -1449,14 +1370,9 @@ exports.flushSync = function(fn: (A) -> R, a: A): R local ok, result if not __YOLO__ then if fn then - ok, result = xpcall( - runWithPriority, - describeError, - ImmediateSchedulerPriority, - function() - return fn(a) - end - ) + ok, result = xpcall(runWithPriority, describeError, ImmediateSchedulerPriority, function() + return fn(a) + end) else ok = true -- ROBLOX note: return (undefined: $FlowFixMe) @@ -1493,8 +1409,7 @@ exports.flushControlled = function(fn: () -> any) local previousLanePriority = getCurrentUpdateLanePriority() -- ROBLOX performance: extract non-throwable call out of try{} to eliminate an anon function setCurrentUpdateLanePriority(ReactFiberLane.SyncLanePriority) - local ok, result = - xpcall(runWithPriority, describeError, ImmediateSchedulerPriority, fn) + local ok, result = xpcall(runWithPriority, describeError, ImmediateSchedulerPriority, fn) -- finally setCurrentUpdateLanePriority(previousLanePriority) @@ -1510,8 +1425,7 @@ exports.flushControlled = function(fn: () -> any) error(result) end else - local ok, result = - xpcall(runWithPriority, describeError, ImmediateSchedulerPriority, fn) + local ok, result = xpcall(runWithPriority, describeError, ImmediateSchedulerPriority, fn) -- finally executionContext = prevExecutionContext if executionContext == NoContext then @@ -1611,19 +1525,12 @@ mod.handleError = function(root, thrownValue): () -- ROBLOX Luau FIXME: Luau doesn't narrow based on the erroredWork == nil then return above if ReactFeatureFlags.enableProfilerTimer - and bit32.band( - (erroredWork :: Fiber).mode, - ReactTypeOfMode.ProfileMode - ) - ~= 0 + and bit32.band((erroredWork :: Fiber).mode, ReactTypeOfMode.ProfileMode) ~= 0 then -- Record the time spent rendering before an error was thrown. This -- avoids inaccurate Profiler durations in the case of a -- suspended render. - ReactProfilerTimer.stopProfilerTimerIfRunningAndRecordDelta( - erroredWork :: Fiber, - true - ) + ReactProfilerTimer.stopProfilerTimerIfRunningAndRecordDelta(erroredWork :: Fiber, true) end -- ROBLOX deviation, we pass in onUncaughtError and renderDidError here since throwException can't call them due to a require cycle @@ -1932,8 +1839,7 @@ mod.performUnitOfWork = function(unitOfWork: Fiber): () local next_ if ReactFeatureFlags.enableProfilerTimer - and bit32.band(unitOfWork.mode, ReactTypeOfMode.ProfileMode) - ~= ReactTypeOfMode.NoMode + and bit32.band(unitOfWork.mode, ReactTypeOfMode.ProfileMode) ~= ReactTypeOfMode.NoMode then ReactProfilerTimer.startProfilerTimer(unitOfWork) next_ = mod.beginWork(current, unitOfWork, exports.subtreeRenderLanes) @@ -1966,26 +1872,19 @@ mod.completeUnitOfWork = function(unitOfWork: Fiber) local returnFiber = completedWork.return_ -- Check if the work completed or if something threw. - if - bit32.band(completedWork.flags, ReactFiberFlags.Incomplete) - == ReactFiberFlags.NoFlags - then + if bit32.band(completedWork.flags, ReactFiberFlags.Incomplete) == ReactFiberFlags.NoFlags then setCurrentDebugFiberInDEV(completedWork) local next_ if not ReactFeatureFlags.enableProfilerTimer - or bit32.band(completedWork.mode, ReactTypeOfMode.ProfileMode) - == ReactTypeOfMode.NoMode + or bit32.band(completedWork.mode, ReactTypeOfMode.ProfileMode) == ReactTypeOfMode.NoMode then next_ = completeWork(current, completedWork, exports.subtreeRenderLanes) else ReactProfilerTimer.startProfilerTimer(completedWork) next_ = completeWork(current, completedWork, exports.subtreeRenderLanes) -- Update render duration assuming we didn't error. - ReactProfilerTimer.stopProfilerTimerIfRunningAndRecordDelta( - completedWork, - false - ) + ReactProfilerTimer.stopProfilerTimerIfRunningAndRecordDelta(completedWork, false) end resetCurrentDebugFiberInDEV() @@ -2014,14 +1913,10 @@ mod.completeUnitOfWork = function(unitOfWork: Fiber) if ReactFeatureFlags.enableProfilerTimer - and bit32.band(completedWork.mode, ReactTypeOfMode.ProfileMode) - ~= ReactTypeOfMode.NoMode + and bit32.band(completedWork.mode, ReactTypeOfMode.ProfileMode) ~= ReactTypeOfMode.NoMode then -- Record the render duration for the fiber that errored. - ReactProfilerTimer.stopProfilerTimerIfRunningAndRecordDelta( - completedWork, - false - ) + ReactProfilerTimer.stopProfilerTimerIfRunningAndRecordDelta(completedWork, false) -- Include the time spent working on failed children before continuing. -- ROBLOX TODO: actualDuration is nil-able and only populated with enableProfilerTimer. contribute default value upstream. @@ -2036,8 +1931,7 @@ mod.completeUnitOfWork = function(unitOfWork: Fiber) if returnFiber ~= nil then -- Mark the parent fiber as incomplete - returnFiber.flags = - bit32.bor(returnFiber.flags, ReactFiberFlags.Incomplete) + returnFiber.flags = bit32.bor(returnFiber.flags, ReactFiberFlags.Incomplete) returnFiber.subtreeFlags = ReactFiberFlags.NoFlags returnFiber.deletions = nil end @@ -2137,10 +2031,7 @@ mod.commitRootImpl = function(root: FiberRoot, renderPriorityLevel) -- `flushDiscreteUpdates` starts a useless render pass which may cancels -- a scheduled timeout. if rootsWithPendingDiscreteUpdates ~= nil then - if - not hasDiscreteLanes(remainingLanes) - and rootsWithPendingDiscreteUpdates:has(root) - then + if not hasDiscreteLanes(remainingLanes) and rootsWithPendingDiscreteUpdates:has(root) then rootsWithPendingDiscreteUpdates:delete(root) end end @@ -2297,10 +2188,8 @@ mod.commitRootImpl = function(root: FiberRoot, renderPriorityLevel) -- If there are pending passive effects, schedule a callback to process them. if - bit32.band(finishedWork.subtreeFlags, ReactFiberFlags.PassiveMask) - ~= ReactFiberFlags.NoFlags - or bit32.band(finishedWork.flags, ReactFiberFlags.PassiveMask) - ~= ReactFiberFlags.NoFlags + bit32.band(finishedWork.subtreeFlags, ReactFiberFlags.PassiveMask) ~= ReactFiberFlags.NoFlags + or bit32.band(finishedWork.flags, ReactFiberFlags.PassiveMask) ~= ReactFiberFlags.NoFlags then if not rootDoesHavePassiveEffects then rootDoesHavePassiveEffects = true @@ -2320,10 +2209,7 @@ mod.commitRootImpl = function(root: FiberRoot, renderPriorityLevel) end executionContext = prevExecutionContext - if - ReactFeatureFlags.decoupleUpdatePriorityFromScheduler - and previousLanePriority ~= nil - then + if ReactFeatureFlags.decoupleUpdatePriorityFromScheduler and previousLanePriority ~= nil then -- Reset the priority to the previous non-sync value. setCurrentUpdateLanePriority(previousLanePriority) end @@ -2359,11 +2245,7 @@ mod.commitRootImpl = function(root: FiberRoot, renderPriorityLevel) local expirationTimes = spawnedWorkDuringRender spawnedWorkDuringRender = nil for i = 1, #expirationTimes do - scheduleInteractions( - root, - expirationTimes[i], - root.memoizedInteractions - ) + scheduleInteractions(root, expirationTimes[i], root.memoizedInteractions) end end mod.schedulePendingInteractions(root, remainingLanes) @@ -2463,8 +2345,7 @@ mod.commitBeforeMutationEffects = function(firstChild: Fiber) end if fiber.child ~= nil then - local primarySubtreeFlags = - bit32.band(fiber.subtreeFlags, ReactFiberFlags.BeforeMutationMask) + local primarySubtreeFlags = bit32.band(fiber.subtreeFlags, ReactFiberFlags.BeforeMutationMask) if primarySubtreeFlags ~= ReactFiberFlags.NoFlags then mod.commitBeforeMutationEffects(fiber.child) end @@ -2482,8 +2363,7 @@ mod.commitBeforeMutationEffects = function(firstChild: Fiber) -- ROBLOX deviation: YOLO flag for disabling pcall local ok, error_ if not __YOLO__ then - ok, error_ = - xpcall(mod.commitBeforeMutationEffectsImpl, describeError, fiber) + ok, error_ = xpcall(mod.commitBeforeMutationEffectsImpl, describeError, fiber) else ok = true mod.commitBeforeMutationEffectsImpl(fiber) @@ -2549,128 +2429,100 @@ mod.commitBeforeMutationEffectsDeletions = function(deletions: Array) end end -mod.commitMutationEffects = - function(firstChild: Fiber, root: FiberRoot, renderPriorityLevel: ReactPriorityLevel) - local fiber = firstChild - while fiber ~= nil do - local deletions = fiber.deletions - if deletions ~= nil then - -- ROBLOX performance: React 18 inlines commitMutationEffectsDeletions, pulling that in based on tab switching hot path - for _, childToDelete in deletions do - -- ROBLOX FIXME Luau: CLI-49835, "Function only returns 1 value, 2 are required" - local ok, error_ = xpcall( - commitDeletion, - describeError, - root, - childToDelete, - fiber, - renderPriorityLevel - ) - if not ok then - exports.captureCommitPhaseError(childToDelete, fiber, error_) - end +mod.commitMutationEffects = function(firstChild: Fiber, root: FiberRoot, renderPriorityLevel: ReactPriorityLevel) + local fiber = firstChild + while fiber ~= nil do + local deletions = fiber.deletions + if deletions ~= nil then + -- ROBLOX performance: React 18 inlines commitMutationEffectsDeletions, pulling that in based on tab switching hot path + for _, childToDelete in deletions do + -- ROBLOX FIXME Luau: CLI-49835, "Function only returns 1 value, 2 are required" + local ok, error_ = + xpcall(commitDeletion, describeError, root, childToDelete, fiber, renderPriorityLevel) + if not ok then + exports.captureCommitPhaseError(childToDelete, fiber, error_) end end + end - if fiber.child ~= nil then - local mutationFlags = - bit32.band(fiber.subtreeFlags, ReactFiberFlags.MutationMask) - if mutationFlags ~= ReactFiberFlags.NoFlags then - mod.commitMutationEffects(fiber.child, root, renderPriorityLevel) - end + if fiber.child ~= nil then + local mutationFlags = bit32.band(fiber.subtreeFlags, ReactFiberFlags.MutationMask) + if mutationFlags ~= ReactFiberFlags.NoFlags then + mod.commitMutationEffects(fiber.child, root, renderPriorityLevel) end + end - if __DEV__ then - setCurrentDebugFiberInDEV(fiber) - invokeGuardedCallback( - nil, - mod.commitMutationEffectsImpl, - nil, - fiber, - root, - renderPriorityLevel - ) - if hasCaughtError() then - local error_ = clearCaughtError() - exports.captureCommitPhaseError(fiber, fiber.return_, error_) - end - resetCurrentDebugFiberInDEV() + if __DEV__ then + setCurrentDebugFiberInDEV(fiber) + invokeGuardedCallback(nil, mod.commitMutationEffectsImpl, nil, fiber, root, renderPriorityLevel) + if hasCaughtError() then + local error_ = clearCaughtError() + exports.captureCommitPhaseError(fiber, fiber.return_, error_) + end + resetCurrentDebugFiberInDEV() + else + -- ROBLOX deviation: YOLO flag for disabling pcall + local ok, result + if not __YOLO__ then + ok, result = xpcall(mod.commitMutationEffectsImpl, describeError, fiber, root, renderPriorityLevel) else - -- ROBLOX deviation: YOLO flag for disabling pcall - local ok, result - if not __YOLO__ then - ok, result = xpcall( - mod.commitMutationEffectsImpl, - describeError, - fiber, - root, - renderPriorityLevel - ) - else - ok = true - mod.commitMutationEffectsImpl(fiber, root, renderPriorityLevel) - end - if not ok then - exports.captureCommitPhaseError(fiber, fiber.return_, result) - end + ok = true + mod.commitMutationEffectsImpl(fiber, root, renderPriorityLevel) + end + if not ok then + exports.captureCommitPhaseError(fiber, fiber.return_, result) end - -- ROBLOX Luau FIXME: Luau doesn't understand the while ~= nil construct - fiber = fiber.sibling :: Fiber end + -- ROBLOX Luau FIXME: Luau doesn't understand the while ~= nil construct + fiber = fiber.sibling :: Fiber end +end + +mod.commitMutationEffectsImpl = function(fiber: Fiber, root: FiberRoot, renderPriorityLevel) + local flags = fiber.flags + -- ROBLOX performance: avoid always-false compare for Roblox renderer in hot path + -- if bit32.band(flags, ReactFiberFlags.ContentReset) ~= 0 then + -- unimplemented("commitResetTextContent") + -- commitResetTextContent(fiber) + -- end -mod.commitMutationEffectsImpl = - function(fiber: Fiber, root: FiberRoot, renderPriorityLevel) - local flags = fiber.flags + if bit32.band(flags, ReactFiberFlags.Ref) ~= 0 then + local current = fiber.alternate + if current ~= nil then + commitDetachRef(current) + end -- ROBLOX performance: avoid always-false compare for Roblox renderer in hot path - -- if bit32.band(flags, ReactFiberFlags.ContentReset) ~= 0 then - -- unimplemented("commitResetTextContent") - -- commitResetTextContent(fiber) + -- if ReactFeatureFlags.enableScopeAPI then + -- -- TODO: This is a temporary solution that allowed us to transition away from React Flare on www. + -- if fiber.tag == ReactWorkTags.ScopeComponent then + -- commitAttachRef(fiber) + -- end -- end + end - if bit32.band(flags, ReactFiberFlags.Ref) ~= 0 then - local current = fiber.alternate - if current ~= nil then - commitDetachRef(current) - end - -- ROBLOX performance: avoid always-false compare for Roblox renderer in hot path - -- if ReactFeatureFlags.enableScopeAPI then - -- -- TODO: This is a temporary solution that allowed us to transition away from React Flare on www. - -- if fiber.tag == ReactWorkTags.ScopeComponent then - -- commitAttachRef(fiber) - -- end - -- end - end - - -- The following switch statement is only concerned about placement, - -- updates, and deletions. To avoid needing to add a case for every possible - -- bitmap value, we remove the secondary effects from the effect tag and - -- switch on that value. - local primaryFlags = bit32.band( - flags, - bit32.bor( - ReactFiberFlags.Placement, - ReactFiberFlags.Update, - ReactFiberFlags.Hydrating - ) - ) - if primaryFlags == ReactFiberFlags.Placement then - commitPlacement(fiber) - -- Clear the "placement" from effect tag so that we know that this is - -- inserted, before any life-cycles like componentDidMount gets called. - -- TODO: findDOMNode doesn't rely on this any more but isMounted does - -- and isMounted is deprecated anyway so we should be able to kill this. - fiber.flags = bit32.band(fiber.flags, bit32.bnot(ReactFiberFlags.Placement)) - elseif primaryFlags == ReactFiberFlags.PlacementAndUpdate then - -- Placement - commitPlacement(fiber) - -- Clear the "placement" from effect tag so that we know that this is - -- inserted, before any life-cycles like componentDidMount gets called. - fiber.flags = bit32.band(fiber.flags, bit32.bnot(ReactFiberFlags.Placement)) - - -- Update - local current = fiber.alternate - commitWork(current, fiber) + -- The following switch statement is only concerned about placement, + -- updates, and deletions. To avoid needing to add a case for every possible + -- bitmap value, we remove the secondary effects from the effect tag and + -- switch on that value. + local primaryFlags = + bit32.band(flags, bit32.bor(ReactFiberFlags.Placement, ReactFiberFlags.Update, ReactFiberFlags.Hydrating)) + if primaryFlags == ReactFiberFlags.Placement then + commitPlacement(fiber) + -- Clear the "placement" from effect tag so that we know that this is + -- inserted, before any life-cycles like componentDidMount gets called. + -- TODO: findDOMNode doesn't rely on this any more but isMounted does + -- and isMounted is deprecated anyway so we should be able to kill this. + fiber.flags = bit32.band(fiber.flags, bit32.bnot(ReactFiberFlags.Placement)) + elseif primaryFlags == ReactFiberFlags.PlacementAndUpdate then + -- Placement + commitPlacement(fiber) + -- Clear the "placement" from effect tag so that we know that this is + -- inserted, before any life-cycles like componentDidMount gets called. + fiber.flags = bit32.band(fiber.flags, bit32.bnot(ReactFiberFlags.Placement)) + + -- Update + local current = fiber.alternate + commitWork(current, fiber) -- ROBLOX performance: avoid always-false compare for Roblox renderer in hot path -- elseif primaryFlags == ReactFiberFlags.Hydrating then -- fiber.flags = bit32.band(fiber.flags, bit32.bnot(ReactFiberFlags.Hydrating)) @@ -2679,30 +2531,27 @@ mod.commitMutationEffectsImpl = -- -- Update -- local current = fiber.alternate -- commitWork(current, fiber) - elseif primaryFlags == ReactFiberFlags.Update then - local current = fiber.alternate - commitWork(current, fiber) - end + elseif primaryFlags == ReactFiberFlags.Update then + local current = fiber.alternate + commitWork(current, fiber) end +end -mod.commitMutationEffectsDeletions = - function(deletions: Array, fiber: Fiber, root: FiberRoot, renderPriorityLevel) - -- ROBLOX performance: align to React 18, which ditches the __DEV__ branch and use of invokeGuardedCallback - for _, childToDelete in deletions do - -- ROBLOX FIXME Luau: CLI-49835, "Function only returns 1 value, 2 are required" - local ok, error_ = xpcall( - commitDeletion, - describeError, - root, - childToDelete, - fiber, - renderPriorityLevel - ) - if not ok then - exports.captureCommitPhaseError(childToDelete, fiber, error_) - end +mod.commitMutationEffectsDeletions = function( + deletions: Array, + fiber: Fiber, + root: FiberRoot, + renderPriorityLevel +) + -- ROBLOX performance: align to React 18, which ditches the __DEV__ branch and use of invokeGuardedCallback + for _, childToDelete in deletions do + -- ROBLOX FIXME Luau: CLI-49835, "Function only returns 1 value, 2 are required" + local ok, error_ = xpcall(commitDeletion, describeError, root, childToDelete, fiber, renderPriorityLevel) + if not ok then + exports.captureCommitPhaseError(childToDelete, fiber, error_) end end +end exports.schedulePassiveEffectCallback = function() if not rootDoesHavePassiveEffects then @@ -2719,8 +2568,7 @@ local flushPassiveEffectsImpl exports.flushPassiveEffects = function(): boolean -- Returns whether passive effects were flushed. if pendingPassiveEffectsRenderPriority ~= NoSchedulerPriority then - local priorityLevel = if pendingPassiveEffectsRenderPriority - > NormalSchedulerPriority + local priorityLevel = if pendingPassiveEffectsRenderPriority > NormalSchedulerPriority then NormalSchedulerPriority else pendingPassiveEffectsRenderPriority pendingPassiveEffectsRenderPriority = NoSchedulerPriority @@ -2732,17 +2580,10 @@ exports.flushPassiveEffects = function(): boolean -- ROBLOX deviation: YOLO flag for disabling pcall local ok, result if not __YOLO__ then - ok, result = xpcall( - runWithPriority, - describeError, - priorityLevel, - flushPassiveEffectsImpl - ) + ok, result = xpcall(runWithPriority, describeError, priorityLevel, flushPassiveEffectsImpl) else ok = true - setCurrentUpdateLanePriority( - schedulerPriorityToLanePriority(priorityLevel) - ) + setCurrentUpdateLanePriority(schedulerPriorityToLanePriority(priorityLevel)) result = runWithPriority(priorityLevel, flushPassiveEffectsImpl) end @@ -2764,26 +2605,20 @@ flushPassiveMountEffects = function(root, firstChild: Fiber): () local fiber = firstChild while fiber ~= nil do local prevProfilerOnStack = nil - if - ReactFeatureFlags.enableProfilerTimer - and ReactFeatureFlags.enableProfilerCommitHooks - then + if ReactFeatureFlags.enableProfilerTimer and ReactFeatureFlags.enableProfilerCommitHooks then if fiber.tag == ReactWorkTags.Profiler then prevProfilerOnStack = nearestProfilerOnStack nearestProfilerOnStack = fiber end end - local primarySubtreeFlags = - bit32.band(fiber.subtreeFlags, ReactFiberFlags.PassiveMask) + local primarySubtreeFlags = bit32.band(fiber.subtreeFlags, ReactFiberFlags.PassiveMask) if fiber.child ~= nil and primarySubtreeFlags ~= ReactFiberFlags.NoFlags then flushPassiveMountEffects(root, fiber.child) end - if - bit32.band(fiber.flags, ReactFiberFlags.Passive) ~= ReactFiberFlags.NoFlags - then + if bit32.band(fiber.flags, ReactFiberFlags.Passive) ~= ReactFiberFlags.NoFlags then if __DEV__ then setCurrentDebugFiberInDEV(fiber) invokeGuardedCallback(nil, commitPassiveMountOnFiber, nil, root, fiber) @@ -2796,8 +2631,7 @@ flushPassiveMountEffects = function(root, firstChild: Fiber): () -- ROBLOX deviation: YOLO flag for disabling pcall local ok, error_ if not __YOLO__ then - ok, error_ = - xpcall(commitPassiveMountOnFiber, describeError, root, fiber) + ok, error_ = xpcall(commitPassiveMountOnFiber, describeError, root, fiber) else ok = true commitPassiveMountOnFiber(root, fiber) @@ -2809,10 +2643,7 @@ flushPassiveMountEffects = function(root, firstChild: Fiber): () end end - if - ReactFeatureFlags.enableProfilerTimer - and ReactFeatureFlags.enableProfilerCommitHooks - then + if ReactFeatureFlags.enableProfilerTimer and ReactFeatureFlags.enableProfilerCommitHooks then if fiber.tag == ReactWorkTags.Profiler then -- Bubble times to the next nearest ancestor Profiler. -- After we process that Profiler, we'll bubble further up. @@ -2849,8 +2680,7 @@ local function flushPassiveUnmountEffects(firstChild: Fiber): () -- Note that this requires checking subtreeFlags of the current Fiber, -- rather than the subtreeFlags/effectsTag of the first child, -- since that would not cover passive effects in siblings. - local passiveFlags = - bit32.band(fiber.subtreeFlags, ReactFiberFlags.PassiveMask) + local passiveFlags = bit32.band(fiber.subtreeFlags, ReactFiberFlags.PassiveMask) if passiveFlags ~= ReactFiberFlags.NoFlags then flushPassiveUnmountEffects(child) end @@ -2868,38 +2698,25 @@ local function flushPassiveUnmountEffects(firstChild: Fiber): () end end -mod.flushPassiveUnmountEffectsInsideOfDeletedTree = - function(fiberToDelete: Fiber, nearestMountedAncestor: Fiber) - if - bit32.band(fiberToDelete.subtreeFlags, ReactFiberFlags.PassiveStatic) - ~= ReactFiberFlags.NoFlags - then - -- If any children have passive effects then traverse the subtree. - -- Note that this requires checking subtreeFlags of the current Fiber, - -- rather than the subtreeFlags/effectsTag of the first child, - -- since that would not cover passive effects in siblings. - local child = fiberToDelete.child - while child ~= nil do - mod.flushPassiveUnmountEffectsInsideOfDeletedTree( - child, - nearestMountedAncestor - ) - child = child.sibling - end +mod.flushPassiveUnmountEffectsInsideOfDeletedTree = function(fiberToDelete: Fiber, nearestMountedAncestor: Fiber) + if bit32.band(fiberToDelete.subtreeFlags, ReactFiberFlags.PassiveStatic) ~= ReactFiberFlags.NoFlags then + -- If any children have passive effects then traverse the subtree. + -- Note that this requires checking subtreeFlags of the current Fiber, + -- rather than the subtreeFlags/effectsTag of the first child, + -- since that would not cover passive effects in siblings. + local child = fiberToDelete.child + while child ~= nil do + mod.flushPassiveUnmountEffectsInsideOfDeletedTree(child, nearestMountedAncestor) + child = child.sibling end + end - if - bit32.band(fiberToDelete.flags, ReactFiberFlags.PassiveStatic) - ~= ReactFiberFlags.NoFlags - then - setCurrentDebugFiberInDEV(fiberToDelete) - commitPassiveUnmountInsideDeletedTreeOnFiber( - fiberToDelete, - nearestMountedAncestor - ) - resetCurrentDebugFiberInDEV() - end + if bit32.band(fiberToDelete.flags, ReactFiberFlags.PassiveStatic) ~= ReactFiberFlags.NoFlags then + setCurrentDebugFiberInDEV(fiberToDelete) + commitPassiveUnmountInsideDeletedTreeOnFiber(fiberToDelete, nearestMountedAncestor) + resetCurrentDebugFiberInDEV() end +end flushPassiveEffectsImpl = function() if rootWithPendingPassiveEffects == nil then @@ -2975,9 +2792,7 @@ flushPassiveEffectsImpl = function() end exports.isAlreadyFailedLegacyErrorBoundary = function(instance): boolean - return - legacyErrorBoundariesThatAlreadyFailed ~= nil - and legacyErrorBoundariesThatAlreadyFailed:has(instance) + return legacyErrorBoundariesThatAlreadyFailed ~= nil and legacyErrorBoundariesThatAlreadyFailed:has(instance) end exports.markLegacyErrorBoundaryAsFailed = function(instance) @@ -3000,8 +2815,7 @@ exports.onUncaughtError = prepareToThrowUncaughtError captureCommitPhaseErrorOnRoot = function(rootFiber: Fiber, sourceFiber: Fiber, error_) local errorInfo = createCapturedValue(error_, sourceFiber) -- ROBLOX deviation: parameterize method onUncaughtError to avoid circular dependency - local update = - createRootErrorUpdate(rootFiber, errorInfo, SyncLane, exports.onUncaughtError) + local update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane, exports.onUncaughtError) enqueueUpdate(rootFiber, update) local eventTime = exports.requestEventTime() local root = mod.markUpdateLaneFromFiberToRoot(rootFiber, SyncLane) @@ -3017,100 +2831,94 @@ end -- nearestMountedAncestor: Fiber | nil, -- error: mixed -- ) -exports.captureCommitPhaseError = - function(sourceFiber: Fiber, nearestMountedAncestor, error_) - if sourceFiber.tag == ReactWorkTags.HostRoot then - -- Error was thrown at the root. There is no parent, so the root - -- itself should capture it. - captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error_) - return - end +exports.captureCommitPhaseError = function(sourceFiber: Fiber, nearestMountedAncestor, error_) + if sourceFiber.tag == ReactWorkTags.HostRoot then + -- Error was thrown at the root. There is no parent, so the root + -- itself should capture it. + captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error_) + return + end - local fiber = nil - if skipUnmountedBoundaries then - fiber = nearestMountedAncestor - else - fiber = sourceFiber.return_ - end + local fiber = nil + if skipUnmountedBoundaries then + fiber = nearestMountedAncestor + else + fiber = sourceFiber.return_ + end - while fiber ~= nil do - if fiber.tag == ReactWorkTags.HostRoot then - captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error_) - return - else - if fiber.tag == ReactWorkTags.ClassComponent then - local ctor = fiber.type - local instance = fiber.stateNode - if - typeof(ctor.getDerivedStateFromError) == "function" - or ( - typeof(instance.componentDidCatch) == "function" - and not exports.isAlreadyFailedLegacyErrorBoundary(instance) - ) - then - local errorInfo = createCapturedValue(error_, sourceFiber) - local update = createClassErrorUpdate(fiber, errorInfo, SyncLane) - enqueueUpdate(fiber, update) - local eventTime = exports.requestEventTime() - local root = mod.markUpdateLaneFromFiberToRoot(fiber, SyncLane) - if root ~= nil then - markRootUpdated(root, SyncLane, eventTime) - ensureRootIsScheduled(root, eventTime) - mod.schedulePendingInteractions(root, SyncLane) - end - return + while fiber ~= nil do + if fiber.tag == ReactWorkTags.HostRoot then + captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error_) + return + else + if fiber.tag == ReactWorkTags.ClassComponent then + local ctor = fiber.type + local instance = fiber.stateNode + if + typeof(ctor.getDerivedStateFromError) == "function" + or ( + typeof(instance.componentDidCatch) == "function" + and not exports.isAlreadyFailedLegacyErrorBoundary(instance) + ) + then + local errorInfo = createCapturedValue(error_, sourceFiber) + local update = createClassErrorUpdate(fiber, errorInfo, SyncLane) + enqueueUpdate(fiber, update) + local eventTime = exports.requestEventTime() + local root = mod.markUpdateLaneFromFiberToRoot(fiber, SyncLane) + if root ~= nil then + markRootUpdated(root, SyncLane, eventTime) + ensureRootIsScheduled(root, eventTime) + mod.schedulePendingInteractions(root, SyncLane) end + return end - fiber = fiber.return_ end + fiber = fiber.return_ end end +end -exports.pingSuspendedRoot = - function(root: FiberRoot, wakeable: Wakeable, pingedLanes: Lanes) - local pingCache = root.pingCache - if pingCache ~= nil then - -- The wakeable resolved, so we no longer need to memoize, because it will - -- never be thrown again. - pingCache[wakeable] = nil - end +exports.pingSuspendedRoot = function(root: FiberRoot, wakeable: Wakeable, pingedLanes: Lanes) + local pingCache = root.pingCache + if pingCache ~= nil then + -- The wakeable resolved, so we no longer need to memoize, because it will + -- never be thrown again. + pingCache[wakeable] = nil + end + + local eventTime = exports.requestEventTime() + markRootPinged(root, pingedLanes, eventTime) + + if workInProgressRoot == root and isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes) then + -- Received a ping at the same priority level at which we're currently + -- rendering. We might want to restart this render. This should mirror + -- the logic of whether or not a root suspends once it completes. - local eventTime = exports.requestEventTime() - markRootPinged(root, pingedLanes, eventTime) + -- TODO: If we're rendering sync either due to Sync, Batched or expired, + -- we should probably never restart. + -- If we're suspended with delay, or if it's a retry, we'll always suspend + -- so we can always restart. if - workInProgressRoot == root - and isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes) + workInProgressRootExitStatus == RootExitStatus.SuspendedWithDelay + or workInProgressRootExitStatus == RootExitStatus.Suspended + and includesOnlyRetries(workInProgressRootRenderLanes) + and now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS then - -- Received a ping at the same priority level at which we're currently - -- rendering. We might want to restart this render. This should mirror - -- the logic of whether or not a root suspends once it completes. - - -- TODO: If we're rendering sync either due to Sync, Batched or expired, - -- we should probably never restart. - - -- If we're suspended with delay, or if it's a retry, we'll always suspend - -- so we can always restart. - if - workInProgressRootExitStatus == RootExitStatus.SuspendedWithDelay - or workInProgressRootExitStatus == RootExitStatus.Suspended - and includesOnlyRetries(workInProgressRootRenderLanes) - and now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS - then - -- Restart from the root. - mod.prepareFreshStack(root, ReactFiberLane.NoLanes) - else - -- Even though we can't restart right now, we might get an - -- opportunity later. So we mark this render as having a ping. - workInProgressRootPingedLanes = - mergeLanes(workInProgressRootPingedLanes, pingedLanes) - end + -- Restart from the root. + mod.prepareFreshStack(root, ReactFiberLane.NoLanes) + else + -- Even though we can't restart right now, we might get an + -- opportunity later. So we mark this render as having a ping. + workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes) end - - ensureRootIsScheduled(root, eventTime) - mod.schedulePendingInteractions(root, pingedLanes) end + ensureRootIsScheduled(root, eventTime) + mod.schedulePendingInteractions(root, pingedLanes) +end + function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) -- The boundary fiber (a Suspense component or SuspenseList component) -- previously was rendered in its fallback state. One of the promises that @@ -3239,40 +3047,20 @@ end function commitDoubleInvokeEffectsInDEV(fiber: Fiber, hasPassiveEffects: boolean) if __DEV__ and enableDoubleInvokingEffects then setCurrentDebugFiberInDEV(fiber) - invokeEffectsInDev( - fiber, - ReactFiberFlags.MountLayoutDev, - invokeLayoutEffectUnmountInDEV - ) + invokeEffectsInDev(fiber, ReactFiberFlags.MountLayoutDev, invokeLayoutEffectUnmountInDEV) if hasPassiveEffects then - invokeEffectsInDev( - fiber, - ReactFiberFlags.MountPassiveDev, - invokePassiveEffectUnmountInDEV - ) + invokeEffectsInDev(fiber, ReactFiberFlags.MountPassiveDev, invokePassiveEffectUnmountInDEV) end - invokeEffectsInDev( - fiber, - ReactFiberFlags.MountLayoutDev, - invokeLayoutEffectMountInDEV - ) + invokeEffectsInDev(fiber, ReactFiberFlags.MountLayoutDev, invokeLayoutEffectMountInDEV) if hasPassiveEffects then - invokeEffectsInDev( - fiber, - ReactFiberFlags.MountPassiveDev, - invokePassiveEffectMountInDEV - ) + invokeEffectsInDev(fiber, ReactFiberFlags.MountPassiveDev, invokePassiveEffectMountInDEV) end resetCurrentDebugFiberInDEV() end end -function invokeEffectsInDev( - firstChild: Fiber, - fiberFlags: Flags, - invokeEffectFn: (fiber: Fiber) -> () -): () +function invokeEffectsInDev(firstChild: Fiber, fiberFlags: Flags, invokeEffectFn: (fiber: Fiber) -> ()): () if __DEV__ and enableDoubleInvokingEffects then local fiber = firstChild while fiber ~= nil do @@ -3301,12 +3089,7 @@ mod.warnAboutUpdateOnNotYetMountedFiberInDEV = function(fiber) return end - if - bit32.band( - fiber.mode, - bit32.bor(ReactTypeOfMode.BlockingMode, ReactTypeOfMode.ConcurrentMode) - ) == 0 - then + if bit32.band(fiber.mode, bit32.bor(ReactTypeOfMode.BlockingMode, ReactTypeOfMode.ConcurrentMode)) == 0 then return end @@ -3372,10 +3155,8 @@ if __DEV__ and ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback -- Before entering the begin phase, copy the work-in-progress onto a dummy -- fiber. If beginWork throws, we'll use this to reset the state. - local originalWorkInProgressCopy = - ReactFiber.assignFiberPropertiesInDEV(dummyFiber, unitOfWork) - local ok, result = - xpcall(originalBeginWork, describeError, current, unitOfWork, lanes) + local originalWorkInProgressCopy = ReactFiber.assignFiberPropertiesInDEV(dummyFiber, unitOfWork) + local ok, result = xpcall(originalBeginWork, describeError, current, unitOfWork, lanes) if not ok then local originalError = result @@ -3459,8 +3240,7 @@ mod.warnAboutRenderPhaseUpdatesInDEV = function(fiber: Fiber): () -- if !didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) if didWarnAboutUpdateInRenderForAnotherComponent[dedupeKey] == nil then didWarnAboutUpdateInRenderForAnotherComponent[dedupeKey] = true - local setStateComponentName = getComponentName(fiber.type) - or "Unknown" + local setStateComponentName = getComponentName(fiber.type) or "Unknown" console.error( "Cannot update a component (`%s`) while rendering a " .. "different component (`%s`). To locate the bad setState() call inside `%s`, " @@ -3608,10 +3388,7 @@ local didWarnAboutUnmockedScheduler = false exports.warnIfUnmockedScheduler = function(fiber: Fiber) if __DEV__ then - if - didWarnAboutUnmockedScheduler == false - and Scheduler.unstable_flushAllWithoutAsserting == nil - then + if didWarnAboutUnmockedScheduler == false and Scheduler.unstable_flushAllWithoutAsserting == nil then if bit32.band(fiber.mode, ReactTypeOfMode.BlockingMode) ~= 0 or bit32.band(fiber.mode, ReactTypeOfMode.ConcurrentMode) ~= 0 @@ -3670,11 +3447,7 @@ exports.markSpawnedWork = function(lane: Lane | Lanes) end end -function scheduleInteractions( - root: FiberRoot, - lane: Lane | Lanes, - interactions: Set -) +function scheduleInteractions(root: FiberRoot, lane: Lane | Lanes, interactions: Set) if not ReactFeatureFlags.enableSchedulerTracing then return end @@ -3749,8 +3522,7 @@ mod.startWorkOnPendingInteractions = function(root: FiberRoot, lanes: Lanes) local subscriber = __subscriberRef.current if subscriber ~= nil then local threadID = computeThreadID(root, lanes) - local ok, error_ = - xpcall(subscriber.onWorkStarted, describeError, interactions, threadID) + local ok, error_ = xpcall(subscriber.onWorkStarted, describeError, interactions, threadID) if not ok then -- If the subscriber throws, rethrow it in a separate task scheduleCallback(ImmediateSchedulerPriority, function() @@ -3779,12 +3551,7 @@ mod.finishPendingInteractions = function(root: FiberRoot, committedLanes) local threadID = computeThreadID(root, committedLanes) subscriber = __subscriberRef.current -- ROBLOX deviation: helper for raw table set/map size > 0 - ok, error_ = xpcall( - subscriber.onWorkStopped, - describeError, - root.memoizedInteractions, - threadID - ) + ok, error_ = xpcall(subscriber.onWorkStopped, describeError, root.memoizedInteractions, threadID) end -- ROBLOX finally @@ -3802,11 +3569,8 @@ mod.finishPendingInteractions = function(root: FiberRoot, committedLanes) interaction.__count -= 1 if subscriber ~= nil and interaction.__count == 0 then - local ok_, error__ = xpcall( - subscriber.onInteractionScheduledWorkCompleted, - describeError, - interaction - ) + local ok_, error__ = + xpcall(subscriber.onInteractionScheduledWorkCompleted, describeError, interaction) if not ok_ then -- If the subscriber throws, rethrow it in a separate task scheduleCallback(ImmediateSchedulerPriority, function() @@ -3906,9 +3670,7 @@ exports.act = function(callback: () -> Thenable): Thenable if didWarnAboutUsingActInProd == false then didWarnAboutUsingActInProd = true -- eslint-disable-next-line react-internal/no-production-logging - console.error( - "act(...) is not supported in production builds of React, and might not behave as expected." - ) + console.error("act(...) is not supported in production builds of React, and might not behave as expected.") end end @@ -3944,11 +3706,7 @@ exports.act = function(callback: () -> Thenable): Thenable error(result) end - if - result ~= nil - and typeof(result) == "table" - and typeof(result.andThen) == "function" - then + if result ~= nil and typeof(result) == "table" and typeof(result.andThen) == "function" then -- setup a boolean that gets set to true only -- once this act() call is await-ed local called = false @@ -3979,10 +3737,7 @@ exports.act = function(callback: () -> Thenable): Thenable return result:andThen(function() if actingUpdatesScopeDepth > 1 - or ( - isSchedulerMocked == true - and previousIsSomeRendererActing == true - ) + or (isSchedulerMocked == true and previousIsSomeRendererActing == true) then onDone() resolve() @@ -4009,8 +3764,7 @@ exports.act = function(callback: () -> Thenable): Thenable if result ~= nil then -- ROBLOX deviation: use Lua syntax console.error( - "The callback passed to act(...) function " - .. "must return nil, or a Promise. You returned %s", + "The callback passed to act(...) function " .. "must return nil, or a Promise. You returned %s", tostring(result) ) end @@ -4039,9 +3793,7 @@ exports.act = function(callback: () -> Thenable): Thenable -- ROBLOX FIXME Luau: have to explicitly annotate the unused generic arg: CLI-49996 andThen = function(self, resolve, reject_) if __DEV__ then - console.error( - "Do not await the result of calling act(...) with sync logic, it is not a Promise." - ) + console.error("Do not await the result of calling act(...) with sync logic, it is not a Promise.") end resolve() end, diff --git a/modules/react-reconciler/src/ReactHookEffectTags.lua b/modules/react-reconciler/src/ReactHookEffectTags.lua index 2a768be8..a81825d0 100644 --- a/modules/react-reconciler/src/ReactHookEffectTags.lua +++ b/modules/react-reconciler/src/ReactHookEffectTags.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/16654436039dd8f16a63928e71081c7745872e8f/packages/react-reconciler/src/ReactHookEffectTags.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactHookEffectTags.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/react-reconciler/src/ReactInternalTypes.lua b/modules/react-reconciler/src/ReactInternalTypes.lua index 336b55fb..caba1a50 100644 --- a/modules/react-reconciler/src/ReactInternalTypes.lua +++ b/modules/react-reconciler/src/ReactInternalTypes.lua @@ -25,14 +25,8 @@ type RefObject = ReactTypes.RefObject type ReactContext = ReactTypes.ReactContext type MutableSourceVersion = ReactTypes.MutableSourceVersion type MutableSource = ReactTypes.MutableSource -type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn< - Source, - Snapshot -> -type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn< - Source, - Snapshot -> +type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn +type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn -- ROBLOX deviation START: These are 'mixed' by default, and specialized by the renderer, need complicated dynamic resolution to do this properly -- local ReactFiberHostConfig = require("./ReactFiberHostConfig") @@ -298,10 +292,7 @@ export type FiberRoot = { current: Fiber, -- ROBLOX deviation START: we use a lightweight unordered set for performance - pingCache: SimpleMap< - Wakeable, - (SimpleSet | SimpleMap>) - > | nil, + pingCache: SimpleMap | SimpleMap>)> | nil, -- ROBLOX deviation END -- A finished work-in-progress HostRoot that's ready to be committed. diff --git a/modules/react-reconciler/src/ReactMutableSource.new.lua b/modules/react-reconciler/src/ReactMutableSource.new.lua index 15f7100f..a1ba4092 100644 --- a/modules/react-reconciler/src/ReactMutableSource.new.lua +++ b/modules/react-reconciler/src/ReactMutableSource.new.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/142d4f1c00c66f3d728177082dbc027fd6335115/packages/react-reconciler/src/ReactMutableSource.new.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactMutableSource.new.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -49,24 +49,22 @@ exports.resetWorkInProgressVersions = function() table.clear(workInProgressSources) end -exports.getWorkInProgressVersion = - function(mutableSource: MutableSource): nil | MutableSourceVersion - if isPrimaryRenderer then - return mutableSource._workInProgressVersionPrimary - else - return mutableSource._workInProgressVersionSecondary - end +exports.getWorkInProgressVersion = function(mutableSource: MutableSource): nil | MutableSourceVersion + if isPrimaryRenderer then + return mutableSource._workInProgressVersionPrimary + else + return mutableSource._workInProgressVersionSecondary end +end -exports.setWorkInProgressVersion = - function(mutableSource: MutableSource, version_: MutableSourceVersion) - if isPrimaryRenderer then - mutableSource._workInProgressVersionPrimary = version_ - else - mutableSource._workInProgressVersionSecondary = version_ - end - table.insert(workInProgressSources, mutableSource) +exports.setWorkInProgressVersion = function(mutableSource: MutableSource, version_: MutableSourceVersion) + if isPrimaryRenderer then + mutableSource._workInProgressVersionPrimary = version_ + else + mutableSource._workInProgressVersionSecondary = version_ end + table.insert(workInProgressSources, mutableSource) +end exports.warnAboutMultipleRenderersDEV = function(mutableSource: MutableSource) if _G.__DEV__ then @@ -96,20 +94,19 @@ end -- This ensures that the version used for server rendering matches the one -- that is eventually read during hydration. -- If they don't match there's a potential tear and a full deopt render is required. -exports.registerMutableSourceForHydration = - function(root: FiberRoot, mutableSource: MutableSource) - local getVersion = mutableSource._getVersion - local version_ = getVersion(mutableSource._source) +exports.registerMutableSourceForHydration = function(root: FiberRoot, mutableSource: MutableSource) + local getVersion = mutableSource._getVersion + local version_ = getVersion(mutableSource._source) - -- TODO Clear this data once all pending hydration work is finished. - -- Retaining it forever may interfere with GC. - if root.mutableSourceEagerHydrationData == nil then - root.mutableSourceEagerHydrationData = { mutableSource, version_ } - else - -- ROBLOX FIXME: having trouble with type coercion in this case - -- table.insert(root.mutableSourceEagerHydrationData, mutableSource) - -- table.insert(root.mutableSourceEagerHydrationData, version_) - end + -- TODO Clear this data once all pending hydration work is finished. + -- Retaining it forever may interfere with GC. + if root.mutableSourceEagerHydrationData == nil then + root.mutableSourceEagerHydrationData = { mutableSource, version_ } + else + -- ROBLOX FIXME: having trouble with type coercion in this case + -- table.insert(root.mutableSourceEagerHydrationData, mutableSource) + -- table.insert(root.mutableSourceEagerHydrationData, version_) end +end return exports diff --git a/modules/react-reconciler/src/ReactProfilerTimer.new.lua b/modules/react-reconciler/src/ReactProfilerTimer.new.lua index 64b30aac..1f9e7a33 100644 --- a/modules/react-reconciler/src/ReactProfilerTimer.new.lua +++ b/modules/react-reconciler/src/ReactProfilerTimer.new.lua @@ -69,10 +69,7 @@ function stopProfilerTimerIfRunning(fiber: Fiber): () profilerStartTime = -1 end -function stopProfilerTimerIfRunningAndRecordDelta( - fiber: Fiber, - overrideBaseTime: boolean -): () +function stopProfilerTimerIfRunningAndRecordDelta(fiber: Fiber, overrideBaseTime: boolean): () if not enableProfilerTimer then return end diff --git a/modules/react-reconciler/src/ReactRootTags.lua b/modules/react-reconciler/src/ReactRootTags.lua index 3fa4d281..6325b824 100644 --- a/modules/react-reconciler/src/ReactRootTags.lua +++ b/modules/react-reconciler/src/ReactRootTags.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/ReactRootTags.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactRootTags.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/react-reconciler/src/ReactStrictModeWarnings.new.lua b/modules/react-reconciler/src/ReactStrictModeWarnings.new.lua index 7963521e..4febdc78 100644 --- a/modules/react-reconciler/src/ReactStrictModeWarnings.new.lua +++ b/modules/react-reconciler/src/ReactStrictModeWarnings.new.lua @@ -69,67 +69,59 @@ if _G.__DEV__ then -- Tracks components we have already warned about. local didWarnAboutUnsafeLifecycles = {} - ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = - function(fiber: Fiber, instance: any) - -- Dedupe strategy: Warn once per component. - if didWarnAboutUnsafeLifecycles[fiber.type] then - return - end + ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function(fiber: Fiber, instance: any) + -- Dedupe strategy: Warn once per component. + if didWarnAboutUnsafeLifecycles[fiber.type] then + return + end - if - typeof(instance.componentWillMount) == "function" - -- Don't warn about react-lifecycles-compat polyfilled components. - -- ROBLOX deviation: Lua doesn't allow fields on function - -- instance.componentWillMount.__suppressDeprecationWarning ~= true - then - table.insert(pendingComponentWillMountWarnings, fiber) - end + if + typeof(instance.componentWillMount) == "function" + -- Don't warn about react-lifecycles-compat polyfilled components. + -- ROBLOX deviation: Lua doesn't allow fields on function + -- instance.componentWillMount.__suppressDeprecationWarning ~= true + then + table.insert(pendingComponentWillMountWarnings, fiber) + end - if - bit32.band(fiber.mode, StrictMode) ~= 0 - and typeof(instance.UNSAFE_componentWillMount) == "function" - then - table.insert(pendingUNSAFE_ComponentWillMountWarnings, fiber) - end + if bit32.band(fiber.mode, StrictMode) ~= 0 and typeof(instance.UNSAFE_componentWillMount) == "function" then + table.insert(pendingUNSAFE_ComponentWillMountWarnings, fiber) + end - if - typeof(instance.componentWillReceiveProps) == "function" - -- ROBLOX deviation: Lua doesn't allow fields on function - -- instance.componentWillReceiveProps.__suppressDeprecationWarning ~= true - then - table.insert(pendingComponentWillReceivePropsWarnings, fiber) - end + if + typeof(instance.componentWillReceiveProps) == "function" + -- ROBLOX deviation: Lua doesn't allow fields on function + -- instance.componentWillReceiveProps.__suppressDeprecationWarning ~= true + then + table.insert(pendingComponentWillReceivePropsWarnings, fiber) + end - if - bit32.band(fiber.mode, StrictMode) ~= 0 - and typeof(instance.UNSAFE_componentWillReceiveProps) == "function" - then - table.insert(pendingUNSAFE_ComponentWillReceivePropsWarnings, fiber) - end + if + bit32.band(fiber.mode, StrictMode) ~= 0 + and typeof(instance.UNSAFE_componentWillReceiveProps) == "function" + then + table.insert(pendingUNSAFE_ComponentWillReceivePropsWarnings, fiber) + end - if - typeof(instance.componentWillUpdate) == "function" - -- ROBLOX deviation: Lua doesn't allow fields on function - -- instance.componentWillUpdate.__suppressDeprecationWarning ~= true - then - table.insert(pendingComponentWillUpdateWarnings, fiber) - end + if + typeof(instance.componentWillUpdate) == "function" + -- ROBLOX deviation: Lua doesn't allow fields on function + -- instance.componentWillUpdate.__suppressDeprecationWarning ~= true + then + table.insert(pendingComponentWillUpdateWarnings, fiber) + end - if - bit32.band(fiber.mode, StrictMode) ~= 0 - and typeof(instance.UNSAFE_componentWillUpdate) == "function" - then - table.insert(pendingUNSAFE_ComponentWillUpdateWarnings, fiber) - end + if bit32.band(fiber.mode, StrictMode) ~= 0 and typeof(instance.UNSAFE_componentWillUpdate) == "function" then + table.insert(pendingUNSAFE_ComponentWillUpdateWarnings, fiber) end + end ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function() -- We do an initial pass to gather component names local componentWillMountUniqueNames = {} if #pendingComponentWillMountWarnings > 0 then for i, fiber in pendingComponentWillMountWarnings do - componentWillMountUniqueNames[getComponentName(fiber.type) or "Component"] = - true + componentWillMountUniqueNames[getComponentName(fiber.type) or "Component"] = true didWarnAboutUnsafeLifecycles[fiber.type] = true end table.clear(pendingComponentWillMountWarnings) @@ -138,8 +130,7 @@ if _G.__DEV__ then local UNSAFE_componentWillMountUniqueNames = {} if #pendingUNSAFE_ComponentWillMountWarnings > 0 then for i, fiber in pendingUNSAFE_ComponentWillMountWarnings do - UNSAFE_componentWillMountUniqueNames[getComponentName(fiber.type) or "Component"] = - true + UNSAFE_componentWillMountUniqueNames[getComponentName(fiber.type) or "Component"] = true didWarnAboutUnsafeLifecycles[fiber.type] = true end table.clear(pendingUNSAFE_ComponentWillMountWarnings) @@ -148,8 +139,7 @@ if _G.__DEV__ then local componentWillReceivePropsUniqueNames = {} if #pendingComponentWillReceivePropsWarnings > 0 then for i, fiber in pendingComponentWillReceivePropsWarnings do - componentWillReceivePropsUniqueNames[getComponentName(fiber.type) or "Component"] = - true + componentWillReceivePropsUniqueNames[getComponentName(fiber.type) or "Component"] = true didWarnAboutUnsafeLifecycles[fiber.type] = true end @@ -159,8 +149,7 @@ if _G.__DEV__ then local UNSAFE_componentWillReceivePropsUniqueNames = {} if #pendingUNSAFE_ComponentWillReceivePropsWarnings > 0 then for i, fiber in pendingUNSAFE_ComponentWillReceivePropsWarnings do - UNSAFE_componentWillReceivePropsUniqueNames[getComponentName(fiber.type) or "Component"] = - true + UNSAFE_componentWillReceivePropsUniqueNames[getComponentName(fiber.type) or "Component"] = true didWarnAboutUnsafeLifecycles[fiber.type] = true end @@ -170,8 +159,7 @@ if _G.__DEV__ then local componentWillUpdateUniqueNames = {} if #pendingComponentWillUpdateWarnings > 0 then for i, fiber in pendingComponentWillUpdateWarnings do - componentWillUpdateUniqueNames[getComponentName(fiber.type) or "Component"] = - true + componentWillUpdateUniqueNames[getComponentName(fiber.type) or "Component"] = true didWarnAboutUnsafeLifecycles[fiber.type] = true end @@ -181,8 +169,7 @@ if _G.__DEV__ then local UNSAFE_componentWillUpdateUniqueNames = {} if #pendingUNSAFE_ComponentWillUpdateWarnings > 0 then for i, fiber in pendingUNSAFE_ComponentWillUpdateWarnings do - UNSAFE_componentWillUpdateUniqueNames[getComponentName(fiber.type) or "Component"] = - true + UNSAFE_componentWillUpdateUniqueNames[getComponentName(fiber.type) or "Component"] = true didWarnAboutUnsafeLifecycles[fiber.type] = true end @@ -205,8 +192,7 @@ if _G.__DEV__ then -- deviation: use `next` to determine whether set is empty if next(UNSAFE_componentWillReceivePropsUniqueNames) ~= nil then - local sortedNames = - setToSortedString(UNSAFE_componentWillReceivePropsUniqueNames) + local sortedNames = setToSortedString(UNSAFE_componentWillReceivePropsUniqueNames) console.error( "Using UNSAFE_componentWillReceiveProps in strict mode is not recommended " .. "and may indicate bugs in your code. " @@ -296,45 +282,41 @@ if _G.__DEV__ then -- Tracks components we have already warned about. local didWarnAboutLegacyContext = {} - ReactStrictModeWarnings.recordLegacyContextWarning = - function(fiber: Fiber, instance: any) - local strictRoot = findStrictRoot(fiber) - if strictRoot == nil then - console.error( - "Expected to find a StrictMode component in a strict mode tree. " - .. "This error is likely caused by a bug in React. Please file an issue." - ) - return - end + ReactStrictModeWarnings.recordLegacyContextWarning = function(fiber: Fiber, instance: any) + local strictRoot = findStrictRoot(fiber) + if strictRoot == nil then + console.error( + "Expected to find a StrictMode component in a strict mode tree. " + .. "This error is likely caused by a bug in React. Please file an issue." + ) + return + end - -- Dedup strategy: Warn once per component. - if didWarnAboutLegacyContext[fiber.type] then - return - end + -- Dedup strategy: Warn once per component. + if didWarnAboutLegacyContext[fiber.type] then + return + end - -- ROBLOX FIXME Luau: Luau should narrow based on the nil guard - local warningsForRoot = pendingLegacyContextWarning[strictRoot :: Fiber] - - -- ROBLOX deviation: Lua can't have fields on functions - if - typeof(fiber.type) ~= "function" - and ( - fiber.type.contextTypes ~= nil - or fiber.type.childContextTypes ~= nil - or ( - instance ~= nil - and typeof(instance.getChildContext) == "function" - ) - ) - then - if warningsForRoot == nil then - warningsForRoot = {} - -- ROBLOX FIXME Luau: Luau should narrow based on the nil guard - pendingLegacyContextWarning[strictRoot :: Fiber] = warningsForRoot - end - table.insert(warningsForRoot, fiber) + -- ROBLOX FIXME Luau: Luau should narrow based on the nil guard + local warningsForRoot = pendingLegacyContextWarning[strictRoot :: Fiber] + + -- ROBLOX deviation: Lua can't have fields on functions + if + typeof(fiber.type) ~= "function" + and ( + fiber.type.contextTypes ~= nil + or fiber.type.childContextTypes ~= nil + or (instance ~= nil and typeof(instance.getChildContext) == "function") + ) + then + if warningsForRoot == nil then + warningsForRoot = {} + -- ROBLOX FIXME Luau: Luau should narrow based on the nil guard + pendingLegacyContextWarning[strictRoot :: Fiber] = warningsForRoot end + table.insert(warningsForRoot, fiber) end + end ReactStrictModeWarnings.flushLegacyContextWarning = function() for strictRoot, fiberArray in pendingLegacyContextWarning do diff --git a/modules/react-reconciler/src/ReactTestSelectors.lua b/modules/react-reconciler/src/ReactTestSelectors.lua index b07cb171..c2cebf64 100644 --- a/modules/react-reconciler/src/ReactTestSelectors.lua +++ b/modules/react-reconciler/src/ReactTestSelectors.lua @@ -518,9 +518,7 @@ end export type IntersectionObserverOptions = Object -export type ObserveVisibleRectsCallback = ( - intersections: Array<{ ratio: number, rect: BoundingRect }> -) -> () +export type ObserveVisibleRectsCallback = (intersections: Array<{ ratio: number, rect: BoundingRect }>) -> () -- exports.observeVisibleRects( -- hostRoot: Instance, diff --git a/modules/react-reconciler/src/ReactTypeOfMode.lua b/modules/react-reconciler/src/ReactTypeOfMode.lua index d568daf4..f1289c4b 100644 --- a/modules/react-reconciler/src/ReactTypeOfMode.lua +++ b/modules/react-reconciler/src/ReactTypeOfMode.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/22dc2e42bdc00d87fc19c5e75fc7c0b3fdcdc572/packages/react-reconciler/src/ReactTypeOfMode.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactTypeOfMode.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/react-reconciler/src/ReactUpdateQueue.new.lua b/modules/react-reconciler/src/ReactUpdateQueue.new.lua index 918eaac2..01152a23 100644 --- a/modules/react-reconciler/src/ReactUpdateQueue.new.lua +++ b/modules/react-reconciler/src/ReactUpdateQueue.new.lua @@ -128,14 +128,12 @@ local ShouldCapture = ReactFiberFlags.ShouldCapture local DidCapture = ReactFiberFlags.DidCapture local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags -local debugRenderPhaseSideEffectsForStrictMode = - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode +local debugRenderPhaseSideEffectsForStrictMode = ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode local ReactTypeOfMode = require("./ReactTypeOfMode") local StrictMode = ReactTypeOfMode.StrictMode -- local ReactFiberWorkLoop = require("./ReactFiberWorkLoop.new.lua") -local markSkippedUpdateLanes = - require("./ReactFiberWorkInProgress").markSkippedUpdateLanes +local markSkippedUpdateLanes = require("./ReactFiberWorkInProgress").markSkippedUpdateLanes -- ROBLOX deviation START: use if-then-error, which avoid string format and function call overhead, as in React 18 -- local invariant = require("@pkg/@jsdotlua/shared").invariant @@ -225,12 +223,7 @@ end exports.cloneUpdateQueue = cloneUpdateQueue -- ROBLOX deviation START: add extra parameters here so updates can be create in single table ctor -local function createUpdate( - eventTime: number, - lane: Lane, - payload: any?, - callback: (() -> ...any)? -): Update +local function createUpdate(eventTime: number, lane: Lane, payload: any?, callback: (() -> ...any)?): Update -- ROBLOX performance: Use pooled update object when available if updatePoolIndex > 0 then local update = updatePool[updatePoolIndex] @@ -409,10 +402,7 @@ local function getStateFromUpdate( -- signature of the updater, which doesn't make sense for our case local nextState = payload(prevState, nextProps) if __DEV__ then - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() -- ROBLOX deviation: YOLO flag for disabling pcall local ok, result @@ -437,10 +427,7 @@ local function getStateFromUpdate( return payload elseif updateTag == CaptureUpdate or updateTag == UpdateState then if updateTag == CaptureUpdate then - workInProgress.flags = bit32.bor( - bit32.band(workInProgress.flags, bit32.bnot(ShouldCapture)), - DidCapture - ) + workInProgress.flags = bit32.bor(bit32.band(workInProgress.flags, bit32.bnot(ShouldCapture)), DidCapture) end -- Intentional fallthrough local payload = update.payload @@ -455,10 +442,7 @@ local function getStateFromUpdate( -- signature of the updater, which doesn't make sense for our case partialState = payload(prevState, nextProps) if __DEV__ then - if - debugRenderPhaseSideEffectsForStrictMode - and bit32.band(workInProgress.mode, StrictMode) ~= 0 - then + if debugRenderPhaseSideEffectsForStrictMode and bit32.band(workInProgress.mode, StrictMode) ~= 0 then disableLogs() -- ROBLOX deviation: YOLO flag for disabling pcall local ok, result @@ -497,12 +481,7 @@ local function getStateFromUpdate( end exports.getStateFromUpdate = getStateFromUpdate -local function processUpdateQueue( - workInProgress: Fiber, - props: any, - instance: any, - renderLanes: Lanes -): () +local function processUpdateQueue(workInProgress: Fiber, props: any, instance: any, renderLanes: Lanes): () -- This is always non-null on a ClassComponent or HostRoot local queue: UpdateQueue = workInProgress.updateQueue :: any @@ -616,14 +595,7 @@ local function processUpdateQueue( end -- Process this update. - newState = getStateFromUpdate( - workInProgress, - queue, - update, - newState, - props, - instance - ) + newState = getStateFromUpdate(workInProgress, queue, update, newState, props, instance) local callback = update.callback if callback ~= nil @@ -652,9 +624,7 @@ local function processUpdateQueue( local lastPendingUpdate = pendingQueue -- Intentionally unsound. Pending updates form a circular list, but we -- unravel them when transferring them to the base queue. - local firstPendingUpdate = ( - lastPendingUpdate.next :: any - ) :: Update + local firstPendingUpdate = (lastPendingUpdate.next :: any) :: Update lastPendingUpdate.next = nil update = firstPendingUpdate queue.lastBaseUpdate = lastPendingUpdate @@ -694,8 +664,7 @@ local function callCallback(callback, context) if type(callback) ~= "function" then error( string.format( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: %s", + "Invalid argument passed as callback. Expected a function. Instead " .. "received: %s", tostring(callback) ) ) @@ -712,11 +681,7 @@ exports.checkHasForceUpdateAfterProcessing = function(): boolean return hasForceUpdate end -local function commitUpdateQueue( - finishedWork: Fiber, - finishedQueue: UpdateQueue, - instance: any -): () +local function commitUpdateQueue(finishedWork: Fiber, finishedQueue: UpdateQueue, instance: any): () -- Commit the effects local effects = finishedQueue.effects finishedQueue.effects = nil diff --git a/modules/react-reconciler/src/ReactWorkTags.lua b/modules/react-reconciler/src/ReactWorkTags.lua index 03f029e7..f8d99cc8 100644 --- a/modules/react-reconciler/src/ReactWorkTags.lua +++ b/modules/react-reconciler/src/ReactWorkTags.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react-reconciler/src/ReactWorkTags.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactWorkTags.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/react-reconciler/src/SchedulerWithReactIntegration.new.lua b/modules/react-reconciler/src/SchedulerWithReactIntegration.new.lua index 46a101cf..a10f5127 100644 --- a/modules/react-reconciler/src/SchedulerWithReactIntegration.new.lua +++ b/modules/react-reconciler/src/SchedulerWithReactIntegration.new.lua @@ -21,8 +21,7 @@ local Scheduler = require("@pkg/@jsdotlua/scheduler") -- local __interactionsRef = require(Packages.Scheduler.tracing).__interactionsRef local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags -- local enableSchedulerTracing = ReactFeatureFlags.enableSchedulerTracing -local decoupleUpdatePriorityFromScheduler = - ReactFeatureFlags.decoupleUpdatePriorityFromScheduler +local decoupleUpdatePriorityFromScheduler = ReactFeatureFlags.decoupleUpdatePriorityFromScheduler local invariant = require("@pkg/@jsdotlua/shared").invariant local describeError = require("@pkg/@jsdotlua/shared").describeError local ReactFiberLane = require("./ReactFiberLane") @@ -46,8 +45,7 @@ local Scheduler_IdlePriority = Scheduler.unstable_IdlePriority -- deviation: Instead of defining these here, we require them from a small file -- with _just_ these constant definitions; it helps us avoid a circular require -- issue with `ReactFiberLanes` -local ReactFiberSchedulerPriorities = - require("./ReactFiberSchedulerPriorities.roblox.lua") +local ReactFiberSchedulerPriorities = require("./ReactFiberSchedulerPriorities.roblox.lua") local ImmediatePriority = ReactFiberSchedulerPriorities.ImmediatePriority local UserBlockingPriority = ReactFiberSchedulerPriorities.UserBlockingPriority local NormalPriority = ReactFiberSchedulerPriorities.NormalPriority @@ -155,10 +153,7 @@ function reactPriorityToSchedulerPriority(reactPriorityLevel) end -- ROBLOX FIXME Luau: should be T... but hits CLI-50289: failure to unify -local function runWithPriority( - reactPriorityLevel: ReactPriorityLevel, - fn: () -> T... -): ...any +local function runWithPriority(reactPriorityLevel: ReactPriorityLevel, fn: () -> T...): ...any local priorityLevel = reactPriorityToSchedulerPriority(reactPriorityLevel) return Scheduler_runWithPriority(priorityLevel, fn) end @@ -178,10 +173,7 @@ local function scheduleSyncCallback(callback: SchedulerCallback) if syncQueue == nil then syncQueue = { callback } -- Flush the queue in the next tick, at the earliest. - immediateQueueCallbackNode = Scheduler_scheduleCallback( - Scheduler_ImmediatePriority, - flushSyncCallbackQueueImpl - ) + immediateQueueCallbackNode = Scheduler_scheduleCallback(Scheduler_ImmediatePriority, flushSyncCallbackQueueImpl) else -- Push onto existing queue. Don't need to schedule a callback because -- we already scheduled one when we created the queue. @@ -268,10 +260,7 @@ flushSyncCallbackQueueImpl = function() syncQueue = Array.slice(syncQueue, i + 1) end -- Resume flushing in the next tick - Scheduler_scheduleCallback( - Scheduler_ImmediatePriority, - flushSyncCallbackQueue - ) + Scheduler_scheduleCallback(Scheduler_ImmediatePriority, flushSyncCallbackQueue) error(result) end else @@ -324,10 +313,7 @@ flushSyncCallbackQueueImpl = function() syncQueue = Array.slice(syncQueue, i + 1) end -- Resume flushing in the next tick - Scheduler_scheduleCallback( - Scheduler_ImmediatePriority, - flushSyncCallbackQueue - ) + Scheduler_scheduleCallback(Scheduler_ImmediatePriority, flushSyncCallbackQueue) error(result) end end diff --git a/modules/react-reconciler/src/SchedulingProfiler.lua b/modules/react-reconciler/src/SchedulingProfiler.lua index 4e7d4cf7..4960ecc6 100644 --- a/modules/react-reconciler/src/SchedulingProfiler.lua +++ b/modules/react-reconciler/src/SchedulingProfiler.lua @@ -34,13 +34,12 @@ local getComponentName = require("@pkg/@jsdotlua/shared").getComponentName -- * require. -- */ local supportsUserTiming = _G.performance ~= nil -local performance = _G.performance - or { - mark = function(str) - debug.profilebegin(str) - debug.profileend() - end, - } +local performance = _G.performance or { + mark = function(str) + debug.profilebegin(str) + debug.profileend() + end, +} function formatLanes(laneOrLanes: Lane | Lanes): string return tostring(laneOrLanes) @@ -89,17 +88,11 @@ exports.markComponentSuspended = function(fiber: Fiber, wakeable: Wakeable): () local id = getWakeableID(wakeable) local componentName = getComponentName(fiber.type) or "Unknown" -- TODO Add component stack id - performance.mark( - "--suspense-suspend-" .. tostring(id) .. "-" .. componentName - ) + performance.mark("--suspense-suspend-" .. tostring(id) .. "-" .. componentName) wakeable:andThen(function() - performance.mark( - "--suspense-resolved-" .. tostring(id) .. "-" .. componentName - ) + performance.mark("--suspense-resolved-" .. tostring(id) .. "-" .. componentName) end, function() - performance.mark( - "--suspense-rejected-" .. tostring(id) .. "-" .. componentName - ) + performance.mark("--suspense-rejected-" .. tostring(id) .. "-" .. componentName) end) end end @@ -174,9 +167,7 @@ exports.markForceUpdateScheduled = function(fiber: Fiber, lane: Lane): () if supportsUserTiming then local componentName = getComponentName(fiber.type) or "Unknown" -- TODO Add component stack id - performance.mark( - "--schedule-forced-update-" .. formatLanes(lane) .. "-" .. componentName - ) + performance.mark("--schedule-forced-update-" .. formatLanes(lane) .. "-" .. componentName) end end end @@ -186,9 +177,7 @@ exports.markStateUpdateScheduled = function(fiber: Fiber, lane: Lane): () if supportsUserTiming then local componentName = getComponentName(fiber.type) or "Unknown" -- TODO Add component stack id - performance.mark( - "--schedule-state-update-" .. formatLanes(lane) .. "-" .. componentName - ) + performance.mark("--schedule-state-update-" .. formatLanes(lane) .. "-" .. componentName) end end end diff --git a/modules/react-reconciler/src/__tests__/DebugTracing-test.internal.spec.lua b/modules/react-reconciler/src/__tests__/DebugTracing-test.internal.spec.lua index e57285b1..2b2a8784 100644 --- a/modules/react-reconciler/src/__tests__/DebugTracing-test.internal.spec.lua +++ b/modules/react-reconciler/src/__tests__/DebugTracing-test.internal.spec.lua @@ -79,41 +79,27 @@ describe("DebugTracing", function() end) -- @gate experimental - it( - "should not log anything for sync render without suspends or state updates", - function() - jestExpect(function() - ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement("div") - ) - ) - end).toLogDev({}) - end - ) + it("should not log anything for sync render without suspends or state updates", function() + jestExpect(function() + ReactTestRenderer.create( + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement("div")) + ) + end).toLogDev({}) + end) -- @gate experimental - it( - "should not log anything for concurrent render without suspends or state updates", - function() - jestExpect(function() - ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement("div") - ), - { unstable_isConcurrent = true } - ) - end).toLogDev({}) + it("should not log anything for concurrent render without suspends or state updates", function() + jestExpect(function() + ReactTestRenderer.create( + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement("div")), + { unstable_isConcurrent = true } + ) + end).toLogDev({}) - jestExpect(function() - jestExpect(Scheduler).toFlushUntilNextPaint({}) - end).toLogDev({}) - end - ) + jestExpect(function() + jestExpect(Scheduler).toFlushUntilNextPaint({}) + end).toLogDev({}) + end) -- ROBLOX FIXME: we never receive "Example resolved", might be a Promise emulation issue -- @gate experimental && build === 'development' && enableDebugTracing @@ -131,11 +117,7 @@ describe("DebugTracing", function() React.createElement( React.unstable_DebugTracingMode, nil, - React.createElement( - React.Suspense, - { fallback = {} }, - React.createElement(Example) - ) + React.createElement(React.Suspense, { fallback = {} }, React.createElement(Example)) ) ) end).toLogDev({ @@ -210,11 +192,7 @@ describe("DebugTracing", function() React.createElement( React.unstable_DebugTracingMode, nil, - React.createElement( - React.Suspense, - { fallback = {} }, - React.createElement(Example) - ) + React.createElement(React.Suspense, { fallback = {} }, React.createElement(Example)) ), { unstable_isConcurrent = true } ) @@ -297,11 +275,7 @@ describe("DebugTracing", function() jestExpect(function() ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)), { unstable_isConcurrent = true } ) end).toLogDev({}) @@ -332,11 +306,7 @@ describe("DebugTracing", function() jestExpect(function() ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)), { unstable_isConcurrent = true } ) end).toLogDev({}) @@ -364,11 +334,7 @@ describe("DebugTracing", function() jestExpect(function() ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)), { unstable_isConcurrent = true } ) end).toLogDev({}) @@ -397,11 +363,7 @@ describe("DebugTracing", function() jestExpect(function() ReactTestRenderer.act(function() ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)), { unstable_isConcurrent = true } ) end) @@ -425,11 +387,7 @@ describe("DebugTracing", function() jestExpect(function() ReactTestRenderer.act(function() ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)), { unstable_isConcurrent = true } ) end) @@ -465,11 +423,7 @@ describe("DebugTracing", function() jestExpect(function() ReactTestRenderer.create( - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)), { unstable_isConcurrent = true } ) end).toLogDev({}) @@ -484,48 +438,41 @@ describe("DebugTracing", function() end) -- @gate experimental - it( - "should not log anything outside of a unstable_DebugTracingMode subtree", - function() - local function ExampleThatCascades() - local didMount, setDidMount = React.useState(false) - React.useLayoutEffect(function() - setDidMount(true) - end, {}) - return didMount - end + it("should not log anything outside of a unstable_DebugTracingMode subtree", function() + local function ExampleThatCascades() + local didMount, setDidMount = React.useState(false) + React.useLayoutEffect(function() + setDidMount(true) + end, {}) + return didMount + end - local fakeSuspensePromise = Promise.new(function() - return {} - end) - local function ExampleThatSuspends() - error(fakeSuspensePromise) - end + local fakeSuspensePromise = Promise.new(function() + return {} + end) + local function ExampleThatSuspends() + error(fakeSuspensePromise) + end - local function Example() - return nil - end + local function Example() + return nil + end - jestExpect(function() - ReactTestRenderer.create( + jestExpect(function() + ReactTestRenderer.create( + React.createElement( + React.Fragment, + nil, + React.createElement(ExampleThatCascades), React.createElement( - React.Fragment, + React.Suspense, + { fallback = {} }, nil, - React.createElement(ExampleThatCascades), - React.createElement( - React.Suspense, - { fallback = {} }, - nil, - React.createElement(ExampleThatSuspends) - ), - React.createElement( - React.unstable_DebugTracingMode, - nil, - React.createElement(Example) - ) - ) + React.createElement(ExampleThatSuspends) + ), + React.createElement(React.unstable_DebugTracingMode, nil, React.createElement(Example)) ) - end).toLogDev({}) - end - ) + ) + end).toLogDev({}) + end) end) diff --git a/modules/react-reconciler/src/__tests__/ReactClassSetStateCallback.spec.lua b/modules/react-reconciler/src/__tests__/ReactClassSetStateCallback.spec.lua index 358232b0..7b1fc379 100644 --- a/modules/react-reconciler/src/__tests__/ReactClassSetStateCallback.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactClassSetStateCallback.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d7dce572c7453737a685e791e7afcbc7e2b2fe16/packages/react-reconciler/src/__tests__/ReactClassSetStateCallback-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactClassSetStateCallback-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/__tests__/ReactComponentLifeCycle.spec.lua b/modules/react-reconciler/src/__tests__/ReactComponentLifeCycle.spec.lua index 717213e0..64226238 100644 --- a/modules/react-reconciler/src/__tests__/ReactComponentLifeCycle.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactComponentLifeCycle.spec.lua @@ -474,15 +474,11 @@ it("should carry through each of the phases of setup", function() }, { withoutStack = 1 }) -- getInitialState - jestExpect(_testJournal.returnedFromGetInitialState).toEqual( - GET_INIT_STATE_RETURN_VAL - ) + jestExpect(_testJournal.returnedFromGetInitialState).toEqual(GET_INIT_STATE_RETURN_VAL) jestExpect(_testJournal.lifeCycleAtStartOfGetInitialState).toBe("UNMOUNTED") -- componentWillMount - jestExpect(_testJournal.stateAtStartOfWillMount).toEqual( - _testJournal.returnedFromGetInitialState - ) + jestExpect(_testJournal.stateAtStartOfWillMount).toEqual(_testJournal.returnedFromGetInitialState) jestExpect(_testJournal.lifeCycleAtStartOfWillMount).toBe("UNMOUNTED") -- componentDidMount @@ -579,17 +575,13 @@ xit("should not throw when updating an auxiliary component", function() end ReactNoop.act(function() - ReactNoop.render( - React.createElement(Component, { text = "uno", tooltipText = "one" }) - ) + ReactNoop.render(React.createElement(Component, { text = "uno", tooltipText = "one" })) end) -- Since `instance` is a root component, we can set its props. This also -- makes Tooltip rerender the tooltip component, which shouldn't throw. ReactNoop.act(function() - ReactNoop.render( - React.createElement(Component, { text = "dos", tooltipText = "two" }) - ) + ReactNoop.render(React.createElement(Component, { text = "dos", tooltipText = "two" })) end) end) @@ -598,8 +590,7 @@ it("should allow state updates in componentDidMount", function() --[[* * calls setState in an componentDidMount. ]] - local SetStateInComponentDidMount = - React.Component:extend("SetStateInComponentDidMount") + local SetStateInComponentDidMount = React.Component:extend("SetStateInComponentDidMount") function SetStateInComponentDidMount:init() self.state = { stateField = self.props.valueToUseInitially, @@ -743,11 +734,7 @@ it("should call nested new lifecycle methods in the right order", function() Outer.componentDidUpdate = logger("outer componentDidUpdate") Outer.componentWillUnmount = logger("outer componentWillUnmount") function Outer:render() - return React.createElement( - "Frame", - {}, - React.createElement(Inner, { x = self.props.x }) - ) + return React.createElement("Frame", {}, React.createElement(Inner, { x = self.props.x })) end function Inner:init() @@ -803,384 +790,365 @@ it("should call nested new lifecycle methods in the right order", function() }) end) -it( - "should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present", - function() - local Component = React.Component:extend("Component") - function Component:init() - self.state = {} - end - function Component.getDerivedStateFromProps() - return nil - end - function Component:componentWillMount() - error(Error("unexpected")) - end - function Component:componentWillReceiveProps() - -- ROBLOX deviation: assert self is non nil - jestExpect(self).never.toEqual(nil) - - error(Error("unexpected")) - end - function Component:componentWillUpdate() - error(Error("unexpected")) - end - function Component:render() - return nil - end - - jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(Component)) - end) - end).toErrorDev( - "Unsafe legacy lifecycles will not be called for components using new component APIs." - ) - end).toWarnDev( - -- We should consider removing this altogether; the old behavior referred - -- to here is unique to React. None of Roact's old behavior is reflected - -- by these messages and is likely to confuse existing users - { - "componentWillMount has been renamed", - "componentWillReceiveProps has been renamed", - "componentWillUpdate has been renamed", - }, - { withoutStack = true } - ) +it("should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present", function() + local Component = React.Component:extend("Component") + function Component:init() + self.state = {} + end + function Component.getDerivedStateFromProps() + return nil end -) + function Component:componentWillMount() + error(Error("unexpected")) + end + function Component:componentWillReceiveProps() + -- ROBLOX deviation: assert self is non nil + jestExpect(self).never.toEqual(nil) --- ROBLOX FIXME: outputs none of the toWarnDev() expected messages in DEV mode -it( - "should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present", - function() - local Component = React.Component:extend("Component") - function Component:init() - self.state = {} - end - function Component:getSnapshotBeforeUpdate() - return nil - end - function Component:componentWillMount() - error(Error("unexpected")) - end - function Component:componentWillReceiveProps() - error(Error("unexpected")) - end - function Component:componentWillUpdate() - -- ROBLOX deviation: assert self is non nil - jestExpect(self).never.toEqual(nil) - error(Error("unexpected")) - end - function Component:componentDidUpdate() - -- ROBLOX deviation: assert self is non nil - jestExpect(self).never.toEqual(nil) - end - function Component:render() - return nil - end + error(Error("unexpected")) + end + function Component:componentWillUpdate() + error(Error("unexpected")) + end + function Component:render() + return nil + end + jestExpect(function() jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(Component, { value = 1 })) - end) - end).toErrorDev( - "Unsafe legacy lifecycles will not be called for components using new component APIs." - ) - end).toWarnDev({ + ReactNoop.act(function() + ReactNoop.render(React.createElement(Component)) + end) + end).toErrorDev("Unsafe legacy lifecycles will not be called for components using new component APIs.") + end).toWarnDev( + -- We should consider removing this altogether; the old behavior referred + -- to here is unique to React. None of Roact's old behavior is reflected + -- by these messages and is likely to confuse existing users + { "componentWillMount has been renamed", "componentWillReceiveProps has been renamed", "componentWillUpdate has been renamed", - }, { withoutStack = true }) - ReactNoop.act(function() - ReactNoop.render(React.createElement(Component, { value = 2 })) - end) - end -) + }, + { withoutStack = true } + ) +end) -it( - "should not invoke new unsafe lifecycles (cWM/cWRP/cWU) if static gDSFP is present", - function() - local Component = React.Component:extend("Component") - function Component:init() - self.state = {} - end - function Component.getDerivedStateFromProps() - return nil - end - function Component:UNSAFE_componentWillMount() - error(Error("unexpected")) - end - function Component:UNSAFE_componentWillReceiveProps() - error(Error("unexpected")) - end - function Component:UNSAFE_componentWillUpdate() - error(Error("unexpected")) - end - function Component:render() - return nil - end +-- ROBLOX FIXME: outputs none of the toWarnDev() expected messages in DEV mode +it("should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present", function() + local Component = React.Component:extend("Component") + function Component:init() + self.state = {} + end + function Component:getSnapshotBeforeUpdate() + return nil + end + function Component:componentWillMount() + error(Error("unexpected")) + end + function Component:componentWillReceiveProps() + error(Error("unexpected")) + end + function Component:componentWillUpdate() + -- ROBLOX deviation: assert self is non nil + jestExpect(self).never.toEqual(nil) + error(Error("unexpected")) + end + function Component:componentDidUpdate() + -- ROBLOX deviation: assert self is non nil + jestExpect(self).never.toEqual(nil) + end + function Component:render() + return nil + end + jestExpect(function() jestExpect(function() ReactNoop.act(function() ReactNoop.render(React.createElement(Component, { value = 1 })) end) - end).toErrorDev({ - "Unsafe legacy lifecycles will not be called for components using new component APIs.", - -- deviation: ReactNoop runs with a StrictMode root and logs more warnings - "Using UNSAFE_componentWillMount in strict mode is not recommended", - "Using UNSAFE_componentWillReceiveProps in strict mode is not recommended", - "Using UNSAFE_componentWillUpdate in strict mode is not recommended", - }, { withoutStack = 3 }) + end).toErrorDev("Unsafe legacy lifecycles will not be called for components using new component APIs.") + end).toWarnDev({ + "componentWillMount has been renamed", + "componentWillReceiveProps has been renamed", + "componentWillUpdate has been renamed", + }, { withoutStack = true }) + ReactNoop.act(function() + ReactNoop.render(React.createElement(Component, { value = 2 })) + end) +end) + +it("should not invoke new unsafe lifecycles (cWM/cWRP/cWU) if static gDSFP is present", function() + local Component = React.Component:extend("Component") + function Component:init() + self.state = {} + end + function Component.getDerivedStateFromProps() + return nil + end + function Component:UNSAFE_componentWillMount() + error(Error("unexpected")) + end + function Component:UNSAFE_componentWillReceiveProps() + error(Error("unexpected")) + end + function Component:UNSAFE_componentWillUpdate() + error(Error("unexpected")) + end + function Component:render() + return nil + end + + jestExpect(function() ReactNoop.act(function() - ReactNoop.render(React.createElement(Component, { value = 2 })) + ReactNoop.render(React.createElement(Component, { value = 1 })) end) - end -) + end).toErrorDev({ + "Unsafe legacy lifecycles will not be called for components using new component APIs.", + -- deviation: ReactNoop runs with a StrictMode root and logs more warnings + "Using UNSAFE_componentWillMount in strict mode is not recommended", + "Using UNSAFE_componentWillReceiveProps in strict mode is not recommended", + "Using UNSAFE_componentWillUpdate in strict mode is not recommended", + }, { withoutStack = 3 }) + ReactNoop.act(function() + ReactNoop.render(React.createElement(Component, { value = 2 })) + end) +end) -it( - "should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present", - function() - local AllLegacyLifecycles = React.Component:extend("AllLegacyLifecycles") - function AllLegacyLifecycles:init() - self.state = {} - end - function AllLegacyLifecycles.getDerivedStateFromProps() - return nil - end - function AllLegacyLifecycles:componentWillMount() end - function AllLegacyLifecycles:UNSAFE_componentWillReceiveProps() end - function AllLegacyLifecycles:componentWillUpdate() end - function AllLegacyLifecycles:render() - return nil - end +it("should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present", function() + local AllLegacyLifecycles = React.Component:extend("AllLegacyLifecycles") + function AllLegacyLifecycles:init() + self.state = {} + end + function AllLegacyLifecycles.getDerivedStateFromProps() + return nil + end + function AllLegacyLifecycles:componentWillMount() end + function AllLegacyLifecycles:UNSAFE_componentWillReceiveProps() end + function AllLegacyLifecycles:componentWillUpdate() end + function AllLegacyLifecycles:render() + return nil + end + jestExpect(function() jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(AllLegacyLifecycles)) - end) - end).toErrorDev({ - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" - .. " componentWillMount\n" - .. " UNSAFE_componentWillReceiveProps\n" - .. " componentWillUpdate\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles", - "UNSAFE_componentWillReceiveProps in strict mode is not recommended", - }, { withoutStack = 1 }) - end).toWarnDev({ - "componentWillMount has been renamed", - "componentWillUpdate has been renamed", - }, { withoutStack = true }) + ReactNoop.act(function() + ReactNoop.render(React.createElement(AllLegacyLifecycles)) + end) + end).toErrorDev({ + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" + .. " componentWillMount\n" + .. " UNSAFE_componentWillReceiveProps\n" + .. " componentWillUpdate\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles", + "UNSAFE_componentWillReceiveProps in strict mode is not recommended", + }, { withoutStack = 1 }) + end).toWarnDev({ + "componentWillMount has been renamed", + "componentWillUpdate has been renamed", + }, { withoutStack = true }) - local WillMount = React.Component:extend("WillMount") - function WillMount:init() - self.state = {} - end - function WillMount.getDerivedStateFromProps() - return nil - end - function WillMount:UNSAFE_componentWillMount() end - function WillMount:render() - return nil - end + local WillMount = React.Component:extend("WillMount") + function WillMount:init() + self.state = {} + end + function WillMount.getDerivedStateFromProps() + return nil + end + function WillMount:UNSAFE_componentWillMount() end + function WillMount:render() + return nil + end - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(WillMount)) - end).toErrorDev({ - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" - .. " UNSAFE_componentWillMount\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles", - "UNSAFE_componentWillMount in strict mode is not recommended", - }, { withoutStack = 1 }) - end) + jestExpect(function() + ReactNoop.act(function() + ReactNoop.render(React.createElement(WillMount)) + end).toErrorDev({ + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" + .. " UNSAFE_componentWillMount\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles", + "UNSAFE_componentWillMount in strict mode is not recommended", + }, { withoutStack = 1 }) + end) - local WillMountAndUpdate = React.Component:extend("WillMountAndUpdate") - function WillMountAndUpdate:init() - self.state = {} - end - function WillMountAndUpdate.getDerivedStateFromProps() - return nil - end - function WillMountAndUpdate:componentWillMount() end - function WillMountAndUpdate:UNSAFE_componentWillUpdate() end - function WillMountAndUpdate:render() - return nil - end + local WillMountAndUpdate = React.Component:extend("WillMountAndUpdate") + function WillMountAndUpdate:init() + self.state = {} + end + function WillMountAndUpdate.getDerivedStateFromProps() + return nil + end + function WillMountAndUpdate:componentWillMount() end + function WillMountAndUpdate:UNSAFE_componentWillUpdate() end + function WillMountAndUpdate:render() + return nil + end + jestExpect(function() jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(WillMountAndUpdate)) - end) - end).toErrorDev({ - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" - .. " componentWillMount\n" - .. " UNSAFE_componentWillUpdate\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles", - "UNSAFE_componentWillUpdate in strict mode is not recommended", - }, { withoutStack = 1 }) - end).toWarnDev({ "componentWillMount has been renamed" }, { - withoutStack = true, - }) - - local WillReceiveProps = React.Component:extend("WillReceiveProps") - function WillReceiveProps:init() - self.state = {} - end - function WillReceiveProps.getDerivedStateFromProps() - return nil - end - function WillReceiveProps:componentWillReceiveProps() end - function WillReceiveProps:render() - return nil - end + ReactNoop.act(function() + ReactNoop.render(React.createElement(WillMountAndUpdate)) + end) + end).toErrorDev({ + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" + .. " componentWillMount\n" + .. " UNSAFE_componentWillUpdate\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles", + "UNSAFE_componentWillUpdate in strict mode is not recommended", + }, { withoutStack = 1 }) + end).toWarnDev({ "componentWillMount has been renamed" }, { + withoutStack = true, + }) - jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(WillReceiveProps)) - end) - end).toErrorDev( - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" - .. " componentWillReceiveProps\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles" - ) - end).toWarnDev({ "componentWillReceiveProps has been renamed" }, { - withoutStack = true, - }) - end -) - -it( - "should warn about deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present", - function() - local AllLegacyLifecycles = React.Component:extend("AllLegacyLifecycles") - function AllLegacyLifecycles:init() - self.state = {} - end - function AllLegacyLifecycles:getSnapshotBeforeUpdate() end - function AllLegacyLifecycles:componentWillMount() end - function AllLegacyLifecycles:UNSAFE_componentWillReceiveProps() end - function AllLegacyLifecycles:componentWillUpdate() end - function AllLegacyLifecycles:componentDidUpdate() end - function AllLegacyLifecycles:render() - return nil - end + local WillReceiveProps = React.Component:extend("WillReceiveProps") + function WillReceiveProps:init() + self.state = {} + end + function WillReceiveProps.getDerivedStateFromProps() + return nil + end + function WillReceiveProps:componentWillReceiveProps() end + function WillReceiveProps:render() + return nil + end + jestExpect(function() jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(AllLegacyLifecycles)) - end) - end).toErrorDev({ - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "AllLegacyLifecycles uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" - .. " componentWillMount\n" - .. " UNSAFE_componentWillReceiveProps\n" - .. " componentWillUpdate\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles", - "UNSAFE_componentWillReceiveProps in strict mode is not recommended", - }, { withoutStack = 1 }) - end).toWarnDev({ - "componentWillMount has been renamed", - "componentWillUpdate has been renamed", - }, { withoutStack = true }) + ReactNoop.act(function() + ReactNoop.render(React.createElement(WillReceiveProps)) + end) + end).toErrorDev( + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n" + .. " componentWillReceiveProps\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles" + ) + end).toWarnDev({ "componentWillReceiveProps has been renamed" }, { + withoutStack = true, + }) +end) - local WillMount = React.Component:extend("WillMount") - function WillMount:init() - self.state = {} - end - function WillMount:getSnapshotBeforeUpdate() end - function WillMount:UNSAFE_componentWillMount() end - function WillMount:componentDidUpdate() end - function WillMount:render() - return nil - end +it("should warn about deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present", function() + local AllLegacyLifecycles = React.Component:extend("AllLegacyLifecycles") + function AllLegacyLifecycles:init() + self.state = {} + end + function AllLegacyLifecycles:getSnapshotBeforeUpdate() end + function AllLegacyLifecycles:componentWillMount() end + function AllLegacyLifecycles:UNSAFE_componentWillReceiveProps() end + function AllLegacyLifecycles:componentWillUpdate() end + function AllLegacyLifecycles:componentDidUpdate() end + function AllLegacyLifecycles:render() + return nil + end + jestExpect(function() jestExpect(function() ReactNoop.act(function() - ReactNoop.render(React.createElement(WillMount)) + ReactNoop.render(React.createElement(AllLegacyLifecycles)) end) end).toErrorDev({ "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "WillMount uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" - .. " UNSAFE_componentWillMount\n\n" + .. "AllLegacyLifecycles uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" + .. " componentWillMount\n" + .. " UNSAFE_componentWillReceiveProps\n" + .. " componentWillUpdate\n\n" .. "The above lifecycles should be removed. Learn more about this warning here:\n" .. "https://reactjs.org/link/unsafe-component-lifecycles", - "UNSAFE_componentWillMount in strict mode is not recommended", + "UNSAFE_componentWillReceiveProps in strict mode is not recommended", }, { withoutStack = 1 }) + end).toWarnDev({ + "componentWillMount has been renamed", + "componentWillUpdate has been renamed", + }, { withoutStack = true }) - local WillMountAndUpdate = React.Component:extend("WillMountAndUpdate") - function WillMountAndUpdate:init() - self.state = {} - end - function WillMountAndUpdate:getSnapshotBeforeUpdate() end - function WillMountAndUpdate:componentWillMount() end - function WillMountAndUpdate:UNSAFE_componentWillUpdate() end - function WillMountAndUpdate:componentDidUpdate() end - function WillMountAndUpdate:render() - return nil - end + local WillMount = React.Component:extend("WillMount") + function WillMount:init() + self.state = {} + end + function WillMount:getSnapshotBeforeUpdate() end + function WillMount:UNSAFE_componentWillMount() end + function WillMount:componentDidUpdate() end + function WillMount:render() + return nil + end - jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(WillMountAndUpdate)) - end) - end).toErrorDev({ - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "WillMountAndUpdate uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" - .. " componentWillMount\n" - .. " UNSAFE_componentWillUpdate\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles", - "UNSAFE_componentWillUpdate in strict mode is not recommended", - }, { withoutStack = 1 }) - end).toWarnDev({ "componentWillMount has been renamed" }, { - withoutStack = true, - }) - - local WillReceiveProps = React.Component:extend("WillReceiveProps") - function WillReceiveProps:init() - self.state = {} - end - function WillReceiveProps:getSnapshotBeforeUpdate() end - function WillReceiveProps:componentWillReceiveProps() end - function WillReceiveProps:componentDidUpdate() end - function WillReceiveProps:render() - return nil - end + jestExpect(function() + ReactNoop.act(function() + ReactNoop.render(React.createElement(WillMount)) + end) + end).toErrorDev({ + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "WillMount uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" + .. " UNSAFE_componentWillMount\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles", + "UNSAFE_componentWillMount in strict mode is not recommended", + }, { withoutStack = 1 }) + local WillMountAndUpdate = React.Component:extend("WillMountAndUpdate") + function WillMountAndUpdate:init() + self.state = {} + end + function WillMountAndUpdate:getSnapshotBeforeUpdate() end + function WillMountAndUpdate:componentWillMount() end + function WillMountAndUpdate:UNSAFE_componentWillUpdate() end + function WillMountAndUpdate:componentDidUpdate() end + function WillMountAndUpdate:render() + return nil + end + + jestExpect(function() jestExpect(function() - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(WillReceiveProps)) - end) - end).toErrorDev( - "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" - .. "WillReceiveProps uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" - .. " componentWillReceiveProps\n\n" - .. "The above lifecycles should be removed. Learn more about this warning here:\n" - .. "https://reactjs.org/link/unsafe-component-lifecycles" - ) - end).toWarnDev({ "componentWillReceiveProps has been renamed" }, { - withoutStack = true, - }) + ReactNoop.act(function() + ReactNoop.render(React.createElement(WillMountAndUpdate)) + end) + end).toErrorDev({ + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "WillMountAndUpdate uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" + .. " componentWillMount\n" + .. " UNSAFE_componentWillUpdate\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles", + "UNSAFE_componentWillUpdate in strict mode is not recommended", + }, { withoutStack = 1 }) + end).toWarnDev({ "componentWillMount has been renamed" }, { + withoutStack = true, + }) + + local WillReceiveProps = React.Component:extend("WillReceiveProps") + function WillReceiveProps:init() + self.state = {} + end + function WillReceiveProps:getSnapshotBeforeUpdate() end + function WillReceiveProps:componentWillReceiveProps() end + function WillReceiveProps:componentDidUpdate() end + function WillReceiveProps:render() + return nil end -) + + jestExpect(function() + jestExpect(function() + ReactNoop.act(function() + ReactNoop.render(React.createElement(WillReceiveProps)) + end) + end).toErrorDev( + "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" + .. "WillReceiveProps uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n" + .. " componentWillReceiveProps\n\n" + .. "The above lifecycles should be removed. Learn more about this warning here:\n" + .. "https://reactjs.org/link/unsafe-component-lifecycles" + ) + end).toWarnDev({ "componentWillReceiveProps has been renamed" }, { + withoutStack = true, + }) +end) -- if !require('shared/ReactFeatureFlags').disableModulePatternComponents) -- it('calls effects on module-pattern component', function() @@ -1347,221 +1315,196 @@ it("should invoke both deprecated and new lifecycles if both are present", funct end) -- ROBLOX TODO: possibly a bug in the test due to divRef deviations, but a function state update doesn't get all the way through -xit( - "should not override state with stale values if prevState is spread within getDerivedStateFromProps", - function() - local divRef = React.createRef() - local childInstance - -- ROBLOX deviation: Noop renderer doesn't udpated the divRef like reactDOM does. figure out idiomatic way to update - local capturedValue - - local Child = React.Component:extend("Child") - function Child:init() - self.state = { local_ = 0 } - end - function Child.getDerivedStateFromProps(nextProps, prevState) - prevState.remote = nextProps.remote - return prevState - end - function Child:updateState() - self:setState(function(state) - return { local_ = state.local_ + 1 } - end) - self.props.onChange(self, self.state.remote + 1) - end - function Child:render() - capturedValue = "remote:" - .. tostring(self.state.remote) - .. ", local:" - .. tostring(self.state.local_) - childInstance = self - local renderedDiv = React.createElement("div", { - onClick = self.updateState, - ref = divRef, - }, capturedValue) - divRef.current = renderedDiv - return renderedDiv - end +xit("should not override state with stale values if prevState is spread within getDerivedStateFromProps", function() + local divRef = React.createRef() + local childInstance + -- ROBLOX deviation: Noop renderer doesn't udpated the divRef like reactDOM does. figure out idiomatic way to update + local capturedValue + + local Child = React.Component:extend("Child") + function Child:init() + self.state = { local_ = 0 } + end + function Child.getDerivedStateFromProps(nextProps, prevState) + prevState.remote = nextProps.remote + return prevState + end + function Child:updateState() + self:setState(function(state) + return { local_ = state.local_ + 1 } + end) + self.props.onChange(self, self.state.remote + 1) + end + function Child:render() + capturedValue = "remote:" .. tostring(self.state.remote) .. ", local:" .. tostring(self.state.local_) + childInstance = self + local renderedDiv = React.createElement("div", { + onClick = self.updateState, + ref = divRef, + }, capturedValue) + divRef.current = renderedDiv + return renderedDiv + end - local Parent = React.Component:extend("Parent") - function Parent:init() - self.state = { value = 0 } - end - function Parent:handleChange(value) - self:setState({ value = value }) - end - function Parent:render() - return React.createElement( - Child, - { remote = self.state.value, onChange = self.handleChange } - ) - end + local Parent = React.Component:extend("Parent") + function Parent:init() + self.state = { value = 0 } + end + function Parent:handleChange(value) + self:setState({ value = value }) + end + function Parent:render() + return React.createElement(Child, { remote = self.state.value, onChange = self.handleChange }) + end - ReactNoop.act(function() - ReactNoop.render(React.createElement(Parent)) - end) + ReactNoop.act(function() + ReactNoop.render(React.createElement(Parent)) + end) - -- ROBLOX TODO: divRef doesn't get updated with Noop renderer like it does in DOM - -- jestExpect(divRef.current.textContent).toBe('remote:0, local:0') - jestExpect(capturedValue).toBe("remote:0, local:0") + -- ROBLOX TODO: divRef doesn't get updated with Noop renderer like it does in DOM + -- jestExpect(divRef.current.textContent).toBe('remote:0, local:0') + jestExpect(capturedValue).toBe("remote:0, local:0") - ReactNoop.act(function() - -- Trigger setState() calls - childInstance:updateState() - end) - -- ROBLOX TODO: remote is still 0 on this next line - -- jestExpect(divRef.current.textContent).toBe('remote:1, local:1') - jestExpect(capturedValue).toBe("remote:1, local:1") - - -- Trigger batched setState() calls - divRef.current.click() - jestExpect(divRef.current.textContent).toBe("remote:2, local:2") - end -) - -it( - "should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate", - function() - local log = {} - - local MyComponent = React.Component:extend("MyComponent") - function MyComponent:init() - self.state = { - value = 0, - } - end - function MyComponent.getDerivedStateFromProps(nextProps, prevState) - return { - value = prevState.value + 1, - } - end - function MyComponent:getSnapshotBeforeUpdate(prevProps, prevState) - table.insert( - log, - string.format( - "getSnapshotBeforeUpdate() prevProps:%s prevState:%s", - prevProps.value, - prevState.value - ) - ) - return "abc" - end - function MyComponent:componentDidUpdate(prevProps, prevState, snapshot) - table.insert( - log, - string.format( - "componentDidUpdate() prevProps:%s prevState:%s snapshot:%s", - prevProps.value, - prevState.value, - snapshot - ) - ) - end - function MyComponent:render() - table.insert(log, "render") - return nil - end + ReactNoop.act(function() + -- Trigger setState() calls + childInstance:updateState() + end) + -- ROBLOX TODO: remote is still 0 on this next line + -- jestExpect(divRef.current.textContent).toBe('remote:1, local:1') + jestExpect(capturedValue).toBe("remote:1, local:1") - ReactNoop.act(function() - ReactNoop.render( - React.createElement( - "Frame", - {}, - React.createElement(MyComponent, { - value = "foo", - }) - ) - ) - end) - jestExpect(log).toEqual({ "render" }) - log = {} + -- Trigger batched setState() calls + divRef.current.click() + jestExpect(divRef.current.textContent).toBe("remote:2, local:2") +end) - ReactNoop.act(function() - ReactNoop.render( - React.createElement( - "Frame", - {}, - React.createElement(MyComponent, { - value = "bar", - }) - ) - ) - end) - jestExpect(log).toEqual({ - "render", - "getSnapshotBeforeUpdate() prevProps:foo prevState:1", - "componentDidUpdate() prevProps:foo prevState:1 snapshot:abc", - }) - log = {} +it("should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate", function() + local log = {} - ReactNoop.act(function() - ReactNoop.render( - React.createElement( - "Frame", - {}, - React.createElement(MyComponent, { - value = "baz", - }) - ) + local MyComponent = React.Component:extend("MyComponent") + function MyComponent:init() + self.state = { + value = 0, + } + end + function MyComponent.getDerivedStateFromProps(nextProps, prevState) + return { + value = prevState.value + 1, + } + end + function MyComponent:getSnapshotBeforeUpdate(prevProps, prevState) + table.insert( + log, + string.format("getSnapshotBeforeUpdate() prevProps:%s prevState:%s", prevProps.value, prevState.value) + ) + return "abc" + end + function MyComponent:componentDidUpdate(prevProps, prevState, snapshot) + table.insert( + log, + string.format( + "componentDidUpdate() prevProps:%s prevState:%s snapshot:%s", + prevProps.value, + prevState.value, + snapshot ) - end) - jestExpect(log).toEqual({ - "render", - "getSnapshotBeforeUpdate() prevProps:bar prevState:2", - "componentDidUpdate() prevProps:bar prevState:2 snapshot:abc", - }) - log = {} + ) + end + function MyComponent:render() + table.insert(log, "render") + return nil + end - ReactNoop.act(function() - ReactNoop.render(React.createElement("Frame")) - end) - jestExpect(log).toEqual({}) - end -) - -it( - "should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps", - function() - local divRef = React.createRef() - local capturedValue - local SimpleComponent = React.Component:extend("SimpleComponent") - function SimpleComponent:init(props) - self.state = { - value = props.value, - } - end + ReactNoop.act(function() + ReactNoop.render(React.createElement( + "Frame", + {}, + React.createElement(MyComponent, { + value = "foo", + }) + )) + end) + jestExpect(log).toEqual({ "render" }) + log = {} - function SimpleComponent.getDerivedStateFromProps(nextProps, prevState) - if nextProps.value == prevState.value then - return nil - end - return { value = nextProps.value } - end + ReactNoop.act(function() + ReactNoop.render(React.createElement( + "Frame", + {}, + React.createElement(MyComponent, { + value = "bar", + }) + )) + end) + jestExpect(log).toEqual({ + "render", + "getSnapshotBeforeUpdate() prevProps:foo prevState:1", + "componentDidUpdate() prevProps:foo prevState:1 snapshot:abc", + }) + log = {} - function SimpleComponent:shouldComponentUpdate(nextProps, nextState) - return nextState.value ~= self.state.value - end + ReactNoop.act(function() + ReactNoop.render(React.createElement( + "Frame", + {}, + React.createElement(MyComponent, { + value = "baz", + }) + )) + end) + jestExpect(log).toEqual({ + "render", + "getSnapshotBeforeUpdate() prevProps:bar prevState:2", + "componentDidUpdate() prevProps:bar prevState:2 snapshot:abc", + }) + log = {} - function SimpleComponent:render() - capturedValue = self.state.value - return React.createElement( - "Frame", - { ref = divRef }, - React.createElement("TextLabel", { Text = self.state.value }) - ) + ReactNoop.act(function() + ReactNoop.render(React.createElement("Frame")) + end) + jestExpect(log).toEqual({}) +end) + +it("should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps", function() + local divRef = React.createRef() + local capturedValue + local SimpleComponent = React.Component:extend("SimpleComponent") + function SimpleComponent:init(props) + self.state = { + value = props.value, + } + end + + function SimpleComponent.getDerivedStateFromProps(nextProps, prevState) + if nextProps.value == prevState.value then + return nil end + return { value = nextProps.value } + end - -- ROBLOX TODO: upstream uses reactDOM renderer, which means divRef gets updated properly. figure out what can work for Noop - ReactNoop.act(function() - ReactNoop.render(React.createElement(SimpleComponent, { value = "initial" })) - end) - jestExpect(capturedValue).toBe("initial") - ReactNoop.act(function() - ReactNoop.render(React.createElement(SimpleComponent, { value = "updated" })) - end) - jestExpect(capturedValue).toBe("updated") + function SimpleComponent:shouldComponentUpdate(nextProps, nextState) + return nextState.value ~= self.state.value end -) + + function SimpleComponent:render() + capturedValue = self.state.value + return React.createElement( + "Frame", + { ref = divRef }, + React.createElement("TextLabel", { Text = self.state.value }) + ) + end + + -- ROBLOX TODO: upstream uses reactDOM renderer, which means divRef gets updated properly. figure out what can work for Noop + ReactNoop.act(function() + ReactNoop.render(React.createElement(SimpleComponent, { value = "initial" })) + end) + jestExpect(capturedValue).toBe("initial") + ReactNoop.act(function() + ReactNoop.render(React.createElement(SimpleComponent, { value = "updated" })) + end) + jestExpect(capturedValue).toBe("updated") +end) -- -- ROBLOX TODO? Don't think we can convert this, since it relies on refs and DOM objects -- xit('should call getSnapshotBeforeUpdate before mutations are committed', function() @@ -1625,32 +1568,29 @@ it( -- ReactDOM.render(, div) -- }) -it( - "should warn if getSnapshotBeforeUpdate is defined with no componentDidUpdate", - function() - local MyComponent = React.Component:extend("MyComponent") - function MyComponent:getSnapshotBeforeUpdate() - return nil - end - function MyComponent:render() - return nil - end - - jestExpect(function() - ReactNoop.act(function() - ReactNoop.render(React.createElement(MyComponent)) - end) - end).toErrorDev( - "MyComponent: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). " - .. "This component defines getSnapshotBeforeUpdate() only." - ) +it("should warn if getSnapshotBeforeUpdate is defined with no componentDidUpdate", function() + local MyComponent = React.Component:extend("MyComponent") + function MyComponent:getSnapshotBeforeUpdate() + return nil + end + function MyComponent:render() + return nil + end - -- De-duped + jestExpect(function() ReactNoop.act(function() ReactNoop.render(React.createElement(MyComponent)) end) - end -) + end).toErrorDev( + "MyComponent: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). " + .. "This component defines getSnapshotBeforeUpdate() only." + ) + + -- De-duped + ReactNoop.act(function() + ReactNoop.render(React.createElement(MyComponent)) + end) +end) it("warns about deprecated unsafe lifecycles", function() local MyComponent = React.Component:extend("MyComponent") diff --git a/modules/react-reconciler/src/__tests__/ReactFiberHostContext-internal.spec.lua b/modules/react-reconciler/src/__tests__/ReactFiberHostContext-internal.spec.lua index 8f165cb3..f5ab933d 100644 --- a/modules/react-reconciler/src/__tests__/ReactFiberHostContext-internal.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactFiberHostContext-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/ea2af878cc3fb139b0e08cf9bc4b2f4178429d69/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -8,6 +8,8 @@ * @emails react-core * @jest-environment node ]] +local Packages --[[ ROBLOX comment: must define Packages module ]] +local Promise = require(Packages.Promise) local Packages = script.Parent.Parent.Parent local JestGlobals = require("@pkg/@jsdotlua/jest-globals") diff --git a/modules/react-reconciler/src/__tests__/ReactHooks-internal.spec.lua b/modules/react-reconciler/src/__tests__/ReactHooks-internal.spec.lua index ed0ddb6f..df02b46a 100644 --- a/modules/react-reconciler/src/__tests__/ReactHooks-internal.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactHooks-internal.spec.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js -- * Copyright (c) Facebook, Inc. and its affiliates. -- * -- * This source code is licensed under the MIT license found in the @@ -13,7 +13,6 @@ local React local ReactFeatureFlags local ReactTestRenderer local Scheduler --- local ReactDOMServer local act local Packages = script.Parent.Parent.Parent local JestGlobals = require("@pkg/@jsdotlua/jest-globals") @@ -23,6 +22,7 @@ local jest = JestGlobals.jest local it = JestGlobals.it local xit = JestGlobals.xit local describe = JestGlobals.describe +local ReactDOMServer describe("ReactHooks", function() local Promise = require("@pkg/@jsdotlua/promise") local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") diff --git a/modules/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer.spec.lua b/modules/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer.spec.lua index c38481c8..0deebb88 100644 --- a/modules/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer.spec.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/99cae887f3a8bde760a111516d254c1225242edf/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -31,6 +31,7 @@ local Suspense local useState local useReducer local useEffect +local useInsertionEffect local useLayoutEffect local useCallback local useMemo @@ -4697,7 +4698,6 @@ it( "Render: -1", "Effect: 1", "Reducer: 1", - "Reducer: 1", "Render: 1", }) diff --git a/modules/react-reconciler/src/__tests__/ReactIdentity.spec.lua b/modules/react-reconciler/src/__tests__/ReactIdentity.spec.lua index fff93abf..91766d98 100644 --- a/modules/react-reconciler/src/__tests__/ReactIdentity.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactIdentity.spec.lua @@ -157,8 +157,7 @@ it("should use table key to express identity when updating children type", funct local function Component(props) local children = {} for i = 1, props.count do - children[tostring(i)] = - React.createElement("TextLabel", { Text = tostring(i) }) + children[tostring(i)] = React.createElement("TextLabel", { Text = tostring(i) }) end if props.count == 0 then @@ -194,8 +193,7 @@ it("should defer to provided key if both are present", function() local key = props.invert and tostring(51 - i) or tostring(i) -- provide both explicit key and table key, where table-key does not -- obey the `invert` prop and should not be the one that's used. - children[tostring(i)] = - React.createElement("TextLabel", { key = key, Text = i }) + children[tostring(i)] = React.createElement("TextLabel", { key = key, Text = i }) end return React.createElement("Frame", { ref = ref }, children) @@ -242,20 +240,12 @@ it("should use composite identity", function() local ref2 = React.createRef() reactRobloxRoot:render( - React.createElement( - Wrapper, - { key = "wrap1" }, - React.createElement("Frame", { ref = ref1 }) - ) + React.createElement(Wrapper, { key = "wrap1" }, React.createElement("Frame", { ref = ref1 })) ) Scheduler.unstable_flushAllWithoutAsserting() reactRobloxRoot:render( - React.createElement( - Wrapper, - { key = "wrap2" }, - React.createElement("Frame", { ref = ref2 }) - ) + React.createElement(Wrapper, { key = "wrap2" }, React.createElement("Frame", { ref = ref2 })) ) Scheduler.unstable_flushAllWithoutAsserting() @@ -267,11 +257,7 @@ local function renderAComponentWithKeyIntoContainer(key, container) local Wrapper = React.Component:extend("Wrapper") function Wrapper:render() - return React.createElement( - "Frame", - nil, - React.createElement("Frame", { ref = ref, key = key }) - ) + return React.createElement("Frame", nil, React.createElement("Frame", { ref = ref, key = key })) end reactRobloxRoot:render(React.createElement(Wrapper), container) @@ -320,13 +306,7 @@ it("should let restructured components retain their uniqueness", function() local TestComponent = React.Component:extend("TestComponent") function TestComponent:render() - return React.createElement( - "Frame", - nil, - instance2, - self.props.children[1], - self.props.children[2] - ) + return React.createElement("Frame", nil, instance2, self.props.children[1], self.props.children[2]) end local TestContainer = React.Component:extend("TestContainer") @@ -347,22 +327,12 @@ it("should let nested restructures retain their uniqueness", function() local TestComponent = React.Component:extend("TestComponent") function TestComponent:render() - return React.createElement( - "Frame", - nil, - instance2, - self.props.children[1], - self.props.children[2] - ) + return React.createElement("Frame", nil, instance2, self.props.children[1], self.props.children[2]) end local TestContainer = React.Component:extend("TestContainer") function TestContainer:render() - return React.createElement( - "Frame", - nil, - React.createElement(TestComponent, nil, instance0, instance1) - ) + return React.createElement("Frame", nil, React.createElement(TestComponent, nil, instance0, instance1)) end jestExpect(function() @@ -375,23 +345,12 @@ end) xit("should let text nodes retain their uniqueness", function() local TestComponent = React.Component:extend("TestComponent") function TestComponent:render() - return React.createElement( - "Frame", - nil, - self.props.children, - React.createElement("Frame") - ) + return React.createElement("Frame", nil, self.props.children, React.createElement("Frame")) end local TestContainer = React.Component:extend("TestContainer") function TestContainer:render() - return React.createElement( - TestComponent, - nil, - React.createElement("Frame"), - nil, - { "second" } - ) + return React.createElement(TestComponent, nil, React.createElement("Frame"), nil, { "second" }) end jestExpect(function() @@ -438,9 +397,7 @@ it("should retain key during updates in composite components", function() return byProp end - reactRobloxRoot:render( - React.createElement(TestContainer, { first = instance0, second = instance1 }) - ) + reactRobloxRoot:render(React.createElement(TestContainer, { first = instance0, second = instance1 })) Scheduler.unstable_flushAllWithoutAsserting() local originalChildren = childrenByProp(ref.current:GetChildren()) diff --git a/modules/react-reconciler/src/__tests__/ReactIncremental.spec.lua b/modules/react-reconciler/src/__tests__/ReactIncremental.spec.lua index e8493abc..39228870 100644 --- a/modules/react-reconciler/src/__tests__/ReactIncremental.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactIncremental.spec.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactIncremental-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactIncremental-test.js -- * Copyright (c) Facebook, Inc. and its affiliates. -- * -- * This source code is licensed under the MIT license found in the @@ -349,6 +349,123 @@ describe("ReactIncremental", function() "div", nil, React.createElement(Bar, nil, props.text), + React.createElement(LegacyHiddenDiv, { mode = "hidden" }, React.createElement(Middle, nil, props.text)), + React.createElement(Bar, nil, props.text), + React.createElement(LegacyHiddenDiv, { mode = "hidden" }, React.createElement(Middle, nil, "Footer")) + ) + end -- Init + ReactNoop:render(React.createElement(Foo, { text = "foo" })) + expect(Scheduler).toFlushAndYield({ "Foo", "Bar", "Bar", "Middle", "Middle" }) -- Render part of the work. This should be enough to flush everything except + -- the middle which has lower priority. + ReactNoop:render(React.createElement(Foo, { text = "bar" })) + expect(Scheduler).toFlushAndYieldThrough({ "Foo", "Bar", "Bar" }) -- Flush only the remaining work + expect(Scheduler).toFlushAndYield({ "Middle", "Middle" }) + end) -- @gate www + it("can deprioritize a tree from without dropping work", function() + local function Bar(props) + Scheduler:unstable_yieldValue("Bar") + return React.createElement("div", nil, props.children) + end + local function Middle(props) + Scheduler:unstable_yieldValue("Middle") + return React.createElement("span", nil, props.children) + end + local function Foo(props) + Scheduler:unstable_yieldValue("Foo") + return React.createElement( + "div", + nil, + React.createElement(Bar, nil, props.text), + React.createElement(LegacyHiddenDiv, { mode = "hidden" }, React.createElement(Middle, nil, props.text)), + React.createElement(Bar, nil, props.text), + React.createElement(LegacyHiddenDiv, { mode = "hidden" }, React.createElement(Middle, nil, "Footer")) + ) + end -- Init + ReactNoop:flushSync(function() + ReactNoop:render(React.createElement(Foo, { text = "foo" })) + end) + expect(Scheduler).toHaveYielded({ "Foo", "Bar", "Bar" }) + expect(Scheduler).toFlushAndYield({ "Middle", "Middle" }) -- Render the high priority work (everything except the hidden trees). + ReactNoop:flushSync(function() + ReactNoop:render(React.createElement(Foo, { text = "foo" })) + end) + expect(Scheduler).toHaveYielded({ "Foo", "Bar", "Bar" }) -- The hidden content was deprioritized from high to low priority. A low + -- priority callback should have been scheduled. Flush it now. + expect(Scheduler).toFlushAndYield({ "Middle", "Middle" }) + end) + xit("can resume work in a subtree even when a parent bails out", function() + local function Bar(props) + Scheduler:unstable_yieldValue("Bar") + return React.createElement("div", nil, props.children) + end + local function Tester() + -- This component is just here to ensure that the bail out is + -- in fact in effect in the expected place for this test. + Scheduler:unstable_yieldValue("Tester") + return React.createElement("div", nil) + end + local function Middle(props) + Scheduler:unstable_yieldValue("Middle") + return React.createElement("span", nil, props.children) + end + local middleContent = React.createElement( + "aaa", + nil, + React.createElement(Tester, nil), + React.createElement( + "bbb", + { hidden = true }, + React.createElement("ccc", nil, React.createElement(Middle, nil, "Hi")) + ) + ) + local function Foo(props) + Scheduler:unstable_yieldValue("Foo") + return React.createElement( + "div", + nil, + React.createElement(Bar, nil, props.text), + middleContent, + React.createElement(Bar, nil, props.text) + ) + end -- Init + ReactNoop:render(React.createElement(Foo, { text = "foo" })) + ReactNoop:flushDeferredPri(52) + expect(Scheduler).toHaveYielded({ "Foo", "Bar", "Tester", "Bar" }) -- We're now rendering an update that will bail out on updating middle. + ReactNoop:render(React.createElement(Foo, { text = "bar" })) + ReactNoop:flushDeferredPri(45 + 5) + expect(Scheduler).toHaveYielded({ "Foo", "Bar", "Bar" }) -- Flush the rest to make sure that the bailout didn't block this work. + expect(Scheduler).toFlushAndYield({ "Middle" }) + end) + xit("can resume work in a bailed subtree within one pass", function() + local function Bar(props) + Scheduler:unstable_yieldValue("Bar") + return React.createElement("div", nil, props.children) + end + type Tester = React_Component & {} + type Tester_statics = {} + local Tester = React.Component:extend("Tester") :: Tester & Tester_statics + function Tester.shouldComponentUpdate(self: Tester) + return false + end + function Tester.render(self: Tester) + -- This component is just here to ensure that the bail out is + -- in fact in effect in the expected place for this test. + Scheduler:unstable_yieldValue("Tester") + return React.createElement("div", nil) + end + local function Middle(props) + Scheduler:unstable_yieldValue("Middle") + return React.createElement("span", nil, props.children) + end -- Should content not just bail out on current, not workInProgress? + type Content = React_Component & {} + type Content_statics = {} + local Content = React.Component:extend("Content") :: Content & Content_statics + function Content.shouldComponentUpdate(self: Content) + return false + end + function Content.render(self: Content) + return { + React.createElement(Tester, { key = "a", unused = self.props.unused }), React.createElement( LegacyHiddenDiv, { mode = "hidden" }, diff --git a/modules/react-reconciler/src/__tests__/ReactIncrementalErrorReplay.spec.lua b/modules/react-reconciler/src/__tests__/ReactIncrementalErrorReplay.spec.lua index af675e2c..535b8228 100644 --- a/modules/react-reconciler/src/__tests__/ReactIncrementalErrorReplay.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactIncrementalErrorReplay.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/__tests__/ReactIncrementalReflection.spec.lua b/modules/react-reconciler/src/__tests__/ReactIncrementalReflection.spec.lua index 517a9ff5..36bef51c 100644 --- a/modules/react-reconciler/src/__tests__/ReactIncrementalReflection.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactIncrementalReflection.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js -- * Copyright (c) Facebook, Inc. and its affiliates. -- * -- * This source code is licensed under the MIT license found in the diff --git a/modules/react-reconciler/src/__tests__/ReactIncrementalUpdates.spec.lua b/modules/react-reconciler/src/__tests__/ReactIncrementalUpdates.spec.lua index 46504451..ea3c7fcd 100644 --- a/modules/react-reconciler/src/__tests__/ReactIncrementalUpdates.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactIncrementalUpdates.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js -- * Copyright (c) Facebook, Inc. and its affiliates. -- * -- * This source code is licensed under the MIT license found in the diff --git a/modules/react-reconciler/src/__tests__/ReactNewContext.spec.lua b/modules/react-reconciler/src/__tests__/ReactNewContext.spec.lua index 4d291593..d04f281e 100644 --- a/modules/react-reconciler/src/__tests__/ReactNewContext.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactNewContext.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/8e5adfbd7e605bda9c5e96c10e015b3dc0df688e/packages/react-reconciler/src/__tests__/ReactNewContext-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactNewContext-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -18,7 +18,6 @@ local React local useContext local ReactNoop local Scheduler --- local gen local JestGlobals = require("@pkg/@jsdotlua/jest-globals") local jestExpect = JestGlobals.expect @@ -26,6 +25,7 @@ local beforeEach = JestGlobals.beforeEach local jest = JestGlobals.jest local it = JestGlobals.it local describe = JestGlobals.describe +local gen beforeEach(function() jest.resetModules() diff --git a/modules/react-reconciler/src/__tests__/ReactNoopRendererAct.spec.lua b/modules/react-reconciler/src/__tests__/ReactNoopRendererAct.spec.lua index 7304de6b..1f3d6703 100644 --- a/modules/react-reconciler/src/__tests__/ReactNoopRendererAct.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactNoopRendererAct.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d17086c7c813402a550d15a2f56dc43f1dbd1735/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/__tests__/ReactSuspense-internal.spec.lua b/modules/react-reconciler/src/__tests__/ReactSuspense-internal.spec.lua index fac48831..290bdeca 100644 --- a/modules/react-reconciler/src/__tests__/ReactSuspense-internal.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactSuspense-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js -- * Copyright (c) Facebook, Inc. and its affiliates. -- * -- * This source code is licensed under the MIT license found in the @@ -7,12 +7,10 @@ -- * @emails react-core -- * @jest-environment node -- */ - local React local ReactTestRenderer local ReactFeatureFlags local Scheduler -local SchedulerTracing local ReactCache local Suspense local _act diff --git a/modules/react-reconciler/src/__tests__/ReactTopLevelFragment.spec.lua b/modules/react-reconciler/src/__tests__/ReactTopLevelFragment.spec.lua index 4d56b7d1..f043b436 100644 --- a/modules/react-reconciler/src/__tests__/ReactTopLevelFragment.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactTopLevelFragment.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/69060e1da6061af845162dcf6854a5d9af28350a/packages/react-reconciler/src/__tests__/ReactTopLevelFragment-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactTopLevelFragment-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -69,11 +69,7 @@ it("should preserve state when switching from a single child", function() else return { React.createElement(Stateful, { key = "a" }), - React.createElement( - "Frame", - { key = "b" }, - React.createElement("TextLabel", { Text = "World" }) - ), + React.createElement("Frame", { key = "b" }, React.createElement("TextLabel", { Text = "World" })), } end end @@ -110,11 +106,7 @@ it("should not preserve state when switching to a nested array", function() return { { React.createElement(Stateful, { key = "a" }), - React.createElement( - "Frame", - { key = "b" }, - React.createElement("TextLabel", { Text = "World" }) - ), + React.createElement("Frame", { key = "b" }, React.createElement("TextLabel", { Text = "World" })), }, React.createElement("Frame", { key = "c" }), } @@ -153,11 +145,7 @@ it("preserves state if an implicit key slot switches from/to nil", function() } :: Array else return { - React.createElement( - "Frame", - { key = "b" }, - React.createElement("TextLabel", { Text = "Hello" }) - ), + React.createElement("Frame", { key = "b" }, React.createElement("TextLabel", { Text = "Hello" })), React.createElement(Stateful, { key = "a" }), -- ROBLOX FIXME Luau: Luau *must* infer mixed arrays } :: Array @@ -200,11 +188,7 @@ it("should preserve state in a reorder", function() return { { -- ROBLOX FIXME Luau: Luau needs to allow mixed arrays and/or normalize these two things to a common ancestor - React.createElement( - "Frame", - { key = "b" }, - React.createElement("TextLabel", { Text = "World" }) - ) :: any, + React.createElement("Frame", { key = "b" }, React.createElement("TextLabel", { Text = "World" })) :: any, React.createElement(Stateful, { key = "a" }), }, } @@ -213,11 +197,7 @@ it("should preserve state in a reorder", function() { -- ROBLOX FIXME Luau: Luau needs to allow mixed arrays and/or normalize these two things to a common ancestor React.createElement(Stateful, { key = "a" }) :: any, - React.createElement( - "Frame", - { key = "b" }, - React.createElement("TextLabel", { Text = "World" }) - ), + React.createElement("Frame", { key = "b" }, React.createElement("TextLabel", { Text = "World" })), } :: any, React.createElement("Frame", { key = "c" }), } diff --git a/modules/react-reconciler/src/__tests__/ReactTopLevelText.spec.lua b/modules/react-reconciler/src/__tests__/ReactTopLevelText.spec.lua index 5bbc5d52..271b5bf2 100644 --- a/modules/react-reconciler/src/__tests__/ReactTopLevelText.spec.lua +++ b/modules/react-reconciler/src/__tests__/ReactTopLevelText.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/69060e1da6061af845162dcf6854a5d9af28350a/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/__tests__/ReactTopLevelText-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react-reconciler/src/__tests__/SchedulingProfiler-internal.spec.lua b/modules/react-reconciler/src/__tests__/SchedulingProfiler-internal.spec.lua index d20bd9cc..aaaad26d 100644 --- a/modules/react-reconciler/src/__tests__/SchedulingProfiler-internal.spec.lua +++ b/modules/react-reconciler/src/__tests__/SchedulingProfiler-internal.spec.lua @@ -133,10 +133,7 @@ describe("SchedulingProfiler", function() -- @gate enableSchedulingProfiler it("should mark concurrent render without suspends or state updates", function() - ReactTestRenderer.create( - React.createElement("div"), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement("div"), { unstable_isConcurrent = true }) jestExpect(marks).toEqual({ "--react-init-" .. tostring(ReactVersion), @@ -365,10 +362,7 @@ describe("SchedulingProfiler", function() return nil end - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) jestExpect(marks).toEqual({ "--react-init-" .. tostring(ReactVersion), @@ -404,10 +398,7 @@ describe("SchedulingProfiler", function() return nil end - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) jestExpect(marks).toEqual({ "--react-init-" .. tostring(ReactVersion), @@ -446,10 +437,7 @@ describe("SchedulingProfiler", function() return nil end - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) jestExpect(marks).toEqual({ "--react-init-" .. tostring(ReactVersion), @@ -485,10 +473,7 @@ describe("SchedulingProfiler", function() return nil end - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) jestExpect(marks).toEqual({ "--react-init-" .. tostring(ReactVersion), @@ -519,10 +504,7 @@ describe("SchedulingProfiler", function() return didMount end - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) jestExpect(marks).toEqual({ "--react-init-" .. tostring(ReactVersion), @@ -559,10 +541,7 @@ describe("SchedulingProfiler", function() end ReactTestRenderer.unstable_concurrentAct(function() - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) end) jestExpect(marks).toEqual({ @@ -595,10 +574,7 @@ describe("SchedulingProfiler", function() end ReactTestRenderer.unstable_concurrentAct(function() - ReactTestRenderer.create( - React.createElement(Example), - { unstable_isConcurrent = true } - ) + ReactTestRenderer.create(React.createElement(Example), { unstable_isConcurrent = true }) end) -- ROBLOX TODO: we don't have a way to gate tests based on features like upstream does diff --git a/modules/react-roblox/src/client/ReactRoblox.lua b/modules/react-roblox/src/client/ReactRoblox.lua index ff19a410..52b13106 100644 --- a/modules/react-roblox/src/client/ReactRoblox.lua +++ b/modules/react-roblox/src/client/ReactRoblox.lua @@ -119,11 +119,7 @@ local Tag = require("@pkg/@jsdotlua/shared").Tag -- batchedEventUpdates -- ) -local function createPortal( - children: ReactNodeList, - container: Container, - key: string? -): any +local function createPortal(children: ReactNodeList, container: Container, key: string?): any -- ): React$Portal invariant( isValidContainer(container), diff --git a/modules/react-roblox/src/client/ReactRobloxComponent.lua b/modules/react-roblox/src/client/ReactRobloxComponent.lua index 9b70302a..7fe55b82 100644 --- a/modules/react-roblox/src/client/ReactRobloxComponent.lua +++ b/modules/react-roblox/src/client/ReactRobloxComponent.lua @@ -34,7 +34,7 @@ local function diffProperties( lastRawProps: Object, nextRawProps: Object, rootContainerElement: HostInstance -): (nil | Array) +): nil | Array -- if _G.__DEV__ then -- validatePropertiesInDevelopment(tag, nextRawProps) -- end diff --git a/modules/react-roblox/src/client/ReactRobloxComponentTree.lua b/modules/react-roblox/src/client/ReactRobloxComponentTree.lua index 78fe97ff..a680dfd0 100644 --- a/modules/react-roblox/src/client/ReactRobloxComponentTree.lua +++ b/modules/react-roblox/src/client/ReactRobloxComponentTree.lua @@ -49,8 +49,7 @@ local exports: { [any]: any } = {} -- (which are Instances). We might consider using the Attributes feature for -- this when it releases local containerToRoot: { [Container]: Fiber } = {} -local instanceToFiber: { [HostInstance | SuspenseInstance | ReactScopeInstance]: Fiber } = - {} +local instanceToFiber: { [HostInstance | SuspenseInstance | ReactScopeInstance]: Fiber } = {} local instanceToProps: { [HostInstance | SuspenseInstance]: Props } = {} local randomKey = string.sub(tostring(math.random()), 3) @@ -61,16 +60,14 @@ local internalContainerInstanceKey = "__reactContainer$" .. randomKey -- local internalEventHandlerListenersKey = '__reactListeners$' + randomKey -- local internalEventHandlesSetKey = '__reactHandles$' + randomKey -exports.precacheFiberNode = - function(hostInst: Fiber, node: HostInstance | SuspenseInstance | ReactScopeInstance) - instanceToFiber[node] = hostInst - end +exports.precacheFiberNode = function(hostInst: Fiber, node: HostInstance | SuspenseInstance | ReactScopeInstance) + instanceToFiber[node] = hostInst +end -exports.uncacheFiberNode = - function(node: HostInstance | SuspenseInstance | ReactScopeInstance) - instanceToFiber[node] = nil - instanceToProps[node] = nil - end +exports.uncacheFiberNode = function(node: HostInstance | SuspenseInstance | ReactScopeInstance) + instanceToFiber[node] = nil + instanceToProps[node] = nil +end exports.markContainerAsRoot = function(hostRoot: Fiber, node: Container) -- deviation: Use our module-level map @@ -133,15 +130,10 @@ exports.getClosestInstanceFromNode = function(targetNode: Instance): Fiber? -- have one on the alternate so we need to check in case this was a -- root. local alternate = targetInst.alternate - if - targetInst.child ~= nil - or (alternate ~= nil and alternate.child ~= nil) - then + if targetInst.child ~= nil or (alternate ~= nil and alternate.child ~= nil) then -- ROBLOX deviation: lazy initialize to work around circular dependency if getParentSuspenseInstance == nil then - getParentSuspenseInstance = (require( - script.Parent.ReactRobloxHostConfig - ) :: any).getParentSuspenseInstance + getParentSuspenseInstance = (require(script.Parent.ReactRobloxHostConfig) :: any).getParentSuspenseInstance end -- Next we need to figure out if the node that skipped past is @@ -194,8 +186,7 @@ exports.getInstanceFromNode = function(node): Fiber? SuspenseComponent = ReactWorkTags.HostComponent end - local inst = (node :: any)[internalInstanceKey] - or (node :: any)[internalContainerInstanceKey] + local inst = (node :: any)[internalInstanceKey] or (node :: any)[internalContainerInstanceKey] if inst then if inst.tag == HostComponent @@ -229,10 +220,9 @@ exports.getNodeFromInstance = function(inst: Fiber): Instance | TextInstance error("getNodeFromInstance: Invalid argument.") end -exports.getFiberCurrentPropsFromNode = - function(node: Instance | TextInstance | SuspenseInstance): Props - return instanceToProps[node] - end +exports.getFiberCurrentPropsFromNode = function(node: Instance | TextInstance | SuspenseInstance): Props + return instanceToProps[node] +end exports.updateFiberProps = function(node: Instance | SuspenseInstance, props: Props) instanceToProps[node] = props diff --git a/modules/react-roblox/src/client/ReactRobloxHostConfig.lua b/modules/react-roblox/src/client/ReactRobloxHostConfig.lua index 9ec7fbcd..7421ecbd 100644 --- a/modules/react-roblox/src/client/ReactRobloxHostConfig.lua +++ b/modules/react-roblox/src/client/ReactRobloxHostConfig.lua @@ -217,10 +217,7 @@ local function recursivelyUncacheFiberNode(node: HostInstance) end local exports: { [any]: any } = {} -Object.assign( - exports, - require("@pkg/@jsdotlua/shared").ReactFiberHostConfig.WithNoPersistence -) +Object.assign(exports, require("@pkg/@jsdotlua/shared").ReactFiberHostConfig.WithNoPersistence) exports.getRootHostContext = function(rootContainerInstance: Container): HostContext -- ROBLOX deviation: This is a lot of HTML-DOM specific logic; I'm not clear on @@ -479,12 +476,7 @@ exports.noTimeout = -1 exports.supportsMutation = true -exports.commitMount = function( - domElement: Instance, - type: string, - newProps: Props, - internalInstanceHandle: Object -) +exports.commitMount = function(domElement: Instance, type: string, newProps: Props, internalInstanceHandle: Object) unimplemented("commitMount") -- -- Despite the naming that might imply otherwise, this method only -- -- fires if there is an `Update` effect scheduled during mounting. @@ -587,27 +579,25 @@ exports.appendChildToContainer = function(container: Container, child: Instance) -- end end -exports.insertBefore = - function(parentInstance: Instance, child: Instance, _beforeChild: Instance) - -- ROBLOX deviation: Roblox's DOM is based on child->parent references - child.Parent = parentInstance - -- parentInstance.insertBefore(child, beforeChild) - if _G.__DEV__ then - checkTags(child) - end +exports.insertBefore = function(parentInstance: Instance, child: Instance, _beforeChild: Instance) + -- ROBLOX deviation: Roblox's DOM is based on child->parent references + child.Parent = parentInstance + -- parentInstance.insertBefore(child, beforeChild) + if _G.__DEV__ then + checkTags(child) end +end -exports.insertInContainerBefore = - function(container: Container, child: Instance, beforeChild: Instance) - -- ROBLOX deviation: use our container definition - local parentNode = container - exports.insertBefore(parentNode, child, beforeChild) - -- if container.nodeType == COMMENT_NODE) - -- (container.parentNode: any).insertBefore(child, beforeChild) - -- } else { - -- container.insertBefore(child, beforeChild) - -- end - end +exports.insertInContainerBefore = function(container: Container, child: Instance, beforeChild: Instance) + -- ROBLOX deviation: use our container definition + local parentNode = container + exports.insertBefore(parentNode, child, beforeChild) + -- if container.nodeType == COMMENT_NODE) + -- (container.parentNode: any).insertBefore(child, beforeChild) + -- } else { + -- container.insertBefore(child, beforeChild) + -- end +end -- function createEvent(type: DOMEventName, bubbles: boolean): Event { -- local event = document.createEvent('Event') @@ -659,58 +649,56 @@ exports.removeChildFromContainer = function(_container: Container, child: Instan -- end end -exports.clearSuspenseBoundary = - function(parentInstance: Instance, suspenseInstance: SuspenseInstance) - -- ROBLOX FIXME: this is a major thing we need to fix for Suspense to work as a feature - unimplemented("clearSuspenseBoundary") - -- local node = suspenseInstance - -- -- Delete all nodes within this suspense boundary. - -- -- There might be nested nodes so we need to keep track of how - -- -- deep we are and only break out when we're back on top. - -- local depth = 0 - -- do { - -- local nextNode = node.nextSibling - -- parentInstance.removeChild(node) - -- if nextNode and nextNode.nodeType == COMMENT_NODE) - -- local data = ((nextNode: any).data: string) - -- if data == SUSPENSE_END_DATA) - -- if depth == 0) - -- parentInstance.removeChild(nextNode) - -- -- Retry if any event replaying was blocked on this. - -- retryIfBlockedOn(suspenseInstance) - -- return - -- } else { - -- depth-- - -- end - -- } else if - -- data == SUSPENSE_START_DATA or - -- data == SUSPENSE_PENDING_START_DATA or - -- data == SUSPENSE_FALLBACK_START_DATA - -- ) - -- depth++ - -- end - -- end - -- node = nextNode - -- } while (node) - -- -- TODO: Warn, we didn't find the end comment boundary. - -- -- Retry if any event replaying was blocked on this. - -- retryIfBlockedOn(suspenseInstance) - end +exports.clearSuspenseBoundary = function(parentInstance: Instance, suspenseInstance: SuspenseInstance) + -- ROBLOX FIXME: this is a major thing we need to fix for Suspense to work as a feature + unimplemented("clearSuspenseBoundary") + -- local node = suspenseInstance + -- -- Delete all nodes within this suspense boundary. + -- -- There might be nested nodes so we need to keep track of how + -- -- deep we are and only break out when we're back on top. + -- local depth = 0 + -- do { + -- local nextNode = node.nextSibling + -- parentInstance.removeChild(node) + -- if nextNode and nextNode.nodeType == COMMENT_NODE) + -- local data = ((nextNode: any).data: string) + -- if data == SUSPENSE_END_DATA) + -- if depth == 0) + -- parentInstance.removeChild(nextNode) + -- -- Retry if any event replaying was blocked on this. + -- retryIfBlockedOn(suspenseInstance) + -- return + -- } else { + -- depth-- + -- end + -- } else if + -- data == SUSPENSE_START_DATA or + -- data == SUSPENSE_PENDING_START_DATA or + -- data == SUSPENSE_FALLBACK_START_DATA + -- ) + -- depth++ + -- end + -- end + -- node = nextNode + -- } while (node) + -- -- TODO: Warn, we didn't find the end comment boundary. + -- -- Retry if any event replaying was blocked on this. + -- retryIfBlockedOn(suspenseInstance) +end -exports.clearSuspenseBoundaryFromContainer = - function(container: Container, suspenseInstance: SuspenseInstance) - -- ROBLOX FIXME: this is a major thing we need to fix for Suspense to work as a feature - unimplemented("clearSuspenseBoundaryFromContainer") - -- if container.nodeType == COMMENT_NODE) - -- clearSuspenseBoundary((container.parentNode: any), suspenseInstance) - -- } else if container.nodeType == ELEMENT_NODE) - -- clearSuspenseBoundary((container: any), suspenseInstance) - -- } else { - -- -- Document nodes should never contain suspense boundaries. - -- end - -- -- Retry if any event replaying was blocked on this. - -- retryIfBlockedOn(container) - end +exports.clearSuspenseBoundaryFromContainer = function(container: Container, suspenseInstance: SuspenseInstance) + -- ROBLOX FIXME: this is a major thing we need to fix for Suspense to work as a feature + unimplemented("clearSuspenseBoundaryFromContainer") + -- if container.nodeType == COMMENT_NODE) + -- clearSuspenseBoundary((container.parentNode: any), suspenseInstance) + -- } else if container.nodeType == ELEMENT_NODE) + -- clearSuspenseBoundary((container: any), suspenseInstance) + -- } else { + -- -- Document nodes should never contain suspense boundaries. + -- end + -- -- Retry if any event replaying was blocked on this. + -- retryIfBlockedOn(container) +end exports.hideInstance = function(instance: Instance) unimplemented("hideInstance") diff --git a/modules/react-roblox/src/client/ReactRobloxRoot.lua b/modules/react-roblox/src/client/ReactRobloxRoot.lua index 43253385..2a403679 100644 --- a/modules/react-roblox/src/client/ReactRobloxRoot.lua +++ b/modules/react-roblox/src/client/ReactRobloxRoot.lua @@ -44,8 +44,7 @@ local updateContainer = ReactFiberReconciler.updateContainer -- local findHostInstanceWithNoPortals = ReactFiberReconciler.findHostInstanceWithNoPortals -- local registerMutableSourceForHydration = ReactFiberReconciler.registerMutableSourceForHydration local invariant = require("@pkg/@jsdotlua/shared").invariant -local enableEagerRootListeners = - require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableEagerRootListeners +local enableEagerRootListeners = require("@pkg/@jsdotlua/shared").ReactFeatureFlags.enableEagerRootListeners local BlockingRoot = ReactFiberReconciler.ReactRootTags.BlockingRoot local ConcurrentRoot = ReactFiberReconciler.ReactRootTags.ConcurrentRoot @@ -63,11 +62,7 @@ function ReactRobloxRoot.new(container: Container, options: RootOptions?): RootT return root end -local function createBlockingRoot( - container: Container, - tag: RootTag, - options: RootOptions? -): RootType +local function createBlockingRoot(container: Container, tag: RootTag, options: RootOptions?): RootType -- deviation: We can just share the logic here via metatables local root: RootType = (setmetatable({}, ReactRobloxRoot) :: any) :: RootType root._internalRoot = createRootImpl(container, tag, options) @@ -200,16 +195,15 @@ exports.createRoot = function(container: Container, options: RootOptions?): Root return ReactRobloxRoot.new(container, options) end -exports.createBlockingRoot = - function(container: Container, options: RootOptions?): RootType - invariant( - isValidContainer(container), - -- ROBLOX deviation: Use roblox engine terminology - "createRoot(...): Target container is not a Roblox Instance." - ) - warnIfReactDOMContainerInDEV(container) - return createBlockingRoot(container, BlockingRoot, options) - end +exports.createBlockingRoot = function(container: Container, options: RootOptions?): RootType + invariant( + isValidContainer(container), + -- ROBLOX deviation: Use roblox engine terminology + "createRoot(...): Target container is not a Roblox Instance." + ) + warnIfReactDOMContainerInDEV(container) + return createBlockingRoot(container, BlockingRoot, options) +end exports.createLegacyRoot = function(container: Container, options: RootOptions?): RootType return createBlockingRoot(container, LegacyRoot, options) diff --git a/modules/react-roblox/src/client/__tests__/ReactRobloxComponentTree.roblox.spec.lua b/modules/react-roblox/src/client/__tests__/ReactRobloxComponentTree.roblox.spec.lua index 7b6fca42..de00504f 100644 --- a/modules/react-roblox/src/client/__tests__/ReactRobloxComponentTree.roblox.spec.lua +++ b/modules/react-roblox/src/client/__tests__/ReactRobloxComponentTree.roblox.spec.lua @@ -39,17 +39,12 @@ end) it("getClosestInstanceFromNode should return a cached instance", function() reactRobloxRoot:render( - React.createElement( - "Frame", - {}, - { Label = React.createElement("TextLabel", { Text = "Hello" }) } - ) + React.createElement("Frame", {}, { Label = React.createElement("TextLabel", { Text = "Hello" }) }) ) Scheduler.unstable_flushAllWithoutAsserting() - local labelNode = - ReactRobloxComponentTree.getClosestInstanceFromNode(parent.Frame.Label) + local labelNode = ReactRobloxComponentTree.getClosestInstanceFromNode(parent.Frame.Label) jestExpect(labelNode.memoizedProps.Text).toEqual("Hello") end) @@ -77,8 +72,6 @@ it("getClosestInstanceFromNode should return portaled instances", function() Scheduler.unstable_flushAllWithoutAsserting() - local portal3Label = ReactRobloxComponentTree.getClosestInstanceFromNode( - portalContainer3:GetChildren()[1] - ) + local portal3Label = ReactRobloxComponentTree.getClosestInstanceFromNode(portalContainer3:GetChildren()[1]) jestExpect(portal3Label.memoizedProps.Text).toEqual("portal3[0]") end) diff --git a/modules/react-roblox/src/client/__tests__/ReactRobloxFiber.spec.lua b/modules/react-roblox/src/client/__tests__/ReactRobloxFiber.spec.lua index e69cf984..950818de 100644 --- a/modules/react-roblox/src/client/__tests__/ReactRobloxFiber.spec.lua +++ b/modules/react-roblox/src/client/__tests__/ReactRobloxFiber.spec.lua @@ -257,10 +257,7 @@ it("should render one portal", function() React.createElement( "Frame", {}, - ReactRoblox.createPortal( - React.createElement("TextLabel", { Text = "portal" }), - portalContainer - ) + ReactRoblox.createPortal(React.createElement("TextLabel", { Text = "portal" }), portalContainer) ) ) Scheduler.unstable_flushAllWithoutAsserting() @@ -523,10 +520,7 @@ it("should reconcile portal children", function() local portalContainer = Instance.new("Frame") reactRobloxRoot:render(React.createElement("Frame", {}, { - ReactRoblox.createPortal( - React.createElement("TextLabel", { Text = "portal:1" }), - portalContainer - ), + ReactRoblox.createPortal(React.createElement("TextLabel", { Text = "portal:1" }), portalContainer), })) Scheduler.unstable_flushAllWithoutAsserting() @@ -536,10 +530,7 @@ it("should reconcile portal children", function() jestExpect(#parent:GetChildren()[1]:GetChildren()).toBe(0) reactRobloxRoot:render(React.createElement("Frame", {}, { - ReactRoblox.createPortal( - React.createElement("TextLabel", { Text = "portal:2" }), - portalContainer - ), + ReactRoblox.createPortal(React.createElement("TextLabel", { Text = "portal:2" }), portalContainer), })) Scheduler.unstable_flushAllWithoutAsserting() @@ -549,10 +540,7 @@ it("should reconcile portal children", function() jestExpect(#parent:GetChildren()[1]:GetChildren()).toBe(0) reactRobloxRoot:render(React.createElement("Frame", {}, { - ReactRoblox.createPortal( - React.createElement("TextLabel", { Text = "portal:3" }), - portalContainer - ), + ReactRoblox.createPortal(React.createElement("TextLabel", { Text = "portal:3" }), portalContainer), })) Scheduler.unstable_flushAllWithoutAsserting() @@ -599,9 +587,7 @@ it("should reconcile portal children", function() jestExpect(#parent:GetChildren()).toBe(1) jestExpect(#parent:GetChildren()[1]:GetChildren()).toBe(0) - reactRobloxRoot:render( - React.createElement("Frame", {}, ReactRoblox.createPortal(nil, portalContainer)) - ) + reactRobloxRoot:render(React.createElement("Frame", {}, ReactRoblox.createPortal(nil, portalContainer))) Scheduler.unstable_flushAllWithoutAsserting() jestExpect(#portalContainer:GetChildren()).toBe(0) diff --git a/modules/react-roblox/src/client/roblox/SingleEventManager.lua b/modules/react-roblox/src/client/roblox/SingleEventManager.lua index 9c2915cb..5b40c025 100644 --- a/modules/react-roblox/src/client/roblox/SingleEventManager.lua +++ b/modules/react-roblox/src/client/roblox/SingleEventManager.lua @@ -77,18 +77,10 @@ function SingleEventManager:connectEvent(key, listener) end function SingleEventManager:connectPropertyChange(key, listener) - local success, event = - pcall(self._instance.GetPropertyChangedSignal, self._instance, key) + local success, event = pcall(self._instance.GetPropertyChangedSignal, self._instance, key) if not success then - error( - string.format( - "Cannot get changed signal on property %q: %s", - tostring(key), - event - ), - 0 - ) + error(string.format("Cannot get changed signal on property %q: %s", tostring(key), event), 0) end self:_connect(CHANGE_PREFIX .. key, event, listener) @@ -113,10 +105,7 @@ function SingleEventManager:_connect(eventKey, event, listener) -- called. local argumentCount = select("#", ...) - table.insert( - self._suspendedEventQueue, - { eventKey, argumentCount, ... } - ) + table.insert(self._suspendedEventQueue, { eventKey, argumentCount, ... }) end end) end @@ -150,11 +139,8 @@ function SingleEventManager:resume() -- Wrap the listener in a coroutine to catch errors and handle -- yielding correctly. local listenerCo = coroutine.create(listener) - local success, result = coroutine.resume( - listenerCo, - self._instance, - unpack(eventInvocation, 3, 2 + argumentCount) - ) + local success, result = + coroutine.resume(listenerCo, self._instance, unpack(eventInvocation, 3, 2 + argumentCount)) -- If the listener threw an error, we log it as a warning, since -- there's no way to write error text in Roblox Lua without killing diff --git a/modules/react-roblox/src/client/roblox/__tests__/SingleEventManager.spec.lua b/modules/react-roblox/src/client/roblox/__tests__/SingleEventManager.spec.lua index 58844b73..61206cc1 100644 --- a/modules/react-roblox/src/client/roblox/__tests__/SingleEventManager.spec.lua +++ b/modules/react-roblox/src/client/roblox/__tests__/SingleEventManager.spec.lua @@ -149,27 +149,24 @@ describe("connectEvent", function() jestExpect(recordedValues).toEqual({ 1, 2, 3, 4 }) end) - it( - "should not invoke events fired during suspension but disconnected before resumption", - function() - local instance = Instance.new("BindableEvent") - local manager = SingleEventManager.new(instance) - local eventSpy = jest.fn() - - manager:connectEvent("Event", function(...) - eventSpy(...) - end) - manager:suspend() - - instance:Fire(1) - waitForEvents() + it("should not invoke events fired during suspension but disconnected before resumption", function() + local instance = Instance.new("BindableEvent") + local manager = SingleEventManager.new(instance) + local eventSpy = jest.fn() - manager:connectEvent("Event") + manager:connectEvent("Event", function(...) + eventSpy(...) + end) + manager:suspend() - manager:resume() - jestExpect(eventSpy).never.toBeCalled() - end - ) + instance:Fire(1) + waitForEvents() + + manager:connectEvent("Event") + + manager:resume() + jestExpect(eventSpy).never.toBeCalled() + end) it("should not yield events through the SingleEventManager when resuming", function() local instance = Instance.new("BindableEvent") @@ -233,37 +230,34 @@ describe("connectEvent", function() -- jestExpect(logInfo.warnings[1]:find(errorText)).to.be.ok() end) - it( - "should not overflow with events if manager:resume() is invoked when resuming a suspended event", - function() - local instance = Instance.new("BindableEvent") - local manager = SingleEventManager.new(instance) - - -- This connection emulates what happens if reconciliation is - -- triggered again in response to reconciliation. Without - -- appropriate guards, the inner resume() call will process the - -- Fire(1) event again, causing a nasty stack overflow. - local eventSpy = jest.fn(function(_, value) - if value == 1 then - manager:suspend() - instance:Fire(2) - manager:resume() - end - end) - - manager:connectEvent("Event", function(...) - eventSpy(...) - end) - - manager:suspend() - instance:Fire(1) - manager:resume() - waitForEvents() - waitForEvents() + it("should not overflow with events if manager:resume() is invoked when resuming a suspended event", function() + local instance = Instance.new("BindableEvent") + local manager = SingleEventManager.new(instance) + + -- This connection emulates what happens if reconciliation is + -- triggered again in response to reconciliation. Without + -- appropriate guards, the inner resume() call will process the + -- Fire(1) event again, causing a nasty stack overflow. + local eventSpy = jest.fn(function(_, value) + if value == 1 then + manager:suspend() + instance:Fire(2) + manager:resume() + end + end) - jestExpect(eventSpy).toBeCalledTimes(2) - end - ) + manager:connectEvent("Event", function(...) + eventSpy(...) + end) + + manager:suspend() + instance:Fire(1) + manager:resume() + waitForEvents() + waitForEvents() + + jestExpect(eventSpy).toBeCalledTimes(2) + end) end) describe("connectPropertyChange", function() diff --git a/modules/react-test-renderer/src/ReactTestHostConfig.lua b/modules/react-test-renderer/src/ReactTestHostConfig.lua index 00d80736..68c789eb 100644 --- a/modules/react-test-renderer/src/ReactTestHostConfig.lua +++ b/modules/react-test-renderer/src/ReactTestHostConfig.lua @@ -21,10 +21,7 @@ local clearTimeout = LuauPolyfill.clearTimeout local console = require("@pkg/@jsdotlua/shared").console local ReactTypes = require("@pkg/@jsdotlua/shared") -type ReactFundamentalComponentInstance = ReactTypes.ReactFundamentalComponentInstance< - T, - U -> +type ReactFundamentalComponentInstance = ReactTypes.ReactFundamentalComponentInstance local ReactSymbols = require("@pkg/@jsdotlua/shared").ReactSymbols local REACT_OPAQUE_ID_TYPE = ReactSymbols.REACT_OPAQUE_ID_TYPE @@ -110,24 +107,23 @@ exports.getPublicInstance = function(inst: Instance | TextInstance) end end -exports.appendChild = - function(parentInstance: Instance | Container, child: Instance | TextInstance) - if _G.__DEV__ then - if not Array.isArray(parentInstance.children) then - console.error( - "An invalid container has been provided. " - .. "This may indicate that another renderer is being used in addition to the test renderer. " - .. "(For example, ReactNoop.createPortal inside of a ReactTestRenderer tree.) " - .. "This is not supported." - ) - end +exports.appendChild = function(parentInstance: Instance | Container, child: Instance | TextInstance) + if _G.__DEV__ then + if not Array.isArray(parentInstance.children) then + console.error( + "An invalid container has been provided. " + .. "This may indicate that another renderer is being used in addition to the test renderer. " + .. "(For example, ReactNoop.createPortal inside of a ReactTestRenderer tree.) " + .. "This is not supported." + ) end - local index = Array.indexOf(parentInstance.children, child) - if index ~= -1 then - Array.splice(parentInstance.children, index, 1) - end - table.insert(parentInstance.children, child) end + local index = Array.indexOf(parentInstance.children, child) + if index ~= -1 then + Array.splice(parentInstance.children, index, 1) + end + table.insert(parentInstance.children, child) +end exports.insertBefore = function( parentInstance: Instance | Container, @@ -142,12 +138,11 @@ exports.insertBefore = function( Array.splice(parentInstance.children, beforeIndex, 0, child) end -exports.removeChild = - function(parentInstance: Instance | Container, child: Instance | TextInstance) - RobloxComponentProps.removeTags(child) - local index = Array.indexOf(parentInstance.children, child) - Array.splice(parentInstance.children, index, 1) - end +exports.removeChild = function(parentInstance: Instance | Container, child: Instance | TextInstance) + RobloxComponentProps.removeTags(child) + local index = Array.indexOf(parentInstance.children, child) + Array.splice(parentInstance.children, index, 1) +end exports.clearContainer = function(container: Container) Array.splice(container.children, 0) @@ -192,14 +187,13 @@ exports.createInstance = function( } end -exports.appendInitialChild = - function(parentInstance: Instance, child: Instance | TextInstance) - local index = Array.indexOf(parentInstance.children, child) - if index ~= -1 then - Array.splice(parentInstance.children, index, 1) - end - table.insert(parentInstance.children, child) +exports.appendInitialChild = function(parentInstance: Instance, child: Instance | TextInstance) + local index = Array.indexOf(parentInstance.children, child) + if index ~= -1 then + Array.splice(parentInstance.children, index, 1) end + table.insert(parentInstance.children, child) +end exports.finalizeInitialChildren = function( testElement: Instance, @@ -266,19 +260,13 @@ exports.commitUpdate = function( RobloxComponentProps.updateTags(instance, newProps, oldProps) end -exports.commitMount = function( - instance: Instance, - type: string, - newProps: Props, - internalInstanceHandle: Object -) +exports.commitMount = function(instance: Instance, type: string, newProps: Props, internalInstanceHandle: Object) -- noop end -exports.commitTextUpdate = - function(textInstance: TextInstance, oldText: string, newText: string) - textInstance.text = newText - end +exports.commitTextUpdate = function(textInstance: TextInstance, oldText: string, newText: string) + textInstance.text = newText +end exports.resetTextContent = function(testElement: Instance) -- noop @@ -304,63 +292,62 @@ exports.unhideTextInstance = function(textInstance: TextInstance, text: string) textInstance.isHidden = false end -exports.getFundamentalComponentInstance = - function(fundamentalInstance: ReactFundamentalComponentInstance): Instance - local impl = fundamentalInstance.impl - local props = fundamentalInstance.props - local state = fundamentalInstance.state - return impl.getInstance(nil, props, state) - end +exports.getFundamentalComponentInstance = function( + fundamentalInstance: ReactFundamentalComponentInstance +): Instance + local impl = fundamentalInstance.impl + local props = fundamentalInstance.props + local state = fundamentalInstance.state + return impl.getInstance(nil, props, state) +end -exports.mountFundamentalComponent = - function(fundamentalInstance: ReactFundamentalComponentInstance) - local impl = fundamentalInstance.impl - local instance = fundamentalInstance.instance - local props = fundamentalInstance.props - local state = fundamentalInstance.state - local onMount = impl.onMount - if onMount ~= nil then - onMount(nil, instance, props, state) - end +exports.mountFundamentalComponent = function(fundamentalInstance: ReactFundamentalComponentInstance) + local impl = fundamentalInstance.impl + local instance = fundamentalInstance.instance + local props = fundamentalInstance.props + local state = fundamentalInstance.state + local onMount = impl.onMount + if onMount ~= nil then + onMount(nil, instance, props, state) end +end -exports.shouldUpdateFundamentalComponent = - function(fundamentalInstance: ReactFundamentalComponentInstance): boolean - local impl = fundamentalInstance.impl - local prevProps = fundamentalInstance.prevProps - local props = fundamentalInstance.props - local state = fundamentalInstance.state - local shouldUpdate = impl.shouldUpdate - if shouldUpdate ~= nil then - return shouldUpdate(nil, prevProps, props, state) - end - return true +exports.shouldUpdateFundamentalComponent = function( + fundamentalInstance: ReactFundamentalComponentInstance +): boolean + local impl = fundamentalInstance.impl + local prevProps = fundamentalInstance.prevProps + local props = fundamentalInstance.props + local state = fundamentalInstance.state + local shouldUpdate = impl.shouldUpdate + if shouldUpdate ~= nil then + return shouldUpdate(nil, prevProps, props, state) end + return true +end -exports.updateFundamentalComponent = - function(fundamentalInstance: ReactFundamentalComponentInstance) - local impl = fundamentalInstance.impl - local instance = fundamentalInstance.instance - local prevProps = fundamentalInstance.prevProps - local props = fundamentalInstance.props - local state = fundamentalInstance.state - local onUpdate = impl.onUpdate - if onUpdate ~= nil then - onUpdate(nil, instance, prevProps, props, state) - end +exports.updateFundamentalComponent = function(fundamentalInstance: ReactFundamentalComponentInstance) + local impl = fundamentalInstance.impl + local instance = fundamentalInstance.instance + local prevProps = fundamentalInstance.prevProps + local props = fundamentalInstance.props + local state = fundamentalInstance.state + local onUpdate = impl.onUpdate + if onUpdate ~= nil then + onUpdate(nil, instance, prevProps, props, state) end +end -exports.unmountFundamentalComponent = - function(fundamentalInstance: ReactFundamentalComponentInstance) - local impl = fundamentalInstance.impl - local instance = fundamentalInstance.instance - local props = fundamentalInstance.props - local state = fundamentalInstance.state - local onUnmount = impl.onUnmount - if onUnmount ~= nil then - onUnmount(nil, instance, props, state) - end +exports.unmountFundamentalComponent = function(fundamentalInstance: ReactFundamentalComponentInstance) + local impl = fundamentalInstance.impl + local instance = fundamentalInstance.instance + local props = fundamentalInstance.props + local state = fundamentalInstance.state + local onUnmount = impl.onUnmount + if onUnmount ~= nil then + onUnmount(nil, instance, props, state) end +end exports.getInstanceFromNode = function(mockNode: Object): Object? local instance = nodeToInstanceMap[mockNode] diff --git a/modules/react-test-renderer/src/ReactTestRenderer.lua b/modules/react-test-renderer/src/ReactTestRenderer.lua index 0564c2fc..4631b0df 100644 --- a/modules/react-test-renderer/src/ReactTestRenderer.lua +++ b/modules/react-test-renderer/src/ReactTestRenderer.lua @@ -293,11 +293,7 @@ toTree = function(nodeInput: Fiber | nil) then return childrenToTree(node.child) else - invariant( - false, - "toTree() does not yet know how to handle nodes with tag=" - .. tostring(node.tag) - ) + invariant(false, "toTree() does not yet know how to handle nodes with tag=" .. tostring(node.tag)) end return end @@ -380,11 +376,7 @@ local function getChildren(parent) return children end -local function findAll( - root: Object, - predicate: Predicate, - options: FindOptions? -): Array +local function findAll(root: Object, predicate: Predicate, options: FindOptions?): Array -- ROBLOX deviation: ternary split to conditional statement local deep = true if options then @@ -487,8 +479,7 @@ end function ReactTestInstance.new(fiber: Fiber) invariant( validWrapperTypes[fiber.tag] ~= nil, - "Unexpected object passed to ReactTestInstance constructor (tag: %s). " - .. "This is probably a bug in React.", + "Unexpected object passed to ReactTestInstance constructor (tag: %s). " .. "This is probably a bug in React.", fiber.tag ) local testInstance = {} @@ -521,10 +512,7 @@ function ReactTestInstance:findByProps(props: Object): Object string.format("with props: %s", JSON:JSONEncode(props)) ) end -function ReactTestInstance:findAll( - predicate: Predicate, - options: FindOptions? -): Array +function ReactTestInstance:findAll(predicate: Predicate, options: FindOptions?): Array return findAll(self, predicate, options) end function ReactTestInstance:findAllByType(type_: any, options: FindOptions?): Array @@ -532,20 +520,14 @@ function ReactTestInstance:findAllByType(type_: any, options: FindOptions?): Arr return node.type == type_ end, options) end -function ReactTestInstance:findAllByProps( - props: Object, - options: FindOptions? -): Array +function ReactTestInstance:findAllByProps(props: Object, options: FindOptions?): Array return findAll(self, function(node) return node.props and propsMatch(node.props, props) end, options) end -- ROBLOX deviation START: the first argument gets an explicit nil in many tests -local function create( - element: ReactElement | nil, - options: TestRendererOptions? -) +local function create(element: ReactElement | nil, options: TestRendererOptions?) -- ROBLOX deviation END local createNodeMock = defaultTestOptions.createNodeMock local isConcurrent = false @@ -714,10 +696,7 @@ local function unstable_concurrentAct(scope: () -> () | Thenable) error("This version of `act` requires a special mock build of Scheduler.") end if typeof(setTimeout) == "table" and setTimeout._isMockFunction ~= true then - error( - "This version of `act` requires Jest's timer mocks " - .. "(i.e. jest.useFakeTimers)." - ) + error("This version of `act` requires Jest's timer mocks " .. "(i.e. jest.useFakeTimers).") end local previousActingUpdatesScopeDepth = actingUpdatesScopeDepth @@ -748,11 +727,7 @@ local function unstable_concurrentAct(scope: () -> () | Thenable) -- our test suite, we should be able to. local ok, error_ = pcall(function() local thenable = batchedUpdates(scope) - if - typeof(thenable) == "table" - and thenable ~= nil - and typeof(thenable.andThen) == "function" - then + if typeof(thenable) == "table" and thenable ~= nil and typeof(thenable.andThen) == "function" then return function(resolve, reject) thenable:andThen(function() flushActWork(function() diff --git a/modules/react/src/React.lua b/modules/react/src/React.lua index d75f30b2..b1bfb4fd 100644 --- a/modules/react/src/React.lua +++ b/modules/react/src/React.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/56e9feead0f91075ba0a4f725c9e4e343bca1c67/packages/react/src/React.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/React.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react/src/ReactBaseClasses.lua b/modules/react/src/ReactBaseClasses.lua index 445e46c1..a5df1fe4 100644 --- a/modules/react/src/ReactBaseClasses.lua +++ b/modules/react/src/ReactBaseClasses.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/0cf22a56a18790ef34c71bef14f64695c0498619/packages/react/src/ReactBaseClasses.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactBaseClasses.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react/src/ReactChildren.lua b/modules/react/src/ReactChildren.lua index 07d39e86..fa5e84b1 100644 --- a/modules/react/src/ReactChildren.lua +++ b/modules/react/src/ReactChildren.lua @@ -108,10 +108,7 @@ local function mapIntoArray( invokeCallback = true elseif type == "table" then local childrenType = (children :: any)["$$typeof"] - if - childrenType == REACT_ELEMENT_TYPE - or childrenType == REACT_PORTAL_TYPE - then + if childrenType == REACT_ELEMENT_TYPE or childrenType == REACT_PORTAL_TYPE then invokeCallback = true end end @@ -122,9 +119,7 @@ local function mapIntoArray( local mappedChild = callback(child) -- If it's the only child, treat the name as if it was wrapped in an array -- so that it's consistent if the number of children grows: - local childKey = if nameSoFar == "" - then SEPARATOR .. getElementKey(child, 1) - else nameSoFar + local childKey = if nameSoFar == "" then SEPARATOR .. getElementKey(child, 1) else nameSoFar if Array.isArray(mappedChild) then local escapedChildKey = "" if childKey ~= nil then @@ -144,11 +139,7 @@ local function mapIntoArray( -- $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key .. ( if mappedChildKey - and ( - not child - or (child :: ReactElement).key - ~= mappedChildKey - ) + and (not child or (child :: ReactElement).key ~= mappedChildKey) -- $FlowFixMe Flow incorrectly thinks existing element's key can be a number then escapeUserProvidedKey(tostring(mappedChildKey)) .. "/" else "" @@ -164,9 +155,7 @@ local function mapIntoArray( local child local nextName local subtreeCount = 0 -- Count of children found in the current subtree. - local nextNamePrefix = if nameSoFar == "" - then SEPARATOR - else nameSoFar .. SUBSEPARATOR + local nextNamePrefix = if nameSoFar == "" then SEPARATOR else nameSoFar .. SUBSEPARATOR if Array.isArray(children) then -- ROBLOX FIXME: Luau doesn't recognize this as non-nil without the `or {}` @@ -204,13 +193,7 @@ local function mapIntoArray( child = step.value nextName = nextNamePrefix .. getElementKey(child, ii) ii += 1 - subtreeCount += mapIntoArray( - child, - array, - escapedPrefix, - nextName, - callback - ) + subtreeCount += mapIntoArray(child, array, escapedPrefix, nextName, callback) step = iterator.next() end --[[ ROBLOX DEVIATION: this condition will never be met with Roact iterator logic. @@ -248,11 +231,7 @@ type MapFunc = (child: React_Node?, index: number) -> ReactNodeList? * @param {*} context Context for mapFunction. * @return {object} Object containing the ordered map of results. ]] -local function mapChildren( - children: ReactNodeList?, - func: MapFunc, - context: any -): Array? +local function mapChildren(children: ReactNodeList?, func: MapFunc, context: any): Array? if children == nil then return nil end @@ -300,11 +279,7 @@ type ForEachFunc = (child: React_Node?, index: number) -> () -- * @param {function(*, int)} forEachFunc -- * @param {*} forEachContext Context for forEachContext. -- ]] -local function forEachChildren( - children: ReactNodeList?, - forEachFunc: ForEachFunc, - forEachContext: any -) +local function forEachChildren(children: ReactNodeList?, forEachFunc: ForEachFunc, forEachContext: any) mapChildren(children, function(...) -- ROBLOX DEVIATION: Don't use javascript apply forEachFunc(...) @@ -342,10 +317,7 @@ end -- ROBLOX deviation START: we skip generics here, because we can't explicitly constrain them. no annotation works as passthrough. local function onlyChild(children) -- ROBLOX deviation END - invariant( - isValidElement(children), - "React.Children.only expected to receive a single React element child." - ) + invariant(isValidElement(children), "React.Children.only expected to receive a single React element child.") return children end diff --git a/modules/react/src/ReactContext.lua b/modules/react/src/ReactContext.lua index ad65a6a4..42a116c1 100644 --- a/modules/react/src/ReactContext.lua +++ b/modules/react/src/ReactContext.lua @@ -20,10 +20,7 @@ type ReactProviderType = Shared.ReactProviderType local exports = {} -exports.createContext = function( - defaultValue: T, - calculateChangedBits: ((a: T, b: T) -> number)? -): ReactContext +exports.createContext = function(defaultValue: T, calculateChangedBits: ((a: T, b: T) -> number)?): ReactContext local context: ReactContext = { ["$$typeof"] = REACT_CONTEXT_TYPE, _calculateChangedBits = calculateChangedBits, diff --git a/modules/react/src/ReactCreateRef.lua b/modules/react/src/ReactCreateRef.lua index 2fac640e..897b64ff 100644 --- a/modules/react/src/ReactCreateRef.lua +++ b/modules/react/src/ReactCreateRef.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react/src/ReactCreateRef.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactCreateRef.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/react/src/ReactElement.lua b/modules/react/src/ReactElement.lua index ae082e37..7792a42f 100644 --- a/modules/react/src/ReactElement.lua +++ b/modules/react/src/ReactElement.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/702fad4b1b48ac8f626ed3f35e8f86f5ea728084/packages/react/src/ReactElement.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactElement.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -349,7 +349,7 @@ exports.jsxDEV = function(type, config, maybeKey, source, self) -- -- if hasValidKey(config)) -- key = '' .. config.key - -- end + end -- -- if hasValidRef(config)) -- ref = config.ref @@ -395,7 +395,6 @@ exports.jsxDEV = function(type, config, maybeKey, source, self) return nil -- ROBLOX deviation END end - --[[* * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement diff --git a/modules/react/src/ReactElementValidator.lua b/modules/react/src/ReactElementValidator.lua index 31a3050b..05d57c45 100644 --- a/modules/react/src/ReactElementValidator.lua +++ b/modules/react/src/ReactElementValidator.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/bc6b7b6b16f771bfc8048fe15e211ac777253b64/packages/react/src/ReactElementValidator.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactElementValidator.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -397,7 +397,7 @@ local function jsxWithValidation( typeString = "nil" elseif Array.isArray(type) then typeString = "array" - elseif typeof(type) == "table" and type["$$typeof"] == REACT_ELEMENT_TYPE then + elseif typeof(type) == "table" and type["$typeof"] == REACT_ELEMENT_TYPE then typeString = string.format("<%s />", getComponentName(type.type) or "Unknown") info ..= " Did you accidentally export a JSX literal or Element instead of a component?" else @@ -530,7 +530,7 @@ local function createElementWithValidation( elseif type_ ~= nil and typeof(type_) == "table" - and type_["$$typeof"] == REACT_ELEMENT_TYPE + and type_["$typeof"] == REACT_ELEMENT_TYPE then typeString = string.format( "<%s />", diff --git a/modules/react/src/ReactForwardRef.lua b/modules/react/src/ReactForwardRef.lua index b8ec9562..d1e450d5 100644 --- a/modules/react/src/ReactForwardRef.lua +++ b/modules/react/src/ReactForwardRef.lua @@ -14,94 +14,84 @@ local ReactSymbols = require("@pkg/@jsdotlua/shared").ReactSymbols local ReactTypes = require("@pkg/@jsdotlua/shared") type React_Node = ReactTypes.React_Node type React_Ref = ReactTypes.React_Ref -type React_AbstractComponent = ReactTypes.React_AbstractComponent< - Config, - Instance -> +type React_AbstractComponent = ReactTypes.React_AbstractComponent local REACT_FORWARD_REF_TYPE = ReactSymbols.REACT_FORWARD_REF_TYPE local REACT_MEMO_TYPE = ReactSymbols.REACT_MEMO_TYPE local exports = {} -- ROBLOX TODO? should return Component's ELementType be REACT_FORWARD_REF_TYPE? probably, right? -exports.forwardRef = - function( - render: (props: Props, ref: React_Ref) -> React_Node - ): React_AbstractComponent - if _G.__DEV__ then - -- ROBLOX deviation START: Lua functions can't have properties given a table (which we can index to see if it's the Memo type) - if - typeof(render :: any) == "table" - and (render :: any)["$$typeof"] == REACT_MEMO_TYPE - then - -- ROBLOX deviation END +exports.forwardRef = function( + render: (props: Props, ref: React_Ref) -> React_Node +): React_AbstractComponent + if _G.__DEV__ then + -- ROBLOX deviation START: Lua functions can't have properties given a table (which we can index to see if it's the Memo type) + if typeof(render :: any) == "table" and (render :: any)["$$typeof"] == REACT_MEMO_TYPE then + -- ROBLOX deviation END + console.error( + "forwardRef requires a render function but received a `memo` " + .. "component. Instead of forwardRef(memo(...)), use " + .. "memo(forwardRef(...))." + ) + elseif typeof(render) ~= "function" then + console.error("forwardRef requires a render function but was given %s.", typeof(render)) + else + local argumentCount, _variadic = debug.info(render, "a") + if argumentCount ~= 0 and argumentCount ~= 2 then console.error( - "forwardRef requires a render function but received a `memo` " - .. "component. Instead of forwardRef(memo(...)), use " - .. "memo(forwardRef(...))." + "forwardRef render functions accept exactly two parameters: props and ref. %s", + (function() + if argumentCount == 1 then + return "Did you forget to use the ref parameter?" + end + return "Any additional parameter will be undefined." + end)() ) - elseif typeof(render) ~= "function" then - console.error( - "forwardRef requires a render function but was given %s.", - typeof(render) - ) - else - local argumentCount, _variadic = debug.info(render, "a") - if argumentCount ~= 0 and argumentCount ~= 2 then - console.error( - "forwardRef render functions accept exactly two parameters: props and ref. %s", - (function() - if argumentCount == 1 then - return "Did you forget to use the ref parameter?" - end - return "Any additional parameter will be undefined." - end)() - ) - end end + end - -- deviation: in Luau, functions cannot have fields; for now, we don't - -- support defaultProps and propTypes on function components anyways, so - -- this check can safely be a no-op + -- deviation: in Luau, functions cannot have fields; for now, we don't + -- support defaultProps and propTypes on function components anyways, so + -- this check can safely be a no-op - -- if render ~= null then - -- if (render.defaultProps != null || render.propTypes != null) { - -- console.error( - -- 'forwardRef render functions do not support propTypes or defaultProps. ' + - -- 'Did you accidentally pass a React component?', - -- ); - -- } - -- } - end + -- if render ~= null then + -- if (render.defaultProps != null || render.propTypes != null) { + -- console.error( + -- 'forwardRef render functions do not support propTypes or defaultProps. ' + + -- 'Did you accidentally pass a React component?', + -- ); + -- } + -- } + end - local elementType = { - ["$$typeof"] = REACT_FORWARD_REF_TYPE, - render = render, - } - if _G.__DEV__ then - local ownName - -- ROBLOX deviation: use metatables to approximate Object.defineProperty logic - setmetatable(elementType, { - __index = function(self, key) - if key == "displayName" then - return ownName - end - return rawget(self, key) - end, - __newindex = function(self, key, value) - if key == "displayName" then - ownName = value + local elementType = { + ["$$typeof"] = REACT_FORWARD_REF_TYPE, + render = render, + } + if _G.__DEV__ then + local ownName + -- ROBLOX deviation: use metatables to approximate Object.defineProperty logic + setmetatable(elementType, { + __index = function(self, key) + if key == "displayName" then + return ownName + end + return rawget(self, key) + end, + __newindex = function(self, key, value) + if key == "displayName" then + ownName = value -- ROBLOX deviation: render is a function and cannot have properties -- if (render.displayName == null) { -- render.displayName = name; -- } - else - rawset(self, key, value) - end - end, - }) - end - -- ROBLOX FIXME Luau: making us explicitly add nilable (optional) fields: because the former is missing fields 'forceUpdate', 'getChildContext', 'props', 'setState', and 'state - return (elementType :: any) :: React_AbstractComponent + else + rawset(self, key, value) + end + end, + }) end + -- ROBLOX FIXME Luau: making us explicitly add nilable (optional) fields: because the former is missing fields 'forceUpdate', 'getChildContext', 'props', 'setState', and 'state + return (elementType :: any) :: React_AbstractComponent +end return exports diff --git a/modules/react/src/ReactHooks.lua b/modules/react/src/ReactHooks.lua index af1f840b..33c85ae5 100644 --- a/modules/react/src/ReactHooks.lua +++ b/modules/react/src/ReactHooks.lua @@ -19,14 +19,8 @@ local ReactTypes = require("@pkg/@jsdotlua/shared") -- ROBLOX TODO: we only pull in Dispatcher here for the typecheck, remove once Luau narrowing improves type Dispatcher = ReactTypes.Dispatcher type MutableSource = ReactTypes.MutableSource -type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn< - Source, - Snapshot -> -type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn< - Source, - Snapshot -> +type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn +type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn type ReactProviderType = ReactTypes.ReactProviderType type ReactContext = ReactTypes.ReactContext local ReactFiberHostConfig = require("@pkg/@jsdotlua/shared") @@ -34,8 +28,7 @@ type OpaqueIDType = ReactFiberHostConfig.OpaqueIDType -- local invariant = require("@pkg/@jsdotlua/shared").invariant -local ReactCurrentDispatcher = - require("@pkg/@jsdotlua/shared").ReactSharedInternals.ReactCurrentDispatcher +local ReactCurrentDispatcher = require("@pkg/@jsdotlua/shared").ReactSharedInternals.ReactCurrentDispatcher type BasicStateAction = ((S) -> S) | S type Dispatch = (A) -> () @@ -105,20 +98,13 @@ local function useContext( end exports.useContext = useContext -local function useState( - initialState: (() -> S) | S, - ... -): (S, Dispatch>) +local function useState(initialState: (() -> S) | S, ...): (S, Dispatch>) local dispatcher = resolveDispatcher() return dispatcher.useState(initialState, ...) end exports.useState = useState -local function useReducer( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? -): (S, Dispatch) +local function useReducer(reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?): (S, Dispatch) local dispatcher = resolveDispatcher() return dispatcher.useReducer(reducer, initialArg, init) end @@ -134,12 +120,7 @@ end exports.useRef = useRef -- ROBLOX deviation: TS models this slightly differently, which is needed to have an initially empty ref and clear the ref, and still typecheck -local function useBinding( - initialValue: T -): ( - ReactTypes.ReactBinding, - ReactTypes.ReactBindingUpdater -) +local function useBinding(initialValue: T): (ReactTypes.ReactBinding, ReactTypes.ReactBindingUpdater) -- ROBLOX deviation END local dispatcher = resolveDispatcher() return dispatcher.useBinding(initialValue) @@ -148,7 +129,7 @@ exports.useBinding = useBinding local function useEffect( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () local dispatcher = resolveDispatcher() @@ -158,7 +139,7 @@ exports.useEffect = useEffect local function useLayoutEffect( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ): () local dispatcher = resolveDispatcher() @@ -218,14 +199,13 @@ exports.useOpaqueIdentifier = function(): OpaqueIDType | nil return dispatcher.useOpaqueIdentifier() end -exports.useMutableSource = - function( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn - ): Snapshot - local dispatcher = resolveDispatcher() - return dispatcher.useMutableSource(source, getSnapshot, subscribe) - end +exports.useMutableSource = function( + source: MutableSource, + getSnapshot: MutableSourceGetSnapshotFn, + subscribe: MutableSourceSubscribeFn +): Snapshot + local dispatcher = resolveDispatcher() + return dispatcher.useMutableSource(source, getSnapshot, subscribe) +end return exports diff --git a/modules/react/src/ReactLazy.lua b/modules/react/src/ReactLazy.lua index fae79d34..3b51f3be 100644 --- a/modules/react/src/ReactLazy.lua +++ b/modules/react/src/ReactLazy.lua @@ -55,11 +55,7 @@ type RejectedPayload = { _result: any, } -type Payload = - UninitializedPayload - | PendingPayload - | ResolvedPayload - | RejectedPayload +type Payload = UninitializedPayload | PendingPayload | ResolvedPayload | RejectedPayload export type LazyComponent = { ["$$typeof"]: number, @@ -115,9 +111,7 @@ end local exports = {} -exports.lazy = function( - ctor: () -> Thenable<{ default: T, [string]: any }> -): LazyComponent> +exports.lazy = function(ctor: () -> Thenable<{ default: T, [string]: any }>): LazyComponent> local payload: Payload = { -- We use these fields to store the result. _status = -1, diff --git a/modules/react/src/ReactMemo.lua b/modules/react/src/ReactMemo.lua index 700ddec3..8d9e9a5e 100644 --- a/modules/react/src/ReactMemo.lua +++ b/modules/react/src/ReactMemo.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/41694201988c5e651f0c3bc69921d5c9717be88b/packages/react/src/ReactMemo.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactMemo.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react/src/ReactMutableSource.lua b/modules/react/src/ReactMutableSource.lua index 5ae8f2d7..53570f7e 100644 --- a/modules/react/src/ReactMutableSource.lua +++ b/modules/react/src/ReactMutableSource.lua @@ -13,10 +13,7 @@ local ReactTypes = require("@pkg/@jsdotlua/shared") type MutableSourceGetVersionFn = ReactTypes.MutableSourceGetVersionFn type MutableSource = ReactTypes.MutableSource -local function createMutableSource( - source: Source, - getVersion: MutableSourceGetVersionFn -): MutableSource +local function createMutableSource(source: Source, getVersion: MutableSourceGetVersionFn): MutableSource local mutableSource: MutableSource = { _getVersion = getVersion, _source = source, diff --git a/modules/react/src/ReactNoopUpdateQueue.lua b/modules/react/src/ReactNoopUpdateQueue.lua index 47c25524..913ae48c 100644 --- a/modules/react/src/ReactNoopUpdateQueue.lua +++ b/modules/react/src/ReactNoopUpdateQueue.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/0cf22a56a18790ef34c71bef14f64695c0498619/packages/react/src/ReactNoopUpdateQueue.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactNoopUpdateQueue.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/react/src/__tests__/ReactChildren.spec.lua b/modules/react/src/__tests__/ReactChildren.spec.lua index 53af5dc2..84825492 100644 --- a/modules/react/src/__tests__/ReactChildren.spec.lua +++ b/modules/react/src/__tests__/ReactChildren.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/7516bdfce3f0f8c675494b5c5d0e7ae441bef1d9/packages/react/src/__tests__/ReactChildren-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/__tests__/ReactChildren-test.js --!nonstrict --[[ ** diff --git a/modules/react/src/__tests__/ReactDeprecationWarnings-internal.spec.lua b/modules/react/src/__tests__/ReactDeprecationWarnings-internal.spec.lua index a61fb64e..7e38b100 100644 --- a/modules/react/src/__tests__/ReactDeprecationWarnings-internal.spec.lua +++ b/modules/react/src/__tests__/ReactDeprecationWarnings-internal.spec.lua @@ -57,8 +57,7 @@ describe("ReactDeprecationWarnings", function() return React.createElement(RefComponent, { ref = "refComponent" }) end ReactNoop.render(React.createElement(Component)) - local expectedName = _G.__DEV__ and "Component" - or "" + local expectedName = _G.__DEV__ and "Component" or "" -- ROBLOX Test Noise: jest setup config makes this hide error -- boundary warnings in upstream (scripts/jest/setupTests.js:72) -- ROBLOX deviation: we removed string ref support ahead of upstream schedule @@ -85,10 +84,7 @@ describe("ReactDeprecationWarnings", function() end local Component = React.Component:extend("") function Component:render() - return React.createElement( - RefComponent, - { ref = "refComponent", __self = self } - ) + return React.createElement(RefComponent, { ref = "refComponent", __self = self }) end ReactNoop.renderLegacySyncRoot(React.createElement(Component)) jestExpect(Scheduler).toFlushWithoutYielding() @@ -100,16 +96,12 @@ describe("ReactDeprecationWarnings", function() end local Component = React.Component:extend("Component") function Component:render() - return React.createElement( - RefComponent, - { ref = "refComponent", __self = {} } - ) + return React.createElement(RefComponent, { ref = "refComponent", __self = {} }) end ReactNoop.render(React.createElement(Component)) -- ROBLOX deviation: we removed string ref support ahead of upstream schedule - local expectedName = _G.__DEV__ and "Component" - or "" + local expectedName = _G.__DEV__ and "Component" or "" -- ROBLOX Test Noise: jest setup config makes this hide error -- boundary warnings in upstream (scripts/jest/setupTests.js:72) jestExpect(function() diff --git a/modules/react/src/__tests__/ReactProfiler-internal.spec.lua b/modules/react/src/__tests__/ReactProfiler-internal.spec.lua index 27897333..1b1d37bd 100644 --- a/modules/react/src/__tests__/ReactProfiler-internal.spec.lua +++ b/modules/react/src/__tests__/ReactProfiler-internal.spec.lua @@ -1,9 +1,9 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react/src/__tests__/ReactProfiler-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/__tests__/ReactProfiler-test.internal.js +--[[* local React local ReactFeatureFlags local ReactNoop local Scheduler -local ReactCache local ReactTestRenderer local ReactTestRendererAct local _SchedulerTracing @@ -78,7 +78,6 @@ local function loadModules(config) if useNoopRenderer then ReactNoop = require("@pkg/@jsdotlua/react-noop-renderer") ReactTestRenderer = nil - ReactTestRendererAct = nil else ReactNoop = nil ReactTestRenderer = require("@pkg/@jsdotlua/react-test-renderer") diff --git a/modules/react/src/__tests__/ReactProfilerDevToolsIntegration-internal.spec.lua b/modules/react/src/__tests__/ReactProfilerDevToolsIntegration-internal.spec.lua index c4be1e76..6c2e79d9 100644 --- a/modules/react/src/__tests__/ReactProfilerDevToolsIntegration-internal.spec.lua +++ b/modules/react/src/__tests__/ReactProfilerDevToolsIntegration-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/__tests__/ReactProfilerDevToolsIntegration-test.internal.js local Packages = script.Parent.Parent.Parent local React local Scheduler diff --git a/modules/react/src/__tests__/ReactStrictMode.spec.lua b/modules/react/src/__tests__/ReactStrictMode.spec.lua index deb4b894..862badd0 100644 --- a/modules/react/src/__tests__/ReactStrictMode.spec.lua +++ b/modules/react/src/__tests__/ReactStrictMode.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/d13f5b9538e48f74f7c571ef3cde652ca887cca0/packages/react/src/__tests__/ReactStrictMode-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/__tests__/ReactStrictMode-test.js -- * Copyright (c) Facebook, Inc. and its affiliates. -- * -- * This source code is licensed under the MIT license found in the @@ -11,7 +11,7 @@ local Packages = script.Parent.Parent.Parent local React local ReactNoop -- local ReactDOM --- local ReactDOMServer +local ReactDOMServer local Scheduler -- local PropTypes local JestGlobals = require("@pkg/@jsdotlua/jest-globals") @@ -970,4 +970,283 @@ describe("context legacy", function() -- end).toErrorDev('Warning: Legacy context API has been detected within a strict-mode tree.' .. '\n\nThe old API will be supported in all 16.x releases, but applications ' .. 'using it should migrate to the new version.' .. '\n\nPlease update the following components: ' .. 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' .. '\n\nLearn more about this warning here: ' .. 'https://reactjs.org/link/legacy-context' .. '\n in LegacyContextProvider (at **)' .. '\n in div (at **)' .. '\n in Root (at **)') -- ReactNoop.render(React.createElement(Root)) end) + describe("console logs logging", function() + beforeEach(function() + jest.resetModules() + React = require_("react") + ReactDOM = require_("react-dom") + ReactDOMClient = require_("react-dom/client") + end) + if Boolean.toJSBoolean(ReactFeatureFlags.consoleManagedByDevToolsDuringStrictMode) then + it("does not disable logs for class double render", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & {} + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.render(self: Foo) + count += 1 + console.log("foo " .. tostring(count)) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(if Boolean.toJSBoolean(__DEV__) then 2 else 1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("does not disable logs for class double ctor", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & {} + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo, props) + count += 1 + console.log("foo " .. tostring(count)) + end + function Foo.render(self: Foo) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(if Boolean.toJSBoolean(__DEV__) then 2 else 1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("does not disable logs for class double getDerivedStateFromProps", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & { state: Object } + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo) + self.state = {} + end + function Foo.getDerivedStateFromProps() + count += 1 + console.log("foo " .. tostring(count)) + return {} + end + function Foo.render(self: Foo) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(if Boolean.toJSBoolean(__DEV__) then 2 else 1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("does not disable logs for class double shouldComponentUpdate", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & { state: Object } + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo) + self.state = {} + end + function Foo.shouldComponentUpdate(self: Foo) + count += 1 + console.log("foo " .. tostring(count)) + return {} + end + function Foo.render(self: Foo) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) -- Trigger sCU: + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(if Boolean.toJSBoolean(__DEV__) then 2 else 1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("does not disable logs for class state updaters", function() + spyOnDevAndProd(console, "log") + local inst + local count = 0 + type Foo = React_Component & { state: Object } + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo) + self.state = {} + end + function Foo.render(self: Foo) + inst = self + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + inst:setState(function() + count += 1 + console.log("foo " .. tostring(count)) + return {} + end) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(if Boolean.toJSBoolean(__DEV__) then 2 else 1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("does not disable logs for function double render", function() + spyOnDevAndProd(console, "log") + local count = 0 + local function Foo() + count += 1 + console.log("foo " .. tostring(count)) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(if Boolean.toJSBoolean(__DEV__) then 2 else 1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + else + it("disable logs for class double render", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & {} + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.render(self: Foo) + count += 1 + console.log("foo " .. tostring(count)) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("disables logs for class double ctor", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & {} + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo, props) + count += 1 + console.log("foo " .. tostring(count)) + end + function Foo.render(self: Foo) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("disable logs for class double getDerivedStateFromProps", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & { state: Object } + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo) + self.state = {} + end + function Foo.getDerivedStateFromProps() + count += 1 + console.log("foo " .. tostring(count)) + return {} + end + function Foo.render(self: Foo) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("disable logs for class double shouldComponentUpdate", function() + spyOnDevAndProd(console, "log") + local count = 0 + type Foo = React_Component & { state: Object } + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo) + self.state = {} + end + function Foo.shouldComponentUpdate(self: Foo) + count += 1 + console.log("foo " .. tostring(count)) + return {} + end + function Foo.render(self: Foo) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) -- Trigger sCU: + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("disable logs for class state updaters", function() + spyOnDevAndProd(console, "log") + local inst + local count = 0 + type Foo = React_Component & { state: Object } + type Foo_statics = {} + local Foo = React.Component:extend("Foo") :: Foo & Foo_statics + function Foo.init(self: Foo) + self.state = {} + end + function Foo.render(self: Foo) + inst = self + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + inst:setState(function() + count += 1 + console.log("foo " .. tostring(count)) + return {} + end) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + it("disable logs for function double render", function() + spyOnDevAndProd(console, "log") + local count = 0 + local function Foo() + count += 1 + console.log("foo " .. tostring(count)) + return nil + end + local container = document:createElement("div") + ReactDOM:render(React.createElement(React.StrictMode, nil, React.createElement(Foo, nil)), container) + expect(count).toBe(if Boolean.toJSBoolean(__DEV__) then 2 else 1) + expect(console.log).toBeCalledTimes(1) -- Note: we should display the first log because otherwise + -- there is a risk of suppressing warnings when they happen, + -- and on the next render they'd get deduplicated and ignored. + expect(console.log).toBeCalledWith("foo 1") + end) + end + end) end) diff --git a/modules/react/src/__tests__/ReactUpdates.spec.lua b/modules/react/src/__tests__/ReactUpdates.spec.lua index 70532602..098c1710 100644 --- a/modules/react/src/__tests__/ReactUpdates.spec.lua +++ b/modules/react/src/__tests__/ReactUpdates.spec.lua @@ -95,11 +95,7 @@ describe("ReactUpdates", function() end function Component:render() instance = self - return React.createElement( - "div", - nil, - string.format("(%s, %s)", self.state.x, self.state.y) - ) + return React.createElement("div", nil, string.format("(%s, %s)", self.state.x, self.state.y)) end ReactTestRenderer.create(React.createElement(Component)) jestExpect(instance.state.x).toBe(0) @@ -171,11 +167,7 @@ describe("ReactUpdates", function() end local childRef = React.createRef() function Parent:render() - return React.createElement( - "div", - nil, - React.createElement(Child, { ref = childRef, x = self.state.x }) - ) + return React.createElement("div", nil, React.createElement(Child, { ref = childRef, x = self.state.x })) end local childUpdateCount = 0 function Child:init() @@ -189,11 +181,7 @@ describe("ReactUpdates", function() end)() end function Child:render() - return React.createElement( - "div", - nil, - tostring(self.props.x) .. tostring(self.state.y) - ) + return React.createElement("div", nil, tostring(self.props.x) .. tostring(self.state.y)) end ReactTestRenderer.create(React.createElement(Parent)) local child = childRef.current @@ -230,11 +218,7 @@ describe("ReactUpdates", function() end local childRef = React.createRef() function Parent:render() - return React.createElement( - "div", - nil, - React.createElement(Child, { ref = childRef, x = self.state.x }) - ) + return React.createElement("div", nil, React.createElement(Child, { ref = childRef, x = self.state.x })) end local childUpdateCount = 0 function Child:init() @@ -248,11 +232,7 @@ describe("ReactUpdates", function() end)() end function Child:render() - return React.createElement( - "div", - nil, - tostring(self.props.x) .. tostring(self.state.y) - ) + return React.createElement("div", nil, tostring(self.props.x) .. tostring(self.state.y)) end ReactTestRenderer.create(React.createElement(Parent)) local child = childRef.current @@ -606,11 +586,7 @@ describe("ReactUpdates", function() local innerRef = React.createRef() function Outer:render() table.insert(updates, "Outer-render-" .. tostring(self.state.x)) - return React.createElement( - "div", - nil, - React.createElement(Inner, { x = self.state.x, ref = innerRef }) - ) + return React.createElement("div", nil, React.createElement(Inner, { x = self.state.x, ref = innerRef })) end function Outer:componentDidUpdate() local x = self.state.x @@ -624,20 +600,11 @@ describe("ReactUpdates", function() self.state = { x = 0 } end function Inner:render() - table.insert( - updates, - "Inner-render-" .. tostring(self.props.x) .. "-" .. tostring(self.state.x) - ) + table.insert(updates, "Inner-render-" .. tostring(self.props.x) .. "-" .. tostring(self.state.x)) return React.createElement("div") end function Inner:componentDidUpdate() - table.insert( - updates, - "Inner-didUpdate-" - .. tostring(self.props.x) - .. "-" - .. tostring(self.state.x) - ) + table.insert(updates, "Inner-didUpdate-" .. tostring(self.props.x) .. "-" .. tostring(self.state.x)) end ReactTestRenderer.create(React.createElement(Outer)) table.insert(updates, "Outer-setState-1") @@ -692,16 +659,11 @@ describe("ReactUpdates", function() < self.props.count --[[ ROBLOX CHECK: operator '<' works only if either both arguments are strings or both are a number ]] then ReactTestRenderer.create( - React.createElement( - MockComponent, - { depth = self.props.depth + 1, count = self.props.count } - ) + React.createElement(MockComponent, { depth = self.props.depth + 1, count = self.props.count }) ) end end - ReactTestRenderer.create( - React.createElement(MockComponent, { depth = 0, count = 2 }) - ) + ReactTestRenderer.create(React.createElement(MockComponent, { depth = 0, count = 2 })) jestExpect(updates).toEqual({ 0, 1, 2 }) ReactTestRenderer.unstable_batchedUpdates(function() -- Simulate update on each component from top to bottom. @@ -725,11 +687,7 @@ describe("ReactUpdates", function() end function X:render() if self.state.s == 0 then - return React.createElement( - "div", - nil, - React.createElement("span", nil, "0") - ) + return React.createElement("div", nil, React.createElement("span", nil, "0")) else return React.createElement("div", nil, "1") end @@ -781,12 +739,7 @@ describe("ReactUpdates", function() local root ReactTestRenderer.unstable_batchedUpdates(function() root = ReactTestRenderer.create( - React.createElement( - "div", - nil, - React.createElement(A), - React.createElement(B) - ) + React.createElement("div", nil, React.createElement(A), React.createElement(B)) ) end) jestExpect(a.state.x).toBe(1) @@ -887,10 +840,7 @@ describe("ReactUpdates", function() .. "a function. Instead received: no.", { withoutStack = true } ) - end).toThrowError( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: no" - ) + end).toThrowError("Invalid argument passed as callback. Expected a function. Instead " .. "received: no") ReactTestRenderer.create(React.createElement(A)) local invalidCallback = { foo = "bar" } @@ -902,19 +852,13 @@ describe("ReactUpdates", function() "setState(...): Expected the last optional `callback` argument to be " .. "a function. Instead received: table." ) - end).toThrowError( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: table" - ) + end).toThrowError("Invalid argument passed as callback. Expected a function. Instead " .. "received: table") -- Make sure the warning is deduplicated and doesn't fire again ReactTestRenderer.create(React.createElement(A)) jestExpect(function() component:setState({}, invalidCallback :: any) - end).toThrowError( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: table" - ) + end).toThrowError("Invalid argument passed as callback. Expected a function. Instead " .. "received: table") end) it("throws in forceUpdate if the update callback is not a function", function() @@ -942,10 +886,7 @@ describe("ReactUpdates", function() .. "a function. Instead received: no.", { withoutStack = true } ) - end).toThrowError( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: no" - ) + end).toThrowError("Invalid argument passed as callback. Expected a function. Instead " .. "received: no") ReactTestRenderer.create(React.createElement(A)) local invalidCallback = { foo = "bar" } @@ -957,19 +898,13 @@ describe("ReactUpdates", function() "forceUpdate(...): Expected the last optional `callback` argument to be " .. "a function. Instead received: table." ) - end).toThrowError( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: table" - ) + end).toThrowError("Invalid argument passed as callback. Expected a function. Instead " .. "received: table") -- Make sure the warning is deduplicated and doesn't fire again ReactTestRenderer.create(React.createElement(A)) jestExpect(function() component:forceUpdate(invalidCallback :: any) - end).toThrowError( - "Invalid argument passed as callback. Expected a function. Instead " - .. "received: table" - ) + end).toThrowError("Invalid argument passed as callback. Expected a function. Instead " .. "received: table") end) it("does not update one component twice in a batch (#2410)", function() @@ -1264,14 +1199,7 @@ describe("ReactUpdates", function() local memoizedStep = self.state.step self:setState(function(baseState) local baseStep = baseState.step - table.insert( - ops, - string.format( - "base: %s, memoized: %s", - tostring(baseStep), - memoizedStep - ) - ) + table.insert(ops, string.format("base: %s, memoized: %s", tostring(baseStep), memoizedStep)) return baseStep == 0 and { step = 1 } or nil end) return nil @@ -1506,30 +1434,27 @@ describe("ReactUpdates", function() jestExpect(container.toJSON()).toBe("1") end) -- ROBLOX TODO: figure out how to do this with test renderer - it.skip( - "does not fall into mutually recursive infinite update loop with same container", - function() - -- Note: this test would fail if there were two or more different roots. - local B = React.Component:extend("B") - local container = ReactTestRenderer.create(React.createElement("div")) - local A = React.Component:extend("A") - function A:componentDidMount() - container:update(React.createElement(B)) - end - function A:render() - return nil - end - function B:componentDidMount() - container:update(React.createElement(A)) - end - function B:render() - return nil - end - jestExpect(function() - container:update(React.createElement(A)) - end).toThrow("Maximum") + it.skip("does not fall into mutually recursive infinite update loop with same container", function() + -- Note: this test would fail if there were two or more different roots. + local B = React.Component:extend("B") + local container = ReactTestRenderer.create(React.createElement("div")) + local A = React.Component:extend("A") + function A:componentDidMount() + container:update(React.createElement(B)) + end + function A:render() + return nil + end + function B:componentDidMount() + container:update(React.createElement(A)) + end + function B:render() + return nil end - ) + jestExpect(function() + container:update(React.createElement(A)) + end).toThrow("Maximum") + end) it("does not fall into an infinite error loop", function() local function BadRender() error(Error.new("error")) @@ -1553,10 +1478,7 @@ describe("ReactUpdates", function() end) end function NonTerminating:render() - return React.createElement( - ErrorBoundary, - { key = self.state.step, parent = self } - ) + return React.createElement(ErrorBoundary, { key = self.state.step, parent = self }) end jestExpect(function() ReactTestRenderer.create(React.createElement(NonTerminating)) diff --git a/modules/react/src/__tests__/forwardRef-internal.spec.lua b/modules/react/src/__tests__/forwardRef-internal.spec.lua index 0cec1762..1bd27e65 100644 --- a/modules/react/src/__tests__/forwardRef-internal.spec.lua +++ b/modules/react/src/__tests__/forwardRef-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/v17.0.1/packages/react/src/__tests__/forwardRef-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/__tests__/forwardRef-test.internal.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -43,17 +43,11 @@ it("should work without a ref to be forwarded", function() end local function Wrapper(props) - return React.createElement( - Child, - Object.assign({}, props, { ref = props.forwardedRef }) - ) + return React.createElement(Child, Object.assign({}, props, { ref = props.forwardedRef })) end local RefForwardingComponent = React.forwardRef(function(props, ref) - return React.createElement( - Wrapper, - Object.assign({}, props, { forwardedRef = ref }) - ) + return React.createElement(Wrapper, Object.assign({}, props, { forwardedRef = ref })) end) ReactNoop.render(React.createElement(RefForwardingComponent, { value = 123 })) @@ -68,24 +62,16 @@ it("should forward a ref for a single child", function() end local function Wrapper(props) - return React.createElement( - Child, - Object.assign({}, props, { ref = props.forwardedRef }) - ) + return React.createElement(Child, Object.assign({}, props, { ref = props.forwardedRef })) end local RefForwardingComponent = React.forwardRef(function(props, ref) - return React.createElement( - Wrapper, - Object.assign({}, props, { forwardedRef = ref }) - ) + return React.createElement(Wrapper, Object.assign({}, props, { forwardedRef = ref })) end) local ref = React.createRef() - ReactNoop.render( - React.createElement(RefForwardingComponent, { ref = ref, value = 123 }) - ) + ReactNoop.render(React.createElement(RefForwardingComponent, { ref = ref, value = 123 })) jestExpect(Scheduler).toFlushAndYield({ 123 }) -- jestExpect(Object.instanceof(ref.current, Child)).toBe(true) jestExpect(getmetatable(ref.current).__index).toBe(Child) @@ -99,17 +85,11 @@ it("should forward a ref for multiple children", function() end local function Wrapper(props) - return React.createElement( - Child, - Object.assign({}, props, { ref = props.forwardedRef }) - ) + return React.createElement(Child, Object.assign({}, props, { ref = props.forwardedRef })) end local RefForwardingComponent = React.forwardRef(function(props, ref) - return React.createElement( - Wrapper, - Object.assign({}, props, { forwardedRef = ref }) - ) + return React.createElement(Wrapper, Object.assign({}, props, { forwardedRef = ref })) end) local ref = React.createRef() @@ -137,17 +117,11 @@ it("should maintain child instance and ref through updates", function() end local function Wrapper(props) - return React.createElement( - Child, - Object.assign({}, props, { ref = props.forwardedRef }) - ) + return React.createElement(Child, Object.assign({}, props, { ref = props.forwardedRef })) end local RefForwardingComponent = React.forwardRef(function(props, ref) - return React.createElement( - Wrapper, - Object.assign({}, props, { forwardedRef = ref }) - ) + return React.createElement(Wrapper, Object.assign({}, props, { forwardedRef = ref })) end) local setRefCount = 0 @@ -158,17 +132,13 @@ it("should maintain child instance and ref through updates", function() ref = r end - ReactNoop.render( - React.createElement(RefForwardingComponent, { ref = setRef, value = 123 }) - ) + ReactNoop.render(React.createElement(RefForwardingComponent, { ref = setRef, value = 123 })) jestExpect(Scheduler).toFlushAndYield({ 123 }) -- ROBLOX FIXME: When instanceof is implemented, use it -- jestExpect(Object.instanceof(ref, Child)).toBe(true) jestExpect(getmetatable(ref).__index).toBe(Child) jestExpect(setRefCount).toBe(1) - ReactNoop.render( - React.createElement(RefForwardingComponent, { ref = setRef, value = 456 }) - ) + ReactNoop.render(React.createElement(RefForwardingComponent, { ref = setRef, value = 456 })) jestExpect(Scheduler).toFlushAndYield({ 456 }) -- ROBLOX FIXME: When instanceof is implemented, use it -- jestExpect(Object.instanceof(ref, Child)).toBe(true) @@ -203,27 +173,17 @@ it("should not break lifecycle error handling", function() local function Wrapper(props) local forwardedRef = props.forwardedRef Scheduler.unstable_yieldValue("Wrapper") - return React.createElement( - BadRender, - Object.assign({}, props, { ref = forwardedRef }) - ) + return React.createElement(BadRender, Object.assign({}, props, { ref = forwardedRef })) end local RefForwardingComponent = React.forwardRef(function(props, ref) - return React.createElement( - Wrapper, - Object.assign({}, props, { forwardedRef = ref }) - ) + return React.createElement(Wrapper, Object.assign({}, props, { forwardedRef = ref })) end) local ref = React.createRef() ReactNoop.render( - React.createElement( - ErrorBoundary, - nil, - React.createElement(RefForwardingComponent, { ref = ref }) - ) + React.createElement(ErrorBoundary, nil, React.createElement(RefForwardingComponent, { ref = ref })) ) -- ROBLOX Test Noise: jest setup config makes this hide error -- boundary warnings in upstream (scripts/jest/setupTests.js:72) @@ -261,10 +221,7 @@ it("should not re-run the render callback on a deep setState", function() local Forward = React.forwardRef(function(props, ref) Scheduler.unstable_yieldValue("Forward") - return React.createElement( - Middle, - Object.assign({}, props, { forwardedRef = ref }) - ) + return React.createElement(Middle, Object.assign({}, props, { forwardedRef = ref })) end) local function App() diff --git a/modules/react/src/init.lua b/modules/react/src/init.lua index 37a0f5a1..0d738674 100644 --- a/modules/react/src/init.lua +++ b/modules/react/src/init.lua @@ -23,15 +23,11 @@ local ReactLazy = require("./ReactLazy") export type LazyComponent = ReactLazy.LazyComponent local SharedModule = require("@pkg/@jsdotlua/shared") -export type StatelessFunctionalComponent

= - SharedModule.React_StatelessFunctionalComponent

+export type StatelessFunctionalComponent

= SharedModule.React_StatelessFunctionalComponent

-- ROBLOX deviation START: we use the definitely-typed version of this, which appears to work for flowtype in VirtualizedList, etc export type ComponentType

= ComponentClass

| FC

-- ROBLOX deviation END -export type AbstractComponent = SharedModule.React_AbstractComponent< - Config, - Instance -> +export type AbstractComponent = SharedModule.React_AbstractComponent export type ElementType = SharedModule.React_ElementType export type Element = SharedModule.React_Element export type Key = SharedModule.React_Key @@ -51,10 +47,7 @@ export type PureComponent = React.PureComponent = SharedModule.ReactElement< - Props, - ElementType -> +export type ReactElement = SharedModule.ReactElement -- we don't include ReactText in ReactChild since roblox renderer doesn't support raw text nodes export type ReactChild = SharedModule.ReactElement | string | number export type FC

= SharedModule.React_StatelessFunctionalComponent

@@ -62,10 +55,7 @@ export type ReactNode = SharedModule.React_Node -- ROBLOX deviation END -- ROBLOX deviation START: export React types that are flowtype built-ins and used by VirtualizedList, etc -export type React_AbstractComponent = SharedModule.React_Component< - Props, - Instance -> +export type React_AbstractComponent = SharedModule.React_Component export type React_Component = SharedModule.React_Component export type React_ComponentType

= SharedModule.React_ComponentType

export type React_Context = SharedModule.React_Context diff --git a/modules/scheduler/src/Scheduler.lua b/modules/scheduler/src/Scheduler.lua index 31c2c01f..c2bd6993 100644 --- a/modules/scheduler/src/Scheduler.lua +++ b/modules/scheduler/src/Scheduler.lua @@ -243,8 +243,7 @@ return function(hostConfig) if not _G.__YOLO__ then -- ROBLOX performance: don't nest try/catch here, Lua can do better, and it eliminated an anon function creation if enableProfiling then - ok, result = - xpcall(workLoop, describeError, hasTimeRemaining, initialTime) + ok, result = xpcall(workLoop, describeError, hasTimeRemaining, initialTime) if not ok then if currentTask ~= nil then @@ -283,13 +282,8 @@ return function(hostConfig) local currentTime = initialTime advanceTimers(currentTime) currentTask = peek(taskQueue) - while - currentTask ~= nil and not (enableSchedulerDebugging and isSchedulerPaused) - do - if - currentTask.expirationTime > currentTime - and (not hasTimeRemaining or shouldYieldToHost()) - then + while currentTask ~= nil and not (enableSchedulerDebugging and isSchedulerPaused) do + if currentTask.expirationTime > currentTime and (not hasTimeRemaining or shouldYieldToHost()) then -- This currentTask hasn't expired, and we've reached the deadline. break end diff --git a/modules/scheduler/src/SchedulerFeatureFlags.lua b/modules/scheduler/src/SchedulerFeatureFlags.lua index e5750259..6f1bc254 100644 --- a/modules/scheduler/src/SchedulerFeatureFlags.lua +++ b/modules/scheduler/src/SchedulerFeatureFlags.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/9abc2785cb070148d64fae81e523246b90b92016/packages/scheduler/src/SchedulerFeatureFlags.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/scheduler/src/SchedulerFeatureFlags.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/scheduler/src/Tracing.lua b/modules/scheduler/src/Tracing.lua index 8de14e85..fb8837a6 100644 --- a/modules/scheduler/src/Tracing.lua +++ b/modules/scheduler/src/Tracing.lua @@ -121,90 +121,89 @@ exports.unstable_getThreadID = function(): number return threadIDCounter end -exports.unstable_trace = - function(name: string, timestamp: number, callback: Function, threadID_: number?): any - -- ROBLOX: default argument value - local threadID = if threadID_ ~= nil then threadID_ else DEFAULT_THREAD_ID - - if not enableSchedulerTracing then - return callback() - end +exports.unstable_trace = function(name: string, timestamp: number, callback: Function, threadID_: number?): any + -- ROBLOX: default argument value + local threadID = if threadID_ ~= nil then threadID_ else DEFAULT_THREAD_ID - local interaction: Interaction = { - __count = 1, - id = interactionIDCounter, - name = name, - timestamp = timestamp, - } - interactionIDCounter += 1 + if not enableSchedulerTracing then + return callback() + end - local prevInteractions = interactionsRef.current + local interaction: Interaction = { + __count = 1, + id = interactionIDCounter, + name = name, + timestamp = timestamp, + } + interactionIDCounter += 1 - -- Traced interactions should stack/accumulate. - -- To do that, clone the current interactions. - -- The previous set will be restored upon completion. - local interactions = Set.new(prevInteractions) - interactions:add(interaction) - interactionsRef.current = interactions + local prevInteractions = interactionsRef.current - local subscriber = subscriberRef.current - local returnValue + -- Traced interactions should stack/accumulate. + -- To do that, clone the current interactions. + -- The previous set will be restored upon completion. + local interactions = Set.new(prevInteractions) + interactions:add(interaction) + interactionsRef.current = interactions - -- ROBLOX try - local ok, result = pcall(function() - if subscriber ~= nil then - subscriber.onInteractionTraced(interaction) - end - end) - -- ROBLOX finally - -- ROBLOX try 2 - local ok2, result2 = pcall(function() - if subscriber ~= nil then - subscriber.onWorkStarted(interactions, threadID) - end - end) - - -- ROBLOX finally 2 - -- ROBLOX try 3 - local ok3, result3 = pcall(function() - returnValue = callback() - end) - -- ROBLOX finally 3 - interactionsRef.current = prevInteractions - -- ROBLOX try 4 - local ok4, result4 = pcall(function() - if subscriber ~= nil then - subscriber.onWorkStopped(interactions, threadID) - end - end) - -- ROBLOX finally 4 - interaction.__count -= 1 + local subscriber = subscriberRef.current + local returnValue - -- If no async work was scheduled for this interaction, - -- Notify subscribers that it's completed. - if subscriber ~= nil and interaction.__count == 0 then - subscriber.onInteractionScheduledWorkCompleted(interaction) + -- ROBLOX try + local ok, result = pcall(function() + if subscriber ~= nil then + subscriber.onInteractionTraced(interaction) end - - if not ok4 then - error(result4) + end) + -- ROBLOX finally + -- ROBLOX try 2 + local ok2, result2 = pcall(function() + if subscriber ~= nil then + subscriber.onWorkStarted(interactions, threadID) end - - if not ok3 then - error(result3) + end) + + -- ROBLOX finally 2 + -- ROBLOX try 3 + local ok3, result3 = pcall(function() + returnValue = callback() + end) + -- ROBLOX finally 3 + interactionsRef.current = prevInteractions + -- ROBLOX try 4 + local ok4, result4 = pcall(function() + if subscriber ~= nil then + subscriber.onWorkStopped(interactions, threadID) end + end) + -- ROBLOX finally 4 + interaction.__count -= 1 + + -- If no async work was scheduled for this interaction, + -- Notify subscribers that it's completed. + if subscriber ~= nil and interaction.__count == 0 then + subscriber.onInteractionScheduledWorkCompleted(interaction) + end - if not ok2 then - error(result2) - end + if not ok4 then + error(result4) + end - if not ok then - error(result) - end + if not ok3 then + error(result3) + end + + if not ok2 then + error(result2) + end - return returnValue + if not ok then + error(result) end + return returnValue +end + exports.unstable_wrap = function( callback: Function, threadID: number diff --git a/modules/scheduler/src/TracingSubscriptions.lua b/modules/scheduler/src/TracingSubscriptions.lua index cc371c5f..4db54904 100644 --- a/modules/scheduler/src/TracingSubscriptions.lua +++ b/modules/scheduler/src/TracingSubscriptions.lua @@ -81,8 +81,7 @@ function onInteractionScheduledWorkCompleted(interaction: Interaction): () for subscriber, _ in subscribers do -- ROBLOX try - local ok, result = - pcall(subscriber.onInteractionScheduledWorkCompleted, interaction) + local ok, result = pcall(subscriber.onInteractionScheduledWorkCompleted, interaction) -- ROBLOX catch if not ok then local error_ = result diff --git a/modules/scheduler/src/__tests__/Scheduler.spec.lua b/modules/scheduler/src/__tests__/Scheduler.spec.lua index af7fc0b7..ec4ecbeb 100644 --- a/modules/scheduler/src/__tests__/Scheduler.spec.lua +++ b/modules/scheduler/src/__tests__/Scheduler.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/9abc2785cb070148d64fae81e523246b90b92016/packages/scheduler/src/__tests__/Scheduler-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/scheduler/src/__tests__/Scheduler-test.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -7,7 +7,7 @@ * * @emails react-core ]] - + ]] local Packages = script.Parent.Parent.Parent local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill") local Error = LuauPolyfill.Error diff --git a/modules/scheduler/src/__tests__/SchedulerProfiling.spec.lua b/modules/scheduler/src/__tests__/SchedulerProfiling.spec.lua index 9863d965..bf57b0e3 100644 --- a/modules/scheduler/src/__tests__/SchedulerProfiling.spec.lua +++ b/modules/scheduler/src/__tests__/SchedulerProfiling.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/8af27aeedbc6b00bc2ef49729fc84f116c70a27c/packages/scheduler/src/__tests__/SchedulerProfiling-test.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/scheduler/src/__tests__/SchedulerProfiling-test.js --[[** * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/scheduler/src/__tests__/Tracing-internal.spec.lua b/modules/scheduler/src/__tests__/Tracing-internal.spec.lua index 6ae71081..c09be933 100644 --- a/modules/scheduler/src/__tests__/Tracing-internal.spec.lua +++ b/modules/scheduler/src/__tests__/Tracing-internal.spec.lua @@ -46,11 +46,9 @@ describe("Tracing", function() end) it("should return the value of a traced function", function() - jestExpect( - SchedulerTracing.unstable_trace("arbitrary", currentTime(), function() - return 123 - end) - ).toBe(123) + jestExpect(SchedulerTracing.unstable_trace("arbitrary", currentTime(), function() + return 123 + end)).toBe(123) end) it("should return the value of a clear function", function() @@ -90,52 +88,46 @@ describe("Tracing", function() jestExpect(SchedulerTracing.unstable_getCurrent()).toContainNoInteractions() end) - it( - "should report the traced interaction from within the trace callback", - function() - local done = false - advanceTimeBy(100) + it("should report the traced interaction from within the trace callback", function() + local done = false + advanceTimeBy(100) - SchedulerTracing.unstable_trace("some event", currentTime(), function() - local interactions = SchedulerTracing.unstable_getCurrent() - jestExpect(interactions).toMatchInteractions({ - { name = "some event", timestamp = 100 }, - }) + SchedulerTracing.unstable_trace("some event", currentTime(), function() + local interactions = SchedulerTracing.unstable_getCurrent() + jestExpect(interactions).toMatchInteractions({ + { name = "some event", timestamp = 100 }, + }) - done = true - end) + done = true + end) - jestExpect(done).toBe(true) - end - ) + jestExpect(done).toBe(true) + end) - it( - "should report the traced interaction from within wrapped callbacks", - function() - local done = false - local wrappedIndirection + it("should report the traced interaction from within wrapped callbacks", function() + local done = false + local wrappedIndirection - local function indirection() - local interactions = SchedulerTracing.unstable_getCurrent() - jestExpect(interactions).toMatchInteractions({ - { name = "some event", timestamp = 100 }, - }) + local function indirection() + local interactions = SchedulerTracing.unstable_getCurrent() + jestExpect(interactions).toMatchInteractions({ + { name = "some event", timestamp = 100 }, + }) - done = true - end + done = true + end - advanceTimeBy(100) + advanceTimeBy(100) - SchedulerTracing.unstable_trace("some event", currentTime(), function() - wrappedIndirection = SchedulerTracing.unstable_wrap(indirection) - end) + SchedulerTracing.unstable_trace("some event", currentTime(), function() + wrappedIndirection = SchedulerTracing.unstable_wrap(indirection) + end) - advanceTimeBy(50) + advanceTimeBy(50) - wrappedIndirection() - jestExpect(done).toBe(true) - end - ) + wrappedIndirection() + jestExpect(done).toBe(true) + end) it("should clear the interaction stack for traced callbacks", function() local innerTestReached = false @@ -148,17 +140,13 @@ describe("Tracing", function() SchedulerTracing.unstable_clear(function() jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({}) - SchedulerTracing.unstable_trace( - "inner event", - currentTime(), - function() - jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ - { name = "inner event" }, - }) - - innerTestReached = true - end - ) + SchedulerTracing.unstable_trace("inner event", currentTime(), function() + jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ + { name = "inner event" }, + }) + + innerTestReached = true + end) end) jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ @@ -181,17 +169,13 @@ describe("Tracing", function() SchedulerTracing.unstable_clear(function() jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({}) - SchedulerTracing.unstable_trace( - "inner event", - currentTime(), - function() - jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ - { name = "inner event" }, - }) - - innerTestReached = true - end - ) + SchedulerTracing.unstable_trace("inner event", currentTime(), function() + jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ + { name = "inner event" }, + }) + + innerTestReached = true + end) end) jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ @@ -243,8 +227,7 @@ describe("Tracing", function() advanceTimeBy(50) - local wrapperOuterIndirection = - SchedulerTracing.unstable_wrap(outerIndirection) + local wrapperOuterIndirection = SchedulerTracing.unstable_wrap(outerIndirection) local wrapperInnerIndirection local innerEventTraced = false @@ -261,8 +244,7 @@ describe("Tracing", function() wrapperOuterIndirection() jestExpect(outerIndirectionTraced).toBe(true) - wrapperInnerIndirection = - SchedulerTracing.unstable_wrap(innerIndirection) + wrapperInnerIndirection = SchedulerTracing.unstable_wrap(innerIndirection) innerEventTraced = true end) @@ -285,107 +267,72 @@ describe("Tracing", function() end) describe("error handling", function() - it( - "should reset state appropriately when an error occurs in a trace callback", - function() - local done = false - advanceTimeBy(100) - - SchedulerTracing.unstable_trace( - "outer event", - currentTime(), - function() - jestExpect(function() - SchedulerTracing.unstable_trace( - "inner event", - currentTime(), - function() - error("intentional") - end - ) - end).toThrow() - - jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ - { name = "outer event", timestamp = 100 }, - }) - - done = true - end - ) - jestExpect(done).toBe(true) - end - ) - - it( - "should reset state appropriately when an error occurs in a wrapped callback", - function() - local done = false - advanceTimeBy(100) - - SchedulerTracing.unstable_trace( - "outer event", - currentTime(), - function() - local wrappedCallback - - SchedulerTracing.unstable_trace( - "inner event", - currentTime(), - function() - wrappedCallback = SchedulerTracing.unstable_wrap( - function() - error("intentional") - end - ) - end - ) - - -- ROBLOX deviation: unstable_wrap returns a table with a __call metamethod so it can have a cancel field - jestExpect(function() - wrappedCallback() - end).toThrow() - - jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ - { name = "outer event", timestamp = 100 }, - }) - - done = true - end - ) - jestExpect(done).toBe(true) - end - ) + it("should reset state appropriately when an error occurs in a trace callback", function() + local done = false + advanceTimeBy(100) + + SchedulerTracing.unstable_trace("outer event", currentTime(), function() + jestExpect(function() + SchedulerTracing.unstable_trace("inner event", currentTime(), function() + error("intentional") + end) + end).toThrow() + + jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ + { name = "outer event", timestamp = 100 }, + }) + + done = true + end) + jestExpect(done).toBe(true) + end) + + it("should reset state appropriately when an error occurs in a wrapped callback", function() + local done = false + advanceTimeBy(100) + + SchedulerTracing.unstable_trace("outer event", currentTime(), function() + local wrappedCallback + + SchedulerTracing.unstable_trace("inner event", currentTime(), function() + wrappedCallback = SchedulerTracing.unstable_wrap(function() + error("intentional") + end) + end) + + -- ROBLOX deviation: unstable_wrap returns a table with a __call metamethod so it can have a cancel field + jestExpect(function() + wrappedCallback() + end).toThrow() + + jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ + { name = "outer event", timestamp = 100 }, + }) + + done = true + end) + jestExpect(done).toBe(true) + end) end) describe("advanced integration", function() it("should return a unique threadID per request", function() - jestExpect(SchedulerTracing.unstable_getThreadID()).never.toBe( - SchedulerTracing.unstable_getThreadID() - ) + jestExpect(SchedulerTracing.unstable_getThreadID()).never.toBe(SchedulerTracing.unstable_getThreadID()) end) - it( - "should expose the current set of interactions to be externally manipulated", - function() - SchedulerTracing.unstable_trace( - "outer event", - currentTime(), - function() - jestExpect(SchedulerTracing.__interactionsRef.current).toBe( - SchedulerTracing.unstable_getCurrent() - ) - - SchedulerTracing.__interactionsRef.current = Set.new({ - { name = "override event" }, - }) - - jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ - { name = "override event" }, - }) - end - ) - end - ) + it("should expose the current set of interactions to be externally manipulated", function() + SchedulerTracing.unstable_trace("outer event", currentTime(), function() + jestExpect(SchedulerTracing.__interactionsRef.current).toBe(SchedulerTracing.unstable_getCurrent()) + + SchedulerTracing.__interactionsRef.current = Set.new({ + { name = "override event" }, + }) + + jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ + { name = "override event" }, + }) + end) + end) it("should expose a subscriber ref to be externally manipulated", function() SchedulerTracing.unstable_trace("outer event", currentTime(), function() @@ -403,11 +350,9 @@ describe("Tracing", function() end) it("should return the value of a traced function", function() - jestExpect( - SchedulerTracing.unstable_trace("arbitrary", currentTime(), function() - return 123 - end) - ).toBe(123) + jestExpect(SchedulerTracing.unstable_trace("arbitrary", currentTime(), function() + return 123 + end)).toBe(123) end) it("should return the value of a wrapped function", function() diff --git a/modules/scheduler/src/__tests__/TracingSubscriptions-internal.spec.lua b/modules/scheduler/src/__tests__/TracingSubscriptions-internal.spec.lua index 3c1c6b32..fb2c916d 100644 --- a/modules/scheduler/src/__tests__/TracingSubscriptions-internal.spec.lua +++ b/modules/scheduler/src/__tests__/TracingSubscriptions-internal.spec.lua @@ -122,21 +122,18 @@ describe("TracingSubscriptions", function() beforeEach(function() return loadModules({ enableSchedulerTracing = true }) end) - it( - "should lazily subscribe to tracing and unsubscribe again if there are no external subscribers", - function() - loadModules({ enableSchedulerTracing = true, autoSubscribe = false }) - jestExpect(SchedulerTracing.__subscriberRef.current).toBe(nil) - SchedulerTracing.unstable_subscribe(firstSubscriber) - jestExpect(SchedulerTracing.__subscriberRef.current).toBeDefined() - SchedulerTracing.unstable_subscribe(secondSubscriber) - jestExpect(SchedulerTracing.__subscriberRef.current).toBeDefined() - SchedulerTracing.unstable_unsubscribe(secondSubscriber) - jestExpect(SchedulerTracing.__subscriberRef.current).toBeDefined() - SchedulerTracing.unstable_unsubscribe(firstSubscriber) - jestExpect(SchedulerTracing.__subscriberRef.current).toBe(nil) - end - ) + it("should lazily subscribe to tracing and unsubscribe again if there are no external subscribers", function() + loadModules({ enableSchedulerTracing = true, autoSubscribe = false }) + jestExpect(SchedulerTracing.__subscriberRef.current).toBe(nil) + SchedulerTracing.unstable_subscribe(firstSubscriber) + jestExpect(SchedulerTracing.__subscriberRef.current).toBeDefined() + SchedulerTracing.unstable_subscribe(secondSubscriber) + jestExpect(SchedulerTracing.__subscriberRef.current).toBeDefined() + SchedulerTracing.unstable_unsubscribe(secondSubscriber) + jestExpect(SchedulerTracing.__subscriberRef.current).toBeDefined() + SchedulerTracing.unstable_unsubscribe(firstSubscriber) + jestExpect(SchedulerTracing.__subscriberRef.current).toBe(nil) + end) describe("error handling", function() it("should cover onInteractionTraced/onWorkStarted within", function() SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() @@ -145,23 +142,13 @@ describe("TracingSubscriptions", function() -- It should call the callback before re-throwing throwInOnInteractionTraced = true jestExpect(function() - return SchedulerTracing.unstable_trace( - secondEvent.name, - currentTime, - mock, - threadID - ) + return SchedulerTracing.unstable_trace(secondEvent.name, currentTime, mock, threadID) end).toThrow("Expected error onInteractionTraced") throwInOnInteractionTraced = false jestExpect(mock).toHaveBeenCalledTimes(1) throwInOnWorkStarted = true jestExpect(function() - return SchedulerTracing.unstable_trace( - secondEvent.name, - currentTime, - mock, - threadID - ) + return SchedulerTracing.unstable_trace(secondEvent.name, currentTime, mock, threadID) end).toThrow("Expected error onWorkStarted") jestExpect(mock).toHaveBeenCalledTimes(2) @@ -171,9 +158,7 @@ describe("TracingSubscriptions", function() }) -- It should call other subscribers despite the earlier error - jestExpect(secondSubscriber.onInteractionTraced).toHaveBeenCalledTimes( - 3 - ) + jestExpect(secondSubscriber.onInteractionTraced).toHaveBeenCalledTimes(3) jestExpect(secondSubscriber.onWorkStarted).toHaveBeenCalledTimes(3) end) end) @@ -181,18 +166,13 @@ describe("TracingSubscriptions", function() SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() local innerInteraction local mock = jest.fn(function() - innerInteraction = - SchedulerTracing.unstable_getCurrent()._array[2] --[[ ROBLOX adaptation: added 1 to array index ]] + innerInteraction = SchedulerTracing.unstable_getCurrent()._array[2] --[[ ROBLOX adaptation: added 1 to array index ]] end) throwInOnWorkStopped = true jestExpect(function() - return SchedulerTracing.unstable_trace( - secondEvent.name, - currentTime, - mock - ) + return SchedulerTracing.unstable_trace(secondEvent.name, currentTime, mock) end).toThrow("Expected error onWorkStopped") throwInOnWorkStopped = false @@ -213,34 +193,22 @@ describe("TracingSubscriptions", function() local mock = jest.fn() throwInOnInteractionScheduledWorkCompleted = true jestExpect(function() - return SchedulerTracing.unstable_trace( - secondEvent.name, - currentTime, - mock - ) - end).toThrow( - "Expected error onInteractionScheduledWorkCompleted" - ) + return SchedulerTracing.unstable_trace(secondEvent.name, currentTime, mock) + end).toThrow("Expected error onInteractionScheduledWorkCompleted") throwInOnInteractionScheduledWorkCompleted = false jestExpect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions({ firstEvent, }) - jestExpect(secondSubscriber.onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes( - 1 - ) + jestExpect(secondSubscriber.onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) end) end) it("should cover the callback within trace", function() jestExpect(onWorkStarted).never.toHaveBeenCalled() jestExpect(onWorkStopped).never.toHaveBeenCalled() jestExpect(function() - SchedulerTracing.unstable_trace( - firstEvent.name, - currentTime, - function() - error("Expected error callback") - end - ) + SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() + error("Expected error callback") + end) end).toThrow("Expected error callback") jestExpect(onWorkStarted).toHaveBeenCalledTimes(1) jestExpect(onWorkStopped).toHaveBeenCalledTimes(1) @@ -278,25 +246,19 @@ describe("TracingSubscriptions", function() it("should cover onWorkStopped within wrap", function() SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() -- ROBLOX FIXME: Array.from() polyfill doesn't recognize Set correctly - local outerInteraction = - SchedulerTracing.unstable_getCurrent()._array[1] --[[ ROBLOX adaptation: added 1 to array index ]] + local outerInteraction = SchedulerTracing.unstable_getCurrent()._array[1] --[[ ROBLOX adaptation: added 1 to array index ]] jestExpect(outerInteraction.__count).toBe(1) local wrapped local innerInteraction - SchedulerTracing.unstable_trace( - secondEvent.name, - currentTime, - function() - -- ROBLOX FIXME: Array.from() polyfill doesn't recognize Set correctly - innerInteraction = - SchedulerTracing.unstable_getCurrent()._array[2] --[[ ROBLOX adaptation: added 1 to array index ]] - jestExpect(outerInteraction.__count).toBe(1) - jestExpect(innerInteraction.__count).toBe(1) - wrapped = SchedulerTracing.unstable_wrap(jest.fn()) - jestExpect(outerInteraction.__count).toBe(2) - jestExpect(innerInteraction.__count).toBe(2) - end - ) + SchedulerTracing.unstable_trace(secondEvent.name, currentTime, function() + -- ROBLOX FIXME: Array.from() polyfill doesn't recognize Set correctly + innerInteraction = SchedulerTracing.unstable_getCurrent()._array[2] --[[ ROBLOX adaptation: added 1 to array index ]] + jestExpect(outerInteraction.__count).toBe(1) + jestExpect(innerInteraction.__count).toBe(1) + wrapped = SchedulerTracing.unstable_wrap(jest.fn()) + jestExpect(outerInteraction.__count).toBe(2) + jestExpect(innerInteraction.__count).toBe(2) + end) jestExpect(outerInteraction.__count).toBe(2) jestExpect(innerInteraction.__count).toBe(1) throwInOnWorkStopped = true @@ -353,9 +315,7 @@ describe("TracingSubscriptions", function() end).toThrow("Expected error onWorkCanceled") jestExpect(onWorkCanceled).toHaveBeenCalledTimes(1) jestExpect(interaction.__count).toBe(0) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(firstEvent) jestExpect(secondSubscriber.onWorkCanceled).toHaveBeenCalledTimes(1) end) end) @@ -364,22 +324,15 @@ describe("TracingSubscriptions", function() jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) - jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) + jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(firstEvent) jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() jestExpect(onWorkStarted).toHaveBeenCalledTimes(1) - jestExpect(onWorkStarted).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent }), - threadID - ) + jestExpect(onWorkStarted).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent }), threadID) jestExpect(onWorkStopped).never.toHaveBeenCalled() SchedulerTracing.unstable_trace(secondEvent.name, currentTime, function() jestExpect(onInteractionTraced).toHaveBeenCalledTimes(2) - jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction( - secondEvent - ) + jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(secondEvent) jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() jestExpect(onWorkStarted).toHaveBeenCalledTimes(2) jestExpect(onWorkStarted).toHaveBeenLastNotifiedOfWork( @@ -389,41 +342,27 @@ describe("TracingSubscriptions", function() jestExpect(onWorkStopped).never.toHaveBeenCalled() end, threadID) jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - secondEvent - ) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(secondEvent) jestExpect(onWorkStopped).toHaveBeenCalledTimes(1) - jestExpect(onWorkStopped).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent, secondEvent }), - threadID - ) + jestExpect(onWorkStopped).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent, secondEvent }), threadID) end, threadID) jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(firstEvent) jestExpect(onWorkScheduled).never.toHaveBeenCalled() jestExpect(onWorkCanceled).never.toHaveBeenCalled() jestExpect(onWorkStarted).toHaveBeenCalledTimes(2) jestExpect(onWorkStopped).toHaveBeenCalledTimes(2) - jestExpect(onWorkStopped).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent }), - threadID - ) + jestExpect(onWorkStopped).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent }), threadID) end) it("calls lifecycle methods for wrap", function() local unwrapped = jest.fn() local wrapped SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) - jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) + jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(firstEvent) SchedulerTracing.unstable_trace(secondEvent.name, currentTime, function() jestExpect(onInteractionTraced).toHaveBeenCalledTimes(2) - jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction( - secondEvent - ) + jestExpect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(secondEvent) wrapped = SchedulerTracing.unstable_wrap(unwrapped, threadID) jestExpect(onWorkScheduled).toHaveBeenCalledTimes(1) jestExpect(onWorkScheduled).toHaveBeenLastNotifiedOfWork( @@ -439,75 +378,48 @@ describe("TracingSubscriptions", function() jestExpect(onWorkScheduled).toHaveBeenCalledTimes(1) jestExpect(onWorkCanceled).never.toHaveBeenCalled() jestExpect(onWorkStarted).toHaveBeenCalledTimes(3) - jestExpect(onWorkStarted).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent, secondEvent }), - threadID - ) + jestExpect(onWorkStarted).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent, secondEvent }), threadID) jestExpect(onWorkStopped).toHaveBeenCalledTimes(3) - jestExpect(onWorkStopped).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent, secondEvent }), - threadID - ) + jestExpect(onWorkStopped).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent, secondEvent }), threadID) jestExpect(onInteractionScheduledWorkCompleted .mock .calls [1] --[[ ROBLOX adaptation: added 1 to array index ]] - [1] --[[ ROBLOX adaptation: added 1 to array index ]]).toMatchInteraction( - firstEvent - ) + [1] --[[ ROBLOX adaptation: added 1 to array index ]]).toMatchInteraction(firstEvent) jestExpect(onInteractionScheduledWorkCompleted .mock .calls [2] --[[ ROBLOX adaptation: added 1 to array index ]] - [1] --[[ ROBLOX adaptation: added 1 to array index ]]).toMatchInteraction( - secondEvent - ) + [1] --[[ ROBLOX adaptation: added 1 to array index ]]).toMatchInteraction(secondEvent) end) - it( - "should call the correct interaction subscriber methods when a wrapped callback is canceled", - function() - local fnOne = jest.fn() - local fnTwo = jest.fn() - local wrappedOne, wrappedTwo - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() - wrappedOne = SchedulerTracing.unstable_wrap(fnOne, threadID) - SchedulerTracing.unstable_trace( - secondEvent.name, - currentTime, - function() - wrappedTwo = SchedulerTracing.unstable_wrap(fnTwo, threadID) - end - ) + it("should call the correct interaction subscriber methods when a wrapped callback is canceled", function() + local fnOne = jest.fn() + local fnTwo = jest.fn() + local wrappedOne, wrappedTwo + SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() + wrappedOne = SchedulerTracing.unstable_wrap(fnOne, threadID) + SchedulerTracing.unstable_trace(secondEvent.name, currentTime, function() + wrappedTwo = SchedulerTracing.unstable_wrap(fnTwo, threadID) end) - jestExpect(onInteractionTraced).toHaveBeenCalledTimes(2) - jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() - jestExpect(onWorkCanceled).never.toHaveBeenCalled() - jestExpect(onWorkStarted).toHaveBeenCalledTimes(2) - jestExpect(onWorkStopped).toHaveBeenCalledTimes(2) - wrappedTwo:cancel() - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - secondEvent - ) - jestExpect(onWorkCanceled).toHaveBeenCalledTimes(1) - jestExpect(onWorkCanceled).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent, secondEvent }), - threadID - ) - wrappedOne:cancel() - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) - jestExpect(onWorkCanceled).toHaveBeenCalledTimes(2) - jestExpect(onWorkCanceled).toHaveBeenLastNotifiedOfWork( - Set.new({ firstEvent }), - threadID - ) - jestExpect(fnOne).never.toHaveBeenCalled() - jestExpect(fnTwo).never.toHaveBeenCalled() - end - ) + end) + jestExpect(onInteractionTraced).toHaveBeenCalledTimes(2) + jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() + jestExpect(onWorkCanceled).never.toHaveBeenCalled() + jestExpect(onWorkStarted).toHaveBeenCalledTimes(2) + jestExpect(onWorkStopped).toHaveBeenCalledTimes(2) + wrappedTwo:cancel() + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(secondEvent) + jestExpect(onWorkCanceled).toHaveBeenCalledTimes(1) + jestExpect(onWorkCanceled).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent, secondEvent }), threadID) + wrappedOne:cancel() + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(firstEvent) + jestExpect(onWorkCanceled).toHaveBeenCalledTimes(2) + jestExpect(onWorkCanceled).toHaveBeenLastNotifiedOfWork(Set.new({ firstEvent }), threadID) + jestExpect(fnOne).never.toHaveBeenCalled() + jestExpect(fnTwo).never.toHaveBeenCalled() + end) it( "should not end an interaction twice if wrap is used to schedule follow up work within another wrap", function() @@ -527,39 +439,32 @@ describe("TracingSubscriptions", function() wrappedTwo() jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) - end - ) - it( - "should not decrement the interaction count twice if a wrapped function is run twice", - function() - local unwrappedOne = jest.fn() - local unwrappedTwo = jest.fn() - local wrappedOne, wrappedTwo - SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() - wrappedOne = SchedulerTracing.unstable_wrap(unwrappedOne, threadID) - wrappedTwo = SchedulerTracing.unstable_wrap(unwrappedTwo, threadID) - end) - jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() - wrappedOne() - jestExpect(unwrappedOne).toHaveBeenCalledTimes(1) - jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() - wrappedOne() - jestExpect(unwrappedOne).toHaveBeenCalledTimes(2) - jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() - wrappedTwo() - jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) - jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction( - firstEvent - ) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(firstEvent) end ) + it("should not decrement the interaction count twice if a wrapped function is run twice", function() + local unwrappedOne = jest.fn() + local unwrappedTwo = jest.fn() + local wrappedOne, wrappedTwo + SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() + wrappedOne = SchedulerTracing.unstable_wrap(unwrappedOne, threadID) + wrappedTwo = SchedulerTracing.unstable_wrap(unwrappedTwo, threadID) + end) + jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) + jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() + wrappedOne() + jestExpect(unwrappedOne).toHaveBeenCalledTimes(1) + jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) + jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() + wrappedOne() + jestExpect(unwrappedOne).toHaveBeenCalledTimes(2) + jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) + jestExpect(onInteractionScheduledWorkCompleted).never.toHaveBeenCalled() + wrappedTwo() + jestExpect(onInteractionTraced).toHaveBeenCalledTimes(1) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1) + jestExpect(onInteractionScheduledWorkCompleted).toHaveBeenLastNotifiedOfInteraction(firstEvent) + end) it("should unsubscribe", function() SchedulerTracing.unstable_unsubscribe(firstSubscriber) SchedulerTracing.unstable_trace(firstEvent.name, currentTime, function() end) diff --git a/modules/scheduler/src/forks/SchedulerHostConfig.default.lua b/modules/scheduler/src/forks/SchedulerHostConfig.default.lua index 26975b12..3c9ce760 100644 --- a/modules/scheduler/src/forks/SchedulerHostConfig.default.lua +++ b/modules/scheduler/src/forks/SchedulerHostConfig.default.lua @@ -77,10 +77,7 @@ local function performWorkUntilDeadline() local ok, result local function doWork() - local hasMoreWork = (scheduledHostCallback :: any)( - hasTimeRemaining, - currentTime - ) + local hasMoreWork = (scheduledHostCallback :: any)(hasTimeRemaining, currentTime) if not hasMoreWork then isMessageLoopRunning = false scheduledHostCallback = nil diff --git a/modules/scheduler/src/forks/SchedulerHostConfig.mock.lua b/modules/scheduler/src/forks/SchedulerHostConfig.mock.lua index 5c3d2708..42e2c251 100644 --- a/modules/scheduler/src/forks/SchedulerHostConfig.mock.lua +++ b/modules/scheduler/src/forks/SchedulerHostConfig.mock.lua @@ -47,11 +47,8 @@ exports.shouldYieldToHost = function(): boolean -- https://jira.rbx.com/browse/CLI-35978 local values: any = yieldedValues if - ( - expectedNumberOfYields ~= -1 - and values ~= nil - and #values >= expectedNumberOfYields - ) or (shouldYieldForPaint and needsPaint) + (expectedNumberOfYields ~= -1 and values ~= nil and #values >= expectedNumberOfYields) + or (shouldYieldForPaint and needsPaint) then -- We yielded at least as many values as expected. Stop flushing. didStop = true @@ -216,10 +213,7 @@ end exports.unstable_flushAll = function() if yieldedValues ~= nil then - error( - "Log is not empty. Assert on the log of yielded values before " - .. "flushing additional work." - ) + error("Log is not empty. Assert on the log of yielded values before " .. "flushing additional work.") end exports.unstable_flushAllWithoutAsserting() if yieldedValues ~= nil then diff --git a/modules/scheduler/src/init.lua b/modules/scheduler/src/init.lua index 6f0a0be1..11c80e65 100644 --- a/modules/scheduler/src/init.lua +++ b/modules/scheduler/src/init.lua @@ -50,9 +50,7 @@ local exports = { unstable_getFirstCallbackNode = Scheduler.unstable_getFirstCallbackNode, unstable_now = Scheduler.unstable_now, unstable_forceFrameRate = Scheduler.unstable_forceFrameRate, - unstable_flushAllWithoutAsserting = onlyInTestError( - "unstable_flushAllWithoutAsserting" - ) :: any, + unstable_flushAllWithoutAsserting = onlyInTestError("unstable_flushAllWithoutAsserting") :: any, unstable_flushAll = onlyInTestError("unstable_flushAll"), unstable_flushNumberOfYields = onlyInTestError("unstable_flushNumberOfYields"), unstable_clearYields = onlyInTestError("unstable_clearYields") :: any, diff --git a/modules/shared/src/ConsolePatchingDev.roblox.lua b/modules/shared/src/ConsolePatchingDev.roblox.lua index 027d9699..85ab8dc9 100644 --- a/modules/shared/src/ConsolePatchingDev.roblox.lua +++ b/modules/shared/src/ConsolePatchingDev.roblox.lua @@ -76,10 +76,7 @@ exports.reenableLogs = function() end if disabledDepth < 0 then - console.error( - "disabledDepth fell below zero. " - .. "This is a bug in React. Please file an issue." - ) + console.error("disabledDepth fell below zero. " .. "This is a bug in React. Please file an issue.") end end end diff --git a/modules/shared/src/ExecutionEnvironment.lua b/modules/shared/src/ExecutionEnvironment.lua index 90f17b7d..26572d5a 100644 --- a/modules/shared/src/ExecutionEnvironment.lua +++ b/modules/shared/src/ExecutionEnvironment.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/55cb0b7eeb0e539d89858b8ed69beabf7fe2fb46/packages/shared/ExecutionEnvironment.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/ExecutionEnvironment.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/ReactComponentStackFrame.lua b/modules/shared/src/ReactComponentStackFrame.lua index 366414c9..30b624db 100644 --- a/modules/shared/src/ReactComponentStackFrame.lua +++ b/modules/shared/src/ReactComponentStackFrame.lua @@ -17,9 +17,7 @@ type Source = ReactElementType.Source -- ROBLOX deviation: Needed to properly type class components local flowtypes = require("./flowtypes.roblox.lua") -type React_StatelessFunctionalComponent

= flowtypes.React_StatelessFunctionalComponent< - P -> +type React_StatelessFunctionalComponent

= flowtypes.React_StatelessFunctionalComponent

type React_ComponentType

= flowtypes.React_ComponentType

type ReactComponent

= React_StatelessFunctionalComponent

| React_ComponentType

@@ -189,11 +187,7 @@ local function describeNativeComponentFrame( local sampleIndex = #sampleLines - 1 local controlIndex = #controlLines - 1 - while - sampleIndex >= 2 - and controlIndex >= 0 - and sampleLines[sampleIndex] ~= controlLines[controlIndex] - do + while sampleIndex >= 2 and controlIndex >= 0 and sampleLines[sampleIndex] ~= controlLines[controlIndex] do -- // We expect at least one stack frame to be shared. -- // Typically this will be the root most one. However, stack frames may be -- // cut off due to maximum stack limits. In this case, one maybe cut off @@ -220,10 +214,7 @@ local function describeNativeComponentFrame( controlIndex = controlIndex - 1 -- // We may still have similar intermediate frames from the construct call. -- // The next one that isn't the same should be our match though. - if - controlIndex < 0 - or sampleLines[sampleIndex] ~= controlLines[controlIndex] - then + if controlIndex < 0 or sampleLines[sampleIndex] ~= controlLines[controlIndex] then -- deviation: add the ' in ' prefix to format the component stack -- similar to React local frame = "\n" .. prefix .. sampleLines[sampleIndex] @@ -280,11 +271,7 @@ end -- ROBLOX deviation: Lua's patterns work slightly differently than regexes local BEFORE_SLASH_PATTERN = "^(.*)[\\/]" -function describeComponentFrame( - name: string | nil, - source: Source | nil, - ownerName: string | nil -): string +function describeComponentFrame(name: string | nil, source: Source | nil, ownerName: string | nil): string local sourceInfo = "" if _G.__DEV__ and source then @@ -365,9 +352,7 @@ function describeFunctionComponentFrame( end -- ROBLOX deviation: use debug.info to discover function names -- ROBLOX FIXME: find out how non-functions are getting into here, they pollute test output - local name = if type(fn) == "function" - then debug.info(fn :: Function, "n") - else tostring(fn) + local name = if type(fn) == "function" then debug.info(fn :: Function, "n") else tostring(fn) local ownerName = nil if _G.__DEV__ and ownerFn then -- ROBLOX deviation: owner may be a function or a table diff --git a/modules/shared/src/ReactErrorUtils.lua b/modules/shared/src/ReactErrorUtils.lua index f14e713c..4928a0dd 100644 --- a/modules/shared/src/ReactErrorUtils.lua +++ b/modules/shared/src/ReactErrorUtils.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/shared/ReactErrorUtils.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/ReactErrorUtils.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -97,10 +97,11 @@ clearCaughtError = function() caughtError = nil return err else - invariant( - false, - "clearCaughtError was called but no error was captured. This error " - .. "is likely caused by a bug in React. Please file an issue." + error( + Error.new( + "clearCaughtError was called but no error was captured. This error " + .. "is likely caused by a bug in React. Please file an issue." + ) ) -- deviation: luau doesn't know that invariant throws, so we return nil return nil diff --git a/modules/shared/src/ReactFeatureFlags.lua b/modules/shared/src/ReactFeatureFlags.lua index 4cfae466..c2f662c8 100644 --- a/modules/shared/src/ReactFeatureFlags.lua +++ b/modules/shared/src/ReactFeatureFlags.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/ba82eea3837e4aaeb5a30b7827b664a8c2128d2e/packages/shared/ReactFeatureFlags.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/ReactFeatureFlags.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/ReactFiberHostConfig/WithNoHydration.lua b/modules/shared/src/ReactFiberHostConfig/WithNoHydration.lua index d6e650a7..0a57a185 100644 --- a/modules/shared/src/ReactFiberHostConfig/WithNoHydration.lua +++ b/modules/shared/src/ReactFiberHostConfig/WithNoHydration.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/ReactFiberHostConfigWithNoHydration.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHostConfigWithNoHydration.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/ReactFiberHostConfig/WithNoPersistence.lua b/modules/shared/src/ReactFiberHostConfig/WithNoPersistence.lua index d915322d..06c89792 100644 --- a/modules/shared/src/ReactFiberHostConfig/WithNoPersistence.lua +++ b/modules/shared/src/ReactFiberHostConfig/WithNoPersistence.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/c5d2fc7127654e43de59fff865b74765a103c4a5/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHostConfigWithNoPersistence.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/ReactFiberHostConfig/WithNoTestSelectors.lua b/modules/shared/src/ReactFiberHostConfig/WithNoTestSelectors.lua index a5d1d05a..4ad19d6e 100644 --- a/modules/shared/src/ReactFiberHostConfig/WithNoTestSelectors.lua +++ b/modules/shared/src/ReactFiberHostConfig/WithNoTestSelectors.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/3cde22a84e246fc5361f038bf0c23405b2572c22/packages/react-reconciler/src/ReactFiberHostConfigWithNoTestSelectors.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHostConfigWithNoTestSelectors.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/ReactInstanceMap.lua b/modules/shared/src/ReactInstanceMap.lua index 2ee5b733..69169808 100644 --- a/modules/shared/src/ReactInstanceMap.lua +++ b/modules/shared/src/ReactInstanceMap.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/2ba43edc2675380a0f2222f351475bf9d750c6a9/packages/shared/ReactInstanceMap.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/ReactInstanceMap.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/shared/src/ReactSharedInternals/ReactCurrentBatchConfig.lua b/modules/shared/src/ReactSharedInternals/ReactCurrentBatchConfig.lua index eb52dae4..72fd0ae1 100644 --- a/modules/shared/src/ReactSharedInternals/ReactCurrentBatchConfig.lua +++ b/modules/shared/src/ReactSharedInternals/ReactCurrentBatchConfig.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/92fcd46cc79bbf45df4ce86b0678dcef3b91078d/packages/react/src/ReactCurrentBatchConfig.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactCurrentBatchConfig.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -16,5 +16,5 @@ local ReactCurrentBatchConfig = { transition = 0, } - +--[[* return ReactCurrentBatchConfig diff --git a/modules/shared/src/ReactSharedInternals/ReactCurrentDispatcher.lua b/modules/shared/src/ReactSharedInternals/ReactCurrentDispatcher.lua index 0502375b..658557e5 100644 --- a/modules/shared/src/ReactSharedInternals/ReactCurrentDispatcher.lua +++ b/modules/shared/src/ReactSharedInternals/ReactCurrentDispatcher.lua @@ -27,33 +27,17 @@ type ReactBindingUpdater = ReactTypes.ReactBindingUpdater -- ROBLOX deviation END: binding support type MutableSourceVersion = ReactTypes.MutableSourceVersion type MutableSource = ReactTypes.MutableSource -type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn< - Source, - Snapshot -> -type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn< - Source, - Snapshot -> +type MutableSourceSubscribeFn = ReactTypes.MutableSourceSubscribeFn +type MutableSourceGetSnapshotFn = ReactTypes.MutableSourceGetSnapshotFn type BasicStateAction = ((S) -> S) | S type Dispatch = (A) -> () export type Dispatcher = { - readContext: ( - context: ReactContext, - observedBits: nil | number | boolean - ) -> T, + readContext: (context: ReactContext, observedBits: nil | number | boolean) -> T, useState: (initialState: (() -> S) | S) -> (S, Dispatch>), - useReducer: ( - reducer: (S, A) -> S, - initialArg: I, - init: ((I) -> S)? - ) -> (S, Dispatch), - useContext: ( - context: ReactContext, - observedBits: nil | number | boolean - ) -> T, + useReducer: (reducer: (S, A) -> S, initialArg: I, init: ((I) -> S)?) -> (S, Dispatch), + useContext: (context: ReactContext, observedBits: nil | number | boolean) -> T, -- ROBLOX deviation START: TS models this slightly differently, which is needed to have an initially empty ref and clear the ref, and still typecheck useRef: (initialValue: T) -> { current: T | nil }, -- ROBLOX deviation END @@ -62,12 +46,12 @@ export type Dispatcher = { -- ROBLOX deviation END useEffect: ( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ) -> (), useLayoutEffect: ( -- ROBLOX TODO: Luau needs union type packs for this type to translate idiomatically - create: (() -> ()) | (() -> (() -> ())), + create: (() -> ()) | (() -> () -> ()), deps: Array | nil ) -> (), useCallback: (callback: T, deps: Array | nil) -> T, diff --git a/modules/shared/src/ReactSharedInternals/init.lua b/modules/shared/src/ReactSharedInternals/init.lua index 2477e9a6..fd0f1837 100644 --- a/modules/shared/src/ReactSharedInternals/init.lua +++ b/modules/shared/src/ReactSharedInternals/init.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/607148673b3156d051d1fed17cd49e83698dce54/packages/react/src/ReactSharedInternals.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactSharedInternals.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -36,7 +36,6 @@ local ReactCurrentBatchConfig = require("./ReactCurrentBatchConfig") local ReactCurrentOwner = require("./ReactCurrentOwner") local ReactDebugCurrentFrame = require("./ReactDebugCurrentFrame") local IsSomeRendererActing = require("./IsSomeRendererActing") - local ReactSharedInternals = { ReactCurrentDispatcher = ReactCurrentDispatcher, ReactCurrentBatchConfig = ReactCurrentBatchConfig, diff --git a/modules/shared/src/ReactVersion.lua b/modules/shared/src/ReactVersion.lua index 1a7e54c8..6b8625e6 100644 --- a/modules/shared/src/ReactVersion.lua +++ b/modules/shared/src/ReactVersion.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/a89854bc936668d325cac9a22e2ebfa128c7addf/packages/shared/ReactVersion.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/ReactVersion.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/shared/src/__tests__/ReactErrorProd-internal.spec.lua b/modules/shared/src/__tests__/ReactErrorProd-internal.spec.lua index 1239ecd2..9a99112d 100644 --- a/modules/shared/src/__tests__/ReactErrorProd-internal.spec.lua +++ b/modules/shared/src/__tests__/ReactErrorProd-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/9a5576f4d263ac5d7a9462a287d1524fda3355b8/packages/shared/__tests__/ReactErrorProd-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/__tests__/ReactErrorProd-test.internal.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/__tests__/ReactErrorUtils-internal.spec.lua b/modules/shared/src/__tests__/ReactErrorUtils-internal.spec.lua index d5cfd7c4..d30baa2f 100644 --- a/modules/shared/src/__tests__/ReactErrorUtils-internal.spec.lua +++ b/modules/shared/src/__tests__/ReactErrorUtils-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/3e94bce765d355d74f6a60feb4addb6d196e3482/packages/shared/__tests__/ReactErrorUtils-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/__tests__/ReactErrorUtils-test.internal.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -167,12 +167,7 @@ it("can be shimmed", function() local callback = function() error(err) end - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( - "foo", - callback, - nil, - "somearg" - ) + ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError("foo", callback, nil, "somearg") jestExpect(function() ReactErrorUtils.rethrowCaughtError() diff --git a/modules/shared/src/__tests__/ReactSymbols-internal.spec.lua b/modules/shared/src/__tests__/ReactSymbols-internal.spec.lua index d712b46b..6d61f21e 100644 --- a/modules/shared/src/__tests__/ReactSymbols-internal.spec.lua +++ b/modules/shared/src/__tests__/ReactSymbols-internal.spec.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/cdbfa6b5dd692220e5996ec453d46fc10aff046a/packages/shared/__tests__/ReactSymbols-test.internal.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/__tests__/ReactSymbols-test.internal.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/checkPropTypes.lua b/modules/shared/src/checkPropTypes.lua index 6182e551..f37b1132 100644 --- a/modules/shared/src/checkPropTypes.lua +++ b/modules/shared/src/checkPropTypes.lua @@ -19,8 +19,7 @@ local console = require("./console") local loggedTypeFailures = {} local ReactComponentStackFrame = require("./ReactComponentStackFrame") -local describeUnknownElementTypeFrameInDEV = - ReactComponentStackFrame.describeUnknownElementTypeFrameInDEV +local describeUnknownElementTypeFrameInDEV = ReactComponentStackFrame.describeUnknownElementTypeFrameInDEV local ReactSharedInternals = require("./ReactSharedInternals") @@ -63,10 +62,7 @@ local function checkPropTypes

( -- ROBLOX deviation: warns if both propType and validateProps defined. if propTypes and validateProps then - console.warn( - "You've defined both propTypes and validateProps on " - .. (componentName or "a component") - ) + console.warn("You've defined both propTypes and validateProps on " .. (componentName or "a component")) end -- ROBLOX deviation: also checks validateProps if present @@ -82,8 +78,7 @@ local function checkPropTypes

( local success, failureReason = validateProps(props) if not success then - failureReason = failureReason - or "" + failureReason = failureReason or "" local message = string.format( "validateProps failed on a %s type in %s: %s", location, @@ -163,21 +158,12 @@ local function checkPropTypes

( end -- ROBLOX FIXME: Luau analyze doesn't understand isErrorObject's effect as a predicate meaning result ~= nil - if - isErrorObject - and loggedTypeFailures[(result :: any).message] == nil - then + if isErrorObject and loggedTypeFailures[(result :: any).message] == nil then -- Only monitor this failure once because there tends to be a lot of the -- same error. loggedTypeFailures[tostring((result :: any).message)] = true setCurrentlyValidatingElement(element) - console.warn( - string.format( - "Failed %s type: %s", - location, - tostring((result :: any).message) - ) - ) + console.warn(string.format("Failed %s type: %s", location, tostring((result :: any).message))) setCurrentlyValidatingElement(nil) end end diff --git a/modules/shared/src/consoleWithStackDev.lua b/modules/shared/src/consoleWithStackDev.lua index 5b6ad2dc..37fd5d52 100644 --- a/modules/shared/src/consoleWithStackDev.lua +++ b/modules/shared/src/consoleWithStackDev.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/cb141681750c8221ac799074df09df2bb448c7a4/packages/shared/consoleWithStackDev.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/consoleWithStackDev.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/formatProdErrorMessage.lua b/modules/shared/src/formatProdErrorMessage.lua index 0a8f46b2..ca661b31 100644 --- a/modules/shared/src/formatProdErrorMessage.lua +++ b/modules/shared/src/formatProdErrorMessage.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/75955bf1d7ff6c2c1f4052f4a84dd2ce6944c62e/packages/shared/formatProdErrorMessage.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/formatProdErrorMessage.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates. diff --git a/modules/shared/src/getComponentName.lua b/modules/shared/src/getComponentName.lua index dab16c2a..8384a4ef 100644 --- a/modules/shared/src/getComponentName.lua +++ b/modules/shared/src/getComponentName.lua @@ -44,10 +44,7 @@ local function getWrappedName(outerType: any, innerType: any, wrapperName: strin functionName = innerType.displayName or innerType.name or "" end return outerType.displayName - or ( - functionName ~= "" and string.format("%s(%s)", wrapperName, functionName) - or wrapperName - ) + or (functionName ~= "" and string.format("%s(%s)", wrapperName, functionName) or wrapperName) end local function getContextName(type: ReactContext): string diff --git a/modules/shared/src/invokeGuardedCallbackImpl.lua b/modules/shared/src/invokeGuardedCallbackImpl.lua index e720e3cd..7dd32d41 100644 --- a/modules/shared/src/invokeGuardedCallbackImpl.lua +++ b/modules/shared/src/invokeGuardedCallbackImpl.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/702fad4b1b48ac8f626ed3f35e8f86f5ea728084/packages/shared/invokeGuardedCallbackImpl.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/invokeGuardedCallbackImpl.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -77,7 +77,7 @@ if _G.__DEV__ then invokeGuardedCallbackImpl = function invokeGuardedCallbackDev(name, func, context, a, b, c, d, e, f) { -- If document doesn't exist we know for sure we will crash in this method -- when we call document.createEvent(). However this can cause confusing - -- errors: https://github.com/facebookincubator/create-react-app/issues/3482 + -- errors: https://github.com/facebook/create-react-app/issues/3482 -- So we preemptively throw with a better message instead. invariant(typeof document ~= 'undefined', 'The `document` global was defined when React was initialized, but is not ' + 'defined anymore. This can happen in a test environment if a component ' + 'schedules an update from an asynchronous callback, but the test has already ' + 'finished running. To solve this, you can either unmount the component at ' + 'the end of your test (and ensure that any asynchronous operations get ' + 'canceled in `componentWillUnmount`), or you can change the test itself ' + 'to be asynchronous.') local evt = document.createEvent('Event') diff --git a/modules/shared/src/isValidElementType.lua b/modules/shared/src/isValidElementType.lua index b25b3894..3bf49fbf 100644 --- a/modules/shared/src/isValidElementType.lua +++ b/modules/shared/src/isValidElementType.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/b61174fb7b09580c1ec2a8f55e73204b706d2935/packages/shared/isValidElementType.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/isValidElementType.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * @@ -57,13 +57,13 @@ return function(type) end if - type["$$typeof"] == REACT_LAZY_TYPE - or type["$$typeof"] == REACT_MEMO_TYPE - or type["$$typeof"] == REACT_PROVIDER_TYPE - or type["$$typeof"] == REACT_CONTEXT_TYPE - or type["$$typeof"] == REACT_FORWARD_REF_TYPE - or type["$$typeof"] == REACT_FUNDAMENTAL_TYPE - or type["$$typeof"] == REACT_BLOCK_TYPE + type["$typeof"] == REACT_LAZY_TYPE + or type["$typeof"] == REACT_MEMO_TYPE + or type["$typeof"] == REACT_PROVIDER_TYPE + or type["$typeof"] == REACT_CONTEXT_TYPE + or type["$typeof"] == REACT_FORWARD_REF_TYPE + or type["$typeof"] == REACT_FUNDAMENTAL_TYPE + or type["$typeof"] == REACT_BLOCK_TYPE or type[1] == REACT_SERVER_BLOCK_TYPE then return true diff --git a/modules/shared/src/objectIs.lua b/modules/shared/src/objectIs.lua index a53deaee..f8d50dcf 100644 --- a/modules/shared/src/objectIs.lua +++ b/modules/shared/src/objectIs.lua @@ -1,5 +1,5 @@ --!strict --- ROBLOX upstream: https://github.com/facebook/react/blob/6faf6f5eb1705eef39a1d762d6ee381930f36775/packages/shared/objectIs.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/objectIs.js --[[* * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/modules/shared/src/shallowEqual.lua b/modules/shared/src/shallowEqual.lua index 7068d857..fbf306eb 100644 --- a/modules/shared/src/shallowEqual.lua +++ b/modules/shared/src/shallowEqual.lua @@ -1,4 +1,4 @@ --- ROBLOX upstream: https://github.com/facebook/react/blob/a9b035b0c2b8235405835beca0c4db2cc37f18d0/packages/shared/shallowEqual.js +-- ROBLOX upstream: https://github.com/facebook/react/blob/v18.2.0/packages/shared/shallowEqual.js --!strict --[[* * Copyright (c) Facebook, Inc. and its affiliates.