Skip to content

Commit e1374f8

Browse files
committed
Fix windows
1 parent 93fa35f commit e1374f8

File tree

3 files changed

+65
-7
lines changed

3 files changed

+65
-7
lines changed

src/Spago/Command/Run.purs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Spago.Command.Run
22
( getNode
33
, run
4+
, encodeFileUrlPath
45
, RunEnv
56
, Node
67
, RunOptions
@@ -13,6 +14,7 @@ import Data.Array as Array
1314
import Data.Array.NonEmpty as NEA
1415
import Data.Map as Map
1516
import Data.String as String
17+
import Data.String.CodeUnits as SCU
1618
import JSURI (encodeURIComponent)
1719
import Node.FS.Perms as Perms
1820
import Registry.Version as Version
@@ -48,6 +50,26 @@ type RunOptions =
4850

4951
type Node = { cmd :: GlobalPath, version :: Version }
5052

53+
-- | Encode a file path for use in a file:// URL.
54+
-- | Encodes special characters (spaces, apostrophes, etc.) but preserves
55+
-- | Windows drive letters (e.g., "C:") since encoding the colon breaks URLs.
56+
encodeFileUrlPath :: String -> String
57+
encodeFileUrlPath str =
58+
String.split (String.Pattern "/") str
59+
# map encodeSegment
60+
# String.joinWith "/"
61+
where
62+
encodeSegment seg
63+
| isWindowsDrive seg = seg
64+
| otherwise = fromMaybe seg (encodeURIComponent seg)
65+
66+
-- Windows drive letter: single ASCII letter followed by colon (e.g., "C:", "D:")
67+
isWindowsDrive seg = case SCU.toCharArray seg of
68+
[ letter, ':' ] -> isAsciiLetter letter
69+
_ -> false
70+
71+
isAsciiLetter c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
72+
5173
nodeVersion :: forall a. Spago (LogEnv a) Version
5274
nodeVersion =
5375
Cmd.exec (Path.global "node") [ "--version" ] Cmd.defaultExecOptions { pipeStdout = false, pipeStderr = false } >>= case _ of
@@ -85,13 +107,6 @@ run = do
85107

86108
nodeArgs = [ Path.toRaw runJsPath ] <> opts.execArgs
87109

88-
-- Encode each path segment for use in file:// URL
89-
-- Splits on /, encodes each segment, rejoins with /
90-
encodeFileUrlPath str =
91-
String.split (String.Pattern "/") str
92-
# map (\seg -> fromMaybe seg (encodeURIComponent seg))
93-
# String.joinWith "/"
94-
95110
nodeContents =
96111
Array.fold
97112
[ "import { main } from 'file://"

test/Spago/Unit.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Test.Spago.Unit.Init as Init
99
import Test.Spago.Unit.NodeVersion as NodeVersion
1010
import Test.Spago.Unit.Path as Path
1111
import Test.Spago.Unit.Printer as Printer
12+
import Test.Spago.Unit.Run as Run
1213
import Test.Spec (Spec)
1314
import Test.Spec as Spec
1415

@@ -21,3 +22,4 @@ spec = Spec.describe "unit" do
2122
Git.spec
2223
Path.spec
2324
NodeVersion.spec
25+
Run.spec

test/Spago/Unit/Run.purs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
module Test.Spago.Unit.Run where
2+
3+
import Test.Prelude
4+
5+
import Spago.Command.Run (encodeFileUrlPath)
6+
import Test.Spec (Spec)
7+
import Test.Spec as Spec
8+
9+
spec :: Spec Unit
10+
spec = Spec.describe "Run" do
11+
12+
Spec.describe "encodeFileUrlPath" do
13+
14+
Spec.it "encodes spaces" do
15+
encodeFileUrlPath "/path/with spaces/file" `shouldEqual` "/path/with%20spaces/file"
16+
17+
Spec.it "encodes apostrophes" do
18+
encodeFileUrlPath "/Volumes/Tim's Docs/project" `shouldEqual` "/Volumes/Tim%27s%20Docs/project"
19+
20+
Spec.it "encodes hash symbols" do
21+
encodeFileUrlPath "/path/test#1/file" `shouldEqual` "/path/test%231/file"
22+
23+
Spec.it "encodes brackets" do
24+
encodeFileUrlPath "/path/test[dev]/file" `shouldEqual` "/path/test%5Bdev%5D/file"
25+
26+
Spec.it "preserves Windows drive letters" do
27+
encodeFileUrlPath "C:/Users/test" `shouldEqual` "C:/Users/test"
28+
encodeFileUrlPath "D:/a/spago/output" `shouldEqual` "D:/a/spago/output"
29+
30+
Spec.it "preserves Windows drive letters with special chars in path" do
31+
encodeFileUrlPath "C:/Users/Tim's Folder/project" `shouldEqual` "C:/Users/Tim%27s%20Folder/project"
32+
33+
Spec.it "handles lowercase drive letters" do
34+
encodeFileUrlPath "c:/users/test" `shouldEqual` "c:/users/test"
35+
36+
Spec.it "encodes colon in non-drive-letter segments" do
37+
-- A colon not at the start as a drive letter should be encoded
38+
encodeFileUrlPath "/path/file:name/test" `shouldEqual` "/path/file%3Aname/test"
39+
40+
Spec.it "leaves normal paths unchanged" do
41+
encodeFileUrlPath "/home/user/project/output" `shouldEqual` "/home/user/project/output"

0 commit comments

Comments
 (0)