feat:unified withStorybook wrapper for entrypoint-swapping#871
Conversation
🦋 Changeset detectedLatest commit: 7960793 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@copilot, can you try again? It looks like you had some internal error. |
…overrides to withStorybook M4 implementation: - Add resolveEntryPoint() to detect app entry (Expo, Expo Router, RN CLI) - Add entry-point swapping in resolver when STORYBOOK_ENABLED=true - Add websocket env variable overrides (STORYBOOK_WS_HOST/PORT/SECURED) - Add comprehensive tests for all new functionality Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/58a5fb17-1ee3-4e0b-ae60-aee474de2d32 Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/58a5fb17-1ee3-4e0b-ae60-aee474de2d32 Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com>
Implemented M4 in commits a889fb4 and 537de98. The changes add:
|
|
@copilot Did you catch that we'd need backwards support? I think it'd be best if we created 2 separate |
- Restore withStorybook to original behavior (no entry-point swapping, no ws env overrides) - Create withStorybookSwap with entry-point swapping + ws env overrides - Export shared utilities (resolveEntryPoint, resolveFileWithExtensions, resolveStorybookEntry, applyWebsocketEnvOverrides) from withStorybook.ts - Add metro/withStorybookSwap.js re-export and package.json export path - Add withStorybookSwap to tsup build entries - Tests: 33 passing across 5 suites Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/a3213ee7-9a44-4b9a-a4b2-f434af51c3ce Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/a3213ee7-9a44-4b9a-a4b2-f434af51c3ce Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com>
|
@copilot I'm aiming for the new config wrapper to be a single agnostic one, exposed as This exports a single named export ( The types of this function should be like so: Though internally we should try to be somewhat type-safe, but externally it's not exposed. |
…/withStorybook Creates a bundler-agnostic withStorybook<T>(config: T, options?): T that auto-detects Metro vs Repack and delegates accordingly: - Metro: entry-point swapping + ws env overrides (via withStorybookSwap) - Repack: adds StorybookPlugin with ws env overrides to plugins array This allows sb init to always generate the same config regardless of bundler. Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/e08bd14d-760c-471e-938c-e26d89e9ae9b Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/e08bd14d-760c-471e-938c-e26d89e9ae9b Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com>
Added a unified
Usage is identical regardless of bundler: const { withStorybook } = require('@storybook/react-native/withStorybook');
module.exports = withStorybook(config);The existing |
|
@copilot the previously existing ./metro/withStorybook and ./repack/withStorybook ARE the backwards support, and should remain completely untouched. The |
There was a problem hiding this comment.
- I believe there is still some unresolved issue regarding resolving the expo router entry point in node_modules when node_modules is not in project root (monorepo), i believe we should solve this before moving forward.
- When
enabledis explicitly false, the new@storybook/react-native/withStorybookwrapper currently returns the original Metro config, so projects that still import.rnstorybookfrom their app entry keep rendering Storybook instead of being stubbed like the existing Metro wrapper does. I think it would still be useful to stub out storybook when its disabled in order to ensure its removed from the bundle. - theres not a easy way to try it in the example app still
- lite mode should still be respected so that users can opt out of the full dependency list when using the lite-ui
- repack config should done using a plugin not with a wrapper imho
Please add something along these lines would make the Expo example easy to exercise locally:
const useNewStorybookWrapper = process.env.EXPO_PUBLIC_STORYBOOK_NEW_WRAPPER === 'true';
const { withStorybook } = useNewStorybookWrapper
? require('@storybook/react-native/withStorybook')
: require('@storybook/react-native/metro/withStorybook');{
"storybook:new-wrapper": "EXPO_PUBLIC_STORYBOOK_ENABLED=true EXPO_PUBLIC_STORYBOOK_NEW_WRAPPER=true expo start",
"storybook:new-wrapper:disabled": "EXPO_PUBLIC_STORYBOOK_NEW_WRAPPER=true expo start"
}For the implementation, the disabled Metro path could be closer to the existing metro/withStorybook behavior: if enabled was explicitly set false, wrap the Metro resolver so storybook/@storybook requests become empty modules and .rnstorybook/index.(ts|tsx|js|jsx) resolves to the stub. If there is no explicit enable signal at all, keep the current strict no-op behavior for the new wrapper.
For example, withStorybook could pass the existing enabled state into the Metro enhancer and let the enhancer apply the disabled resolver behavior when enabled === false:
const hasExplicitEnabled = process.env.STORYBOOK_ENABLED != null || options.enabled != null;
const enabled = envVariableToBoolean(process.env.STORYBOOK_ENABLED, options.enabled ?? false);
if (!enabled && !hasExplicitEnabled) {
return config;
}
if (isMetroConfig(config)) {
return enhanceMetroConfig(config, { enabled, configPath });
}Then the enhancer can branch on enabled === false while preserving its current swap behavior:
export function enhanceMetroConfig(config: MetroConfig, options: EnhanceMetroOptions = {}) {
const { swap, enabled = true, configPath } = options;
const shouldStubStorybook = enabled === false;
const normalizedConfigPath = configPath ? path.resolve(configPath) : undefined;
const stubPath = path.resolve(
__dirname,
__dirname.includes(path.sep + 'src') ? './stub.tsx' : './stub.js'
);
return {
...config,
resolver: {
...config.resolver,
resolveRequest: (context, moduleName, platform) => {
const resolveFunction = config.resolver?.resolveRequest ?? context.resolveRequest;
if (shouldStubStorybook && (moduleName.startsWith('storybook') || moduleName.startsWith('@storybook'))) {
return { type: 'empty' };
}
if (platform !== 'web' && (moduleName === 'tty' || moduleName === 'os')) {
return { type: 'empty' };
}
const resolved = resolveFunction(context, moduleName, platform);
if (shouldStubStorybook && resolved.filePath && normalizedConfigPath) {
const resolvedFilePath = path.resolve(resolved.filePath);
const isConfigIndex = ['index.ts', 'index.tsx', 'index.js', 'index.jsx'].some(
(file) => resolvedFilePath === path.join(normalizedConfigPath, file)
);
if (isConfigIndex) {
return { filePath: stubPath, type: 'sourceFile' };
}
if (resolvedFilePath.startsWith(normalizedConfigPath + path.sep)) {
return { type: 'empty' };
}
}
return resolved;
},
},
};
}|
after thinking it through I'm not sure a unified wrapper is the right direction since repack is quite different, repack builds on webpack/rspack and uses plugins for extended behaviour, we only wrap metro config because of its lacking support for plugins or other ways to extend metro behaviour. it would likely be better to just extract reusable code and provide a repack plugin like we currently do but updated for the expected new flow (or exclude it entirely from this initial version) |
- Replaced multiple checks for different main config file types (main.ts, main.js, main.cjs) with a single call to getInterpretedFile, simplifying the logic and improving maintainability. - Updated error handling to provide clearer feedback when the main config file is not found.
- Deleted the ws-smoke-server.mjs script as it was no longer needed. - Removed the corresponding entry from package.json to streamline the configuration.
- Updated the View constructor to default the options parameter to an empty object if not provided, ensuring better handling of undefined values. - Modified the onDeviceUI assignment to use optional chaining for improved safety when accessing properties on the options object.
- Introduced a new optional `liteMode` parameter to both enhanceMetroConfig and enhanceRepackConfig, allowing the removal of the default Storybook UI from the bundle. - Updated the withStorybook function to pass the liteMode option, ensuring consistent behavior across configurations. - Enhanced the logic to conditionally handle the presence of the liteMode option in both configurations.
…ts in storybook.requires.ts - Added missing newlines at the end of package.json files in various directories to adhere to formatting standards. - Cleaned up import statements in storybook.requires.ts for consistency and readability.
|
@dannyhw considering how the new setup needs a differently formatted |
|
sounds like a good choice, thanks for that. I will take a look soon. |
|
Its really nice to have the entrypoint just magically swap, can't wait to see it with the new init changes too |
…877) * feat: update documentation for entry-point swapping and deviceAddons - Added a note in MANUAL_SETUP.md to clarify that the guide is for v8 and provided a link to v10 setup instructions. - Enhanced MIGRATION.md with detailed steps for migrating to the new `deviceAddons` property and entry-point swapping introduced in v10.4. - Updated README.md to reflect changes in bundler configuration and removed outdated instructions regarding app entry point modifications. - Revised various documentation files to emphasize the use of `deviceAddons` for on-device addons and the benefits of the bundler-agnostic `withStorybook` wrapper. All documentation updates aim to improve clarity and guide users through the new setup processes effectively. * refactor: streamline documentation and code for deviceAddons - Consolidated the representation of `deviceAddons` in various configuration files, ensuring consistency across `MIGRATION.md`, `README.md`, and setup guides. - Removed unnecessary line breaks for improved readability in code examples. - Added clarifying notes in documentation regarding the use of `deviceAddons` for on-device addons, enhancing user understanding of the new setup. These changes aim to provide clearer guidance and a more cohesive experience for users transitioning to the updated configuration. * docs: remove STORYBOOK_WS_HOST=auto usage from env var examples Agent-Logs-Url: https://github.com/storybookjs/react-native/sessions/d3d2217a-7248-48a9-a110-5fe72bae5bf2 Co-authored-by: ndelangen <3070389+ndelangen@users.noreply.github.com> * docs: update Expo Router setup for Storybook integration - Revised the guide to clarify the setup of Storybook as a route within an Expo Router app, emphasizing the benefits of entry-point swapping. - Updated Metro configuration instructions to use the Metro-specific `withStorybook` wrapper, ensuring proper integration without entry-point swapping. - Enhanced navigation setup details for accessing Storybook within the app, including recommendations for disabling headers for a better user experience. - Added tips and warnings to guide users on the preferred setup methods and potential pitfalls. These changes aim to improve clarity and usability for developers integrating Storybook with Expo Router. * docs: revise Re.Pack setup guide for Storybook integration - Updated the Re.Pack setup documentation to clarify the differences when using Rspack/Webpack instead of Metro, emphasizing the bundler-agnostic `withStorybook` wrapper. - Streamlined installation instructions and entry-point swapping details, ensuring users understand the necessary configurations for Rspack. - Added warnings regarding the `enablePackageExports` requirement for proper package resolution and clarified the in-app integration process using the `StorybookPlugin`. These changes aim to enhance clarity and usability for developers integrating Storybook with Re.Pack projects. * docs: update entry-point swapping instructions for Storybook integration - Revised the documentation to clarify that `.rnstorybook/index.tsx` must register a root component when Storybook is enabled. - Added detailed code examples for setting up the entry point correctly, ensuring users understand the necessary changes. - Updated subsequent steps to reflect the new structure and improved clarity on managing on-device addons and script updates. These changes aim to enhance the user experience and provide clear guidance for developers integrating Storybook with their applications. * docs: refine Metro configuration documentation for Storybook integration - Updated the documentation to clarify the use of the bundler-agnostic `withStorybook` wrapper as the recommended approach for new projects. - Enhanced the explanation of configuration options, including environment variables and their precedence over wrapper options. - Streamlined the sections on basic usage and options for both the bundler-agnostic and Metro-specific wrappers, improving clarity and usability for developers. These changes aim to provide clearer guidance for integrating Storybook with Metro projects. * docs: enhance Storybook UI configuration documentation - Removed redundant export statements for `StorybookUIRoot` across multiple sections to streamline the code examples. - Updated the WebSocket options section to clarify the auto-injection of settings when using the `withStorybook` wrapper. - Added a new subsection for testing setup with WebSocket control, providing clearer instructions for users on configuring their environments. These changes aim to improve clarity and usability for developers integrating Storybook with their applications. * docs: clarify CLI configuration for Storybook integration - Updated the CLI configuration documentation to emphasize that the `sb-rn-get-stories` command is only necessary for custom build pipelines without the `withStorybook` wrapper. - Added a note highlighting that the `storybook.requires.ts` file is automatically generated when using the recommended setup, reducing the need for manual CLI commands. - Streamlined sections on when the CLI is needed, improving clarity for users on integrating Storybook with their applications. These changes aim to enhance user understanding and streamline the setup process for developers. * docs: update documentation to reflect changes from addons to deviceAddons - Replaced references to `addons` with `deviceAddons` across multiple documentation files, including setup guides and README files for on-device addons. - Clarified instructions for integrating on-device addons in the Storybook configuration, ensuring consistency and accuracy in the setup process. - Enhanced user understanding of the new configuration structure, promoting best practices for integrating Storybook with React Native applications. These changes aim to improve clarity and usability for developers transitioning to the updated addon configuration. * docs: update WebSocket configuration examples to TypeScript - Changed code snippets in the WebSocket configuration documentation from JavaScript to TypeScript for consistency and clarity. - Updated various sections to reflect the correct syntax and improve readability, ensuring users have accurate examples for integrating WebSocket with Storybook. - Enhanced the overall documentation to better support developers using TypeScript in their projects. These changes aim to improve the developer experience and provide clearer guidance for WebSocket integration in Storybook. * docs: update backgrounds configuration instructions - Clarified the process for regenerating the `storybook.requires.ts` file after modifying `main.ts`, emphasizing the automatic regeneration when using the `withStorybook` wrapper. - Added a detailed note for users not utilizing the `withStorybook` wrapper, providing manual regeneration instructions. - Improved overall clarity and usability of the documentation for configuring backgrounds in Storybook. These changes aim to enhance the developer experience and ensure accurate guidance for background configuration. * docs: update environment variable documentation for Storybook - Changed code snippet formatting from `bash` to `sh` for consistency across environment variable examples. - Ensured clarity in the usage of environment variables related to Storybook configuration, enhancing readability and usability for developers. - Aimed to provide accurate and uniform documentation for setting up Storybook with various environment variables. These changes improve the overall developer experience by standardizing code examples in the documentation. * docs: standardize code snippet formatting to TypeScript - Updated all code snippets in the documentation from `typescript` to `ts` for consistency across various files, including migration, development workflows, testing, and configuration sections. - Enhanced readability and uniformity in the documentation, ensuring a better developer experience when referencing code examples. These changes aim to improve clarity and maintain a consistent style throughout the documentation. * docs: standardize code snippet formatting to shell - Updated all code snippets in the documentation from `bash` to `sh` for consistency across various files, including setup guides, configuration, and usage instructions. - Enhanced readability and uniformity in the documentation, ensuring a better developer experience when referencing code examples. These changes aim to improve clarity and maintain a consistent style throughout the documentation. * copy edits * Update section title and clarify WebSocket testing setup Clarified the section title and improved the explanation for the WebSocket-controlled testing setup. * docs: update migration guide for Storybook version 10.4 - Revised the migration guide to reflect changes from version 10.3 to 10.4, including updates to the metro configuration and the introduction of entry-point swapping. - Added instructions for migrating to the new `deviceAddons` property and clarified the process for updating Storybook dependencies. - Enhanced clarity and usability of the documentation to support developers transitioning to the latest version of Storybook. * docs: remove deprecated addons section from configuration documentation - Eliminated the outdated `addons` section in the Storybook configuration documentation, focusing on the current `deviceAddons` usage. - Streamlined the content to enhance clarity and ensure users are directed towards the correct configuration practices for on-device addons. - Aimed to improve the overall documentation quality and developer experience by removing unnecessary information. * docs: remove export statement for StorybookUIRoot in migration guide - Eliminated the export statement for `StorybookUIRoot` in the migration guide to align with the updated configuration practices. - This change simplifies the example and focuses on the registration of the component, enhancing clarity for developers migrating to entry-point swapping. * docs: update withStorybook configuration in migration guide - Enhanced the `withStorybook` export to include a configuration object, allowing for custom `configPath` and WebSocket settings. - Improved clarity on the new configuration capabilities, emphasizing the automatic detection of Metro or Re.Pack usage. - Aimed to streamline the migration process for developers transitioning to the latest Storybook practices. * docs: remove empty line in migration guide for Storybook version updates - Cleaned up the migration guide by removing an unnecessary empty line, improving the overall formatting and readability of the document. - This minor edit contributes to a more polished presentation of the migration instructions for developers transitioning between Storybook versions. * Apply suggestion from @ndelangen * docs: add missing code block in repack documentation - Included a missing code block in the repack documentation to enhance clarity and provide a complete example for users. - This addition aims to improve the overall usability of the documentation for developers getting started with Re.Pack. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: winkerVSbecks <varunvachhar@gmail.com>
Tracked by storybookjs/storybook#34276 ("Easy React Native Setup")
Companion PR: storybookjs/storybook#34333 (CLI init overhaul, codemods, automigrations)
Closes #873 (
deviceAddons)Documentation PR: storybookjs/react-native#877
What this does
This PR adds a bundler-agnostic
withStorybookwrapper at@storybook/react-native/withStorybookthat introduces entry-point swapping as the new recommended way to run Storybook for React Native — matching the mental model of Storybook for Web, where Storybook runs as its own entry point rather than being deeply integrated into the user's app.It also introduces the
deviceAddonsconfig property, separating on-device addons from theaddonsarray to preventCriticalPresetLoadErrorduring server-side operations likeextract.Entry-point swapping
The new
withStorybookwrapper:.rnstorybook/indexwhenSTORYBOOK_ENABLED=trueis set — no changes toApp.tsxrequired.STORYBOOK_ENABLEDis not set — zero Storybook code in the production bundle.STORYBOOK_ENABLED,STORYBOOK_WS_HOST,STORYBOOK_WS_PORT,STORYBOOK_WS_SECURED,STORYBOOK_SERVER,STORYBOOK_DISABLE_UI), so the same metro config works for all environments.storybook.requiresfile, eliminating the need to manually match config betweenwithStorybookandgetStorybookUI.The existing
./metro/withStorybookand./repack/withStorybookexports are completely untouched and remain fully supported for projects using the in-app integration approach.deviceAddonsOn-device addons (like
@storybook/addon-ondevice-controls) contain React Native code that can't be evaluated in Node.js. When listed inaddons, Storybook Core tries to load them as presets during operations likeextract, which fails. The newdeviceAddonsproperty ensures they're only loaded at runtime on the device.Backwards compatible —
addonsstill works and a console warning guides users to migrate. The companion PR (storybookjs/storybook#34333) includes an automigration (rn-ondevice-addons-to-device-addons) that handles this automatically vianpx storybook automigrate.How it works together
This PR (react-native repo) provides the runtime bundler wrapper. The companion PR (storybook repo) provides the CLI tooling:
withStorybookdeviceAddonstype + runtimeStorybookConfigtype, generate.jsstorybook initfor RNdeviceAddonsautomigrationrn-ondevice-addons-to-device-addonswithStorybook()wrappingManual verification
Existing
withStorybook(backwards compatibility)Upgraded two existing projects to the canary release of this branch to verify the legacy
./metro/withStorybookpath is unaffected:Both projects continued to function correctly with their existing metro config and in-app Storybook integration, confirming zero regressions.
npx storybook initCLITested
npx storybook@0.0.0-pr-34333-sha-865b50bc initin a fresh Expo project:.rnstorybook/index.tsgenerated correctly.metro.config.jswrapped withwithStorybook()using the bundler-agnostic import.storybook:iosandstorybook:androidscripts added topackage.json.main.ts(and other templates) from thelatestpublished version of@storybook/react-native, not from the canary. This means the generatedmain.tswon't havedeviceAddonsuntil this PR is released. This will resolve itself once we publish the minor release.npx storybook automigrateVerified that
npx storybook@0.0.0-pr-34333-sha-865b50bc automigratecorrectly moves on-device addons fromaddons→deviceAddonsin an existing project's.rnstorybook/main.ts.Release plan
Targeting release as a new minor version this week (v10.4), alongside:
Test coverage
@storybook/react-native(unit tests forwithStorybook,enhanceMetroConfig,enhanceRepackConfig, entry-point resolution, env var handling).testspackage (story generation snapshots).