diff --git a/CHANGELOG.md b/CHANGELOG.md index b7860353..43049c96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ main natively since `5.0`, and every Rails version this gem supports is `>= 5.2`. To upgrade, remove `rails_12factor` from your project's `Gemfile` * Replace `EmberCli::App#yarn_enabled?` with `EmberCli::App#yarn?` +* Pin the NodeJS version in the `package.json` that `rails generate + ember:heroku` writes, from the `engines.node` the Ember applications + declare. Applications that declare different versions are left unpinned +* Add `EmberCli::App#node_engine`, reading `engines.node` from the Ember + application's `package.json` 0.13.2 ------ diff --git a/lib/ember_cli/app.rb b/lib/ember_cli/app.rb index 0349f3a2..9745d06a 100644 --- a/lib/ember_cli/app.rb +++ b/lib/ember_cli/app.rb @@ -1,3 +1,5 @@ +require "json" + require "html_page/renderer" require "ember_cli/path_set" require "ember_cli/shell" @@ -99,6 +101,16 @@ def bower? paths.bower_json.exist? end + def node_engine + package_json = paths.package_json + + if package_json.exist? + JSON.parse(package_json.read).dig("engines", "node") + end + rescue JSON::ParserError + nil + end + def to_rack deploy.to_rack end diff --git a/lib/ember_cli/path_set.rb b/lib/ember_cli/path_set.rb index b14a2c5b..e3936880 100644 --- a/lib/ember_cli/path_set.rb +++ b/lib/ember_cli/path_set.rb @@ -35,6 +35,10 @@ def gemfile @gemfile ||= root.join("Gemfile") end + def package_json + @package_json ||= root.join("package.json") + end + def bower_json root.join("bower.json") end diff --git a/lib/generators/ember/heroku/heroku_generator.rb b/lib/generators/ember/heroku/heroku_generator.rb index 36717e35..9378096e 100644 --- a/lib/generators/ember/heroku/heroku_generator.rb +++ b/lib/generators/ember/heroku/heroku_generator.rb @@ -16,6 +16,26 @@ def identify_as_yarn_project private + def node_engine + return @node_engine if defined?(@node_engine) + + declared = apps.map(&:node_engine).compact.uniq + + @node_engine = + if declared.size > 1 + say_status( + :conflict, + "Ember applications declare different `engines.node` " \ + "(#{declared.join(", ")}); pin one in package.json by hand", + :red, + ) + + nil + else + declared.first + end + end + def cache_directories all_cached_directories.map do |cached_directory| cached_directory.relative_path_from(Rails.root).to_s diff --git a/lib/generators/ember/heroku/templates/package.json.erb b/lib/generators/ember/heroku/templates/package.json.erb index a029a9cb..f9845a0c 100644 --- a/lib/generators/ember/heroku/templates/package.json.erb +++ b/lib/generators/ember/heroku/templates/package.json.erb @@ -4,5 +4,10 @@ "bower": "*" }, <% end %> + <% if node_engine %> + "engines": { + "node": <%= node_engine.to_json %> + }, + <% end %> "cacheDirectories": <%= cache_directories.to_json %> } diff --git a/spec/generators/ember/heroku/heroku_generator_spec.rb b/spec/generators/ember/heroku/heroku_generator_spec.rb index 51d86442..0b07423f 100644 --- a/spec/generators/ember/heroku/heroku_generator_spec.rb +++ b/spec/generators/ember/heroku/heroku_generator_spec.rb @@ -82,6 +82,54 @@ def gemfile_contents end end + describe "engines" do + it "pins the NodeJS version the Ember applications declare" do + setup_destination + configure_applications(node_engine: ">= 20.19.0") + + run_generator + + expect(package_json.fetch("engines")).to eq("node" => ">= 20.19.0") + end + + it "omits engines when no Ember application declares one" do + setup_destination + configure_applications(node_engine: nil) + + run_generator + + expect(package_json.keys).not_to include("engines") + end + + it "omits engines when the Ember applications disagree" do + setup_destination + configure_applications( + { node_engine: ">= 20.19.0" }, + { node_engine: ">= 22.0.0" }, + ) + + run_generator + + expect(package_json.keys).not_to include("engines") + end + + def configure_applications(*attributes) + apps = attributes.map do |app_attributes| + instance_double( + EmberCli::App, + { + bower?: false, + cached_directories: [], + yarn?: false, + }.merge(app_attributes), + ) + end + + allow(EmberCli).to receive(:apps). + and_return(apps.map.with_index { |app, i| ["app-#{i}", app] }.to_h) + end + end + def depend_on_bower(bower_enabled) allow_any_instance_of(EmberCli::App). to receive(:bower?).and_return(bower_enabled) diff --git a/spec/lib/ember_cli/app_spec.rb b/spec/lib/ember_cli/app_spec.rb index 3d906ab6..0ead0291 100644 --- a/spec/lib/ember_cli/app_spec.rb +++ b/spec/lib/ember_cli/app_spec.rb @@ -93,6 +93,42 @@ end end + describe "#node_engine" do + it "reads engines.node from the application's package.json" do + stub_package_json('{"engines":{"node":">= 20.19.0"}}') + app = EmberCli::App.new("with node engine") + + expect(app.node_engine).to eq ">= 20.19.0" + end + + it "returns nil when the package.json declares no engines" do + stub_package_json('{"name":"frontend"}') + app = EmberCli::App.new("without engines") + + expect(app.node_engine).to be_nil + end + + it "returns nil when the package.json is absent" do + stub_paths(package_json: double("Pathname", exist?: false)) + app = EmberCli::App.new("without package json") + + expect(app.node_engine).to be_nil + end + + it "returns nil when the package.json is malformed" do + stub_package_json("{") + app = EmberCli::App.new("with malformed package json") + + expect(app.node_engine).to be_nil + end + + def stub_package_json(contents) + stub_paths( + package_json: double("Pathname", exist?: true, read: contents), + ) + end + end + describe "#compile" do it "exits with exit status of 0" do passed = EmberCli["my-app"].compile