Skip to content

Conversation

@sensei-woo
Copy link

Summary

Fixes #50

When using a Vite app with a custom base path (e.g., base: "/dashboard"), the normalizeFileName function incorrectly includes the base path in the extracted file path.

Before: /dashboard/src/routes/orders/Button.tsx
After: /src/routes/orders/Button.tsx

Changes

Added detection and stripping of Vite's base path using import.meta.env.BASE_URL, which Vite exposes to the client. This mirrors how Vite's own base middleware handles path stripping internally.

Test

Tested with a Vite React app configured with base: "/dashboard" - the "Open in Editor" feature now correctly resolves file paths.

Copilot AI review requested due to automatic review settings January 23, 2026 00:42
@changeset-bot
Copy link

changeset-bot bot commented Jan 23, 2026

⚠️ No Changeset found

Latest commit: 1b4b1b8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Jan 23, 2026

@sensei-woo is attempting to deploy a commit to the Million Team on Vercel.

A member of the Team first needs to authorize it.

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link

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 fixes an issue where the normalizeFileName function incorrectly includes Vite's custom base path in extracted file paths, causing the "Open in Editor" feature to fail when using a Vite app with a non-root base configuration.

Changes:

  • Added logic to detect and strip Vite's base path using import.meta.env.BASE_URL after URL parsing
  • The base path is stripped from normalized file names when present, ensuring correct file path resolution

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +98 to +104
// Strip Vite base path if present
if (typeof import.meta !== 'undefined' && (import.meta as any).env?.BASE_URL) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
}
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

Missing test coverage for the new Vite base path stripping functionality. The test file packages/bippy/src/test/source.test.tsx has comprehensive tests for other path normalization scenarios (webpack prefixes, HTTP URLs, query parameters) but lacks tests for Vite base path handling.

Add test cases covering:

  1. URL with base path: http://localhost:5173/dashboard/src/component.tsx/src/component.tsx
  2. Base path with trailing slash
  3. Root base path (/) which should not be stripped
  4. Path without base path
  5. Base path combined with query parameters
  6. Edge case where base path is similar to but not exactly matching the start of the path

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +103
if (typeof import.meta !== 'undefined' && (import.meta as any).env?.BASE_URL) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The runtime check for import.meta may cause issues when the code is bundled as CommonJS (CJS). Based on tsdown.config.ts, this library is built in both ESM and CJS formats. In CJS builds, import.meta is not available and attempting to access it could cause a ReferenceError even with the typeof check, depending on how the bundler transforms the code.

Consider wrapping this in a try-catch block or using a more robust environment detection approach that works across both ESM and CJS contexts. Alternatively, document that this feature only works in ESM builds or ensure the bundler properly handles this check in CJS output.

Suggested change
if (typeof import.meta !== 'undefined' && (import.meta as any).env?.BASE_URL) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
}
try {
if (
typeof import.meta !== 'undefined' &&
(import.meta as any).env?.BASE_URL
) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
}
}
} catch {
// Ignore environments or bundlers where import.meta is not supported

Copilot uses AI. Check for mistakes.
if (typeof import.meta !== 'undefined' && (import.meta as any).env?.BASE_URL) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The logic for handling edge cases with the base path stripping is incomplete. Specifically:

  1. When normalizedFileName.slice(basePath.length) results in an empty string, it falls back to '/', but this may not be correct. If the file path after stripping the base path is empty, it likely means the path was exactly the base path, which is not a valid file path and should probably be handled differently (e.g., return empty string or the original value).

  2. The condition basePath !== '/' correctly avoids stripping the root path, but there's no handling for when the base path itself is malformed or when it appears in the middle of the path rather than at the start (though the startsWith check handles the latter).

Consider whether returning '/' is the correct fallback when the entire path is consumed by the base path, or if this should return an empty string to match the behavior of the early return on line 79.

Suggested change
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
normalizedFileName = normalizedFileName.slice(basePath.length);

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +104
// Strip Vite base path if present
if (typeof import.meta !== 'undefined' && (import.meta as any).env?.BASE_URL) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
normalizedFileName = normalizedFileName.slice(basePath.length) || '/';
}
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The Vite base path stripping runs before internal scheme prefix stripping (lines 106-133), which could cause incorrect behavior when source paths from other bundlers happen to start with the same string as the Vite base path.

For example, if BASE_URL = "/dashboard" and a file path is webpack://dashboard/src/file.tsx:

  1. The URL parsing step (lines 88-96) is skipped since it doesn't start with http:// or https://
  2. The Vite base path check would incorrectly strip /dashboard, resulting in webpack://src/file.tsx (note: the base path check on line 101 uses startsWith which wouldn't match here since the path starts with webpack://, not /dashboard)

Actually, on closer inspection, the startsWith(basePath) check on line 101 would prevent this specific issue since webpack://dashboard/src/file.tsx doesn't start with /dashboard. However, the current placement is still semantically incorrect because:

  1. The Vite base path is a URL path concept that should only apply to paths that have already been extracted from URLs (after line 94) or paths that start with /
  2. It should run after scheme prefix stripping to ensure we're working with clean file paths
  3. Consider moving this block to after line 148 (after the // handling) or after line 133 (after internal scheme stripping) for clearer semantics and to avoid potential edge cases.

Copilot uses AI. Check for mistakes.
// Strip Vite base path if present
if (typeof import.meta !== 'undefined' && (import.meta as any).env?.BASE_URL) {
const basePath = ((import.meta as any).env.BASE_URL as string).replace(/\/$/, '');
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
Copy link

Choose a reason for hiding this comment

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

Suggested change
if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {
if (basePath && basePath !== '/' && (normalizedFileName === basePath || normalizedFileName.startsWith(basePath + '/'))) {

The Vite base path stripping logic uses startsWith() without checking for path segment boundaries, which can cause incorrect path normalization when the base path is a prefix of other path names (e.g., base: "/app" would incorrectly strip "/app-utils.tsx" to "-utils.tsx").

View Details

Analysis

Vite base path stripping uses incomplete path matching causing incorrect file normalization

What fails: The normalizeFileName() function in packages/bippy/src/source/get-source.ts line 101 uses startsWith() to check if a file path should be stripped of the Vite base path. This causes false positives when different files share the base path as a prefix.

How to reproduce:

// With BASE_URL = "/app"
normalizeFileName("/app-utils.tsx"); // Returns "-utils.tsx" (incorrect!)
normalizeFileName("/api-v2/index.ts", basePath="/api"); // Returns "-v2/index.ts" (incorrect!)

// Should return the original paths unchanged

Result: Files like /app-utils.tsx get incorrectly stripped to -utils.tsx when BASE_URL is /app, because the check only looks for prefix match without verifying complete path segments.

Expected: Only strip the base path when it matches a complete path segment - either as the exact path or followed by a forward slash.

Fix: Changed line 101 from:

if (basePath && basePath !== '/' && normalizedFileName.startsWith(basePath)) {

To:

if (basePath && basePath !== '/' && (normalizedFileName === basePath || normalizedFileName.startsWith(basePath + '/'))) {

This ensures only paths actually under the base directory are stripped, not arbitrary paths that happen to start with the base path string. All existing tests pass with this fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

normalizeFileName doesn't strip Vite base path from URLs

1 participant