Skip to content

Commit ede2738

Browse files
committed
Node commands (npm, node, npx) resolution now works on Windows.
1 parent 02d6f2b commit ede2738

File tree

15 files changed

+90
-20
lines changed

15 files changed

+90
-20
lines changed

waspc/cli/src/Wasp/Cli/Command/BuildStart/Client.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import qualified Wasp.Generator.WebAppGenerator.Common as WebApp
1212
import qualified Wasp.Job as J
1313
import Wasp.Job.Except (ExceptJob, toExceptJob)
1414
import Wasp.Job.Process (runNodeCommandAsJob, runNodeCommandAsJobWithExtraEnv)
15+
import Wasp.Node.Executables (npmExec)
1516

1617
buildClient :: BuildStartConfig -> ExceptJob
1718
buildClient config =
1819
runNodeCommandAsJobWithExtraEnv
1920
envVars
2021
webAppDir
21-
"npm"
22+
npmExec
2223
["run", "build"]
2324
J.WebApp
2425
& toExceptJob (("Building the client failed with exit code: " <>) . show)
@@ -31,7 +32,7 @@ startClient :: BuildStartConfig -> ExceptJob
3132
startClient config =
3233
runNodeCommandAsJob
3334
webAppDir
34-
"npm"
35+
npmExec
3536
[ "run",
3637
"preview", -- `preview` launches a static file server for the built client.
3738
"--",

waspc/cli/src/Wasp/Cli/Command/TsConfigSetup.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Wasp.Cli.Command.Require (InWaspProject (InWaspProject))
1111
import qualified Wasp.Job as J
1212
import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed)
1313
import Wasp.Job.Process (runNodeCommandAsJob)
14+
import Wasp.Node.Executables (npmExec)
1415
import Wasp.NodePackageFFI (InstallablePackage (WaspConfigPackage), getPackageInstallationPath)
1516

1617
-- | Prepares the project for using Wasp's TypeScript SDK.
@@ -32,7 +33,7 @@ installWaspConfigPackage chan projectDir = do
3233
(_, exitCode) <-
3334
concurrently
3435
(readJobMessagesAndPrintThemPrefixed chan)
35-
(runNodeCommandAsJob projectDir "npm" ["install", "--save-dev", "file:" ++ installationPath] J.Wasp chan)
36+
(runNodeCommandAsJob projectDir npmExec ["install", "--save-dev", "file:" ++ installationPath] J.Wasp chan)
3637
return $ case exitCode of
3738
ExitSuccess -> Right ()
3839
ExitFailure _ -> Left "Failed to install wasp-config package"

waspc/packages/wasp-config/__tests__/cli.unit.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ describe("parseProcessArgsOrThrow", () => {
1010
]);
1111
});
1212

13+
test("should parse arguments correctly even if node has absolute path", () => {
14+
expectParseProcessArgsToSucceed([
15+
"main.wasp.js",
16+
"output.json",
17+
JSON.stringify(["entity1"]),
18+
], "/usr/bin/node");
19+
});
20+
1321
test("should parse 0 entities correctly", () => {
1422
expectParseProcessArgsToSucceed(["main.wasp.js", "output.json", "[]"]);
1523
});
@@ -59,8 +67,8 @@ describe("parseProcessArgsOrThrow", () => {
5967
]);
6068
});
6169

