Limited Wasp version to ^16.0.0 && <=16.15.0 (#634)

* Limited Wasp node version to ^16.0.0 && <=16.15.0.

In the process, I removed .nvmrc files from generated code and
refactored SemanticVersion to follow npm spec.
I also updated CI to use node 16.15.0, and not any 16.
I also updated docs.
This commit is contained in:
Martin Šošić 2022-06-17 17:02:54 +02:00 committed by GitHub
parent fdd8e47b25
commit 62f0a76937
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 307 additions and 225 deletions

View File

@ -90,7 +90,7 @@ jobs:
- name: Set up Node - name: Set up Node
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: '16' node-version: '16.15.0'
- name: Run tests - name: Run tests
run: cabal test run: cabal test

View File

@ -1,2 +0,0 @@
{{={= =}=}}
{= nodeVersion =}

View File

@ -14,9 +14,10 @@
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
}, },
"engineStrict": true,
"engines": { "engines": {
"node": "{= nodeVersionBounds =}", "node": "{=& nodeVersionRange =}",
"npm": "{= npmVersionBounds =}" "npm": "{=& npmVersionRange =}"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

View File

@ -1,2 +0,0 @@
{{={= =}=}}
{= nodeVersion =}

View File

@ -15,9 +15,10 @@
"nodemonConfig": { "nodemonConfig": {
"delay": "1000" "delay": "1000"
}, },
"engineStrict": true,
"engines": { "engines": {
"node": "{= nodeVersionBounds =}", "node": "{=& nodeVersionRange =}",
"npm": "{= npmVersionBounds =}" "npm": "{=& npmVersionRange =}"
}, },
{=& depsChunk =}, {=& depsChunk =},
{=& devDepsChunk =} {=& devDepsChunk =}

View File

@ -4,7 +4,6 @@ waspBuild/.wasp/build/Dockerfile
waspBuild/.wasp/build/db/schema.prisma waspBuild/.wasp/build/db/schema.prisma
waspBuild/.wasp/build/installedFullStackNpmDependencies.json waspBuild/.wasp/build/installedFullStackNpmDependencies.json
waspBuild/.wasp/build/server/.npmrc waspBuild/.wasp/build/server/.npmrc
waspBuild/.wasp/build/server/.nvmrc
waspBuild/.wasp/build/server/README.md waspBuild/.wasp/build/server/README.md
waspBuild/.wasp/build/server/package.json waspBuild/.wasp/build/server/package.json
waspBuild/.wasp/build/server/src/app.js waspBuild/.wasp/build/server/src/app.js
@ -26,7 +25,6 @@ waspBuild/.wasp/build/server/src/routes/operations/index.js
waspBuild/.wasp/build/server/src/server.js waspBuild/.wasp/build/server/src/server.js
waspBuild/.wasp/build/server/src/utils.js waspBuild/.wasp/build/server/src/utils.js
waspBuild/.wasp/build/web-app/.npmrc waspBuild/.wasp/build/web-app/.npmrc
waspBuild/.wasp/build/web-app/.nvmrc
waspBuild/.wasp/build/web-app/README.md waspBuild/.wasp/build/web-app/README.md
waspBuild/.wasp/build/web-app/netlify.toml waspBuild/.wasp/build/web-app/netlify.toml
waspBuild/.wasp/build/web-app/package.json waspBuild/.wasp/build/web-app/package.json

View File

@ -34,13 +34,6 @@
], ],
"8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27" "8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27"
], ],
[
[
"file",
"server/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -53,7 +46,7 @@
"file", "file",
"server/package.json" "server/package.json"
], ],
"54d8353286dae688a59458973227cfc1fae57549f49f318ddd46a1abdd716da2" "66df15608f1f501f1eda942061e7b627a82a1033b0d73f8e7d00e430b6906df1"
], ],
[ [
[ [
@ -195,13 +188,6 @@
], ],
"7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a" "7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a"
], ],
[
[
"file",
"web-app/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -221,7 +207,7 @@
"file", "file",
"web-app/package.json" "web-app/package.json"
], ],
"1c8677831516081acee6696e3553d36b887332ccfeaad6c84fa758dcba88031e" "3b3724d8f44d2a7652b6426f7e1e494031df868d23a29850afa260e67ff8da72"
], ],
[ [
[ [

View File

@ -16,9 +16,10 @@
"prisma": "3.9.1", "prisma": "3.9.1",
"standard": "^14.3.4" "standard": "^14.3.4"
}, },
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"name": "server", "name": "server",
"nodemonConfig": { "nodemonConfig": {

View File

@ -23,9 +23,10 @@
"uuid": "^3.4.0" "uuid": "^3.4.0"
}, },
"devDependencies": {}, "devDependencies": {},
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

View File

@ -4,7 +4,6 @@ waspCompile/.wasp/out/Dockerfile
waspCompile/.wasp/out/db/schema.prisma waspCompile/.wasp/out/db/schema.prisma
waspCompile/.wasp/out/installedFullStackNpmDependencies.json waspCompile/.wasp/out/installedFullStackNpmDependencies.json
waspCompile/.wasp/out/server/.npmrc waspCompile/.wasp/out/server/.npmrc
waspCompile/.wasp/out/server/.nvmrc
waspCompile/.wasp/out/server/README.md waspCompile/.wasp/out/server/README.md
waspCompile/.wasp/out/server/package.json waspCompile/.wasp/out/server/package.json
waspCompile/.wasp/out/server/src/app.js waspCompile/.wasp/out/server/src/app.js
@ -26,7 +25,6 @@ waspCompile/.wasp/out/server/src/routes/operations/index.js
waspCompile/.wasp/out/server/src/server.js waspCompile/.wasp/out/server/src/server.js
waspCompile/.wasp/out/server/src/utils.js waspCompile/.wasp/out/server/src/utils.js
waspCompile/.wasp/out/web-app/.npmrc waspCompile/.wasp/out/web-app/.npmrc
waspCompile/.wasp/out/web-app/.nvmrc
waspCompile/.wasp/out/web-app/README.md waspCompile/.wasp/out/web-app/README.md
waspCompile/.wasp/out/web-app/netlify.toml waspCompile/.wasp/out/web-app/netlify.toml
waspCompile/.wasp/out/web-app/package.json waspCompile/.wasp/out/web-app/package.json

View File

@ -34,13 +34,6 @@
], ],
"8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27" "8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27"
], ],
[
[
"file",
"server/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -53,7 +46,7 @@
"file", "file",
"server/package.json" "server/package.json"
], ],
"54d8353286dae688a59458973227cfc1fae57549f49f318ddd46a1abdd716da2" "66df15608f1f501f1eda942061e7b627a82a1033b0d73f8e7d00e430b6906df1"
], ],
[ [
[ [
@ -195,13 +188,6 @@
], ],
"7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a" "7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a"
], ],
[
[
"file",
"web-app/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -221,7 +207,7 @@
"file", "file",
"web-app/package.json" "web-app/package.json"
], ],
"386f6add035afddc94aacd930bd52a1b184cb41063ece66c5a83aba3ef1246d9" "8351f0d9a0eb46ffc9b222567e1bcf89b68f59cec062caaaac0a71ff6f001b26"
], ],
[ [
[ [

View File

@ -16,9 +16,10 @@
"prisma": "3.9.1", "prisma": "3.9.1",
"standard": "^14.3.4" "standard": "^14.3.4"
}, },
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"name": "server", "name": "server",
"nodemonConfig": { "nodemonConfig": {

View File

@ -23,9 +23,10 @@
"uuid": "^3.4.0" "uuid": "^3.4.0"
}, },
"devDependencies": {}, "devDependencies": {},
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

View File

@ -4,7 +4,6 @@ waspJob/.wasp/out/Dockerfile
waspJob/.wasp/out/db/schema.prisma waspJob/.wasp/out/db/schema.prisma
waspJob/.wasp/out/installedFullStackNpmDependencies.json waspJob/.wasp/out/installedFullStackNpmDependencies.json
waspJob/.wasp/out/server/.npmrc waspJob/.wasp/out/server/.npmrc
waspJob/.wasp/out/server/.nvmrc
waspJob/.wasp/out/server/README.md waspJob/.wasp/out/server/README.md
waspJob/.wasp/out/server/package.json waspJob/.wasp/out/server/package.json
waspJob/.wasp/out/server/src/app.js waspJob/.wasp/out/server/src/app.js
@ -28,7 +27,6 @@ waspJob/.wasp/out/server/src/routes/operations/index.js
waspJob/.wasp/out/server/src/server.js waspJob/.wasp/out/server/src/server.js
waspJob/.wasp/out/server/src/utils.js waspJob/.wasp/out/server/src/utils.js
waspJob/.wasp/out/web-app/.npmrc waspJob/.wasp/out/web-app/.npmrc
waspJob/.wasp/out/web-app/.nvmrc
waspJob/.wasp/out/web-app/README.md waspJob/.wasp/out/web-app/README.md
waspJob/.wasp/out/web-app/netlify.toml waspJob/.wasp/out/web-app/netlify.toml
waspJob/.wasp/out/web-app/package.json waspJob/.wasp/out/web-app/package.json

View File

@ -34,13 +34,6 @@
], ],
"8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27" "8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27"
], ],
[
[
"file",
"server/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -53,7 +46,7 @@
"file", "file",
"server/package.json" "server/package.json"
], ],
"ed4cedc6f1595457dbcf57380909f11e789d5b86af900009dbc1cfe6afa2bb04" "af2ab353120775cc7d4e9ff61c853994c4e02e761e942fae06b70cee58651020"
], ],
[ [
[ [
@ -209,13 +202,6 @@
], ],
"7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a" "7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a"
], ],
[
[
"file",
"web-app/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -235,7 +221,7 @@
"file", "file",
"web-app/package.json" "web-app/package.json"
], ],
"67e7b3c15c7f0316d4988c50e0d03d3103759b369662ceca6cdc6217fe2b5632" "b6e6aef85445808b54fa8cd9b5ac7b85105eeb2d55dc002fe20f374f06212c66"
], ],
[ [
[ [

View File

@ -17,9 +17,10 @@
"prisma": "3.9.1", "prisma": "3.9.1",
"standard": "^14.3.4" "standard": "^14.3.4"
}, },
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"name": "server", "name": "server",
"nodemonConfig": { "nodemonConfig": {

View File

@ -23,9 +23,10 @@
"uuid": "^3.4.0" "uuid": "^3.4.0"
}, },
"devDependencies": {}, "devDependencies": {},
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

View File

@ -9,7 +9,6 @@ waspMigrate/.wasp/out/db/schema.prisma.wasp-generate-checksum
waspMigrate/.wasp/out/db/schema.prisma.wasp-migrate-checksum waspMigrate/.wasp/out/db/schema.prisma.wasp-migrate-checksum
waspMigrate/.wasp/out/installedFullStackNpmDependencies.json waspMigrate/.wasp/out/installedFullStackNpmDependencies.json
waspMigrate/.wasp/out/server/.npmrc waspMigrate/.wasp/out/server/.npmrc
waspMigrate/.wasp/out/server/.nvmrc
waspMigrate/.wasp/out/server/README.md waspMigrate/.wasp/out/server/README.md
waspMigrate/.wasp/out/server/package.json waspMigrate/.wasp/out/server/package.json
waspMigrate/.wasp/out/server/src/app.js waspMigrate/.wasp/out/server/src/app.js
@ -31,7 +30,6 @@ waspMigrate/.wasp/out/server/src/routes/operations/index.js
waspMigrate/.wasp/out/server/src/server.js waspMigrate/.wasp/out/server/src/server.js
waspMigrate/.wasp/out/server/src/utils.js waspMigrate/.wasp/out/server/src/utils.js
waspMigrate/.wasp/out/web-app/.npmrc waspMigrate/.wasp/out/web-app/.npmrc
waspMigrate/.wasp/out/web-app/.nvmrc
waspMigrate/.wasp/out/web-app/README.md waspMigrate/.wasp/out/web-app/README.md
waspMigrate/.wasp/out/web-app/netlify.toml waspMigrate/.wasp/out/web-app/netlify.toml
waspMigrate/.wasp/out/web-app/package.json waspMigrate/.wasp/out/web-app/package.json

View File

@ -34,13 +34,6 @@
], ],
"8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27" "8a74b1091affea3c71c2cf04acbaa38c4a51a261a9a98dcc9e96473d07a85a27"
], ],
[
[
"file",
"server/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -53,7 +46,7 @@
"file", "file",
"server/package.json" "server/package.json"
], ],
"8d4dae59b2eeae186ef18098d546e6850a3702b9c7c0c059fb20b3f6e1d8a113" "d75927e049f63a022dc0740641115b5841a393031899b2b84d1d0c43ecf2388a"
], ],
[ [
[ [
@ -195,13 +188,6 @@
], ],
"7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a" "7151cf397def0c2cb0ab65643701d27d335a72c90f775675b5f826bc7005818a"
], ],
[
[
"file",
"web-app/.nvmrc"
],
"e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017"
],
[ [
[ [
"file", "file",
@ -221,7 +207,7 @@
"file", "file",
"web-app/package.json" "web-app/package.json"
], ],
"b8135251bd0b41b4e0460a700d21d0433ef4690b8bda65f919967532f4fd43f4" "581f025e9f75fdca5ec2b11b1f2b7981142ea8ebb9662e61b535276eba6ae149"
], ],
[ [
[ [

View File

@ -16,9 +16,10 @@
"prisma": "3.9.1", "prisma": "3.9.1",
"standard": "^14.3.4" "standard": "^14.3.4"
}, },
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"name": "server", "name": "server",
"nodemonConfig": { "nodemonConfig": {

View File

@ -23,9 +23,10 @@
"uuid": "^3.4.0" "uuid": "^3.4.0"
}, },
"devDependencies": {}, "devDependencies": {},
"engineStrict": true,
"engines": { "engines": {
"node": "^16.0.0", "node": "^16.0.0 <=16.15.0",
"npm": "^8.0.0" "npm": "^8.0.0 <=8.5.5"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

View File

@ -1,9 +1,8 @@
module Wasp.Generator.Common module Wasp.Generator.Common
( ProjectRootDir, ( ProjectRootDir,
nodeVersion, nodeVersionRange,
nodeVersionBounds, npmVersionRange,
npmVersionBounds, prismaVersion,
prismaVersionBounds,
) )
where where
@ -12,21 +11,30 @@ import qualified Wasp.SemanticVersion as SV
-- | Directory where the whole web app project (client, server, ...) is generated. -- | Directory where the whole web app project (client, server, ...) is generated.
data ProjectRootDir data ProjectRootDir
-- | Node version that node packages generated by this generator expect. -- | Range of node versions that node packages generated by this generator work correctly with.
nodeVersion :: SV.Version nodeVersionRange :: SV.Range
nodeVersion = SV.Version 16 0 0 -- Latest LTS version. nodeVersionRange =
SV.rangeFromVersionsIntersection
[ (SV.BackwardsCompatibleWith, latestLTSVersion),
(SV.LessThanOrEqual, latestLTSExactVersionThatWeKnowWorks)
]
where
latestLTSVersion = SV.Version 16 0 0
-- There is a bug in node 16.15.1 (more correctly, in npm 8.11.0 that comes with it)
-- that messes up how Wasp uses Prisma. That is why we limited ourselves to <=16.15.0 for now.
-- Bug issue on NPM CLI repo: https://github.com/npm/cli/issues/5018 .
latestLTSExactVersionThatWeKnowWorks = SV.Version 16 15 0
nodeVersionBounds :: SV.VersionBounds -- | Range of npm versions that Wasp and generated projects work correctly with.
nodeVersionBounds = SV.BackwardsCompatibleWith nodeVersion npmVersionRange :: SV.Range
npmVersionRange =
npmVersion :: SV.Version SV.rangeFromVersionsIntersection
npmVersion = SV.Version 8 0 0 -- Latest LTS version. [ (SV.BackwardsCompatibleWith, latestLTSVersion),
(SV.LessThanOrEqual, latestLTSExactVersionThatWeKnowWorks)
npmVersionBounds :: SV.VersionBounds ]
npmVersionBounds = SV.BackwardsCompatibleWith npmVersion where
latestLTSVersion = SV.Version 8 0 0 -- Goes with node 16
latestLTSExactVersionThatWeKnowWorks = SV.Version 8 5 5 -- Goes with node 16.15.0
prismaVersion :: SV.Version prismaVersion :: SV.Version
prismaVersion = SV.Version 3 9 1 prismaVersion = SV.Version 3 9 1
prismaVersionBounds :: SV.VersionBounds
prismaVersionBounds = SV.Exact prismaVersion

View File

@ -11,7 +11,7 @@ import StrongPath (Abs, Dir, Path', (</>))
import qualified StrongPath as SP import qualified StrongPath as SP
import System.Exit (ExitCode (..)) import System.Exit (ExitCode (..))
import qualified System.Info import qualified System.Info
import Wasp.Generator.Common (ProjectRootDir, prismaVersionBounds) import Wasp.Generator.Common (ProjectRootDir, prismaVersion)
import Wasp.Generator.DbGenerator.Common (dbSchemaFileInProjectRootDir) import Wasp.Generator.DbGenerator.Common (dbSchemaFileInProjectRootDir)
import Wasp.Generator.Job (JobMessage, JobMessageData (JobExit, JobOutput)) import Wasp.Generator.Job (JobMessage, JobMessageData (JobExit, JobOutput))
import qualified Wasp.Generator.Job as J import qualified Wasp.Generator.Job as J
@ -24,7 +24,7 @@ import Wasp.Generator.ServerGenerator.Common (serverRootDirInProjectRootDir)
-- We also pin the version to what we need so it won't accidentally find a different version globally -- We also pin the version to what we need so it won't accidentally find a different version globally
-- somewhere on the PATH. -- somewhere on the PATH.
npxPrismaCmd :: [String] npxPrismaCmd :: [String]
npxPrismaCmd = ["npx", "--no-install", "prisma@" ++ show prismaVersionBounds] npxPrismaCmd = ["npx", "--no-install", "prisma@" ++ show prismaVersion]
migrateDev :: Path' Abs (Dir ProjectRootDir) -> Maybe String -> J.Job migrateDev :: Path' Abs (Dir ProjectRootDir) -> Maybe String -> J.Job
migrateDev projectDir maybeMigrationName = do migrateDev projectDir maybeMigrationName = do

View File

@ -100,7 +100,7 @@ runNodeCommandAsJob fromDir command args jobType chan = do
case errorOrNodeVersion of case errorOrNodeVersion of
Left errorMsg -> exitWithError (ExitFailure 1) (T.pack errorMsg) Left errorMsg -> exitWithError (ExitFailure 1) (T.pack errorMsg)
Right nodeVersion -> Right nodeVersion ->
if SV.isVersionInBounds nodeVersion C.nodeVersionBounds if SV.isVersionInRange nodeVersion C.nodeVersionRange
then do then do
let process = (P.proc command args) {P.cwd = Just $ SP.fromAbsDir fromDir} let process = (P.proc command args) {P.cwd = Just $ SP.fromAbsDir fromDir}
runProcessAsJob process jobType chan runProcessAsJob process jobType chan
@ -167,6 +167,6 @@ makeNodeVersionMismatchMessage nodeVersion =
waspNodeRequirementMessage :: String waspNodeRequirementMessage :: String
waspNodeRequirementMessage = waspNodeRequirementMessage =
unwords unwords
[ "Wasp requires node " ++ show C.nodeVersionBounds ++ ".", [ "Wasp requires node " ++ show C.nodeVersionRange ++ ".",
"Check Wasp docs for more details: https://wasp-lang.dev/docs#requirements." "Check Wasp docs for more details: https://wasp-lang.dev/docs#requirements."
] ]

View File

@ -34,7 +34,7 @@ import qualified Wasp.AppSpec.App.Server as AS.App.Server
import qualified Wasp.AppSpec.Entity as AS.Entity import qualified Wasp.AppSpec.Entity as AS.Entity
import Wasp.AppSpec.Util (isPgBossJobExecutorUsed) import Wasp.AppSpec.Util (isPgBossJobExecutorUsed)
import Wasp.AppSpec.Valid (getApp, isAuthEnabled) import Wasp.AppSpec.Valid (getApp, isAuthEnabled)
import Wasp.Generator.Common (nodeVersion, nodeVersionBounds, npmVersionBounds, prismaVersionBounds) import Wasp.Generator.Common (nodeVersionRange, npmVersionRange, prismaVersion)
import Wasp.Generator.ExternalCodeGenerator (genExternalCodeDir) import Wasp.Generator.ExternalCodeGenerator (genExternalCodeDir)
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir) import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
import Wasp.Generator.FileDraft (FileDraft, createCopyFileDraft) import Wasp.Generator.FileDraft (FileDraft, createCopyFileDraft)
@ -48,7 +48,6 @@ import qualified Wasp.Generator.ServerGenerator.ExternalCodeGenerator as ServerE
import Wasp.Generator.ServerGenerator.JobGenerator (depsRequiredByJobs, genJobExecutors, genJobs) import Wasp.Generator.ServerGenerator.JobGenerator (depsRequiredByJobs, genJobExecutors, genJobs)
import Wasp.Generator.ServerGenerator.OperationsG (genOperations) import Wasp.Generator.ServerGenerator.OperationsG (genOperations)
import Wasp.Generator.ServerGenerator.OperationsRoutesG (genOperationsRoutes) import Wasp.Generator.ServerGenerator.OperationsRoutesG (genOperationsRoutes)
import qualified Wasp.SemanticVersion as SV
import Wasp.Util ((<++>)) import Wasp.Util ((<++>))
genServer :: AppSpec -> Generator [FileDraft] genServer :: AppSpec -> Generator [FileDraft]
@ -57,7 +56,6 @@ genServer spec =
[ genReadme, [ genReadme,
genPackageJson spec (npmDepsForWasp spec), genPackageJson spec (npmDepsForWasp spec),
genNpmrc, genNpmrc,
genNvmrc,
genGitignore genGitignore
] ]
<++> genSrcDir spec <++> genSrcDir spec
@ -94,8 +92,8 @@ genPackageJson spec waspDependencies = do
object object
[ "depsChunk" .= N.getDependenciesPackageJsonEntry combinedDependencies, [ "depsChunk" .= N.getDependenciesPackageJsonEntry combinedDependencies,
"devDepsChunk" .= N.getDevDependenciesPackageJsonEntry combinedDependencies, "devDepsChunk" .= N.getDevDependenciesPackageJsonEntry combinedDependencies,
"nodeVersionBounds" .= show nodeVersionBounds, "nodeVersionRange" .= show nodeVersionRange,
"npmVersionBounds" .= show npmVersionBounds, "npmVersionRange" .= show npmVersionRange,
"startProductionScript" "startProductionScript"
.= ( (if not (null $ AS.getDecls @AS.Entity.Entity spec) then "npm run db-migrate-prod && " else "") .= ( (if not (null $ AS.getDecls @AS.Entity.Entity spec) then "npm run db-migrate-prod && " else "")
++ "NODE_ENV=production node ./src/server.js" ++ "NODE_ENV=production node ./src/server.js"
@ -113,7 +111,7 @@ npmDepsForWasp spec =
("debug", "~2.6.9"), ("debug", "~2.6.9"),
("express", "~4.16.1"), ("express", "~4.16.1"),
("morgan", "~1.9.1"), ("morgan", "~1.9.1"),
("@prisma/client", show prismaVersionBounds), ("@prisma/client", show prismaVersion),
("jsonwebtoken", "^8.5.1"), ("jsonwebtoken", "^8.5.1"),
("secure-password", "^4.0.0"), ("secure-password", "^4.0.0"),
("dotenv", "8.2.0"), ("dotenv", "8.2.0"),
@ -124,7 +122,7 @@ npmDepsForWasp spec =
AS.Dependency.fromList AS.Dependency.fromList
[ ("nodemon", "^2.0.4"), [ ("nodemon", "^2.0.4"),
("standard", "^14.3.4"), ("standard", "^14.3.4"),
("prisma", show prismaVersionBounds) ("prisma", show prismaVersion)
] ]
} }
@ -136,19 +134,6 @@ genNpmrc =
(C.asServerFile [relfile|.npmrc|]) (C.asServerFile [relfile|.npmrc|])
Nothing Nothing
genNvmrc :: Generator FileDraft
genNvmrc =
return $
C.mkTmplFdWithDstAndData
(C.asTmplFile [relfile|nvmrc|])
(C.asServerFile [relfile|.nvmrc|])
-- We want to specify only the major version here. If we specified the
-- entire version string (i.e., 16.0.0), our project would work only with
-- that exact version, which we don't want. Unfortunately, the nvmrc file
-- format doesn't allow semver compatibility strings (e.g., ^16.0.0) so
-- listing the major version was the next best thing.
(Just (object ["nodeVersion" .= show (SV.major nodeVersion)]))
genGitignore :: Generator FileDraft genGitignore :: Generator FileDraft
genGitignore = genGitignore =
return $ return $

View File

@ -1,7 +1,7 @@
module Wasp.Generator.ServerGenerator.JobGenerator module Wasp.Generator.ServerGenerator.JobGenerator
( genJobs, ( genJobs,
genJobExecutors, genJobExecutors,
pgBossVersionBounds, pgBossVersionRange,
pgBossDependency, pgBossDependency,
depsRequiredByJobs, depsRequiredByJobs,
) )
@ -136,11 +136,11 @@ jobsDirInServerRootDir = SP.castRel jobsDirInServerTemplatesDir
-- NOTE: Our pg-boss related documentation references this version in URLs. -- NOTE: Our pg-boss related documentation references this version in URLs.
-- Please update the docs when this changes (until we solve: https://github.com/wasp-lang/wasp/issues/596). -- Please update the docs when this changes (until we solve: https://github.com/wasp-lang/wasp/issues/596).
pgBossVersionBounds :: SV.VersionBounds pgBossVersionRange :: SV.Range
pgBossVersionBounds = SV.BackwardsCompatibleWith (SV.Version 7 2 1) pgBossVersionRange = SV.rangeFromVersion (SV.BackwardsCompatibleWith, SV.Version 7 2 1)
pgBossDependency :: AS.Dependency.Dependency pgBossDependency :: AS.Dependency.Dependency
pgBossDependency = AS.Dependency.make ("pg-boss", show pgBossVersionBounds) pgBossDependency = AS.Dependency.make ("pg-boss", show pgBossVersionRange)
depsRequiredByJobs :: AppSpec -> [AS.Dependency.Dependency] depsRequiredByJobs :: AppSpec -> [AS.Dependency.Dependency]
depsRequiredByJobs spec = [pgBossDependency | isPgBossJobExecutorUsed spec] depsRequiredByJobs spec = [pgBossDependency | isPgBossJobExecutorUsed spec]

View File

@ -23,7 +23,7 @@ import qualified Wasp.AppSpec.App as AS.App
import Wasp.AppSpec.App.Client as AS.App.Client import Wasp.AppSpec.App.Client as AS.App.Client
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
import Wasp.AppSpec.Valid (getApp) import Wasp.AppSpec.Valid (getApp)
import Wasp.Generator.Common (nodeVersion, nodeVersionBounds, npmVersionBounds) import Wasp.Generator.Common (nodeVersionRange, npmVersionRange)
import Wasp.Generator.ExternalCodeGenerator (genExternalCodeDir) import Wasp.Generator.ExternalCodeGenerator (genExternalCodeDir)
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir) import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
import Wasp.Generator.FileDraft import Wasp.Generator.FileDraft
@ -35,7 +35,6 @@ import qualified Wasp.Generator.WebAppGenerator.Common as C
import qualified Wasp.Generator.WebAppGenerator.ExternalCodeGenerator as WebAppExternalCodeGenerator import qualified Wasp.Generator.WebAppGenerator.ExternalCodeGenerator as WebAppExternalCodeGenerator
import Wasp.Generator.WebAppGenerator.OperationsGenerator (genOperations) import Wasp.Generator.WebAppGenerator.OperationsGenerator (genOperations)
import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter) import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter)
import qualified Wasp.SemanticVersion as SV
import Wasp.Util ((<++>)) import Wasp.Util ((<++>))
genWebApp :: AppSpec -> Generator [FileDraft] genWebApp :: AppSpec -> Generator [FileDraft]
@ -44,7 +43,6 @@ genWebApp spec = do
[ genReadme, [ genReadme,
genPackageJson spec (npmDepsForWasp spec), genPackageJson spec (npmDepsForWasp spec),
genNpmrc, genNpmrc,
genNvmrc,
genGitignore, genGitignore,
return $ C.mkTmplFd $ C.asTmplFile [relfile|netlify.toml|] return $ C.mkTmplFd $ C.asTmplFile [relfile|netlify.toml|]
] ]
@ -67,8 +65,8 @@ genPackageJson spec waspDependencies = do
[ "appName" .= (fst (getApp spec) :: String), [ "appName" .= (fst (getApp spec) :: String),
"depsChunk" .= N.getDependenciesPackageJsonEntry combinedDependencies, "depsChunk" .= N.getDependenciesPackageJsonEntry combinedDependencies,
"devDepsChunk" .= N.getDevDependenciesPackageJsonEntry combinedDependencies, "devDepsChunk" .= N.getDevDependenciesPackageJsonEntry combinedDependencies,
"nodeVersionBounds" .= show nodeVersionBounds, "nodeVersionRange" .= show nodeVersionRange,
"npmVersionBounds" .= show npmVersionBounds "npmVersionRange" .= show npmVersionRange
] ]
) )
@ -80,15 +78,6 @@ genNpmrc =
(C.asWebAppFile [relfile|.npmrc|]) (C.asWebAppFile [relfile|.npmrc|])
Nothing Nothing
genNvmrc :: Generator FileDraft
genNvmrc =
return $
C.mkTmplFdWithDstAndData
(C.asTmplFile [relfile|nvmrc|])
(C.asWebAppFile [relfile|.nvmrc|])
-- We want to specify only the major version, check the comment in `ServerGenerator.hs` for details
(Just (object ["nodeVersion" .= show (SV.major nodeVersion)]))
npmDepsForWasp :: AppSpec -> N.NpmDepsForWasp npmDepsForWasp :: AppSpec -> N.NpmDepsForWasp
npmDepsForWasp _spec = npmDepsForWasp _spec =
N.NpmDepsForWasp N.NpmDepsForWasp

View File

@ -1,13 +1,23 @@
module Wasp.SemanticVersion module Wasp.SemanticVersion
( Version (..), ( Version (..),
VersionBounds (..), Operator (..),
isVersionInBounds, Comparator (..),
ComparatorSet (..),
Range (..),
isVersionInRange,
rangeFromVersion,
rangeFromVersionsIntersection,
) )
where where
import Data.List (intercalate)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE
import Numeric.Natural import Numeric.Natural
import Text.Printf (printf) import Text.Printf (printf)
-- Implements SemVer (semantic versioning) by following spec from https://github.com/npm/node-semver .
data Version = Version data Version = Version
{ major :: Natural, { major :: Natural,
minor :: Natural, minor :: Natural,
@ -15,27 +25,80 @@ data Version = Version
} }
deriving (Eq, Ord) deriving (Eq, Ord)
-- | We rely on `show` here to produce valid semver representation of version.
instance Show Version where instance Show Version where
show (Version mjr mnr ptc) = printf "%d.%d.%d" mjr mnr ptc show (Version mjr mnr ptc) = printf "%d.%d.%d" mjr mnr ptc
data VersionBounds data Operator
= -- | Allows only the version exactly equal to the one specified in the bounds = Equal
Exact Version | LessThanOrEqual
| -- | Allows changes that do not modify the leftmost non-zero digit in major.minor.patch, as described | -- | TODO: BackwardsCompatibleWith (^) is actually, by semver spec, a special "range" which desugarizes into basic
-- in node semver docs: https://github.com/npm/node-semver#caret-ranges-123-025-004 -- comparators.
BackwardsCompatibleWith Version -- Same goes for other special ranges like Hyphen, v.x.x, ~, ... . They are all sugars that can be expressed with basic
-- comparators.
-- Although they call them special "ranges", since they all desugarize into comparator sets (no ||), we can also
-- more strictly treat them as special "comparator sets".
-- Therefore, we should remove ^ as an operator from here and instead extend ComparatorSet as:
-- data ComparatorSet = Regular (NonEmpty Comparator) | Special ComparatorSetSpecial
-- data ComparatorSetSpecial = BackwardsCompatibleWith Version | Hyphen Version Version | ...
BackwardsCompatibleWith
deriving (Eq)
instance Show VersionBounds where -- | We rely on `show` here to produce valid semver representation of operator.
show (BackwardsCompatibleWith version) = "^" ++ show version instance Show Operator where
show (Exact version) = show version show Equal = "="
show LessThanOrEqual = "<="
show BackwardsCompatibleWith = "^"
isVersionInBounds :: Version -> VersionBounds -> Bool data Comparator = Comparator Operator Version
isVersionInBounds version bounds = case bounds of deriving (Eq)
(BackwardsCompatibleWith reference) -> version >= reference && version < nextBreakingChangeVersion reference
(Exact reference) -> version == reference -- | We rely on `show` here to produce valid semver representation of comparator.
instance Show Comparator where
show (Comparator op v) = show op ++ show v
data ComparatorSet = ComparatorSet (NonEmpty Comparator)
deriving (Eq)
-- | We rely on `show` here to produce valid semver representation of comparator set.
instance Show ComparatorSet where
show (ComparatorSet comps) = unwords $ show <$> NE.toList comps
data Range = Range [ComparatorSet]
deriving (Eq)
-- | We rely on `show` here to produce valid semver representation of version range.
instance Show Range where
show (Range compSets) = intercalate " || " $ show <$> compSets
-- | We define concatenation of two version ranges as union of their comparator sets.
instance Semigroup Range where
(Range csets1) <> (Range csets2) = Range $ csets1 <> csets2
instance Monoid Range where
mempty = Range []
nextBreakingChangeVersion :: Version -> Version nextBreakingChangeVersion :: Version -> Version
nextBreakingChangeVersion version = case version of nextBreakingChangeVersion version = case version of
(Version 0 0 x) -> Version 0 0 (succ x) (Version 0 0 x) -> Version 0 0 (succ x)
(Version 0 x _) -> Version 0 (succ x) 0 (Version 0 x _) -> Version 0 (succ x) 0
(Version x _ _) -> Version (succ x) 0 0 (Version x _ _) -> Version (succ x) 0 0
doesVersionSatisfyComparator :: Version -> Comparator -> Bool
doesVersionSatisfyComparator version (Comparator operator compVersion) = case operator of
Equal -> version == compVersion
LessThanOrEqual -> version <= compVersion
BackwardsCompatibleWith -> version >= compVersion && version < nextBreakingChangeVersion compVersion
doesVersionSatisfyComparatorSet :: Version -> ComparatorSet -> Bool
doesVersionSatisfyComparatorSet version (ComparatorSet comps) = all (doesVersionSatisfyComparator version) comps
isVersionInRange :: Version -> Range -> Bool
isVersionInRange version (Range compSets) = any (doesVersionSatisfyComparatorSet version) compSets
rangeFromVersion :: (Operator, Version) -> Range
rangeFromVersion = Range . pure . ComparatorSet . pure . uncurry Comparator
rangeFromVersionsIntersection :: [(Operator, Version)] -> Range
rangeFromVersionsIntersection [] = Range []
rangeFromVersionsIntersection compPairs = Range $ pure $ ComparatorSet $ uncurry Comparator <$> NE.fromList compPairs

View File

@ -1,39 +1,133 @@
module SemanticVersionTest where module SemanticVersionTest where
import Data.List.NonEmpty (fromList)
import Numeric.Natural
import Test.Tasty.Hspec import Test.Tasty.Hspec
import Wasp.SemanticVersion import Wasp.SemanticVersion
spec_isVersionInBounds :: Spec spec_SemanticVersion :: Spec
spec_isVersionInBounds = do spec_SemanticVersion = do
it "Correctly determines satisfied exact version bounds" $ describe "`show` produces valid semver strings" $ do
Version 1 2 3 `isVersionInBounds` Exact (Version 1 2 3) it "show Version" $ do
it "Correctly determines unsatisfied exact version bounds" $ show (Version 0 0 0) `shouldBe` "0.0.0"
not $ show (Version 0 19 0) `shouldBe` "0.19.0"
any show (Version 1 2 314) `shouldBe` "1.2.314"
(`isVersionInBounds` Exact (Version 1 2 3)) it "show empty Range" $ do
[Version 2 2 3, Version 1 3 3, Version 1 2 4] show (mempty :: Range) `shouldBe` ""
it "Correctly determine satisfied backwards compatible version bounds" $ it "show complex Range" $ do
all let versionComp1 = (Equal, Version 1 2 3)
(`isVersionInBounds` BackwardsCompatibleWith (Version 1 2 3)) let versionComp2 = (LessThanOrEqual, Version 1 3 6)
[Version 1 2 3, Version 1 2 4, Version 1 3 0] let versionComp3 = (BackwardsCompatibleWith, Version 1 2 0)
it "Correctly determines unsatisfied backwards compatible version bounds" $ let complexRange =
not $ rangeFromVersionsIntersection [versionComp2, versionComp3]
any <> rangeFromVersion versionComp1
(`isVersionInBounds` BackwardsCompatibleWith (Version 1 2 3)) show complexRange `shouldBe` "<=1.3.6 ^1.2.0 || =1.2.3"
[Version 1 2 0, Version 1 2 2, Version 2 0 0] it "concatenating two version ranges with `<>` produces union of their comparator sets" $ do
it "Correctly determines satisfied backwards compatible version bounds with 0.x versions" $ let concatenatedRange =
all rangeFromVersion (LessThanOrEqual, Version 1 2 3)
(`isVersionInBounds` BackwardsCompatibleWith (Version 0 2 3)) <> rangeFromVersion (Equal, Version 1 0 0)
[Version 0 2 3, Version 0 2 4] let expectedRange =
it "Correctly determines unsatisfied backwards compatible version bounds with 0.x versions" $ Range
not $ [ ComparatorSet $ pure $ Comparator LessThanOrEqual (Version 1 2 3),
any ComparatorSet $ pure $ Comparator Equal (Version 1 0 0)
(`isVersionInBounds` BackwardsCompatibleWith (Version 0 2 3)) ]
[Version 0 0 0, Version 0 1 3, Version 0 2 0, Version 0 2 2, Version 0 3 0, Version 1 0 0] concatenatedRange `shouldBe` expectedRange
it "Correctly determines satisfied backwards compatible version bounds with 0.0.x versions" $ it "mempty of Range is empty Range" $ do
Version 0 0 2 `isVersionInBounds` BackwardsCompatibleWith (Version 0 0 2) mempty `shouldBe` Range []
it "Correctly determines unsatisfied backwards compatible version bounds with 0.0.x versions" $ it "rangeFromVersionsIntersection produces version range that puts all version comparators into one comparator set" $ do
not $ rangeFromVersionsIntersection [(LessThanOrEqual, Version 1 2 3), (Equal, Version 1 0 0)]
any `shouldBe` ( Range . pure . ComparatorSet $
(`isVersionInBounds` BackwardsCompatibleWith (Version 0 0 2)) fromList [Comparator LessThanOrEqual (Version 1 2 3), Comparator Equal (Version 1 0 0)]
[Version 0 0 1, Version 0 0 3, Version 0 1 0, Version 1 0 0] )
it "rangeFromVersion produces version range with single version comparator in it" $ do
rangeFromVersion (LessThanOrEqual, Version 1 2 3)
`shouldBe` (Range . pure . ComparatorSet . pure $ Comparator LessThanOrEqual (Version 1 2 3))
describe "isVersionInRange" $ do
it "No version is in empty range" $
testRange
mempty
[ ((0, 5, 5), False),
((1, 0, 0), False),
((1, 2, 3), False),
((1, 2, 4), False),
((1, 3, 0), False),
((2, 0, 0), False)
]
it "Recognizes only version v to be in range '=v'" $
testRange
(rangeFromVersion (Equal, Version 1 2 3))
[ ((0, 5, 5), False),
((1, 0, 0), False),
((1, 2, 3), True),
((1, 2, 4), False),
((1, 3, 0), False),
((2, 0, 0), False)
]
it "Recognizes only versions lesser or equal to v to be in range '<=v'" $
testRange
(rangeFromVersion (LessThanOrEqual, Version 1 2 3))
[ ((0, 5, 5), True),
((1, 0, 0), True),
((1, 2, 3), True),
((1, 2, 4), False),
((1, 3, 0), False),
((2, 0, 0), False)
]
describe "Recognizes only versions >=v but smaller than next breaking change to be in range '^v'" $ do
it "when v is of shape x.y.z where x != 0." $
testRange
(rangeFromVersion (BackwardsCompatibleWith, Version 1 2 3))
[ ((0, 5, 5), False),
((1, 0, 0), False),
((1, 2, 3), True),
((1, 2, 4), True),
((1, 3, 0), True),
((2, 0, 0), False)
]
it "when v is of shape 0.y.z where y != 0." $
testRange
(rangeFromVersion (BackwardsCompatibleWith, Version 0 2 3))
[ ((0, 0, 0), False),
((0, 1, 3), False),
((0, 2, 0), False),
((0, 2, 2), False),
((0, 2, 3), True),
((0, 2, 4), True),
((0, 3, 0), False),
((1, 0, 0), False)
]
it "when v is of shape 0.0.z." $
testRange
(rangeFromVersion (BackwardsCompatibleWith, Version 0 0 2))
[ ((0, 0, 1), False),
((0, 0, 2), True),
((0, 0, 3), False),
((0, 1, 0), False),
((1, 0, 0), False)
]
it "Correctly works for complex version range." $
testRange
( rangeFromVersionsIntersection
[ (LessThanOrEqual, Version 1 2 3),
(BackwardsCompatibleWith, Version 1 1 0)
]
<> rangeFromVersion (Equal, Version 0 5 6)
)
[ ((0, 5, 5), False),
((0, 5, 6), True),
((0, 5, 7), False),
((1, 0, 9), False),
((1, 1, 0), True),
((1, 1, 9), True),
((1, 2, 3), True),
((1, 2, 4), False),
((1, 3, 0), False),
((2, 0, 0), False)
]
where
testRange :: Range -> [((Natural, Natural, Natural), Bool)] -> Expectation
testRange range versionsWithResults =
( (`isVersionInRange` range) . (\(x, y, z) -> Version x y z)
<$> map fst versionsWithResults
)
`shouldBe` map snd versionsWithResults

View File

@ -28,10 +28,8 @@ data-files:
Generator/templates/dockerignore Generator/templates/dockerignore
Generator/templates/react-app/gitignore Generator/templates/react-app/gitignore
Generator/templates/react-app/npmrc Generator/templates/react-app/npmrc
Generator/templates/react-app/nvmrc
Generator/templates/server/gitignore Generator/templates/server/gitignore
Generator/templates/server/npmrc Generator/templates/server/npmrc
Generator/templates/server/nvmrc
Generator/templates/**/*.prisma Generator/templates/**/*.prisma
Generator/templates/**/*.toml Generator/templates/**/*.toml
Generator/templates/**/*.json Generator/templates/**/*.json

View File

@ -11,14 +11,14 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
## 1. Requirements ## 1. Requirements
### Node.js compatible with 16.0.0 You need to have `node` (and `npm`) installed on your machine and available in `PATH`.
```shell-session - `node`: >=16.11.0 and <=16.15.0
node -v # must be something like 16.x.x - `npm`: >= 8.0.0 and <= 8.5.5
```
### NPM compatible with 8.0.0 You can check `node` and `npm` versions by running:
```shell-session ```shell-session
npm -v # must be something like 8.x.x node -v
npm -v
``` ```
We recommend using [nvm](https://github.com/nvm-sh/nvm) for managing your Node.js installation version(s). We recommend using [nvm](https://github.com/nvm-sh/nvm) for managing your Node.js installation version(s).
@ -31,14 +31,14 @@ We recommend using [nvm](https://github.com/nvm-sh/nvm) for managing your Node.j
Install nvm via your OS package manager (aptitude, pacman, homebrew, ...) or alternatively via [nvm install script](https://github.com/nvm-sh/nvm#install--update-script). Install nvm via your OS package manager (aptitude, pacman, homebrew, ...) or alternatively via [nvm install script](https://github.com/nvm-sh/nvm#install--update-script).
Then, install a version of node that you need (any version compatible with 16.0.0), e.g.: Then, install a version of node that you need, e.g.:
```shell-session ```shell-session
nvm install 16 nvm install 16.15.0
``` ```
Finally, whenever you need to ensure specific version of node is used, run e.g. Finally, whenever you need to ensure specific version of node is used, run e.g.
```shell-session ```shell-session
nvm use 16 nvm use 16.15.0
``` ```
to set the node version for current shell session. to set the node version for current shell session.
@ -53,6 +53,17 @@ We recommend using [nvm](https://github.com/nvm-sh/nvm) for managing your Node.j
</div> </div>
</details> </details>
:::info
Why does Wasp require this specific `node` range and doesn't support a newer version x.y.z?
At Wasp, we focus on supporting the latest LTS ("long-term-support") Node.js version, since it guarantees stability and active maintainance, which is why official Node.js team recommends it for usage in production.
Therefore, a specific Wasp release will usually require the version of Node.js that was LTS at that point of time.
Check out https://nodejs.org/en/about/releases/ for more details about Node.js releases.
Sometimes we will make an exception to that and additionaly limit the Node.js version or postpone switching to the latest LTS if there are certain issues with new Node.js version, in which case we will catch up once those are resolved on Node.js side or we find a workaround on Wasp side.
:::
## 2. Installation ## 2. Installation