From 3f915679a4fa04fc00a1ee0d44aed0c7ec624910 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 08:59:37 +0000 Subject: [PATCH 1/2] Serve script tags from Vite's development server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `include_ember_script_tags` always read the output of `ember build`, so a Vite-based application served by ember-cli-rails's development server (tricknotes/ember-cli-rails#653) could not use the asset helpers without opting out of the server — and with it, out of fast reloads. When the application is served by the development server (feature-detected, so older ember-cli-rails releases keep their behavior), read `index.html` from the server instead of a build directory and emit the startup tags with root-relative URLs rewritten to absolute URLs on the server, the same way ember-cli-rails serves the document itself. Nothing is built, so a change to the application is picked up by reloading the page. The extraction shared with the build-directory path now also leaves protocol-relative (`//`) URLs alone, and `include_ember_stylesheet_tags` raises its Vite guidance for development-server applications too (their `dist` may not exist, so the Vite detection alone would miss them). Co-Authored-By: Claude Fable 5 --- Gemfile | 2 +- README.md | 6 ++ app/helpers/ember_cli_rails_assets_helper.rb | 47 ++++++++++++--- .../ember_cli_rails_assets_helper_spec.rb | 60 ++++++++++++++++++- 4 files changed, 104 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index d4e59e3..51ca9cf 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,7 @@ gem "rails", rails_constraint gem "webrick" group :development, :test do - gem "ember-cli-rails", github: "tricknotes/ember-cli-rails" + gem "ember-cli-rails", github: "tricknotes/ember-cli-rails", branch: "vite-recompile-on-change" end group :test do diff --git a/README.md b/README.md index d34205e..58fc5a9 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,12 @@ extracted from the generated `index.html`: <%= include_ember_script_tags :frontend %> ``` +When ember-cli-rails serves the application from Vite's development server in +`development`, `include_ember_script_tags` reads `index.html` from the server +instead of a build directory, and emits the startup tags with absolute URLs +pointing at the server. Nothing is built, so changes to the application are +picked up by reloading the page. + `include_ember_stylesheet_tags` only supports classic applications, and raises an error when called for a Vite-based application. diff --git a/app/helpers/ember_cli_rails_assets_helper.rb b/app/helpers/ember_cli_rails_assets_helper.rb index 158c589..745e9f2 100644 --- a/app/helpers/ember_cli_rails_assets_helper.rb +++ b/app/helpers/ember_cli_rails_assets_helper.rb @@ -7,12 +7,16 @@ def include_ember_script_tags(name, prepend: "") app = EmberCli[name] app.build - paths = EmberCli::Assets::Paths.new(app) - - if paths.vite? - vite_ember_script_tags(paths, prepend) + if dev_server?(app) + dev_server_ember_script_tags(app) else - classic_ember_script_tags(app, prepend) + paths = EmberCli::Assets::Paths.new(app) + + if paths.vite? + vite_ember_script_tags(paths, prepend) + else + classic_ember_script_tags(app, prepend) + end end end @@ -22,7 +26,7 @@ def include_ember_stylesheet_tags(name, prepend: "") paths = EmberCli::Assets::Paths.new(app) - if paths.vite? + if dev_server?(app) || paths.vite? raise EmberCli::Assets::NotSupportedError, <<~MSG `include_ember_stylesheet_tags` does not support Vite-based applications (`ember-cli >= 6.8`). @@ -42,6 +46,22 @@ def include_ember_stylesheet_tags(name, prepend: "") private + # Whether ember-cli-rails serves the application from Vite's development + # server. Older ember-cli-rails releases have no development server, so + # feature-detect the reader. + def dev_server?(app) + app.respond_to?(:dev_server?) && app.dev_server? + end + + # The application is served by Vite's development server, so read + # `index.html` from the server instead of a build directory, and rewrite + # its root-relative URLs to absolute URLs on the server — the same way + # ember-cli-rails serves the document itself. `app.build` has already + # booted the server. + def dev_server_ember_script_tags(app) + vite_startup_tags(app.dev_server.index_html, prefix: app.dev_server.origin) + end + # Classic builds ship a fixed set of scripts (vendor and app) resolved # through the asset map, so emit a plain script tag per JavaScript asset # with `prepend` joined onto each path. @@ -58,15 +78,24 @@ def classic_ember_script_tags(app, prepend) # the tags required for startup (including the config meta tag and # stylesheets) and remap root-absolute paths onto the mount point. def vite_ember_script_tags(paths, prepend) - document = Nokogiri::HTML5(paths.index_html.read) - prefix = prepend.to_s.chomp("/") + vite_startup_tags(paths.index_html.read, prefix: prepend.to_s.chomp("/")) + end + + # Extracts the tags a Vite-based application needs to boot from an + # `index.html` document, with `prefix` joined onto every root-relative + # URL. Protocol-relative URLs (`//`) are left alone. + def vite_startup_tags(html, prefix:) + document = Nokogiri::HTML5(html) tags = document.css( 'meta[name$="/config/environment"], link[rel="stylesheet"], link[rel="modulepreload"], script' ).map do |tag| %w(href src).each do |attribute| value = tag[attribute] - tag[attribute] = "#{prefix}#{value}" if value&.start_with?("/") + + if value&.start_with?("/") && !value.start_with?("//") + tag[attribute] = "#{prefix}#{value}" + end end tag.to_html.html_safe end diff --git a/spec/helpers/ember_cli_rails_assets_helper_spec.rb b/spec/helpers/ember_cli_rails_assets_helper_spec.rb index 5e19698..7c0ce51 100644 --- a/spec/helpers/ember_cli_rails_assets_helper_spec.rb +++ b/spec/helpers/ember_cli_rails_assets_helper_spec.rb @@ -1,10 +1,68 @@ require "rails_helper" describe EmberCliRailsAssetsHelper do + describe "#include_ember_script_tags" do + context "when the application is served by Vite's development server" do + it "emits the startup tags with root-relative URLs rewritten onto the server" do + index_html = <<~HTML + + + + + + + + + + + + + HTML + dev_server = instance_double( + EmberCli::DevServer, + index_html: index_html, + origin: "http://127.0.0.1:4200", + ) + app = instance_double( + EmberCli::App, + build: true, + dev_server?: true, + dev_server: dev_server, + ) + allow(EmberCli).to receive(:[]).with(:frontend).and_return(app) + + tags = helper.include_ember_script_tags(:frontend) + + expect(app).to have_received(:build) + expect(tags).to include(%{src="http://127.0.0.1:4200/@vite/client"}) + expect(tags).to include(%{src="http://127.0.0.1:4200/@embroider/virtual/vendor.js"}) + expect(tags).to include(%{href="http://127.0.0.1:4200/@embroider/virtual/app.css"}) + expect(tags).to include(%{name="my-app/config/environment"}) + expect(tags).to include(%{src="https://cdn.example.com/analytics.js"}) + expect(tags).to include(%{src="//cdn.example.com/protocol-relative.js"}) + end + end + end + describe "#include_ember_stylesheet_tags" do + context "when the application is served by Vite's development server" do + it "raises an error pointing at `include_ember_script_tags`" do + app = instance_double(EmberCli::App, build: true, dev_server?: true) + paths = instance_double(EmberCli::Assets::Paths) + allow(EmberCli).to receive(:[]).with(:frontend).and_return(app) + allow(EmberCli::Assets::Paths). + to receive(:new).with(app).and_return(paths) + + expect { helper.include_ember_stylesheet_tags(:frontend) }.to raise_error( + EmberCli::Assets::NotSupportedError, + /include_ember_script_tags/, + ) + end + end + context "when the application is built with Vite" do it "raises an error pointing at `include_ember_script_tags`" do - app = instance_double(EmberCli::App, build: true) + app = instance_double(EmberCli::App, build: true, dev_server?: false) paths = instance_double(EmberCli::Assets::Paths, vite?: true) allow(EmberCli).to receive(:[]).with(:frontend).and_return(app) allow(EmberCli::Assets::Paths). From 5c45178094a251b547224599b89f2160f24e649d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 09:38:41 +0000 Subject: [PATCH 2/2] Test against the released ember-cli-rails and update the changelog Point the development dependency back at the released ember-cli-rails. The release carries no development-server API, so the examples covering it stand in plain doubles for `EmberCli::App` and `EmberCli::DevServer`; the helper itself already feature-detects the API with `respond_to?`. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 10 ++++++++++ Gemfile | 2 +- .../ember_cli_rails_assets_helper_spec.rb | 16 ++++++++++------ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ea7a90..3ceaf06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +Unreleased +------ + +* Serve the `include_ember_script_tags` startup tags from Vite's development + server when ember-cli-rails serves the application from it, rewriting + root-relative URLs to absolute URLs on the server +* Leave protocol-relative (`//`) URLs alone when remapping startup tags +* Raise the Vite guidance from `include_ember_stylesheet_tags` for + applications served by the development server, whose `dist` may not exist + 0.8.1 ------ diff --git a/Gemfile b/Gemfile index 51ca9cf..a7e71af 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,7 @@ gem "rails", rails_constraint gem "webrick" group :development, :test do - gem "ember-cli-rails", github: "tricknotes/ember-cli-rails", branch: "vite-recompile-on-change" + gem "ember-cli-rails" end group :test do diff --git a/spec/helpers/ember_cli_rails_assets_helper_spec.rb b/spec/helpers/ember_cli_rails_assets_helper_spec.rb index 7c0ce51..c5a5de8 100644 --- a/spec/helpers/ember_cli_rails_assets_helper_spec.rb +++ b/spec/helpers/ember_cli_rails_assets_helper_spec.rb @@ -3,6 +3,10 @@ describe EmberCliRailsAssetsHelper do describe "#include_ember_script_tags" do context "when the application is served by Vite's development server" do + # The development server API ships with the ember-cli-rails release + # that serves Vite-based applications from the development server, so + # plain doubles stand in for `EmberCli::App` and `EmberCli::DevServer` + # here until the Gemfile can be pointed at a release carrying it. it "emits the startup tags with root-relative URLs rewritten onto the server" do index_html = <<~HTML @@ -18,13 +22,13 @@ HTML - dev_server = instance_double( - EmberCli::DevServer, + dev_server = double( + "EmberCli::DevServer", index_html: index_html, origin: "http://127.0.0.1:4200", ) - app = instance_double( - EmberCli::App, + app = double( + "EmberCli::App", build: true, dev_server?: true, dev_server: dev_server, @@ -47,7 +51,7 @@ describe "#include_ember_stylesheet_tags" do context "when the application is served by Vite's development server" do it "raises an error pointing at `include_ember_script_tags`" do - app = instance_double(EmberCli::App, build: true, dev_server?: true) + app = double("EmberCli::App", build: true, dev_server?: true) paths = instance_double(EmberCli::Assets::Paths) allow(EmberCli).to receive(:[]).with(:frontend).and_return(app) allow(EmberCli::Assets::Paths). @@ -62,7 +66,7 @@ context "when the application is built with Vite" do it "raises an error pointing at `include_ember_script_tags`" do - app = instance_double(EmberCli::App, build: true, dev_server?: false) + app = double("EmberCli::App", build: true, dev_server?: false) paths = instance_double(EmberCli::Assets::Paths, vite?: true) allow(EmberCli).to receive(:[]).with(:frontend).and_return(app) allow(EmberCli::Assets::Paths).