pakyow-assets 0.1.2 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +3 -19
  3. data/lib/pakyow/assets.rb +19 -0
  4. data/lib/pakyow/assets/actions/process.rb +63 -0
  5. data/lib/pakyow/assets/actions/public.rb +75 -0
  6. data/lib/pakyow/assets/asset.rb +238 -0
  7. data/lib/pakyow/assets/babel.rb +34 -0
  8. data/lib/pakyow/assets/behavior/assets.rb +45 -0
  9. data/lib/pakyow/assets/behavior/config.rb +137 -0
  10. data/lib/pakyow/assets/behavior/externals.rb +58 -0
  11. data/lib/pakyow/assets/behavior/packs.rb +162 -0
  12. data/lib/pakyow/assets/behavior/prelaunching.rb +19 -0
  13. data/lib/pakyow/assets/behavior/processing.rb +27 -0
  14. data/lib/pakyow/assets/behavior/rendering/install_assets.rb +39 -0
  15. data/lib/pakyow/assets/behavior/silencing.rb +35 -0
  16. data/lib/pakyow/assets/behavior/watching.rb +25 -0
  17. data/lib/pakyow/assets/errors.rb +16 -0
  18. data/lib/pakyow/assets/external.rb +103 -0
  19. data/lib/pakyow/assets/framework.rb +52 -0
  20. data/lib/pakyow/assets/pack.rb +171 -0
  21. data/lib/pakyow/assets/precompiler.rb +63 -0
  22. data/lib/pakyow/assets/source_map.rb +99 -0
  23. data/lib/pakyow/assets/tasks/precompile.rake +9 -0
  24. data/lib/pakyow/assets/tasks/update.rake +20 -0
  25. data/lib/pakyow/assets/types/css.rb +45 -0
  26. data/lib/pakyow/assets/types/js.rb +98 -0
  27. data/lib/pakyow/assets/types/sass.rb +69 -0
  28. data/lib/pakyow/assets/types/scss.rb +14 -0
  29. data/src/@babel/standalone@7.3.1/babel.min.js +165 -0
  30. data/src/@babel/standalone@7.4.5/babel.min.js +92359 -0
  31. metadata +146 -46
  32. data/CHANGELOG.md +0 -18
  33. data/README.md +0 -58
  34. data/lib/assets.rb +0 -221
  35. data/lib/config.rb +0 -37
  36. data/lib/middleware.rb +0 -42
  37. data/lib/pakyow-assets.rb +0 -31
  38. data/lib/preprocessors/css-preprocessor.rb +0 -16
  39. data/lib/preprocessors/image-preprocessor.rb +0 -1
  40. data/lib/preprocessors/javascript-preprocessor.rb +0 -16
  41. data/lib/preprocessors/sass-preprocessor.rb +0 -23
  42. data/lib/version.rb +0 -5
  43. data/pakyow-assets.gemspec +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c322853e853a355500e4b045b22acbcbec3c8925
4
- data.tar.gz: 29ce5c954d0d4c71025efdd12dfe70548155a9be
2
+ SHA256:
3
+ metadata.gz: bfdfa0ac5ee2f817f474ac114dc81491ff8dbaa4bbbc7a4a15b2020c5c102bd6
4
+ data.tar.gz: 57af0c11222e8c0ed16e6a3c18627f6d9971d395919cf199b5f13d4b348b0817
5
5
  SHA512:
