# ----------------------------------------------------------------------------
# Frozen-string-literal: true
# Copyright: 2012 - 2016 - MIT License
# Encoding: utf-8
# ----------------------------------------------------------------------------
require "fastimage"
module Jekyll
module Assets
module Liquid
class Tag < ::Liquid::Tag
require_relative "tag/proxies"
require_relative "tag/proxied_asset"
require_relative "tag/defaults"
require_relative "tag/parser"
attr_reader :args
# --------------------------------------------------------------------
class << self
public :new
end
# --------------------------------------------------------------------
class AssetNotFoundError < StandardError
def initialize(asset)
super "Could not find the asset `#{asset}'"
end
end
# --------------------------------------------------------------------
# Tags that we allow our users to use.
# --------------------------------------------------------------------
AcceptableTags = %W(
img
image
javascript
asset_source
stylesheet
asset_path
style
asset
css
js
).freeze
# --------------------------------------------------------------------
# The HTML version of every tag that we accept.
# --------------------------------------------------------------------
Tags = {
"css" => %(),
"js" => %(),
"img" => %()
}.freeze
# --------------------------------------------------------------------
# Allows us to normalize tags so we can simplify logic.
# --------------------------------------------------------------------
Alias = {
"image" => "img",
"stylesheet" => "css",
"javascript" => "js",
"style" => "css"
}.freeze
# --------------------------------------------------------------------
def initialize(tag, args, tokens)
tag = tag.to_s
@tokens = tokens
@tag = from_alias(tag)
@args = Parser.new(args, @tag)
@og_tag = tag
super
end
# --------------------------------------------------------------------
# NOTE: We only attach to the regenerator if you are using digested
# assets, otherwise we forego any association with it so that we keep
# your builds ultra fast, this is ideal in dev. Disable digests and
# let us process independent so the entire site isn't regenerated
# because of a single asset change.
# --------------------------------------------------------------------
def render(context)
site = context.registers[:site]
page = context.registers.fetch(:page, {})
args = @args.parse_liquid(context)
sprockets = site.sprockets
page = page["path"]
asset = find_asset(args, sprockets)
add_as_jekyll_dependency(site, sprockets, page, asset)
process_tag(args, sprockets, asset)
rescue => e
capture_and_out_error(
site, e
)
end
# --------------------------------------------------------------------
private
def from_alias(tag)
Alias.key?(tag) ? Alias[tag] : tag
end
# --------------------------------------------------------------------
private
def process_tag(args, sprockets, asset)
sprockets.used.add(asset) unless @tag == "asset_source"
Defaults.set_defaults_for!(@tag, args ||= {}, asset, sprockets)
build_html(args, sprockets, asset)
end
# --------------------------------------------------------------------
private
def build_html(args, sprockets, asset, path = get_path(sprockets, asset))
return path if @tag == "asset_path"
return asset.to_s if @tag == "asset" || @tag == "asset_source"
data = args.key?(:data) && args[:data].key?(:uri) ? asset.data_uri : path
format(Tags[@tag], data, asset.integrity, args.to_html)
end
# --------------------------------------------------------------------
private
def get_path(sprockets, asset)
sprockets.prefix_path(
sprockets.digest?? asset.digest_path : asset.logical_path
)
end
# --------------------------------------------------------------------
private
def add_as_jekyll_dependency(site, sprockets, page, asset)
if page && sprockets.digest?
site.regenerator.add_dependency(
site.in_source_dir(page), site.in_source_dir(asset.logical_path)
)
end
end
# --------------------------------------------------------------------
private
def find_asset(args, sprockets)
out = _find_asset(args[:file],
args[:sprockets] ||= {}, sprockets
)
if !out
raise(
AssetNotFoundError, args[
:file
]
)
else
out.liquid_tags << self
!args.proxies?? out : ProxiedAsset.new(
out, args, sprockets, self
)
end
end
# --------------------------------------------------------------------
private
def _find_asset(file, args, sprockets)
if args.empty? then sprockets.manifest.find(file).first
elsif args.size == 1 && args.key?(:accept)
if File.extname(file) == ""
file = file + _ext_for(args[
:accept
])
end
sprockets.manifest.find(file).find do |asset|
asset.content_type == args[
:accept
]
end
else
sprockets.find_asset(
file, args
)
end
end
# --------------------------------------------------------------------
private
def _ext_for(type)
out = Sprockets.mime_exts.select do |k, v|
v == type
end
out.keys \
.first
end
# --------------------------------------------------------------------
# There is no guarantee that Jekyll will pass on the error for some
# reason (unless you are just booting up) so we capture that error and
# always output it, it can lead to some double errors but I would
# rather there be a double error than no error.
# --------------------------------------------------------------------
private
def capture_and_out_error(site, error)
if error.is_a?(Sass::SyntaxError)
file = error.sass_filename.gsub(/#{Regexp.escape(site.source)}\//, "")
Jekyll.logger.error(%(Error in #{file}:#{error.sass_line} #{
error
}))
else
Jekyll.logger.error(
"", error.to_s
)
end
raise error
end
end
end
end
end
# ----------------------------------------------------------------------------
Jekyll::Assets::Liquid::Tag::AcceptableTags.each do |tag|
Liquid::Template.register_tag tag, Jekyll::Assets::Liquid::Tag
end