lib/hanami/assets/compiler.rb in hanami-assets-0.2.1 vs lib/hanami/assets/compiler.rb in hanami-assets-0.3.0

- old
+ new

@@ -1,19 +1,23 @@ +require 'set' require 'find' +require 'hanami/utils/class_attribute' module Hanami module Assets + # Missing Asset error class MissingAsset < Error def initialize(name, sources) sources = sources.map(&:to_s).join(', ') - super("Missing asset: `#{ name }' (sources: #{ sources })") + super("Missing asset: `#{name}' (sources: #{sources})") end end + # Unknown Asset Engine error class UnknownAssetEngine < Error def initialize(source) - super("No asset engine registered for `#{ ::File.basename(source) }'") + super("No asset engine registered for `#{::File.basename(source)}'") end end # Assets compiler # @@ -22,28 +26,37 @@ # # Vanilla javascripts or stylesheets are just copied over. # # @since 0.1.0 # @api private - class Compiler + class Compiler # rubocop:disable Metrics/ClassLength # @since 0.1.0 # @api private - DEFAULT_PERMISSIONS = 0644 + DEFAULT_PERMISSIONS = 0o644 # @since 0.1.0 # @api private COMPILE_PATTERN = '*.*.*'.freeze # Example hello.js.es6 # @since 0.1.0 # @api private - EXTENSIONS = {'.js' => true, '.css' => true, '.map' => true}.freeze + EXTENSIONS = { '.js' => true, '.css' => true, '.map' => true }.freeze - # @since 0.1.0 + include Utils::ClassAttribute + + # @since 0.3.0 # @api private - SASS_CACHE_LOCATION = Pathname(Hanami.respond_to?(:root) ? - Hanami.root : Dir.pwd).join('tmp', 'sass-cache') + class_attribute :subclasses + self.subclasses = Set.new + # @since 0.3.0 + # @api private + def self.inherited(subclass) + super + subclasses.add(subclass) + end + # Compile the given asset # # @param configuration [Hanami::Assets::Configuration] the application # configuration associated with the given asset # @@ -54,21 +67,40 @@ def self.compile(configuration, name) return unless configuration.compile require 'tilt' require 'hanami/assets/cache' - new(configuration, name).compile + require 'hanami/assets/compilers/sass' + require 'hanami/assets/compilers/less' + fabricate(configuration, name).compile end + # @since 0.3.0 + # @api private + def self.fabricate(configuration, name) + source = configuration.source(name) + engine = (subclasses + [self]).find do |klass| + klass.eligible?(source) + end + + engine.new(configuration, name) + end + + # @since 0.3.0 + # @api private + def self.eligible?(_name) + true + end + # Assets cache # # @since 0.1.0 # @api private # # @see Hanami::Assets::Cache def self.cache - @@cache ||= Assets::Cache.new + @@cache ||= Assets::Cache.new # rubocop:disable Style/ClassVars end # Return a new instance # # @param configuration [Hanami::Assets::Configuration] the application @@ -92,11 +124,11 @@ # # @since 0.1.0 # @api private def compile raise MissingAsset.new(@name, @configuration.sources) unless exist? - return unless fresh? + return unless modified? if compile? compile! else copy! @@ -108,14 +140,11 @@ private # @since 0.1.0 # @api private def source - @source ||= begin - @name.absolute? ? @name : - @configuration.find(@name) - end + @source ||= @configuration.source(@name) end # @since 0.1.0 # @api private def destination @@ -139,42 +168,28 @@ def exist? !source.nil? && source.exist? end - # @since 0.1.0 + # @since 0.3.0 # @api private - def fresh? + def modified? !destination.exist? || - cache.fresh?(source) + cache.modified?(source) end # @since 0.1.0 # @api private def compile? - @compile ||= ::File.fnmatch(COMPILE_PATTERN, source.to_s) && - !EXTENSIONS[::File.extname(source.to_s)] + @compile ||= ::File.fnmatch(COMPILE_PATTERN, ::File.basename(source.to_s)) && + !EXTENSIONS[::File.extname(source.to_s)] end # @since 0.1.0 # @api private def compile! - # NOTE `:load_paths' is useful only for Sass engine, to make `@include' directive to work. - # For now we don't want to maintan a specialized Compiler version for Sass. - # - # If in the future other precompilers will need special treatment, - # we can consider to maintain several specialized versions in order to - # don't add a perf tax to all the other preprocessors who "just work". - # - # Example: if Less "just works", we can keep it in the general `Compiler', - # but have a `SassCompiler` if it requires more than `:load_paths'. - # - # NOTE: We need another option to pass for Sass: `:cache_location'. - # - # This is needed to don't create a `.sass-cache' directory at the root of the project, - # but to have it under `tmp/sass-cache'. - write { Tilt.new(source, nil, load_paths: sass_load_paths, cache_location: sass_cache_location).render } + write { renderer.render } rescue RuntimeError raise UnknownAssetEngine.new(source) end # @since 0.1.0 @@ -184,45 +199,51 @@ end # @since 0.1.0 # @api private def cache! - cache.store(source) + cache.store(source, dependencies) end # @since 0.1.0 # @api private def write destination.dirname.mkpath - destination.open(File::WRONLY|File::TRUNC|File::CREAT, DEFAULT_PERMISSIONS) do |file| + destination.open(File::WRONLY | File::TRUNC | File::CREAT, DEFAULT_PERMISSIONS) do |file| file.write(yield) end end # @since 0.1.0 # @api private def cache self.class.cache end - # @since x.x.x + # @since 0.3.0 # @api private - def sass_load_paths + def renderer + Tilt.new(source) + end + + # @since 0.3.0 + # @api private + def dependencies + nil + end + + # @since 0.3.0 + # @api private + def load_paths result = [] @configuration.sources.each do |source| Find.find(source) do |path| result << path if File.directory?(path) end end result - end - - # @since 0.1.0 - # @api private - def sass_cache_location - SASS_CACHE_LOCATION end end end end