diff --git a/.github/workflows/runtime_build.yml b/.github/workflows/runtime_build.yml index 73b86ae9d772c..eea96e36d3ff5 100644 --- a/.github/workflows/runtime_build.yml +++ b/.github/workflows/runtime_build.yml @@ -7,21 +7,30 @@ on: paths-ignore: - 'compiler/**' +env: + WORKER_COUNT: 20 + jobs: define_build_params: name: Build build params runs-on: ubuntu-latest outputs: - bundle_type: ${{ steps.define_bundle_types.outputs.result }} + worker_id: ${{ steps.define_worker_id.outputs.result }} release_channel: ${{ steps.define_release_channels.outputs.result }} steps: - uses: actions/checkout@v4 - uses: actions/github-script@v7 - id: define_bundle_types + id: define_worker_id with: script: | - const {bundleTypes} = require('./scripts/rollup/bundles'); - return Object.values(bundleTypes); + function range(from, to) { + const arr = []; + for (let n = from; n < to; n++) { + arr.push(n); + } + return arr; + } + return range(0, process.env.WORKER_COUNT); - uses: actions/github-script@v7 id: define_release_channels with: @@ -34,7 +43,7 @@ jobs: needs: define_build_params strategy: matrix: - bundle_type: ${{ fromJSON(needs.define_build_params.outputs.bundle_type) }} + worker_id: ${{ fromJSON(needs.define_build_params.outputs.worker_id) }} release_channel: ${{ fromJSON(needs.define_build_params.outputs.release_channel) }} steps: - uses: actions/checkout@v4 @@ -50,7 +59,7 @@ jobs: path: "**/node_modules" key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - - run: yarn build --b=${{ matrix.bundle_type }} --r=${{ matrix.release_channel }} --ci=github + - run: yarn build --index=${{ matrix.worker_id }} --total=${{ env.WORKER_COUNT }} --r=${{ matrix.release_channel }} --ci=github env: CI: github RELEASE_CHANNEL: ${{ matrix.release_channel }} @@ -59,18 +68,13 @@ jobs: - name: Archive build uses: actions/upload-artifact@v4 with: - name: ${{ matrix.bundle_type }}_${{ matrix.release_channel }} + name: build_${{ matrix.worker_id }}_${{ matrix.release_channel }} path: | build lint_build: name: yarn lint-build - needs: [define_build_params, build] - strategy: - matrix: - bundle_type: ${{ fromJSON(needs.define_build_params.outputs.bundle_type) }} - release_channel: ${{ fromJSON(needs.define_build_params.outputs.release_channel) }} - continue-on-error: true + needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -89,8 +93,8 @@ jobs: - name: Restore archived build uses: actions/download-artifact@v4 with: - name: ${{ matrix.bundle_type }}_${{ matrix.release_channel }} path: build + merge-multiple: true - name: Display structure of build run: ls -R build - run: yarn lint-build diff --git a/fixtures/flight/package.json b/fixtures/flight/package.json index c0d8b4fea25db..f07b667dab910 100644 --- a/fixtures/flight/package.json +++ b/fixtures/flight/package.json @@ -70,8 +70,8 @@ "predev": "cp -r ../../build/oss-experimental/* ./node_modules/", "prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/", "dev": "concurrently \"npm run dev:region\" \"npm run dev:global\"", - "dev:global": "NODE_ENV=development BUILD_PATH=dist node --experimental-loader ./loader/global.js server/global", - "dev:region": "NODE_ENV=development BUILD_PATH=dist nodemon --watch src --watch dist -- --enable-source-maps --experimental-loader ./loader/region.js --conditions=react-server server/region", + "dev:global": "NODE_ENV=development BUILD_PATH=dist node --experimental-loader ./loader/global.js --inspect=127.0.0.1:9230 server/global", + "dev:region": "NODE_ENV=development BUILD_PATH=dist nodemon --watch src --watch dist -- --enable-source-maps --experimental-loader ./loader/region.js --conditions=react-server --inspect=127.0.0.1:9229 server/region", "start": "node scripts/build.js && concurrently \"npm run start:region\" \"npm run start:global\"", "start:global": "NODE_ENV=production node --experimental-loader ./loader/global.js server/global", "start:region": "NODE_ENV=production node --experimental-loader ./loader/region.js --conditions=react-server server/region", diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 75b195e1e25fb..b5e9ed28fde52 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -150,6 +150,7 @@ import { disableDefaultPropsExceptForClasses, enableAsyncIterableChildren, disableStringRefs, + enableOwnerStacks, } from 'shared/ReactFeatureFlags'; import assign from 'shared/assign'; @@ -2372,6 +2373,27 @@ function renderNodeDestructive( key == null ? (childIndex === -1 ? 0 : childIndex) : key; const keyPath = [task.keyPath, name, keyOrIndex]; if (task.replay !== null) { + if (__DEV__ && enableOwnerStacks) { + const debugTask: null | ConsoleTask = element._debugTask; + if (debugTask) { + debugTask.run( + replayElement.bind( + null, + request, + task, + keyPath, + name, + keyOrIndex, + childIndex, + type, + props, + ref, + task.replay, + ), + ); + return; + } + } replayElement( request, task, @@ -2388,6 +2410,23 @@ function renderNodeDestructive( // prelude and skip it during the replay. } else { // We're doing a plain render. + if (__DEV__ && enableOwnerStacks) { + const debugTask: null | ConsoleTask = element._debugTask; + if (debugTask) { + debugTask.run( + renderElement.bind( + null, + request, + task, + keyPath, + type, + props, + ref, + ), + ); + return; + } + } renderElement(request, task, keyPath, type, props, ref); } return; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 7ff1e47897299..cb0d5ee395232 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -168,7 +168,7 @@ function getStack(error: Error): string { function initCallComponentFrame(): string { // Extract the stack frame of the callComponentInDEV function. - const error = callComponentInDEV(Error, 'react-stack-top-frame', {}); + const error = callComponentInDEV(Error, 'react-stack-top-frame', {}, null); const stack = getStack(error); const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0; const endIdx = stack.indexOf('\n', startIdx); @@ -991,6 +991,7 @@ function callComponentInDEV( Component: (p: Props, arg: void) => R, props: Props, componentDebugInfo: ReactComponentInfo, + debugTask: null | ConsoleTask, ): R { // The secondArg is always undefined in Server Components since refs error early. const secondArg = undefined; @@ -998,6 +999,18 @@ function callComponentInDEV( try { if (supportsComponentStorage) { // Run the component in an Async Context that tracks the current owner. + if (enableOwnerStacks && debugTask) { + return debugTask.run( + // $FlowFixMe[method-unbinding] + componentStorage.run.bind( + componentStorage, + componentDebugInfo, + Component, + props, + secondArg, + ), + ); + } return componentStorage.run( componentDebugInfo, Component, @@ -1005,6 +1018,9 @@ function callComponentInDEV( secondArg, ); } else { + if (enableOwnerStacks && debugTask) { + return debugTask.run(Component.bind(null, props, secondArg)); + } return Component(props, secondArg); } } finally { @@ -1028,6 +1044,7 @@ function renderFunctionComponent( props: Props, owner: null | ReactComponentInfo, // DEV-only stack: null | string, // DEV-only + debugTask: null | ConsoleTask, // DEV-only validated: number, // DEV-only ): ReactJSONValue { // Reset the task's thenable state before continuing, so that if a later @@ -1075,11 +1092,22 @@ function renderFunctionComponent( task.environmentName = componentEnv; if (enableOwnerStacks) { - warnForMissingKey(request, key, validated, componentDebugInfo); + warnForMissingKey( + request, + key, + validated, + componentDebugInfo, + debugTask, + ); } } prepareToUseHooksForComponent(prevThenableState, componentDebugInfo); - result = callComponentInDEV(Component, props, componentDebugInfo); + result = callComponentInDEV( + Component, + props, + componentDebugInfo, + debugTask, + ); } else { prepareToUseHooksForComponent(prevThenableState, null); // The secondArg is always undefined in Server Components since refs error early. @@ -1235,6 +1263,7 @@ function warnForMissingKey( key: null | string, validated: number, componentDebugInfo: ReactComponentInfo, + debugTask: null | ConsoleTask, ): void { if (__DEV__) { if (validated !== 2) { @@ -1267,6 +1296,7 @@ function warnForMissingKey( }, null, componentDebugInfo, + debugTask, ); } } @@ -1482,6 +1512,7 @@ function renderElement( props: any, owner: null | ReactComponentInfo, // DEV only stack: null | string, // DEV only + debugTask: null | ConsoleTask, // DEV only validated: number, // DEV only ): ReactJSONValue { if (ref !== null && ref !== undefined) { @@ -1514,6 +1545,7 @@ function renderElement( props, owner, stack, + debugTask, validated, ); } else if (type === REACT_FRAGMENT_TYPE && key === null) { @@ -1562,6 +1594,7 @@ function renderElement( props, owner, stack, + debugTask, validated, ); } @@ -1574,6 +1607,7 @@ function renderElement( props, owner, stack, + debugTask, validated, ); } @@ -1587,6 +1621,7 @@ function renderElement( props, owner, stack, + debugTask, validated, ); } @@ -2190,6 +2225,7 @@ function renderModelDestructive( ? element._debugStack : filterDebugStack(element._debugStack) : null, + __DEV__ && enableOwnerStacks ? element._debugTask : null, __DEV__ && enableOwnerStacks ? element._store.validated : 0, ); if ( diff --git a/scripts/rollup/build-all-release-channels.js b/scripts/rollup/build-all-release-channels.js index 621b5d2a68600..93c0b60ceda62 100644 --- a/scripts/rollup/build-all-release-channels.js +++ b/scripts/rollup/build-all-release-channels.js @@ -16,7 +16,6 @@ const { rcNumber, } = require('../../ReactVersions'); const yargs = require('yargs'); -const Bundles = require('./bundles'); const {buildEverything} = require('./build-ghaction'); // Runs the build script for both stable and experimental release channels, @@ -65,12 +64,17 @@ const argv = yargs.wrap(yargs.terminalWidth()).options({ default: 'experimental', choices: ['experimental', 'stable'], }, - bundleType: { - alias: 'b', - describe: 'Build the given bundle type.', + index: { + alias: 'i', + describe: 'Worker id.', requiresArg: true, - type: 'string', - choices: Object.values(Bundles.bundleTypes), + type: 'number', + }, + total: { + alias: 't', + describe: 'Total number of workers.', + requiresArg: true, + type: 'number', }, ci: { describe: 'Run tests in CI', @@ -82,7 +86,7 @@ const argv = yargs.wrap(yargs.terminalWidth()).options({ async function main() { if (argv.ci === 'github') { - await buildEverything(argv.bundleType); + await buildEverything(argv.index, argv.total); switch (argv.releaseChannel) { case 'stable': { processStable('./build'); diff --git a/scripts/rollup/build-ghaction.js b/scripts/rollup/build-ghaction.js index ce2ee7addb878..d1338fdd26400 100644 --- a/scripts/rollup/build-ghaction.js +++ b/scripts/rollup/build-ghaction.js @@ -803,7 +803,7 @@ function handleRollupError(error) { } } -async function buildEverything(bundleTypeToBuild) { +async function buildEverything(index, total) { if (!argv['unsafe-partial']) { await asyncRimRaf('build'); } @@ -840,10 +840,9 @@ async function buildEverything(bundleTypeToBuild) { return !shouldSkipBundle(bundle, bundleType); }); - // TODO: cleanup the rest of this function - bundles = bundles.filter( - ([, bundleType]) => bundleType === bundleTypeToBuild - ); + const nodeTotal = parseInt(total, 10); + const nodeIndex = parseInt(index, 10); + bundles = bundles.filter((_, i) => i % nodeTotal === nodeIndex); await Promise.all( bundles.map(([bundle, bundleType]) => createBundle(bundle, bundleType))