Skip to content

React: Improve import rewriting when tsconfig paths are used#33072

Merged
kasperpeulen merged 9 commits into
nextfrom
kasper/imports
Nov 18, 2025
Merged

React: Improve import rewriting when tsconfig paths are used#33072
kasperpeulen merged 9 commits into
nextfrom
kasper/imports

Conversation

@kasperpeulen
Copy link
Copy Markdown
Member

@kasperpeulen kasperpeulen commented Nov 18, 2025

Closes #

What I did

This makes sure that imports in the story that are not valid package names:

    import { Button } from '~/components/Button';

are rewritten to the package name where the component is located in:

    import { Button } from 'reshaped';

If the package is a valid package

    import { Button } from 'reshaped/button';

we keep the import.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-33072-sha-abdc858b. Try it out in a new sandbox by running npx storybook@0.0.0-pr-33072-sha-abdc858b sandbox or in an existing project with npx storybook@0.0.0-pr-33072-sha-abdc858b upgrade.

More information
Published version 0.0.0-pr-33072-sha-abdc858b
Triggered by @shilman
Repository storybookjs/storybook
Branch kasper/imports
Commit abdc858b
Datetime Tue Nov 18 15:23:43 UTC 2025 (1763479423)
Workflow run 19471372668

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=33072

Summary by CodeRabbit

  • New Features

    • Added package-name validation and subpath utilities to improve import resolution.
  • Refactor

    • Import-rewriting now checks package-name validity before rewriting tilde/hash or relative imports, preserving valid scoped subpaths.
  • Tests

    • Added tests covering tilde/hash rewrites, scoped package preservation, relative-import override behavior, and non-relative import invariants.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Nov 18, 2025

View your CI Pipeline Execution ↗ for commit abdc858

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 43s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-18 13:58:53 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 18, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds an npm-like package name validator and uses it to guard import-rewrite logic: imports are rewritten to the provided packageName only when the stripped import identifier is not a valid package name, preserving valid scoped/bare specifiers and existing overrideSource behavior.

Changes

Cohort / File(s) Summary
Package Validation Module
code/renderers/react/src/componentManifest/valid-package-name.ts
New module exporting stripSubpath(name: string) and validPackageName(name: string) to extract top-level package names and validate npm-like package-name rules (scoped handling, length, characters, encoding).
Import Rewriting Logic
code/renderers/react/src/componentManifest/getComponentImports.ts
Replaces local relative-check with validPackageName(stripSubpath(importId)); import rewriting to packageName now occurs only when the stripped import id is not a valid package name, while preserving overrideSource behavior.
Import Rewriting Tests
code/renderers/react/src/componentManifest/getComponentImports.test.ts
Adds tests verifying tilde/hash-prefixed import rewriting to packageName, ensuring scoped/bare specifiers and non-relative imports are not rewritten, and cases with/without packageName or overrideSource.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller as getComponentImports
  participant Validator as valid-package-name
  participant Decision as Rewrite Logic

  Caller->>Decision: receive importId, packageName, overrideSource?
  Decision->>Validator: stripSubpath(importId) -> top
  Decision->>Validator: validPackageName(top)?
  alt valid
    Decision-->>Caller: return original import (no rewrite)
  else
    alt overrideSource present
      Decision-->>Caller: use overrideSource / rewrite -> packageName
    else
      Decision-->>Caller: rewrite import -> packageName
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review valid-package-name.ts regex/encoding edge cases (scoped vs unscoped, encoding behavior).
  • Verify getComponentImports.ts uses stripSubpath + validPackageName correctly and preserves overrideSource.
  • Confirm tests in getComponentImports.test.ts cover rewrite, non-rewrite, scoped/bare, tilde/hash cases, and override scenarios.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb4be98 and bf50ed9.

📒 Files selected for processing (1)
  • code/renderers/react/src/componentManifest/valid-package-name.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
📚 Learning: 2025-10-01T15:24:01.060Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.

Applied to files:

  • code/renderers/react/src/componentManifest/valid-package-name.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/renderers/react/src/componentManifest/valid-package-name.ts (1)

4-17: Logic is correct.

The stripSubpath function correctly handles both scoped and unscoped package specifiers. As noted in a previous review, consider adding JSDoc to document the function's purpose, parameters, and return value.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
code/renderers/react/src/componentManifest/getComponentImports.test.ts (1)

588-667: Good test coverage for new rewrite behavior.

