From 4ace702de4bf0c4f20cc11ac766677085921789d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=AA=E3=81=A4=E3=81=8D?= Date: Fri, 24 Oct 2025 17:20:31 -0700 Subject: [PATCH] Add Sass::GemPackageImporter --- lib/sass/compiler.rb | 1 + lib/sass/gem_package_importer.rb | 18 ++++++++ spec/sass/gem_package_importer_spec.rb | 61 ++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 lib/sass/gem_package_importer.rb create mode 100644 spec/sass/gem_package_importer_spec.rb diff --git a/lib/sass/compiler.rb b/lib/sass/compiler.rb index 0d118152..79a5516a 100644 --- a/lib/sass/compiler.rb +++ b/lib/sass/compiler.rb @@ -11,6 +11,7 @@ require_relative 'embedded_protocol' require_relative 'exception' require_relative 'fork_tracker' +require_relative 'gem_package_importer' require_relative 'logger/silent' require_relative 'logger/source_location' require_relative 'logger/source_span' diff --git a/lib/sass/gem_package_importer.rb b/lib/sass/gem_package_importer.rb new file mode 100644 index 00000000..b0b0eee2 --- /dev/null +++ b/lib/sass/gem_package_importer.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Sass + # The built-in RubyGems package importer. This loads pkg: URLs from gems. + class GemPackageImporter + # @!visibility private + def find_file_url(url, _canonicalize_context) + return unless url.start_with?('pkg:') + + library, _, path = url[4..].partition('/') + gem_dir = Gem::Dependency.new(library).to_spec.gem_dir + + "file://#{'/' unless gem_dir.start_with?('/')}#{gem_dir}/#{path}" + rescue Gem::MissingSpecError + nil + end + end +end diff --git a/spec/sass/gem_package_importer_spec.rb b/spec/sass/gem_package_importer_spec.rb new file mode 100644 index 00000000..cce9ebd6 --- /dev/null +++ b/spec/sass/gem_package_importer_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sass::GemPackageImporter do + around do |example| + require 'bundler/inline' + + sandbox do |dir| + dir.write({ + '_test.scss' => '/* test */', + '_index.scss' => '@use "test";', + 'test.gemspec' => <<~GEMSPEC + Gem::Specification.new do |spec| + spec.name = 'test' + spec.summary = 'test' + spec.authors = ['test'] + spec.version = '0.0.1' + spec.files = Dir['**/*'] + end + GEMSPEC + }) + + gemfile do + gem 'test', path: dir.path, require: false + end + + example.call + end + end + + describe 'resolves pkg: url' do + it 'without subpath' do + expect(Sass.compile_string('@use "pkg:test";', + importers: [described_class.new], + logger: Sass::Logger.silent).css) + .to eq('/* test */') + end + + it 'with subpath' do + expect(Sass.compile_string('@use "pkg:test/test";', + importers: [described_class.new], + logger: Sass::Logger.silent).css) + .to eq('/* test */') + end + + it 'with uppercase scheme' do + expect(Sass.compile_string('@use "PKG:test";', + importers: [described_class.new], + logger: Sass::Logger.silent).css) + .to eq('/* test */') + end + + it 'with precent-encoding' do + expect(Sass.compile_string('@use "pkg:%74%65%73%74";', + importers: [described_class.new], + logger: Sass::Logger.silent).css) + .to eq('/* test */') + end + end +end