Skip to content

Commit 0087e2a

Browse files
authored
Merge branch 'main' into fix/homebrew-env-shims
2 parents 4fbe50f + 1eb39c6 commit 0087e2a

8 files changed

Lines changed: 707 additions & 446 deletions

File tree

crates/vite_global_cli/src/commands/vpx.rs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -243,13 +243,15 @@ pub fn find_local_binary(cwd: &AbsolutePath, cmd: &str) -> Option<AbsolutePathBu
243243
let mut current = cwd;
244244
loop {
245245
let bin_dir = current.join("node_modules").join(".bin");
246-
let bin_path = bin_dir.join(cmd);
247246

248-
if bin_path.as_path().exists() {
249-
return Some(bin_path);
247+
#[cfg(not(windows))]
248+
{
249+
let bin_path = bin_dir.join(cmd);
250+
if bin_path.as_path().exists() {
251+
return Some(bin_path);
252+
}
250253
}
251254

252-
// On Windows, check for .cmd extension
253255
#[cfg(windows)]
254256
{
255257
let cmd_path = bin_dir.join(format!("{cmd}.cmd"));
@@ -536,11 +538,18 @@ mod tests {
536538
let temp_dir = tempfile::tempdir().unwrap();
537539
let temp_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
538540

539-
// Create node_modules/.bin/eslint
541+
// Create node_modules/.bin/eslint and eslint.cmd
540542
let bin_dir = temp_path.join("node_modules").join(".bin");
541543
std::fs::create_dir_all(&bin_dir).unwrap();
544+
// Unix script
545+
std::fs::write(bin_dir.join("eslint"), "#!/bin/sh\n").unwrap();
546+
#[cfg(windows)]
547+
std::fs::write(bin_dir.join("eslint.cmd"), "@eslint %*\r\n").unwrap();
548+
549+
#[cfg(not(windows))]
542550
let eslint_path = bin_dir.join("eslint");
543-
std::fs::write(&eslint_path, "#!/bin/sh\n").unwrap();
551+
#[cfg(windows)]
552+
let eslint_path = bin_dir.join("eslint.cmd");
544553

545554
let result = find_local_binary(&temp_path, "eslint");
546555
assert!(result.is_some());
@@ -552,11 +561,17 @@ mod tests {
552561
let temp_dir = tempfile::tempdir().unwrap();
553562
let temp_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
554563

555-
// Create node_modules/.bin/eslint at root
564+
// Create node_modules/.bin/eslint and eslint.cmd at root
556565
let bin_dir = temp_path.join("node_modules").join(".bin");
557566
std::fs::create_dir_all(&bin_dir).unwrap();
567+
std::fs::write(bin_dir.join("eslint"), "#!/bin/sh\n").unwrap();
568+
#[cfg(windows)]
569+
std::fs::write(bin_dir.join("eslint.cmd"), "@eslint %*\r\n").unwrap();
570+
571+
#[cfg(not(windows))]
558572
let eslint_path = bin_dir.join("eslint");
559-
std::fs::write(&eslint_path, "#!/bin/sh\n").unwrap();
573+
#[cfg(windows)]
574+
let eslint_path = bin_dir.join("eslint.cmd");
560575

561576
// Create nested directory
562577
let nested_dir = temp_path.join("packages").join("app");
@@ -586,19 +601,58 @@ mod tests {
586601
let root_bin = temp_path.join("node_modules").join(".bin");
587602
std::fs::create_dir_all(&root_bin).unwrap();
588603
std::fs::write(root_bin.join("eslint"), "root").unwrap();
604+
#[cfg(windows)]
605+
std::fs::write(root_bin.join("eslint.cmd"), "@eslint %*\r\n").unwrap();
589606

590607
// Create eslint in nested package
591608
let nested = temp_path.join("packages").join("app");
592609
let nested_bin = nested.join("node_modules").join(".bin");
593610
std::fs::create_dir_all(&nested_bin).unwrap();
594611
std::fs::write(nested_bin.join("eslint"), "nested").unwrap();
612+
#[cfg(windows)]
613+
std::fs::write(nested_bin.join("eslint.cmd"), "@eslint %*\r\n").unwrap();
614+
615+
#[cfg(not(windows))]
616+
let nested_bin = nested_bin.join("eslint");
617+
#[cfg(windows)]
618+
let nested_bin = nested_bin.join("eslint.cmd");
595619

596620
let nested_abs = AbsolutePathBuf::new(nested.as_path().to_path_buf()).unwrap();
597621
let result = find_local_binary(&nested_abs, "eslint");
598622
assert!(result.is_some());
599623
// Should find the nested one first
600624
let found = result.unwrap();
601-
assert_eq!(found.as_path(), nested_bin.join("eslint").as_path());
625+
assert_eq!(found.as_path(), nested_bin.as_path());
626+
}
627+
628+
#[cfg(windows)]
629+
#[test]
630+
fn test_find_local_binary_windows_prefers_cmd_over_extensionless() {
631+
let temp_dir = tempfile::tempdir().unwrap();
632+
let temp_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
633+
634+
let bin_dir = temp_path.join("node_modules").join(".bin");
635+
std::fs::create_dir_all(&bin_dir).unwrap();
636+
std::fs::write(bin_dir.join("playwright"), "#!/bin/sh\n").unwrap();
637+
std::fs::write(bin_dir.join("playwright.cmd"), "@playwright %*\r\n").unwrap();
638+
639+
let result = find_local_binary(&temp_path, "playwright");
640+
assert!(result.is_some());
641+
assert_eq!(result.unwrap().as_path(), bin_dir.join("playwright.cmd").as_path());
642+
}
643+
644+
#[cfg(windows)]
645+
#[test]
646+
fn test_find_local_binary_windows_ignores_extensionless_only() {
647+
let temp_dir = tempfile::tempdir().unwrap();
648+
let temp_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
649+
650+
let bin_dir = temp_path.join("node_modules").join(".bin");
651+
std::fs::create_dir_all(&bin_dir).unwrap();
652+
std::fs::write(bin_dir.join("playwright"), "#!/bin/sh\n").unwrap();
653+
654+
let result = find_local_binary(&temp_path, "playwright");
655+
assert!(result.is_none());
602656
}
603657

604658
// =========================================================================

packages/cli/snap-tests-global/migration-add-git-hooks/snap.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,20 @@ i="${XDG_CONFIG_HOME:-$HOME/.config}/vite-plus/hooks-init.sh"
6060
{ [ "${HUSKY-}" = "0" ] || [ "${VITE_GIT_HOOKS-}" = "0" ]; } && exit 0
6161

6262
d="$(dirname "$(dirname "$(dirname "$0")")")"
63+
__vp_shell=/bin/sh
64+
[ -x "$__vp_shell" ] || __vp_shell=$(command -v sh)
65+
66+
if [ -n "${VP_HOME-}" ]; then
67+
__vp_bin="$VP_HOME/bin"
68+
elif [ -n "${HOME-}" ]; then
69+
__vp_bin="$HOME/.vite-plus/bin"
70+
else
71+
__vp_bin=""
72+
fi
73+
[ -n "$__vp_bin" ] && [ -d "$__vp_bin" ] && export PATH="$PATH:$__vp_bin"
74+
6375
export PATH="$d/node_modules/.bin:$PATH"
64-
sh -e "$s" "$@"
76+
"$__vp_shell" -e "$s" "$@"
6577
c=$?
6678

6779
[ $c != 0 ] && echo "VITE+ - $n script failed (code $c)"

packages/cli/snap-tests-global/migration-composed-husky-custom-dir/snap.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,20 @@ i="${XDG_CONFIG_HOME:-$HOME/.config}/vite-plus/hooks-init.sh"
5252
{ [ "${HUSKY-}" = "0" ] || [ "${VITE_GIT_HOOKS-}" = "0" ]; } && exit 0
5353

5454
d="$(dirname "$(dirname "$(dirname "$(dirname "$0")")")")"
55+
__vp_shell=/bin/sh
56+
[ -x "$__vp_shell" ] || __vp_shell=$(command -v sh)
57+
58+
if [ -n "${VP_HOME-}" ]; then
59+
__vp_bin="$VP_HOME/bin"
60+
elif [ -n "${HOME-}" ]; then
61+
__vp_bin="$HOME/.vite-plus/bin"
62+
else
63+
__vp_bin=""
64+
fi
65+
[ -n "$__vp_bin" ] && [ -d "$__vp_bin" ] && export PATH="$PATH:$__vp_bin"
66+
5567
export PATH="$d/node_modules/.bin:$PATH"
56-
sh -e "$s" "$@"
68+
"$__vp_shell" -e "$s" "$@"
5769
c=$?
5870

5971
[ $c != 0 ] && echo "VITE+ - $n script failed (code $c)"

packages/cli/src/config/__tests__/hooks.spec.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { execSync } from 'node:child_process';
2-
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
2+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
33
import { tmpdir } from 'node:os';
44
import { join } from 'node:path';
55

@@ -73,4 +73,86 @@ describe('hookScript', () => {
7373
expect(countDirnameCalls(withDot)).toBe(countDirnameCalls(withoutDot));
7474
expect(countDirnameCalls(withDot)).toBe(3);
7575
});
76+
77+
it.skipIf(process.platform === 'win32')(
78+
'should add Vite+ managed bin to PATH as a fallback before running user hook',
79+
() => {
80+
const tmp = mkdtempSync(join(tmpdir(), 'hooks-path-test-'));
81+
try {
82+
const hooksDir = join(tmp, '.vite-hooks');
83+
const internalHooksDir = join(hooksDir, '_');
84+
const nodeModulesBin = join(tmp, 'node_modules', '.bin');
85+
const vpHomeBin = join(tmp, 'vp-home', 'bin');
86+
const systemBin = join(tmp, 'system-bin');
87+
88+
mkdirSync(internalHooksDir, { recursive: true });
89+
mkdirSync(nodeModulesBin, { recursive: true });
90+
mkdirSync(vpHomeBin, { recursive: true });
91+
mkdirSync(systemBin, { recursive: true });
92+
93+
writeFileSync(join(internalHooksDir, 'h'), hookScript('.vite-hooks'), { mode: 0o755 });
94+
writeFileSync(
95+
join(internalHooksDir, 'pre-commit'),
96+
'#!/usr/bin/env sh\n. "$(dirname "$0")/h"',
97+
{ mode: 0o755 },
98+
);
99+
writeFileSync(join(hooksDir, 'pre-commit'), 'vp staged\n');
100+
101+
writeFileSync(
102+
join(nodeModulesBin, 'vp'),
103+
'#!/bin/sh\nbasedir=$(dirname "$0")\nexec node "$basedir/../vite-plus/bin/vp" "$@"\n',
104+
{ mode: 0o755 },
105+
);
106+
writeFileSync(
107+
join(vpHomeBin, 'node'),
108+
'#!/bin/sh\necho "fake-node $*" > "$VP_HOME/node-used"\n',
109+
{ mode: 0o755 },
110+
);
111+
writeFileSync(
112+
join(vpHomeBin, 'dirname'),
113+
'#!/bin/sh\necho "wrong dirname" > "$VP_HOME/dirname-used"\nexit 1\n',
114+
{ mode: 0o755 },
115+
);
116+
writeFileSync(
117+
join(vpHomeBin, 'sh'),
118+
'#!/bin/sh\necho "wrong sh" > "$VP_HOME/sh-used"\nexit 1\n',
119+
{ mode: 0o755 },
120+
);
121+
122+
writeFileSync(join(systemBin, 'sh'), '#!/bin/sh\nexec /bin/sh "$@"\n', {
123+
mode: 0o755,
124+
});
125+
writeFileSync(join(systemBin, 'dirname'), '#!/bin/sh\nexec /usr/bin/dirname "$@"\n', {
126+
mode: 0o755,
127+
});
128+
writeFileSync(join(systemBin, 'basename'), '#!/bin/sh\nexec /usr/bin/basename "$@"\n', {
129+
mode: 0o755,
130+
});
131+
132+
execSync('sh .vite-hooks/_/pre-commit', {
133+
cwd: tmp,
134+
env: {
135+
HOME: join(tmp, 'home'),
136+
PATH: systemBin,
137+
VP_HOME: join(tmp, 'vp-home'),
138+
},
139+
});
140+
141+
expect(existsSync(join(tmp, 'vp-home', 'node-used'))).toBe(true);
142+
expect(existsSync(join(tmp, 'vp-home', 'dirname-used'))).toBe(false);
143+
expect(existsSync(join(tmp, 'vp-home', 'sh-used'))).toBe(false);
144+
} finally {
145+
rmSync(tmp, { recursive: true, force: true });
146+
}
147+
},
148+
);
149+
150+
it('should compute root and shell before appending Vite+ managed bin', () => {
151+
const script = hookScript('.vite-hooks');
152+
expect(script.indexOf('d=')).toBeLessThan(script.indexOf('export PATH="$PATH:$__vp_bin"'));
153+
expect(script.indexOf('__vp_shell=')).toBeLessThan(
154+
script.indexOf('export PATH="$PATH:$__vp_bin"'),
155+
);
156+
expect(script).toContain('"$__vp_shell" -e "$s" "$@"');
157+
});
76158
});

packages/cli/src/config/hooks.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,20 @@ i="\${XDG_CONFIG_HOME:-$HOME/.config}/vite-plus/hooks-init.sh"
5050
{ [ "\${HUSKY-}" = "0" ] || [ "\${VITE_GIT_HOOKS-}" = "0" ]; } && exit 0
5151
5252
d=${rootExpr}
53+
__vp_shell=/bin/sh
54+
[ -x "$__vp_shell" ] || __vp_shell=$(command -v sh)
55+
56+
if [ -n "\${VP_HOME-}" ]; then
57+
__vp_bin="$VP_HOME/bin"
58+
elif [ -n "\${HOME-}" ]; then
59+
__vp_bin="$HOME/.vite-plus/bin"
60+
else
61+
__vp_bin=""
62+
fi
63+
[ -n "$__vp_bin" ] && [ -d "$__vp_bin" ] && export PATH="$PATH:$__vp_bin"
64+
5365
export PATH="$d/node_modules/.bin:$PATH"
54-
sh -e "$s" "$@"
66+
"$__vp_shell" -e "$s" "$@"
5567
c=$?
5668
5769
[ $c != 0 ] && echo "VITE+ - $n script failed (code $c)"

packages/core/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
"@babel/types": "^7.28.5",
113113
"@oxc-node/cli": "catalog:",
114114
"@oxc-node/core": "catalog:",
115-
"@vitejs/devtools": "^0.1.24",
115+
"@vitejs/devtools": "^0.2.0",
116116
"es-module-lexer": "^1.7.0",
117117
"hookable": "^6.0.1",
118118
"magic-string": "^0.30.21",
@@ -218,7 +218,7 @@
218218
"node": "^20.19.0 || >=22.12.0"
219219
},
220220
"bundledVersions": {
221-
"vite": "8.0.13",
221+
"vite": "8.0.14",
222222
"rolldown": "1.0.2",
223223
"tsdown": "0.22.0"
224224
}

packages/tools/.upstream-versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"vite": {
88
"repo": "https://github.com/vitejs/vite.git",
99
"branch": "main",
10-
"hash": "a46f11a6c218f74b08ffb3e33a25c2ce02ba6643"
10+
"hash": "c917f1ef9d9c6ef131af96d89089d8ec680b18f2"
1111
}
1212
}

0 commit comments

Comments
 (0)