From 349e03c5ee9a1f76c87d9bb9640c831c5523533d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 17 Sep 2026 17:30:02 +0000 Subject: [PATCH] Keep assets hosted outside the build out of the asset lookup Since assets are resolved from `dist/index.html`, every `}.html_safe }. inject(&:+) end + # `prepend` mounts the build's assets onto a path (or another host), so it only applies to the URLs that point into the build. + # Assets hosted elsewhere (a CDN, a font service) are already absolute. + def mount_asset(src, prepend) + if EmberCli::Assets::Url.remote?(src) + src + else + [prepend, src].join + end + end + # Vite builds boot from ES modules declared in dist/index.html, so extract # the tags required for startup (including the config meta tag and # stylesheets) and remap root-absolute paths onto the mount point. diff --git a/lib/ember_cli/assets/asset_map.rb b/lib/ember_cli/assets/asset_map.rb index 1b21187..e83c665 100644 --- a/lib/ember_cli/assets/asset_map.rb +++ b/lib/ember_cli/assets/asset_map.rb @@ -1,5 +1,6 @@ require 'nokogiri' require "ember_cli/assets/errors" +require "ember_cli/assets/url" module EmberCli module Assets @@ -13,25 +14,35 @@ def initialize(name:, asset_map:, index_html:) def javascripts assert_asset_map! - Nokogiri::HTML(index_html.read).css('script').map {|s| - filename = File.basename(s['src']) - asset_matching(/#{Regexp.escape(filename)}\z/) - } + document.css('script').filter_map { |script| asset_for(script['src']) } end def stylesheets assert_asset_map! - Nokogiri::HTML(index_html.read).css('link[rel="stylesheet"]').map {|s| - filename = File.basename(s['href']) - asset_matching(/#{Regexp.escape(filename)}\z/) - } + document.css('link[rel="stylesheet"]').filter_map { |link| asset_for(link['href']) } end private attr_reader :name, :asset_map, :index_html + def document + @document ||= Nokogiri::HTML(index_html.read) + end + + # Assets hosted outside the build (a CDN, a font service) have no entry in the asset map, so their URL is emitted untouched. + # A tag without a URL (an inline ` + + + + + HTML + assets = build_assets(name: "bar", asset_map: asset_map, index_html: index_html) + + javascripts = assets.javascripts + + expect(javascripts).to match_array([ + "https://cdn.example.com/analytics.js", + "//cdn.example.com/protocol-relative.js", + "foo/bar-abc123.js", + ]) + end + + it "ignores scripts without a `src`" do + asset_map = { + "assets" => { + "bar.js" => "bar-abc123.js", + }, + "prepend" => "foo/", + } + index_html = StringIO.new(<<~HTML) + + + + + + + HTML + assets = build_assets(name: "bar", asset_map: asset_map, index_html: index_html) + + javascripts = assets.javascripts + + expect(javascripts).to match_array(["foo/bar-abc123.js"]) + end + context "when the asset_map is empty" do it "raises a BuildError" do assets = build_assets(asset_map: {}, name: "bar", index_html: StringIO.new) @@ -66,6 +115,33 @@ ]) end + it "passes stylesheets hosted outside the build through untouched" do + asset_map = { + "assets" => { + "bar.css" => "bar-abc123.css", + }, + "prepend" => "foo/", + } + index_html = StringIO.new(<<~HTML) + + + + + + + + HTML + assets = build_assets(name: "bar", asset_map: asset_map, index_html: index_html) + + stylesheets = assets.stylesheets + + expect(stylesheets).to match_array([ + "https://fonts.example.com/css?family=Bar", + "//fonts.example.com/protocol-relative.css", + "foo/bar-abc123.css", + ]) + end + context "when the asset_map is empty" do it "raises a BuildError" do assets = build_assets(asset_map: {}, name: "bar", index_html: StringIO.new)