The three new tests effectively cover:

  • Tilde-prefixed sources being rewritten to packageName
  • Hash-prefixed sources being rewritten to packageName
  • Scoped package subpaths remaining unchanged (not rewritten)

Consider adding a test for simple unscoped bare specifiers with packageName provided:

test('Does not rewrite simple unscoped package (valid bare specifier)', () => {
  const code = dedent`
    import { useState } from 'react';

    const meta = {};
    export default meta;
    export const S = () => { const [x] = useState(); return <div />; };
  `;
  expect(getImports(code, 'pkg')).toMatchInlineSnapshot(`
    {
      "components": [],
      "imports": [],
    }
  `);
});

This would verify that valid unscoped packages like "react" are not rewritten when packageName is provided, and would catch the bug identified in valid-package-name.ts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6222a0d and 6f65dbc.

📒 Files selected for processing (3)
  • code/renderers/react/src/componentManifest/getComponentImports.test.ts (1 hunks)
  • code/renderers/react/src/componentManifest/getComponentImports.ts (2 hunks)
  • code/renderers/react/src/componentManifest/valid-package-name.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-10-01T15:24:01.060Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.

Applied to files:

  • code/renderers/react/src/componentManifest/getComponentImports.test.ts
  • code/renderers/react/src/componentManifest/getComponentImports.ts
🧬 Code graph analysis (2)
code/renderers/react/src/componentManifest/getComponentImports.test.ts (1)
code/renderers/react/src/componentManifest/getComponentImports.ts (1)
  • getImports (244-496)
code/renderers/react/src/componentManifest/getComponentImports.ts (1)
code/renderers/react/src/componentManifest/valid-package-name.ts (2)
  • validPackageName (19-67)
  • stripSubpath (4-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (3)
code/renderers/react/src/componentManifest/getComponentImports.ts (2)

9-9: LGTM - Import additions support new validation logic.

The new imports enable package-name validation for import rewriting.


280-285: Import rewrite logic updated to use package-name validation.

The new condition !validPackageName(stripSubpath(importId)) correctly gates rewrites: imports are rewritten to packageName only when the stripped import identifier is not a valid package name. This preserves valid bare specifiers (including scoped packages with subpaths) while rewriting relative paths, tilde-prefixed, hash-prefixed, and other invalid specifiers.

However, this logic relies on the correctness of validPackageName - see critical issue flagged in valid-package-name.ts.

code/renderers/react/src/componentManifest/valid-package-name.ts (1)

4-17: LGTM - Subpath stripping logic is correct.

The stripSubpath function correctly extracts the base package name, handling both scoped (@scope/pkg/subpath@scope/pkg) and unscoped (react/jsx-runtimereact) forms.

Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts
Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts
kasperpeulen and others added 2 commits November 18, 2025 11:32
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Jeppe Reinhold <jeppe@chromatic.com>
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Nov 18, 2025

Package Benchmarks

Commit: bf50ed9, ran on 18 November 2025 at 11:42:41 UTC

No significant changes detected, all good. 👏

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 18, 2025

@kasperpeulen I've opened a new pull request, #33075, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR improves how React imports are rewritten when TypeScript path aliases (like ~/components/Button or #Button) are used. Instead of only rewriting relative imports (those starting with .), the new logic rewrites any import that is not a valid npm package name to the provided package name.

Key Changes

  • Added utility functions stripSubpath and validPackageName to validate npm package names
  • Changed import rewriting logic from checking isRelative() to checking !validPackageName(stripSubpath(importId))
  • Added comprehensive test coverage for tilde-prefixed, hash-prefixed, and scoped package imports

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
code/renderers/react/src/componentManifest/valid-package-name.ts New file with utilities to strip subpaths from package specifiers and validate npm package names
code/renderers/react/src/componentManifest/getComponentImports.ts Updated import rewriting logic to use package name validation instead of relative path check
code/renderers/react/src/componentManifest/getComponentImports.test.ts Added tests for tilde/hash-prefixed imports and scoped packages with subpaths

Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts
Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts
Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts Outdated
Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts
Comment thread code/renderers/react/src/componentManifest/valid-package-name.ts
@kasperpeulen kasperpeulen merged commit da3a563 into next Nov 18, 2025
11 of 15 checks passed
@kasperpeulen kasperpeulen deleted the kasper/imports branch November 18, 2025 13:40
@github-actions github-actions Bot mentioned this pull request Nov 19, 2025
17 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants