Skip to content

Commit 5fe83a6

Browse files
committed
Merge remote-tracking branch 'origin/windows-support'
2 parents 74a455d + c4d6f9d commit 5fe83a6

File tree

2 files changed

+98
-39
lines changed

2 files changed

+98
-39
lines changed

lib/NVMPL/Shell/PowerShell.pm

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,36 @@ use warnings;
44
use feature 'say';
55
use NVMPL::Utils qw(log_info);
66

7+
# ---------------------------------------------------------
8+
# Update PATH (used for shell printouts, rarely called)
9+
# ---------------------------------------------------------
710
sub update_path {
811
my ($bin_path) = @_;
912
say '$Env:PATH = "' . $bin_path . ';$Env:PATH"';
1013
}
1114

15+
# ---------------------------------------------------------
16+
# Initialization snippet for PowerShell profile
17+
# ---------------------------------------------------------
1218
sub init_snippet {
13-
return <<'PS1';
14-
if (Test-Path "$HOME\.nvm-pl\install\versions\current\bin") {
15-
$Env:PATH = "$HOME\.nvm-pl\install\versions\current\bin;" + $Env:PATH
19+
return <<'EOS';
20+
# nvm-pl managed Node.js path (Windows-aware)
21+
$CurrentNodePath = "$env:USERPROFILE\.nvm-pl\install\versions\current"
22+
23+
if (Test-Path $CurrentNodePath) {
24+
# Look for nested node-v*-win-x64 folder (typical Windows layout)
25+
$NodeFolder = Get-ChildItem $CurrentNodePath -Directory -Filter "node-v*-win-x64" -ErrorAction SilentlyContinue | Select-Object -First 1
26+
if ($NodeFolder) {
27+
$NodeBin = $NodeFolder.FullName
28+
} else {
29+
$NodeBin = $CurrentNodePath
30+
}
31+
32+
if (-not ($env:PATH -like "*$NodeBin*")) {
33+
$env:PATH = "$NodeBin;" + $env:PATH
34+
}
1635
}
17-
PS1
36+
EOS
1837
}
1938

20-
1;
39+
1;

lib/NVMPL/Switcher.pm

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,31 @@ use File::Spec;
66
use NVMPL::Config;
77
use NVMPL::Utils qw(detect_platform);
88

9-
109
# ---------------------------------------------------------
1110
# Public entry point
1211
# ---------------------------------------------------------
1312

1413
sub _detect_shell {
15-
my $shell = $ENV{SHELL} || '/bin/bash';
16-
if ($shell =~ /zsh/) {
14+
# --- Windows shell detection first ---
15+
if ($^O eq 'MSWin32') {
16+
# PowerShell defines PSModulePath, CMD defines ComSpec
17+
if ($ENV{PSModulePath}) {
18+
return 'PowerShell';
19+
} elsif ($ENV{ComSpec} && $ENV{ComSpec} =~ /cmd\.exe/i) {
20+
return 'Cmd';
21+
} else {
22+
return 'PowerShell'; # safe default on Windows
23+
}
24+
}
25+
26+
# --- Unix-like environments ---
27+
my $shell = $ENV{SHELL} || '';
28+
if ($shell =~ /zsh/i) {
1729
return 'Zsh';
18-
} elsif ($shell =~ /bash/) {
30+
} elsif ($shell =~ /bash/i) {
1931
return 'Bash';
20-
} elsif ($shell =~ /cmd\.exe/i || $^O eq 'MSWin32') {
21-
return 'Cmd';
22-
} elsif ($shell =~ /powershell|pwsh/i) {
23-
return 'PowerShell';
2432
} else {
25-
return 'Bash';
33+
return 'Bash'; # fallback for unknown shells
2634
}
2735
}
2836

@@ -37,12 +45,21 @@ sub use_version {
3745
my $vtag = "v$version";
3846

3947
my $cfg = NVMPL::Config->load();
40-
my $install_dir = $cfg->{install_dir};
48+
my $install_dir = $cfg->{install_dir};
4149
my $versions_dir = File::Spec->catdir($install_dir, 'versions');
42-
my $target_dir = File::Spec->catdir($versions_dir, $vtag);
50+
my $target_dir = File::Spec->catdir($versions_dir, $vtag);
51+
52+
# --- Windows nested node folder fix ---
53+
if ($^O eq 'MSWin32') {
54+
my @matches = glob("$target_dir\\node-v*-win-x64");
55+
if (@matches && -d $matches[0]) {
56+
$target_dir = $matches[0];
57+
}
58+
}
59+
4360
my $current_link = File::Spec->catfile($versions_dir, 'current');
4461

45-
unless(-d $target_dir) {
62+
unless (-d $target_dir) {
4663
say "[nvm-pl] Version $vtag is not installed.";
4764
return 1;
4865
}
@@ -61,33 +78,60 @@ sub use_version {
6178
_update_shell_config($current_link);
6279

6380
say "[nvm-pl] Active version is now $vtag";
64-
say "Restart your shell or run the appropriate source command for your shell.";
6581

82+
# --- PowerShell live PATH update ---
83+
if ($^O eq 'MSWin32' && $ENV{PSModulePath}) {
84+
my $ps_path = $target_dir;
85+
$ps_path =~ s#/#\\#g;
86+
87+
# only update if node.exe isn't already on PATH
88+
my $node_check = `where node 2> NUL`;
89+
if ($node_check !~ /\Q$ps_path\E/i) {
90+
# Detect which PowerShell to use
91+
my $ps_exe = `where pwsh 2> NUL`;
92+
chomp($ps_exe);
93+
if (!$ps_exe) {
94+
$ps_exe = `where powershell 2> NUL`;
95+
chomp($ps_exe);
96+
}
97+
98+
if ($ps_exe) {
99+
say "[nvm-pl] (PowerShell) Updating PATH for current session...";
100+
system($ps_exe, "-NoLogo", "-NoProfile", "-Command",
101+
"\$env:PATH = '$ps_path;' + \$env:PATH; Write-Host 'PATH updated for this session.'");
102+
} else {
103+
say "[nvm-pl] (PowerShell) Could not find PowerShell executable — skipping live PATH update.";
104+
}
105+
} else {
106+
say "[nvm-pl] (PowerShell) Node path already active.";
107+
}
108+
}
109+
110+
say "Restart your shell or run the appropriate source command for your shell.";
66111
return 0;
67112
}
68113

114+
69115
sub _update_shell_config {
70116
my ($current_link) = @_;
71117
my $shell_type = _detect_shell();
72-
73-
# Load the appropriate shell module
118+
119+
# Load the appropriate shell module dynamically
74120
my $shell_module = "NVMPL::Shell::$shell_type";
75121
eval "require $shell_module" or do {
76122
warn "[nvm-pl] Could not load $shell_module: $@";
77123
return;
78124
};
79-
125+
80126
my $init_snippet = $shell_module->init_snippet();
81-
82-
# Update the appropriate config file
83-
my $config_file = _get_shell_config($shell_type);
127+
my $config_file = _get_shell_config($shell_type);
84128
_write_shell_config($config_file, $init_snippet, $shell_type);
85129
}
86130

87131
sub _get_shell_config {
88132
my ($shell_type) = @_;
89-
my $home = $ENV{HOME};
90-
133+
my $home = $ENV{HOME} // $ENV{USERPROFILE};
134+
91135
return {
92136
Bash => "$home/.bashrc",
93137
Zsh => "$home/.zshrc",
@@ -98,60 +142,56 @@ sub _get_shell_config {
98142

99143
sub _write_shell_config {
100144
my ($config_file, $init_snippet, $shell_type) = @_;
101-
102145
return unless $config_file;
103-
146+
104147
# Read existing config
105148
my @lines;
106149
if (-f $config_file) {
107150
open my $in, '<', $config_file or return;
108151
@lines = <$in>;
109152
close $in;
110153
}
111-
154+
112155
# Remove ONLY the nvm-pl managed section (more precise)
113156
my @clean_lines;
114157
my $in_nvm_section = 0;
115-
158+
116159
foreach my $line (@lines) {
117160
# Detect start of nvm-pl section
118161
if ($line =~ /^# nvm-pl managed Node\.js path$/) {
119162
$in_nvm_section = 1;
120163
next;
121164
}
122-
165+
123166
# Skip lines until we're out of the nvm-pl section
124167
if ($in_nvm_section) {
125-
# Detect end of nvm-pl section (empty line or new section)
126168
if ($line =~ /^\s*$/ || $line =~ /^#/) {
127169
$in_nvm_section = 0;
128170
} else {
129171
next;
130172
}
131173
}
132-
133-
# Keep the line if we're not in nvm-pl section
174+
134175
push @clean_lines, $line unless $in_nvm_section;
135176
}
136-
177+
137178
# Write new config with nvm-pl snippet at the end
138179
open my $out, '>', $config_file or return;
139180
print $out @clean_lines;
140181
print $out "\n# nvm-pl managed Node.js path\n";
141182
print $out "$init_snippet\n";
142183
close $out;
143-
184+
144185
say "[nvm-pl] Updated $config_file for $shell_type";
145186
}
146187

147-
148188
# ---------------------------------------------------------
149189
# Helpers
150190
# ---------------------------------------------------------
151191

152192
sub _win_junction {
153193
my ($link, $target) = @_;
154-
$link =~ s#/#\\#g;
194+
$link =~ s#/#\\#g;
155195
$target =~ s#/#\\#g;
156196
my $cmd = "cmd /C mklink /J \"$link\" \"$target\"";
157197
system($cmd) == 0
@@ -195,4 +235,4 @@ sub _get_current_version {
195235
return undef;
196236
}
197237

198-
1;
238+
1;

0 commit comments

Comments
 (0)