Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/plenty-schools-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen': patch
---

Fixed bug where file paths containing spaces were causing errors with virtual routes by decoding URL-encoded paths
38 changes: 37 additions & 1 deletion packages/hydrogen/src/vite/get-virtual-routes.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {describe, it, expect} from 'vitest';
import {getVirtualRoutesV3} from './get-virtual-routes.js';
import {
getVirtualRoutesV3,
createVirtualRoutesPath,
} from './get-virtual-routes.js';

describe('virtual routes', () => {
it('gets virtual routes V3', async () => {
Expand Down Expand Up @@ -29,4 +32,37 @@ describe('virtual routes', () => {
]),
});
});

it('decodes URL-encoded file paths with spaces', () => {
// Mock import.meta.url with a path containing a space
const mockImportMetaUrl =
'file:///path/with%20space/vite/get-virtual-routes.ts';

const result = createVirtualRoutesPath(
mockImportMetaUrl,
['vite', 'virtual-routes'],
'layout.jsx',
);

// Should decode %20 to actual space
expect(result).toContain('with space');
expect(result).not.toContain('%20');
});

it('removes Windows drive path prefix (e.g. /C:/)', () => {
// Mock a Windows file URL with C: drive letter
const mockWindowsUrl =
'file:///C:/Users/developer/project/vite/get-virtual-routes.ts';

const result = createVirtualRoutesPath(
mockWindowsUrl,
['vite', 'virtual-routes'],
'layout.jsx',
);

// Should remove /C:/ prefix and normalize to Unix-style path
expect(result).not.toContain('/C:/');
expect(result).toContain('/Users/developer/project');
expect(result).toContain('layout.jsx');
});
});
21 changes: 18 additions & 3 deletions packages/hydrogen/src/vite/get-virtual-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,35 @@ export const VIRTUAL_ROUTES_ROUTES_DIR_PARTS = [
];
export const VIRTUAL_ROUTES_DIR_PARTS = ['vite', 'virtual-routes'];

function getVirtualRoutesPath(
/**
* @internal
* Exported for testing only.
*/
export function createVirtualRoutesPath(
baseUrl: string,
pathParts: Array<string>,
forFile: string,
): string {
const basePath = new URL('../', import.meta.url);
const basePath = new URL('../', baseUrl);
const virtualRoutesPath = pathParts.reduce((working, dirPart) => {
return new URL(`${dirPart}/`, working);
}, basePath);

// Getting rid of the drive path (ie. '/C:/') in windows
return new URL(forFile, virtualRoutesPath).pathname.replace(
const pathname = new URL(forFile, virtualRoutesPath).pathname.replace(
/^\/[a-zA-Z]:\//,
'/',
);

// Decode URI components to handle spaces and special characters in file paths
return decodeURIComponent(pathname);
}

function getVirtualRoutesPath(
pathParts: Array<string>,
forFile: string,
): string {
return createVirtualRoutesPath(import.meta.url, pathParts, forFile);
}

export async function getVirtualRoutesV3() {
Expand Down
Loading