diff --git a/docs/pages/docs/providers/node.md b/docs/pages/docs/providers/node.md index 2370f7008..d6ddb87fa 100644 --- a/docs/pages/docs/providers/node.md +++ b/docs/pages/docs/providers/node.md @@ -30,6 +30,7 @@ The version can be overridden by - Setting the `NIXPACKS_NODE_VERSION` environment variable - Specifying the `engines.node` field in `package.json` +- Creating a `.nvmrc` file in your project and specify the version or alias (`lts/*`) Only a major version can be specified. For example, `18.x` or `20`. diff --git a/examples/node-nvmrc-invalid-lts/.nvmrc b/examples/node-nvmrc-invalid-lts/.nvmrc new file mode 100644 index 000000000..e40d76f49 --- /dev/null +++ b/examples/node-nvmrc-invalid-lts/.nvmrc @@ -0,0 +1 @@ +some_invalid_version \ No newline at end of file diff --git a/examples/node-nvmrc-invalid-lts/index.js b/examples/node-nvmrc-invalid-lts/index.js new file mode 100644 index 000000000..cd4463680 --- /dev/null +++ b/examples/node-nvmrc-invalid-lts/index.js @@ -0,0 +1 @@ +console.log("Oops this version is invalid. Using default 18"); diff --git a/examples/node-nvmrc-invalid-lts/package-lock.json b/examples/node-nvmrc-invalid-lts/package-lock.json new file mode 100644 index 000000000..0f3eca8df --- /dev/null +++ b/examples/node-nvmrc-invalid-lts/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "node", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "node", + "version": "1.0.0" + } + } + } + \ No newline at end of file diff --git a/examples/node-nvmrc-invalid-lts/package.json b/examples/node-nvmrc-invalid-lts/package.json new file mode 100644 index 000000000..2f12d8fba --- /dev/null +++ b/examples/node-nvmrc-invalid-lts/package.json @@ -0,0 +1,8 @@ +{ + "name": "node", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "node index.js" + } +} diff --git a/examples/node-nvmrc-lts/.nvmrc b/examples/node-nvmrc-lts/.nvmrc new file mode 100644 index 000000000..0a47c855e --- /dev/null +++ b/examples/node-nvmrc-lts/.nvmrc @@ -0,0 +1 @@ +lts/iron \ No newline at end of file diff --git a/examples/node-nvmrc-lts/index.js b/examples/node-nvmrc-lts/index.js new file mode 100644 index 000000000..bf464707d --- /dev/null +++ b/examples/node-nvmrc-lts/index.js @@ -0,0 +1 @@ +console.log("Hello from the NVM test"); diff --git a/examples/node-nvmrc-lts/package-lock.json b/examples/node-nvmrc-lts/package-lock.json new file mode 100644 index 000000000..0f3eca8df --- /dev/null +++ b/examples/node-nvmrc-lts/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "node", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "node", + "version": "1.0.0" + } + } + } + \ No newline at end of file diff --git a/examples/node-nvmrc-lts/package.json b/examples/node-nvmrc-lts/package.json new file mode 100644 index 000000000..2f12d8fba --- /dev/null +++ b/examples/node-nvmrc-lts/package.json @@ -0,0 +1,8 @@ +{ + "name": "node", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "node index.js" + } +} diff --git a/src/nixpacks/plan/mod.rs b/src/nixpacks/plan/mod.rs index 9fa1ae568..2b2db9e72 100644 --- a/src/nixpacks/plan/mod.rs +++ b/src/nixpacks/plan/mod.rs @@ -407,6 +407,47 @@ mod test { assert_eq!(result, env_plan); } + #[test] + fn test_to_json_and_from_json() { + let original_plan = BuildPlan::from_toml( + r#" + [phases.setup] + nixPkgs = ["nodejs", "yarn"] + aptPkgs = ["git"] + + [phases.install] + cmds = ["yarn install"] + cacheDirectories = ["node_modules"] + dependsOn = ["setup"] + + [phases.build] + cmds = ["yarn build"] + dependsOn = ["install"] + + [start] + cmd = "yarn start" + "#, + ) + .unwrap(); + + let json_str = original_plan.to_json().unwrap(); + let deserialized_plan = BuildPlan::from_json(json_str).unwrap(); + + assert_eq!(original_plan, deserialized_plan); + assert_eq!( + deserialized_plan.get_phase("setup").unwrap().nix_pkgs, + Some(vec!["nodejs".to_string(), "yarn".to_string()]) + ); + assert_eq!( + deserialized_plan.get_phase("setup").unwrap().apt_pkgs, + Some(vec!["git".to_string()]) + ); + assert_eq!( + deserialized_plan.start_phase.unwrap().cmd.unwrap(), + "yarn start".to_string() + ); + } + #[test] fn test_get_phases_with_dependencies() { let setup = Phase::new("setup"); diff --git a/src/providers/node/mod.rs b/src/providers/node/mod.rs index ff457b5bd..e4e72e391 100644 --- a/src/providers/node/mod.rs +++ b/src/providers/node/mod.rs @@ -335,7 +335,7 @@ impl NodeProvider { let nvmrc_node_version = if app.includes_file(".nvmrc") { let nvmrc = app.read_file(".nvmrc")?; - Some(nvmrc.trim().replace('v', "")) + Some(parse_nvmrc(&nvmrc)) } else { None }; @@ -636,6 +636,35 @@ fn parse_node_version_into_pkg(node_version: &str) -> String { default_node_pkg_name } +fn parse_nvmrc(nvmrc_content: &str) -> String { + let lts_versions: HashMap<&str, u32> = { + let mut nvm_map = HashMap::new(); + nvm_map.insert("lts/*", 22); + nvm_map.insert("lts/jod", 22); + nvm_map.insert("lts/argon", 4); + nvm_map.insert("lts/boron", 6); + nvm_map.insert("lts/carbon", 8); + nvm_map.insert("lts/dubnium", 10); + nvm_map.insert("lts/erbium", 12); + nvm_map.insert("lts/fermium", 14); + nvm_map.insert("lts/gallium", 16); + nvm_map.insert("lts/hydrogen", 18); + nvm_map.insert("lts/iron", 20); + nvm_map + }; + + let trimmed_version = nvmrc_content.trim(); + if let Some(&version) = lts_versions.get(trimmed_version) { + return version.to_string(); + } + + // Only remove v if it is in the starting character, lts/ will never have that in starting + trimmed_version + .strip_prefix('v') + .unwrap_or(trimmed_version) + .to_string() +} + #[cfg(test)] mod test { use std::collections::BTreeMap; @@ -932,6 +961,40 @@ mod test { Ok(()) } + #[test] + fn test_version_from_nvmrc_lts() -> Result<()> { + assert_eq!( + NodeProvider::get_nix_node_pkg( + &PackageJson { + name: Some(String::default()), + ..Default::default() + }, + &App::new("examples/node-nvmrc-lts")?, + &Environment::default() + )?, + Pkg::new("nodejs_20") + ); + + Ok(()) + } + + #[test] + fn test_invalid_version_from_nvmrc_lts() -> Result<()> { + assert_eq!( + NodeProvider::get_nix_node_pkg( + &PackageJson { + name: Some(String::default()), + ..Default::default() + }, + &App::new("examples/node-nvmrc-invalid-lts")?, + &Environment::default() + )?, + Pkg::new("nodejs_18") + ); + + Ok(()) + } + #[test] fn test_engine_invalid_version() -> Result<()> { // this test now defaults to lts