62-
function expectParseProcessArgsToSucceed(args: string[]) {
63-
const result = parseProcessArgsOrThrow(["node", "run.js", ...args]);
70+
function expectParseProcessArgsToSucceed(args: string[], nodeExec: string = "node") {
71+
const result = parseProcessArgsOrThrow([nodeExec, "run.js", ...args]);
6472

6573
const [waspTsSpecPath, outputFilePath, entityNames] = args;
6674
expect(result).toEqual({

waspc/src/Wasp/Generator/DbGenerator/Jobs.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ where
1515

1616
import StrongPath (Abs, Dir, File', Path', (</>))
1717
import qualified StrongPath as SP
18-
import StrongPath.TH (relfile)
1918
import Wasp.Generator.Common (ProjectRootDir)
2019
import Wasp.Generator.DbGenerator.Common (MigrateArgs (..), dbSchemaFileInProjectRootDir)
2120
import Wasp.Generator.ServerGenerator.Common (serverRootDirInProjectRootDir)
2221
import Wasp.Generator.ServerGenerator.Db.Seed (dbSeedNameEnvVarName)
2322
import qualified Wasp.Job as J
2423
import Wasp.Job.Process (runNodeCommandAsJobWithExtraEnv)
25-
import Wasp.Project.Common (WaspProjectDir, waspProjectDirFromProjectRootDir)
24+
import Wasp.Node.NodeModules (getPathToExecutableInNodeModules)
25+
import Wasp.Project.Common (WaspProjectDir, nodeModulesDirInWaspProjectDir, waspProjectDirFromProjectRootDir)
2626

2727
migrateDev :: Path' Abs (Dir ProjectRootDir) -> MigrateArgs -> J.Job
2828
migrateDev projectRootDir migrateArgs =
@@ -178,4 +178,7 @@ absPrismaExecutableFp :: Path' Abs (Dir WaspProjectDir) -> FilePath
178178
absPrismaExecutableFp waspProjectDir = SP.fromAbsFile prismaExecutableAbs
179179
where
180180
prismaExecutableAbs :: Path' Abs File'
181-
prismaExecutableAbs = waspProjectDir </> [relfile|./node_modules/.bin/prisma|]
181+
prismaExecutableAbs =
182+
waspProjectDir
183+
</> nodeModulesDirInWaspProjectDir
184+
</> SP.castRel (getPathToExecutableInNodeModules "prisma")

waspc/src/Wasp/Generator/SdkGenerator.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import Wasp.Generator.WebAppGenerator.DepVersions
7676
import qualified Wasp.Job as J
7777
import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed)
7878
import Wasp.Job.Process (runNodeCommandAsJob)
79+
import Wasp.Node.Executables (npmExec)
7980
import qualified Wasp.Node.Version as NodeVersion
8081
import Wasp.Project.Common (WaspProjectDir, waspProjectDirFromAppComponentDir)
8182
import qualified Wasp.Project.Db as Db
@@ -90,7 +91,7 @@ buildSdk projectRootDir = do
9091
(_, exitCode) <-
9192
concurrently
9293
(readJobMessagesAndPrintThemPrefixed chan)
93-
(runNodeCommandAsJob dstDir "npm" ["run", "build"] J.Wasp chan)
94+
(runNodeCommandAsJob dstDir npmExec ["run", "build"] J.Wasp chan)
9495
case exitCode of
9596
ExitSuccess -> return $ Right ()
9697
ExitFailure code -> return $ Left $ "SDK build failed with exit code: " ++ show code
@@ -332,7 +333,7 @@ depsRequiredByTailwind spec =
332333
-- Also, fix imports for wasp project.
333334
installNpmDependencies :: Path' Abs (Dir WaspProjectDir) -> J.Job
334335
installNpmDependencies projectDir =
335-
runNodeCommandAsJob projectDir "npm" ["install"] J.Wasp
336+
runNodeCommandAsJob projectDir npmExec ["install"] J.Wasp
336337

337338
-- todo(filip): consider reorganizing/splitting the file.
338339

waspc/src/Wasp/Generator/ServerGenerator/Start.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import Wasp.Generator.Common (ProjectRootDir)
88
import qualified Wasp.Generator.ServerGenerator.Common as Common
99
import qualified Wasp.Job as J
1010
import Wasp.Job.Process (runNodeCommandAsJob)
11+
import Wasp.Node.Executables (npmExec)
1112

1213
startServer :: Path' Abs (Dir ProjectRootDir) -> J.Job
1314
startServer projectDir = do
1415
let serverDir = projectDir </> Common.serverRootDirInProjectRootDir
15-
runNodeCommandAsJob serverDir "npm" ["run", "watch"] J.Server
16+
runNodeCommandAsJob serverDir npmExec ["run", "watch"] J.Server

waspc/src/Wasp/Generator/WebAppGenerator/Start.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import Wasp.Generator.Common (ProjectRootDir)
88
import qualified Wasp.Generator.WebAppGenerator.Common as Common
99
import qualified Wasp.Job as J
1010
import Wasp.Job.Process (runNodeCommandAsJob)
11+
import Wasp.Node.Executables (npmExec)
1112

1213
startWebApp :: Path' Abs (Dir ProjectRootDir) -> J.Job
1314
startWebApp projectDir = do
1415
let webAppDir = projectDir </> Common.webAppRootDirInProjectRootDir
15-
runNodeCommandAsJob webAppDir "npm" ["start"] J.WebApp
16+
runNodeCommandAsJob webAppDir npmExec ["start"] J.WebApp

waspc/src/Wasp/Generator/WebAppGenerator/Test.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import qualified StrongPath as SP
88
import Wasp.Generator.WebAppGenerator.Common (webAppRootDirInProjectRootDir)
99
import qualified Wasp.Job as J
1010
import Wasp.Job.Process (runNodeCommandAsJob)
11+
import Wasp.Node.Executables (npxExec)
1112
import Wasp.Project.Common (WaspProjectDir, dotWaspDirInWaspProjectDir, generatedCodeDirInDotWaspDir)
1213

1314
testWebApp :: [String] -> Path' Abs (Dir WaspProjectDir) -> J.Job
1415
testWebApp args projectDir = do
15-
runNodeCommandAsJob projectDir "npx" (vitestCommand ++ args) J.WebApp
16+
runNodeCommandAsJob projectDir npxExec (vitestCommand ++ args) J.WebApp
1617
where
1718
vitestCommand = ["vitest", "--config", SP.fromRelFile viteConfigPath]
1819
viteConfigPath =

waspc/src/Wasp/Node/Executables.hs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module Wasp.Node.Executables
2+
( nodeExec,
3+
npmExec,
4+
npxExec,
5+
)
6+
where
7+
8+
import GHC.IO (unsafePerformIO)
9+
import StrongPath (fromAbsFile)
10+
import Wasp.Util.System (ExecName, resolveExecNameIO)
11+
12+
{-# NOINLINE nodeExec #-} -- To ensure single execution, for unsafePerformIO.
13+
nodeExec :: ExecName
14+
nodeExec = fromAbsFile $ unsafePerformIO $ resolveExecNameIO "node"
15+
16+
{-# NOINLINE npmExec #-} -- To ensure single execution, for unsafePerformIO.
17+
npmExec :: ExecName
18+
npmExec = fromAbsFile $ unsafePerformIO $ resolveExecNameIO "npm"
19+
20+
{-# NOINLINE npxExec #-} -- To ensure single execution, for unsafePerformIO.
21+
npxExec :: ExecName
22+
npxExec = fromAbsFile $ unsafePerformIO $ resolveExecNameIO "npx"

waspc/src/Wasp/Node/NodeModules.hs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module Wasp.Node.NodeModules
2+
( getPathToExecutableInNodeModules,
3+
)
4+
where
5+
6+
import Data.Maybe (fromJust)
7+
import StrongPath (File, Path', Rel, parseRelFile, reldir, (</>))
8+
import Wasp.Util.System (isSystemWindows)
9+
10+
-- | Represents some node_modules dir.
11+
data NodeModulesDir
12+
13+
-- | Node modules (node_modules) have a place where they put all the executables/binaries
14+
-- produced by the packages/modules.
15+
-- This function returns a path to such an executable with a given name, taking into account
16+
-- details like current operating system.
17+
--
18+
-- Example: @getPathToExecutableInNodeModules "npm"@ -> @".bin/npm.cmd"@
19+
getPathToExecutableInNodeModules :: String -> Path' (Rel NodeModulesDir) (File f)
20+
getPathToExecutableInNodeModules execName =
21+
[reldir|.bin|] </> fromJust (parseRelFile systemSpecificExecFilename)
22+
where
23+
systemSpecificExecFilename
24+
| isSystemWindows = execName <> ".cmd"
25+
| otherwise = execName

0 commit comments

Comments
 (0)