11module Spago.Command.Run
22 ( getNode
33 , run
4+ , encodeFileUrlPath
45 , RunEnv
56 , Node
67 , RunOptions
@@ -13,6 +14,7 @@ import Data.Array as Array
1314import Data.Array.NonEmpty as NEA
1415import Data.Map as Map
1516import Data.String as String
17+ import Data.String.CodeUnits as SCU
1618import JSURI (encodeURIComponent )
1719import Node.FS.Perms as Perms
1820import Registry.Version as Version
@@ -48,6 +50,26 @@ type RunOptions =
4850
4951type 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+
5173nodeVersion :: forall a . Spago (LogEnv a ) Version
5274nodeVersion =
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://"
0 commit comments