6
- metadata.gz: 3e8c251d6f988046ff36e19bfef1200d4d5eefdb3d5009eb1eca1e02652f519e7567d0284400212b1178c2b826fd9eae8febacdefed5a2afa30fc621746c5dba
7
- data.tar.gz: 760555a99ee7ee4ba3078831066c40aec304b3ac455eb4228b56107cbd544a8d507e12a0e706df66f6186077e954caf51929d1b2fd67e127b6231a536f5bd7ca
6
+ metadata.gz: 293fbdf6b36ad59ff7c046374c899c9ba26aab45ff365175e90dc3f2ce8ddd385ae0d8c47909721efa16ddad9d66e9e5fae09884737258bfb167a3dd57ee2124
7
+ data.tar.gz: 443a19539eff33d4650568901ee1244453b2aa2e36b996202572846dd5fad51980fb567494f421b04de9405833a50a93ece963a18a3f3a842a8656395f256e82
data/LICENSE CHANGED
@@ -1,20 +1,4 @@
1
- Copyright (c) 2015 Bryan Powell
1
+ Copyright (c) Metabahn, LLC
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3
+ Pakyow Assets is an open-source project licensed under the terms of the LGPLv3 license.
4
+ See <https://choosealicense.com/licenses/lgpl-3.0/> for license text.
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/routing"
4
+ require "pakyow/support"
5
+ require "pakyow/presenter"
6
+
7
+ require "pakyow/assets/framework"
8
+
9
+ require "pakyow/assets/actions/process"
10
+ require "pakyow/assets/actions/public"
11
+
12
+ require "pakyow/assets/types/js"
13
+ require "pakyow/assets/types/css"
14
+ require "pakyow/assets/types/sass"
15
+ require "pakyow/assets/types/scss"
16
+
17
+ module Pakyow
18
+ config.tasks.paths << File.expand_path("../assets/tasks", __FILE__)
19
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pakyow
4
+ module Assets
5
+ module Actions
6
+ # Pipeline Action that processes assets at request time.
7
+ #
8
+ # This is intended for development use, please don't use it in production.
9
+ # Instead, precompile assets into the public directory.
10
+ #
11
+ class Process
12
+ def call(connection)
13
+ if connection.app.config.assets.process
14
+ # TODO: can we short circuit if the request path doesn't match the connection?
15
+ if asset = find_asset(connection) || find_pack(connection) || find_asset_map(connection) || find_pack_map(connection)
16
+ connection.set_header("content-type", asset.mime_type)
17
+ connection.body = asset
18
+ connection.halt
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def find_asset(connection, path = connection.path)
26
+ connection.app.state(:asset).find { |asset|
27
+ asset.public_path == path
28
+ }
29
+ end
30
+
31
+ def find_pack(connection, path = connection.path)
32
+ connection.app.state(:pack).lazy.map { |pack|
33
+ pack.packed(path)
34
+ }.find { |packed| !packed.nil? && packed.any? }
35
+ end
36
+
37
+ def find_asset_map(connection)
38
+ if wants_map?(connection)
39
+ if (asset = find_asset(connection, unmapped(connection))) && asset.source_map?
40
+ asset.source_map
41
+ end
42
+ end
43
+ end
44
+
45
+ def find_pack_map(connection)
46
+ if (pack = find_pack(connection, unmapped(connection))) && pack.source_map?
47
+ pack.source_map
48
+ else
49
+ nil
50
+ end
51
+ end
52
+
53
+ def wants_map?(connection)
54
+ connection.path.end_with?(".map")
55
+ end
56
+
57
+ def unmapped(connection)
58
+ File.join(File.dirname(connection.path), File.basename(connection.path, ".map"))
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mini_mime"
4
+
5
+ require "pakyow/support/core_refinements/string/normalization"
6
+
7
+ module Pakyow
8
+ module Assets
9
+ module Actions
10
+ class Public
11
+ using Support::Refinements::String::Normalization
12
+
13
+ # Pipeline Action that serves files out of your public directory.
14
+ #
15
+ def initialize(app)
16
+ @asset_paths = app.state(:asset).map(&:public_path) + app.state(:pack).flat_map { |pack|
17
+ [pack.public_css_path, pack.public_js_path]
18
+ }
19
+
20
+ @prefix = if app.is_a?(Plugin)
21
+ Pathname.new(app.class.mount_path)
22
+ else
23
+ Pathname.new("/")
24
+ end
25
+ end
26
+
27
+ def call(connection)
28
+ if connection.app.config.assets.public
29
+ public_path = public_path(connection)
30
+
31
+ if public?(public_path)
32
+ if mime = MiniMime.lookup_by_filename(public_path)
33
+ connection.set_header("content-type", mime.content_type)
34
+ end
35
+
36
+ if connection.app.config.assets.cache && asset?(connection)
37
+ set_cache_headers(connection, public_path)
38
+ end
39
+
40
+ connection.body = File.open(public_path)
41
+ connection.halt
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def public?(path)
49
+ File.file?(path)
50
+ end
51
+
52
+ def public_path(connection)
53
+ File.join(
54
+ connection.app.config.assets.public_path,
55
+ String.normalize_path(
56
+ Pathname.new(connection.path).relative_path_from(@prefix).to_s
57
+ )
58
+ )
59
+ end
60
+
61
+ def asset?(connection)
62
+ @asset_paths.include?(connection.path)
63
+ end
64
+
65
+ def set_cache_headers(connection, public_path)
66
+ mtime = File.mtime(public_path)
67
+ connection.set_header("age", (Time.now - mtime).to_i.to_s)
68
+ connection.set_header("cache-control", "public, max-age=31536000")
69
+ connection.set_header("vary", "accept-encoding")
70
+ connection.set_header("last-modified", mtime.httpdate)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/md5"
4
+
5
+ require "pakyow/support/class_state"
6
+ require "pakyow/support/core_refinements/string/normalization"
7
+
8
+ require "pakyow/assets/source_map"
9
+
10
+ module Pakyow
11
+ module Assets
12
+ # Represents an asset.
13
+ #
14
+ # Instances are created when booting in all environments, meaning the app
15
+ # is guaranteed access to these objects. Contents are loaded and processed
16
+ # eagerly. This is expected to happen under two scenarios:
17
+ #
18
+ # 1. In development, an asset is loaded and processed when it's requested.
19
+ # 2. In production, when assets are precompiled during a deployment.
20
+ #
21
+ class Asset
22
+ using Support::Refinements::String::Normalization
23
+
24
+ extend Support::ClassState
25
+ class_state :__types, default: []
26
+ class_state :__emits, default: nil
27
+ class_state :__extensions, default: [], inheritable: true
28
+ class_state :__minifiable, default: false, inheritable: true
29
+
30
+ class << self
31
+ def new_from_path(path, config:, source_location: "", prefix: "/", related: [])
32
+ asset_class = @__types.find { |type|
33
+ type.__extensions.include?(File.extname(path))
34
+ } || self
35
+
36
+ asset_class.load; asset_class.new(
37
+ local_path: path,
38
+ source_location: source_location,
39
+ config: config,
40
+ prefix: prefix,
41
+ related: related
42
+ )
43
+ end
44
+
45
+ # Implemented by subclasses to load any libraries they need.
46
+ #
47
+ def load
48
+ # intentionally empty
49
+ end
50
+
51
+ # @api private
52
+ def inherited(asset_class)
53
+ @__types << asset_class
54
+ super
55
+ end
56
+
57
+ # @api private
58
+ def update_path_for_emitted_type(path)
59
+ if @__emits
60
+ path.sub(File.extname(path), @__emits)
61
+ else
62
+ path
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # Registers +extension+ for this asset.
69
+ #
70
+ def extension(extension)
71
+ extensions(extension)
72
+ end
73
+
74
+ # Registers multiple extensions for this asset.
75
+ #
76
+ def extensions(*extensions)
77
+ extensions.each do |extension|
78
+ @__extensions << ".#{extension}"
79
+ end
80
+ end
81
+
82
+ # Defines the emitted asset type (e.g. +sass+ emits +css+).
83
+ #
84
+ def emits(type)
85
+ @__emits = ".#{type}"
86
+ end
87
+ end
88
+
89
+ attr_reader :logical_path, :mime_type, :mime_suffix, :dependencies
90
+
91
+ def initialize(local_path:, config:, dependencies: [], source_location: "", prefix: "/", related: [])
92
+ @local_path, @config, @source_location, @dependencies, @related = local_path, config, source_location, dependencies, related
93
+
94
+ @logical_path = self.class.update_path_for_emitted_type(
95
+ String.normalize_path(
96
+ File.join(prefix, local_path.sub(source_location, ""))
97
+ )
98
+ )
99
+
100
+ @public_path = String.normalize_path(
101
+ File.join(config.prefix, @logical_path)
102
+ )
103
+
104
+ @mime_type = case File.extname(@public_path)
105
+ when ".js"
106
+ # Resolves an issue with mini_mime returning `application/ecmascript`
107
+ #
108
+ "application/javascript"
109
+ else
110
+ MiniMime.lookup_by_filename(@public_path)&.content_type.to_s
111
+ end
112
+
113
+ @mime_prefix, @mime_suffix = @mime_type.split("/", 2)
114
+
115
+ @source_map_enabled = config.source_maps
116
+
117
+ @mutex = Mutex.new
118
+ end
119
+
120
+ # Overriding and freezing after content is set lets us eagerly process the
121
+ # content rather than incurring that cost on boot.
122
+ #
123
+ def freeze
124
+ super if instance_variable_defined?(:@content)
125
+ end
126
+
127
+ def each(&block)
128
+ ensure_content do |content|
129
+ StringIO.new(post_process(content)).each(&block)
130
+ end
131
+ end
132
+
133
+ def read
134
+ String.new.tap do |asset|
135
+ each do |content|
136
+ asset << content
137
+ end
138
+ end
139
+ end
140
+
141
+ def bytesize
142
+ ensure_content(&:bytesize)
143
+ end
144
+
145
+ def fingerprint
146
+ [@local_path].concat(dependencies).each_with_object(Digest::MD5.new) { |path, digest|
147
+ digest.update(Digest::MD5.file(path).hexdigest)
148
+ }.hexdigest
149
+ end
150
+
151
+ def fingerprinted_filename
152
+ extension = File.extname(@public_path)
153
+ File.basename(@public_path, extension) + "__" + fingerprint + extension
154
+ end
155
+
156
+ def public_path
157
+ if @config.fingerprint
158
+ File.join(
159
+ File.dirname(@public_path),
160
+ fingerprinted_filename
161
+ )
162
+ else
163
+ @public_path
164
+ end
165
+ end
166
+
167
+ def source_map?
168
+ respond_to?(:source_map_content)
169
+ end
170
+
171
+ def source_map
172
+ if source_map?
173
+ SourceMap.new(
174
+ source_map_content,
175
+ file: File.basename(public_path)
176
+ )
177
+ else
178
+ nil
179
+ end
180
+ end
181
+
182
+ def disable_source_map
183
+ tap do
184
+ @source_map_enabled = false
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ def ensure_content
191
+ @mutex.synchronize do
192
+ unless frozen? || instance_variable_defined?(:@content)
193
+ @content = load_content; freeze
194
+ end
195
+ end
196
+
197
+ yield @content if block_given?
198
+ end
199
+
200
+ def load_content
201
+ content = process(File.read(@local_path)).to_s
202
+
203
+ if mime_suffix == "css" || mime_suffix == "javascript"
204
+ # Update references to related assets with prefixed path, fingerprints.
205
+ # Do this here rather than in post-processing so that the source maps reflect the changes.
206
+ #
207
+ @related.each do |asset|
208
+ if asset != self && content.include?(asset.logical_path)
209
+ content.gsub!(asset.logical_path, asset.public_path)
210
+ end
211
+ end
212
+ end
213
+
214
+ content
215
+ end
216
+
217
+ def process(content)
218
+ content
219
+ end
220
+
221
+ def post_process(content)
222
+ if @source_map_enabled && source_map?
223
+ embed_mapping_url(content)
224
+ else
225
+ content
226
+ end
227
+ end
228
+
229
+ def embed_mapping_url(content)
230
+ content + SourceMap.mapping_url(path: public_path, type: @mime_suffix)
231
+ end
232
+
233
+ def external?
234
+ File.dirname(@local_path) == @config.externals.path
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mini_racer"
4
+ require "execjs"
5
+
6
+ require "pakyow/support/inflector"
7
+
8
+ module Pakyow
9
+ module Assets
10
+ class Babel
11
+ def self.transform(content, **options)
12
+ context.call("Babel.transform", content, camelize_keys(options))
13
+ end
14
+
15
+ private
16
+
17
+ def self.context
18
+ @context ||= ExecJS.compile(
19
+ File.read(
20
+ File.expand_path("../../../../src/@babel/standalone@7.4.5/babel.min.js", __FILE__)
21
+ )
22
+ )
23
+ end
24
+
25
+ def self.camelize_keys(options)
26
+ Hash[options.map { |key, value|
27
+ key = Support.inflector.camelize(key)
28
+ key = key[0, 1].downcase + key[1..-1]
29
+ [key, value]
30
+ }]
31
+ end
32
+ end
33
+ end
34
+ end