Skip to content

Commit 73b7a01

Browse files
authored
fix(create): normalize target directory to forward slashes on Windows (#971)
On Windows, `path.join` and `path.normalize` produce backslash-separated paths (e.g., `packages\core`). When passed as CLI arguments to external commands like `create-vite`, the backslash gets lost, creating `packagescore` instead of `packages/core`. Normalize all relative target directory paths to forward slashes at the source: in `formatTargetDir()` and in `bin.ts` path joins. Closes #938
1 parent 8c6c1b2 commit 73b7a01

File tree

3 files changed

+16
-4
lines changed

3 files changed

+16
-4
lines changed

packages/cli/src/create/__tests__/utils.spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ describe('formatTargetDir', () => {
1717
expect(formatTargetDir('../../foo/bar')).matchSnapshot();
1818
});
1919

20-
it.skipIf(process.platform === 'win32')('should format target dir with valid input', () => {
20+
// Should work on all platforms (including Windows) - directory must always use forward slashes
21+
it('should format target dir with valid input', () => {
2122
expect(formatTargetDir('./my-package')).matchSnapshot();
2223
expect(formatTargetDir('my-package')).matchSnapshot();
2324
expect(formatTargetDir('@my-scope/my-package')).matchSnapshot();
@@ -28,6 +29,17 @@ describe('formatTargetDir', () => {
2829
expect(formatTargetDir('./foo/bar/@scope/my-package/sub-package')).matchSnapshot();
2930
});
3031

32+
// Regression test for https://github.com/voidzero-dev/vite-plus/issues/938
33+
// On Windows, path.join/normalize produce backslashes which break when passed as CLI args.
34+
// Nested paths are the critical cases since they involve path separators.
35+
it('should always use forward slashes in directory (issue #938)', () => {
36+
expect(formatTargetDir('foo/@my-scope/my-package').directory).toBe('foo/my-package');
37+
expect(formatTargetDir('./foo/bar/@scope/my-package').directory).toBe('foo/bar/my-package');
38+
expect(formatTargetDir('./foo/bar/@scope/my-package/sub-package').directory).toBe(
39+
'foo/bar/@scope/my-package/sub-package',
40+
);
41+
});
42+
3143
it('should format target dir with invalid package name', () => {
3244
expect(formatTargetDir('my-package@').error).matchSnapshot();
3345
expect(formatTargetDir('my-package@1.0.0').error).matchSnapshot();

packages/cli/src/create/bin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
584584
const selected = await promptPackageNameAndTargetDir(defaultPackageName, options.interactive);
585585
packageName = selected.packageName;
586586
targetDir = selectedParentDir
587-
? path.join(selectedParentDir, selected.targetDir)
587+
? path.join(selectedParentDir, selected.targetDir).split(path.sep).join('/')
588588
: selected.targetDir;
589589
}
590590
}
@@ -782,7 +782,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
782782
const selected = await promptPackageNameAndTargetDir(defaultPackageName, options.interactive);
783783
packageName = selected.packageName;
784784
targetDir = templateInfo.parentDir
785-
? path.join(templateInfo.parentDir, selected.targetDir)
785+
? path.join(templateInfo.parentDir, selected.targetDir).split(path.sep).join('/')
786786
: selected.targetDir;
787787
}
788788
pauseCreateProgress();

packages/cli/src/create/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function formatTargetDir(input: string): {
8585
error: `Parsed package name "${packageName}" is invalid: ${message}`,
8686
};
8787
}
88-
return { directory: targetDir, packageName };
88+
return { directory: targetDir.split(path.sep).join('/'), packageName };
8989
}
9090

9191
// Get the project directory from the project name

0 commit comments

Comments
 (0)