1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
# frozen_string_literal: true
require 'uri'
module Sprockets
# Internal: Asset URI related parsing utilities. Mixed into Environment.
#
# An Asset URI identifies the compiled Asset result. It shares the file:
# scheme and requires an absolute path.
#
# Other query parameters
#
# type - String output content type. Otherwise assumed from file extension.
# This maybe different than the extension if the asset is transformed
# from one content type to another. For an example .coffee -> .js.
#
# id - Unique fingerprint of the entire asset and all its metadata. Assets
# will only have the same id if they serialize to an identical value.
#
# pipeline - String name of pipeline.
#
module URIUtils
extend self
# Internal: Parse URI into component parts.
#
# uri - String uri
#
# Returns Array of components.
def split_uri(uri)
URI.split(uri)
end
# Internal: Join URI component parts into String.
#
# Returns String.
def join_uri(scheme, userinfo, host, port, registry, path, opaque, query, fragment)
URI::Generic.new(scheme, userinfo, host, port, registry, path, opaque, query, fragment).to_s
end
# Internal: Parse file: URI into component parts.
#
# uri - String uri
#
# Returns [scheme, host, path, query].
def split_file_uri(uri)
scheme, _, host, _, _, path, _, query, _ = URI.split(uri)
path = URI::Generic::DEFAULT_PARSER.unescape(path)
path.force_encoding(Encoding::UTF_8)
# Hack for parsing Windows "/C:/Users/IEUser" paths
if File::ALT_SEPARATOR && path[2] == ':'
path = path[1..-1]
end
[scheme, host || '', path, query]
end
# Internal: Join file: URI component parts into String.
#
# Returns String.
def join_file_uri(scheme, host, path, query)
str = +"#{scheme}://"
str << host if host
path = "/#{path}" unless path.start_with?("/".freeze)
str << URI::Generic::DEFAULT_PARSER.escape(path)
str << "?#{query}" if query
str
end
# Internal: Check if String is a valid Asset URI.
#
# str - Possible String asset URI.
#
# Returns true or false.
def valid_asset_uri?(str)
# Quick prefix check before attempting a full parse
str.start_with?("file://".freeze) && parse_asset_uri(str) ? true : false
rescue URI::InvalidURIError
false
end
# Internal: Parse Asset URI.
#
# Examples
#
# parse("file:///tmp/js/application.coffee?type=application/javascript")
# # => "/tmp/js/application.coffee", {type: "application/javascript"}
#
# uri - String asset URI
#
# Returns String path and Hash of symbolized parameters.
def parse_asset_uri(uri)
scheme, _, path, query = split_file_uri(uri)
unless scheme == 'file'
raise URI::InvalidURIError, "expected file:// scheme: #{uri}"
end
return path, parse_uri_query_params(query)
end
# Internal: Build Asset URI.
#
# Examples
#
# build("/tmp/js/application.coffee", type: "application/javascript")
# # => "file:///tmp/js/application.coffee?type=application/javascript"
#
# path - String file path
# params - Hash of optional parameters
#
# Returns String URI.
def build_asset_uri(path, params = {})
join_file_uri("file", nil, path, encode_uri_query_params(params))
end
# Internal: Parse file-digest dependency URI.
#
# Examples
#
# parse("file-digest:/tmp/js/application.js")
# # => "/tmp/js/application.js"
#
# uri - String file-digest URI
#
# Returns String path.
def parse_file_digest_uri(uri)
scheme, _, path, _ = split_file_uri(uri)
unless scheme == 'file-digest'.freeze
raise URI::InvalidURIError, "expected file-digest scheme: #{uri}"
end
path
end
# Internal: Build file-digest dependency URI.
#
# Examples
#
# build("/tmp/js/application.js")
# # => "file-digest:/tmp/js/application.js"
#
# path - String file path
#
# Returns String URI.
def build_file_digest_uri(path)
join_file_uri('file-digest'.freeze, nil, path, nil)
end
# Internal: Serialize hash of params into query string.
#
# params - Hash of params to serialize
#
# Returns String query or nil if empty.
def encode_uri_query_params(params)
query = []
params.each do |key, value|
case value
when Integer
query << "#{key}=#{value}"
when String, Symbol
query << "#{key}=#{URI::Generic::DEFAULT_PARSER.escape(value.to_s)}"
when TrueClass
query << "#{key}"
when FalseClass, NilClass
else
raise TypeError, "unexpected type: #{value.class}"
end
end
"#{query.join('&'.freeze)}" if query.any?
end
# Internal: Parse query string into hash of params
#
# query - String query string
#
# Return Hash of params.
def parse_uri_query_params(query)
query.to_s.split('&'.freeze).reduce({}) do |h, p|
k, v = p.split('='.freeze, 2)
v = URI::Generic::DEFAULT_PARSER.unescape(v) if v
h[k.to_sym] = v || true
h
end
end
end
end
|