darkroom 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ff7abd45467a1fd1e3f3ae707ad1ff04ff45c9e0a4f4a512fb24fc6bd794879
4
- data.tar.gz: 03f15709d9310d94068f3706e02ac1d4998887cf3c5bb09f3463eb72875a1ae9
3
+ metadata.gz: ec78b55ac1e82e265e1f5546a929ed3b12ab72585bc448d5f3c3d06c8804ec10
4
+ data.tar.gz: 2df42517847826a826ebe32d90b8bbbe41fe242872e56db06672e5b5c1d4fb3b
5
5
  SHA512:
6
- metadata.gz: bd87bd335cb7abb1e3bf26365d0dca2247b5d5450c82ded04d16709d97031bdcf2e4e59cf345c2f5a18218be05157ee2dc4c9f1d5ef27ed07ab433bf33a968aa
7
- data.tar.gz: d1141e492dcedde15aca909de9354756d95db7ecf4b925421711589eeef96df973126e7d603ccde93ee6d078252ea56eba6d89c9c594eef00c9ff55a3fcb666c
6
+ metadata.gz: 49970a3c8e73e1a44f54ccca8bef6c31e55c56bd5c0b1a9194beef31ecc188ce6c54d225d02c68261e42f6c9a8fee15b160a492f0dd5bbba1be07097d7f6182f
7
+ data.tar.gz: 109f67f9c64b8443d11404af38d33f154f94a3acef3a041b66499c3cfade22ad3516d6abb55cbb48c42ed2592e2a46575e4c52c6b9745f147f0af9fb84fbda4b
data/CHANGELOG.md ADDED
@@ -0,0 +1,83 @@
1
+ # Darkroom Changelog
2
+
3
+ ## Upcoming (Unreleased)
4
+
5
+ * Nothing yet
6
+
7
+ ## 0.0.9 (2025 January 21)
8
+
9
+ * **Remove deprecated delegate creation via hash and associated accessors**
10
+ * **Remove deprecated `:minified_pattern` option**
11
+ * **Remove deprecated `:internal_pattern` option and `Asset#internal?` method**
12
+ * **Fix assets with error(s) not getting reprocessed until directly modified**
13
+ * Add support for AVIF assets
14
+ * Add support for animated PNG assets
15
+ * Add support for GIF assets
16
+ * Add support for WebP assets
17
+
18
+ ## 0.0.8 (2023 July 15)
19
+
20
+ * **Fix issues with ensuring previous steps of asset processing**
21
+ * **Fix ordering of JavaScript import statements in IIFE output**
22
+
23
+ ## 0.0.7 (2023 July 15)
24
+
25
+ * **Add option to use IIFEs for JavaScript assets instead of concatenating**
26
+ * Add line info to `ProcessingError#to_s` output for non-`AssetError` errors
27
+ * Raise error in `Darkroom#dump` if present from last process run
28
+ * Yield to parse handlers and accumulate errors in order of appearance
29
+ * Add deprecation warning to `Asset#internal?`
30
+ * **Add support for importing and referencing assets by relative path**
31
+ * Escape quotes with HTML entities in reference content with UTF8 format
32
+ * **Implement DSL for delegates and deprecate configuring via hash**
33
+ * Move delegate registration back to `Darkroom` class
34
+ * Rework asset processing code to reduce chances of asset dependency bugs
35
+ * **Deprecate `:minified_pattern` and replace with more flexible `:minified`**
36
+ * **Deprecate `:internal_pattern` and replace with more flexible `:entries`**
37
+ * Skip exception raise in `Darkroom#process!` if processing was skipped
38
+ * Allow compiled assets to be further processed as target content type
39
+ * Ensure pristine files are never treated as internal
40
+ * Move delegate registration and management to Asset class
41
+
42
+ ## 0.0.6 (2022 May 3)
43
+
44
+ * Use [Terser](https://github.com/ahorek/terser-ruby) instead of
45
+ [UglifyJS](https://github.com/lautis/uglifier) for JavaScript minification
46
+ * **Fix dependencies sometimes getting missed due to circular references**
47
+
48
+ ## 0.0.5 (2022 March 31)
49
+
50
+ * Ensure load paths are absolute and fully normalized
51
+ * **Ensure a dependency has been processed before fetching its dependencies**
52
+ * **Remove deprecated `Asset.add_spec` and `Asset.spec` methods**
53
+
54
+ ## 0.0.4 (2021 August 3)
55
+
56
+ * **Add ability to reference other asset paths and content within an asset**
57
+ * Ensure `FileUtils` is loaded when dumping assets to disk
58
+ * **Add `Asset#image?` for determining if an asset is an image**
59
+ * **Add `Asset#font?` for determining if an asset is a font**
60
+ * **Add `Asset#binary?` for determining if an asset is binary**
61
+ * Ensure errors array always exists on `Darkroom` instances
62
+ * Improve error class organization, naming, and messages
63
+ * **Use text/javascript for content type of JavaScript and HTX files**
64
+ * Disallow troublesome characters in asset paths
65
+ * Add support for JSON assets
66
+
67
+ ## 0.0.3 (2021 March 27)
68
+
69
+ * Fix and improve quote matching in dependency regexes
70
+ * Use [SassC](https://github.com/sass/sassc-ruby) for minifying CSS since it is more up to date than
71
+ [CSSminify](https://github.com/matthiassiegel/cssminify)
72
+ * Fix HTML spec to recognize .htm extensions in addition to .html
73
+
74
+ ## 0.0.2 (2021 March 25)
75
+
76
+ * Improve output of compile and minify library load errors
77
+ * **Rework how assets are processed so circular dependencies work properly**
78
+ * **Add methods for getting subresource integrity string for an asset**
79
+ * **Remove `Darkroom#asset_path!` and move its behavior to `#asset_path`**
80
+
81
+ ## 0.0.1 (2021 February 25)
82
+
83
+ * Release initial gem
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2021-2022 Nate Pickens
1
+ Copyright 2021-2025 Nate Pickens
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
4
  documentation files (the "Software"), to deal in the Software without restriction, including without
data/README.md CHANGED
@@ -11,7 +11,10 @@ The following file types are supported out of the box, though support for others
11
11
 
12
12
  | Name | Content Type | Extension(s) |
13
13
  |------------|------------------|--------------|
14
+ | APNG | image/apng | .apng |
15
+ | AVIF | image/avif | .avif |
14
16
  | CSS | text/css | .css |
17
+ | GIF | image/gif | .gif |
15
18
  | HTML | text/html | .htm, .html |
16
19
  | HTX | text/javascript | .htx |
17
20
  | ICO | image/x-icon | .ico |
@@ -21,6 +24,7 @@ The following file types are supported out of the box, though support for others
21
24
  | PNG | image/png | .png |
22
25
  | SVG | image/svg+xml | .svg |
23
26
  | Text | text/plain | .txt |
27
+ | WebP | image/webp | .webp |
24
28
  | WOFF | font/woff | .woff |
25
29
  | WOFF2 | font/woff2 | .woff2 |
26
30
 
@@ -56,20 +60,22 @@ optional):
56
60
 
57
61
  ```ruby
58
62
  darkroom = Darkroom.new('app/assets', 'vendor/assets', '...',
59
- hosts: [ # Hosts to prepend to asset paths (useful in production
60
- 'https://cname1.cdn.com', # when assets are served from a CDN with multiple
61
- 'https://cname2.cdn.com', # cnames); hosts are chosen round-robin per thread
63
+ hosts: [ # Hosts to prepend to asset paths (useful in production
64
+ 'https://cname1.cdn.com', # when assets are served from a CDN with multiple
65
+ 'https://cname2.cdn.com', # cnames); hosts are chosen round-robin per thread
62
66
  '...',
63
67
  ],
64
- prefix: '/static', # Prefix to add to all asset paths
65
- pristine: ['/google-verify.html'], # Paths with no prefix or versioning (/favicon.ico,
66
- # /mask-icon.svg, /humans.txt, and /robots.txt are
67
- # included automatically)
68
- minify: true, # Minify assets that can be minified
69
- minified_pattern: /(\.|-)min\.\w+$/, # Files to skip minification on when minify: true
70
- internal_pattern: /^\/components\//, # Files to disallow direct external access to (they can
71
- # still be imported into other assets)
72
- min_process_interval: 1, # Minimum time that must elapse between process calls
68
+ prefix: '/static', # Prefix to add to all asset paths
69
+ pristine: ['/google-verify.html'], # Paths with no prefix or versioning (assets such as
70
+ # /favicon.ico and /robots.txt are included
71
+ # automatically)
72
+ entries: /^\/controllers\//, # Assets that will be directly accessed (fewer means
73
+ # better performance); can be a string, regex, or
74
+ # array of such
75
+ minify: true, # Minify assets that can be minified
76
+ minified: /(\.|-)min\.\w+$/, # Files to skip minification on when minify: true; can
77
+ # be a string, regex, or array of such
78
+ min_process_interval: 1, # Minimum time that must elapse between process calls
73
79
  )
74
80
  ```
75
81
 
@@ -98,85 +104,138 @@ To work with assets:
98
104
 
99
105
  ```ruby
100
106
  # A Darkroom instance has a few convenience helper methods.
101
- path = darkroom.asset_path('/js/app.js') # => '/static/js/app-[fingerprint].js'
102
- integrity = darkroom.asset_integrity('/js/app.js') # => 'sha384-[hash]'
107
+ path = darkroom.asset_path('/js/app.js') # => "/static/js/app-[fingerprint].js"
108
+ integrity = darkroom.asset_integrity('/js/app.js') # => "sha384-[hash]"
103
109
 
104
110
  # Retrieve the Asset object associated with a path.
105
111
  asset = darkroom.asset(path)
106
112
 
107
- # Prefix (if set on the Darkroom instance) is included in the unversioned and versioned paths.
108
- assest.path # => '/js/app.js'
109
- assest.path_unversioned # => '/static/js/app.js'
110
- assest.path_versioned # => '/static/js/app-[fingerprint].js'
113
+ # Prefix (if set on the Darkroom instance) is included in the unversioned and versioned
114
+ # paths.
115
+ assest.path # => "/js/app.js"
116
+ assest.path_unversioned # => "/static/js/app.js"
117
+ assest.path_versioned # => "/static/js/app-[fingerprint].js"
111
118
 
112
- asset.content_type # => 'text/javascript'
113
119
  asset.content # Content of processed /js/app.js file
114
120
 
115
- asset.headers # => {'Content-Type' => 'text/javascript',
116
- # 'Cache-Control' => 'public, max-age=31536000'}
117
- asset.headers(versioned: false) # => {'Content-Type' => 'text/javascript',
118
- # 'ETag' => '[fingerprint]'}
119
-
120
- asset.integrity # => 'sha384-[hash]'
121
- asset.integrity(:sha256) # => 'sha256-[hash]'
122
- asset.integrity(:sha512) # => 'sha512-[hash]'
121
+ asset.content_type # => "text/javascript"
122
+ asset.binary? # => false
123
+ asset.font? # => false
124
+ asset.image? # => false
125
+ asset.entry? # => true
126
+
127
+ asset.error? # => true
128
+ asset.errors # => [#<Darkroom::AssetError ...>, ...]
129
+
130
+ asset.fingerprint # => "[MD5 hash of asset content]"
131
+ asset.headers # => {"Content-Type" => "text/javascript",
132
+ # "Cache-Control" => "public, max-age=31536000"}
133
+ asset.headers(versioned: false) # => {"Content-Type" => "text/javascript",
134
+ # "ETag" => "[fingerprint]"}
135
+
136
+ asset.integrity # => "sha384-[hash]"
137
+ asset.integrity(:sha256) # => "sha256-[hash]"
138
+ asset.integrity(:sha384) # => "sha384-[hash]"
139
+ asset.integrity(:sha512) # => "sha512-[hash]"
123
140
  ```
124
141
 
125
142
  ## Asset Bundling
126
143
 
127
144
  CSS and JavaScript assets specify their dependencies by way of each language's native import statement. Each
128
- import statement is replaced with content of the referenced asset. Example:
145
+ import statement is replaced with the content of the imported asset. Example:
146
+
147
+ ```css
148
+ /* Unprocessed /header.css */
149
+ header { background: #f1f1f1; }
150
+ ```
151
+
152
+ ```css
153
+ /* Unprocessed /app.css */
154
+ @import '/header.css';
155
+
156
+ body { background: #fff; }
157
+ ```
158
+
159
+ ```css
160
+ /* Processed /app.css */
161
+ header { background: #f1f1f1; }
162
+
163
+ body { background: #fff; }
164
+ ```
165
+
166
+ Imported assets can also contain import statements, and those assets are all included in the base asset.
167
+ Imports can even be cyclical. If `asset-a.css` imports `asset-b.css` and vice-versa, each asset will simply
168
+ contain the content of both of those assets (though order will be different as an asset's own content always
169
+ comes after any imported assets' contents).
170
+
171
+ By default, JavaScript files are concatenated in the same way that CSS files are. Example:
129
172
 
130
173
  ```javascript
131
174
  // Unprocessed /api.js
132
- function api() {
133
- console.log('API called!')
134
- }
175
+ function API() { console.log('API called!') }
176
+ ```
135
177
 
178
+ ```javascript
136
179
  // Unprocessed /app.js
137
180
  import '/api.js'
138
181
 
139
- api()
182
+ API()
183
+ ```
140
184
 
185
+ ```javascript
141
186
  // Processed /app.js
142
- function api() {
143
- console.log('API called!')
144
- }
187
+ function API() { console.log('API called!') }
145
188
 
189
+ API()
190
+ ```
146
191
 
147
- api()
192
+ Alternatively, setting `Darkroom.javascript_iife = true` will cause JavaScript assets to be compiled to a
193
+ series of IIFEs that provide the same encapsulation as native ES6 modules (indentation is not quite as
194
+ pretty as shown here, but has been altered here for readability):
195
+
196
+ ```javascript
197
+ // Unprocessed /api.js
198
+ export function API() { console.log('API called!') }
148
199
  ```
149
200
 
150
- The same applies for CSS files. Example:
201
+ ```javascript
202
+ // Unprocessed /app.js
203
+ import {API} from '/api.js'
151
204
 
152
- ```css
153
- /* Unprocessed /header.css */
154
- header {
155
- background: #f1f1f1;
156
- }
205
+ API()
206
+ ```
157
207
 
158
- /* Unprocessed /app.css */
159
- @import '/header.css';
208
+ ```javascript
209
+ // Processed /app.js
210
+ ((...bundle) => {
211
+ const modules = {}
212
+ const setters = []
213
+ const $import = (name, setter) =>
214
+ modules[name] ? setter(modules[name]) : setters.push([setter, name])
160
215
 
161
- body {
162
- background: #fff;
163
- }
216
+ for (const [name, def] of bundle)
217
+ modules[name] = def($import)
164
218
 
165
- /* Processed /app.css */
166
- header {
167
- background: #f1f1f1;
168
- }
219
+ for (const [setter, name] of setters)
220
+ setter(modules[name])
221
+ })(
222
+ ['/api.js', $import => {
223
+ function API() { console.log('API called!') }
169
224
 
225
+ return Object.seal({
226
+ API: API,
227
+ })
228
+ }],
170
229
 
171
- body {
172
- background: #fff;
173
- }
174
- ```
230
+ ['/app.js', $import => {
231
+ let API; $import('/api.js', m => API = m.API)
175
232
 
176
- Imported assets can also contain import statements, and those assets are all included in the base asset.
177
- Imports can even be cyclical. If `asset-a.css` imports `asset-b.css` and vice-versa, each asset will simply
178
- contain the content of both of those assets (though order will be different as an asset's own content always
179
- comes after any imported assets' contents).
233
+ API()
234
+
235
+ return Object.seal({})
236
+ }],
237
+ )
238
+ ```
180
239
 
181
240
  ## Asset References
182
241
 
@@ -212,9 +271,11 @@ replaced appropriately.
212
271
  </head>
213
272
 
214
273
  <body>
215
- <img src='/logo.svg?asset-content-displace'>
274
+ <img src='/logo.svg?asset-content=displace'>
216
275
  </body>
276
+ ```
217
277
 
278
+ ```html
218
279
  <!-- Result -->
219
280
  <head>
220
281
  <title>My App</title>
@@ -229,23 +290,234 @@ replaced appropriately.
229
290
 
230
291
  ## Extending
231
292
 
232
- Darkroom is extensible. Support for arbitrary file types can be added as follows (`content_type` is required
233
- but all other keyword arguments are optional):
293
+ Darkroom is extensible. Support for arbitrary file types can be added by specifying one or more extensions
294
+ and a content type:
234
295
 
235
296
  ```ruby
236
- # Simple type with no special behavior.
237
- Darkroom.register('.extension1', 'extension2', '...', 'content/type')
238
-
239
- # Complex type with special behavior.
240
- Darkroom.register('.extension1', 'extension2', '...',
241
- content_type: 'content/type', # HTTP MIME type string
242
- import_regex: /import (?<path>.*)/, # Regex for identifying imports for bundling
243
- reference_regex: /ref=(?<path>.*)/, # Regex for identifying references to other assets
244
- compile_lib: 'some-compile-lib', # Name of library required for compilation
245
- compile: ->(path, content) { '...' }, # Lambda that returns compiled content
246
- minify_lib: 'some-minify-lib', # Name of library required for minification
247
- minify: ->(content) { '...' }, # Lambda that returns minified content
248
- )
297
+ Darkroom.register('.ext1', '.ext2', '...', 'content/type')
298
+ ```
299
+
300
+ ### DSL
301
+
302
+ For more advanced functionality, the DSL can be used one of three ways. With a block:
303
+
304
+ ```ruby
305
+ Darkroom.register('.ext1', '.ext2', '...') do
306
+ # ...
307
+ end
308
+ ```
309
+
310
+ Or with a class that extends `Darkroom::Delegate`:
311
+
312
+ ```ruby
313
+ class MyDelegate < Darkroom::Delegate
314
+ # ...
315
+ end
316
+
317
+ Darkroom.register('.ext1', '.ext2', '...', MyDelegate)
318
+ ```
319
+
320
+ Or with both:
321
+
322
+ ```ruby
323
+ class MyDelegate < Darkroom::Delegate
324
+ # ...
325
+ end
326
+
327
+ Darkroom.register('.ext1', '.ext2', '...', MyDelegate) do
328
+ # Extend MyDelegate
329
+ end
330
+ ```
331
+
332
+ The DSL supports basic parsing via regular expressions, with special behavior for import statements and
333
+ references. Compilation, finalization, and minification behavior can also be configured.
334
+
335
+ #### Content Type
336
+
337
+ ```ruby
338
+ Darkroom.register('.ext1', '.ext2', '...') do
339
+ content_type('content/type') # HTTP MIME type string.
340
+
341
+ # ...
342
+ end
343
+ ```
344
+
345
+ #### Imports
346
+
347
+ Imports are references to other assets, identified via regex, which get prepended to an asset's own content.
348
+ The regex requires a named component, `path`, as it is used internally to determine the asset being imported
349
+ (leveraging `Asset::QUOTED_PATH_REGEX` within one's own regex is helpful).
350
+
351
+ A block is optional, but can be used to accumulate parse data and/or override the default behavior of
352
+ removing an import statement altogether by returning a string to replace it with.
353
+
354
+ ```ruby
355
+ Darkroom.register('.ext1', '.ext2', '...') do
356
+ # ...
357
+
358
+ # The (optional) block is passed three keyword arguments:
359
+ # parse_data: - Hash for storing data across calls to this and other parse handlers.
360
+ # match: - MatchData object from the match against the provided regex.
361
+ # asset: - Asset object of the asset being imported.
362
+ import(/import #{Asset::QUOTED_PATH_REGEX.source};/) do |parse_data:, match:, asset:|
363
+ parse_data[:imports] ||= [] # Accumulate and use arbitrary parse data.
364
+ parse_data[:imports] << match[:path] # Use the MatchData object of the regex match.
365
+
366
+ if asset.binary? # Access the Asset object of the imported asset.
367
+ error('Binary asset not allowed!') # Halt execution of the block and record an error.
368
+ end
369
+
370
+ # Return nil for default behavior (import statement is removed).
371
+ nil
372
+
373
+ # ...Or return a string as the replacement for the import statement.
374
+ "/* [#{asset.path}] */"
375
+ end
376
+ end
377
+ ```
378
+
379
+ #### References
380
+
381
+ References are non-import references to other assets, identified via regex, which result in either the
382
+ asset's path or content being inserted in place. The regex requires named components `quote`, `quoted`,
383
+ `path`, `entity`, and `format`, as these are used internally to determine the asset being referenced and how
384
+ it should be treated (leveraging `Asset::REFERENCE_REGEX` within one's own regex is helpful). See the [Asset
385
+ References](#asset-references) section for more detail.
386
+
387
+ * `quote` - The type of quote used (e.g. `'` or `"`)
388
+ * `quoted` - The portion of text within the `quote`s
389
+ * `path` - The path of the asset
390
+ * `entity` - Either 'path' or 'content'
391
+ * `format` - Format of the path or content
392
+ * If `entity` == 'path' - Either 'versioned' or 'unversioned'
393
+ * If `entity` == 'content' - One of 'base64', 'utf8', or 'displace'
394
+
395
+ A block is optional, but can be used to accumulate parse data and/or override the default substitution
396
+ behavior.
397
+
398
+ ```ruby
399
+ Darkroom.register('.ext1', '.ext2', '...') do
400
+ # ...
401
+
402
+ reference_regex = /ref=#{Asset::REFERENCE_REGEX.source}/x
403
+
404
+ # The (optional) block is passed four keyword arguments:
405
+ # parse_data: - Hash for storing data across calls to this and other parse handlers.
406
+ # match: - MatchData object from the match against the provided regex.
407
+ # asset: - Asset object of the asset being referenced.
408
+ # format: - Format of the reference (see Asset::REFERENCE_FORMATS).
409
+ reference(reference_regex) do |parse_data:, match:, asset:, format:|
410
+ parse_data[:refs] ||= [] # Accumulate and use arbitrary parse data.
411
+ parse_data[:refs] << match[:path] # Use the MatchData object of the regex match.
412
+
413
+ if format == 'base64' # See Asset References section for format details.
414
+ error('Format must be utf8!') # Halt execution of the block and register an error.
415
+ end
416
+
417
+ # Return nil for default behavior (path or content is substituted based on format).
418
+ nil
419
+
420
+ # ...Or return a string to use in lieu of default substitution.
421
+ asset.content.gsub('#', '%23') if format == 'utf8'
422
+
423
+ # ...Or return nil or a string, a start index, and an end index of text to substitute.
424
+ ["[ref]#{asset.content.gsub('#', '%23')}[/ref]", match.begin(0), match.end(0)]
425
+ end
426
+ end
427
+ ```
428
+
429
+ #### Parsing
430
+
431
+ More generalized parsing of any asset-specific text of interest can be performed with `parse` calls, which
432
+ take a name, regex, and block that returns the substitution for the matched text.
433
+
434
+
435
+ ```ruby
436
+ Darkroom.register('.ext1', '.ext2', '...') do
437
+ # ...
438
+
439
+ # The block is passed two keyword arguments:
440
+ # parse_data: - Hash for storing data across calls to this and other parse handlers.
441
+ # match: - MatchData object from the match against the provided regex.
442
+ parse(:exports, /export (?<name>.+)/) do |parse_data:, match:|
443
+ parse_data[:exports] ||= [] # Accumulate and use arbitrary parse data.
444
+ parse_data[:exports] << match[:name] # Use the MatchData object of the regex match.
445
+
446
+ # Return nil for default behavior (matched text is removed).
447
+ nil
448
+
449
+ # ...Or return a string as the replacement for the matched text.
450
+ "exports.#{match[:name]} = "
451
+
452
+ # ...Or return a string, a start index, and an end index of text to substitute.
453
+ [match[:name].upcase, match.begin(:name), match.end(:name)]
454
+ end
455
+
456
+ # Any number of parse statements are allowed and are run in the order they are declared.
457
+ parse(:something_else, /.../) do |parse_data:, match:|
458
+ # ...
459
+ end
460
+ end
461
+ end
462
+ ```
463
+
464
+ #### Compile
465
+
466
+ Compilation allows for a library to require (optional), a delegate to use after compilation (optional), and
467
+ a block that returns the compiled version of the asset's own content.
468
+
469
+ ```ruby
470
+ Darkroom.register('.ext1', '.ext2', '...') do
471
+ # ...
472
+
473
+ # The block is passed three keyword arguments:
474
+ # parse_data: - Hash of data collected during parsing.
475
+ # path: - Path of the asset being compiled.
476
+ # own_content: - Asset's own content (without imports).
477
+ compile(lib: 'compile_lib', delegate: SomeDelegate) do |parse_data:, path:, own_content:|
478
+ CompileLib.compile(own_content)
479
+ end
480
+ end
481
+ ```
482
+
483
+ #### Finalize
484
+
485
+ Finalization happens once an asset is fully processed and compiled (though before minification). A library
486
+ can be provided to require (optional) and the block should return the finalized version of the asset's
487
+ compiled content.
488
+
489
+ ```ruby
490
+ Darkroom.register('.ext1', '.ext2', '...') do
491
+ # ...
492
+
493
+ # The block is passed three keyword arguments:
494
+ # parse_data: - Hash of data collected during parsing.
495
+ # path: - Path of the asset being finalized.
496
+ # content: - Asset's compiled content (with imports prepended).
497
+ finalize(lib: 'finalize_lib') do |parse_data:, path:, content:|
498
+ FinalizeLib.finalize(content)
499
+ end
500
+ end
501
+ ```
502
+
503
+ #### Minify
504
+
505
+ Minification is the very last thing that happens to an asset's content, though it will only happen if
506
+ minification is enabled on the Darkroom instance. A library can be provided to require (optional) and the
507
+ block should return the minified version of the asset's finalized content.
508
+
509
+ ```ruby
510
+ Darkroom.register('.ext1', '.ext2', '...') do
511
+ # ...
512
+
513
+ # The block is passed three keyword arguments:
514
+ # parse_data: - Hash of data collected during parsing.
515
+ # path: - Path of the asset being finalized.
516
+ # content: - Asset's finalized content.
517
+ minify(lib: 'minify_lib') do |parse_data:, path:, content:|
518
+ MinifyLib.compress(content)
519
+ end
520
+ end
249
521
 
250
522
  ```
251
523
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
1
+ 0.0.9
@@ -129,29 +129,19 @@ class Darkroom
129
129
  @entry
130
130
  end
131
131
 
132
- ##
133
- # DEPRECATED: use #entry? instead. Returns boolean indicating whether or not the asset is marked as
134
- # internal.
135
- #
136
- def internal?
137
- Darkroom.deprecated("#{self.class.name}#internal? is deprecated: use #entry? instead")
138
-
139
- !entry?
140
- end
141
-
142
132
  ##
143
133
  # Returns boolean indicating whether or not an error was encountered the last time the asset was
144
134
  # processed.
145
135
  #
146
136
  def error?
147
- !@errors.empty?
137
+ @errors && !@errors.empty?
148
138
  end
149
139
 
150
140
  ##
151
141
  # Returns ProcessingError wrapper of all errors if any exist, or nil if there are none.
152
142
  #
153
143
  def error
154
- @error ||= @errors.empty? ? nil : ProcessingError.new(@errors)
144
+ @error ||= error? ? ProcessingError.new(@errors) : nil
155
145
  end
156
146
 
157
147
  ##
@@ -261,7 +251,7 @@ class Darkroom
261
251
  # Returns high-level object info string.
262
252
  #
263
253
  def inspect
264
- "#<#{self.class}: "\
254
+ "#<#{self.class} "\
265
255
  "@entry=#{@entry.inspect}, "\
266
256
  "@errors=#{@errors.inspect}, "\
267
257
  "@extension=#{@extension.inspect}, "\
@@ -286,7 +276,7 @@ class Darkroom
286
276
  @modified_key == @darkroom.process_key ? (return @modified) : @modified_key = @darkroom.process_key
287
277
 
288
278
  begin
289
- @modified = !!@error
279
+ @modified = error?
290
280
  @modified ||= (@mtime != (@mtime = File.mtime(@file)))
291
281
  @modified ||= @intermediate_asset.modified? if @intermediate_asset
292
282
  @modified ||= dependencies.any? { |d| d.modified? }
@@ -41,10 +41,6 @@ class Darkroom
41
41
 
42
42
  delegate = Class.new(Delegate, &block)
43
43
  delegate.content_type(content_type) if content_type && !delegate.content_type
44
- elsif delegate.kind_of?(Hash)
45
- deprecated("#{self.name}.register with a Hash is deprecated: use the Delegate DSL inside a block "\
46
- 'instead')
47
- delegate = Delegate.deprecated_from_hash(**delegate)
48
44
  elsif delegate && delegate < Delegate
49
45
  delegate = block ? Class.new(delegate, &block) : delegate
50
46
  end
@@ -90,34 +86,16 @@ class Darkroom
90
86
  # [minify:] Boolean specifying whether or not to minify assets.
91
87
  # [minified:] String, regex, or array of strings and regexes specifying paths of assets that are already
92
88
  # minified and thus should be skipped for minification.
93
- # [minified_pattern:] DEPRECATED: use +minified:+ instead. Regex used against asset paths to determine if
94
- # they are already minified and should therefore be skipped over for minification.
95
- # [internal_pattern:] DEPRECATED: use +entries:+ instead. Regex used against asset paths to determine if
96
- # they should be marked as internal and therefore made inaccessible externally.
97
89
  # [min_process_interval:] Minimum time required between one run of asset processing and another.
98
90
  #
99
91
  def initialize(*load_paths, host: nil, hosts: nil, prefix: nil, pristine: nil, entries: nil,
100
- minify: false, minified: DEFAULT_MINIFIED, minified_pattern: nil, internal_pattern: nil,
101
- min_process_interval: MIN_PROCESS_INTERVAL)
92
+ minify: false, minified: DEFAULT_MINIFIED, min_process_interval: MIN_PROCESS_INTERVAL)
102
93
  @load_paths = load_paths.map { |load_path| File.expand_path(load_path) }
103
94
 
104
95
  @hosts = (Array(host) + Array(hosts)).map! { |host| host.sub(TRAILING_SLASHES, '') }
105
96
  @entries = Array(entries)
106
97
  @minify = minify
107
98
  @minified = Array(minified)
108
- @internal_pattern = internal_pattern
109
-
110
- if minified_pattern
111
- self.class.deprecated("#{self.class.name} :minified_pattern is deprecated: use :minified instead "\
112
- 'and pass a string, regex, or array of strings and regexes')
113
- @minified = [minified_pattern]
114
- end
115
-
116
- if @internal_pattern
117
- self.class.deprecated("#{self.class.name} :internal_pattern is deprecated: use :entries to instead "\
118
- 'specify which assets are entry points (i.e. available externally) and pass a string, regex, or '\
119
- 'array of strings and regexes')
120
- end
121
99
 
122
100
  @prefix = prefix&.sub(TRAILING_SLASHES, '')
123
101
  @prefix = nil if @prefix && @prefix.empty?
@@ -316,11 +294,10 @@ class Darkroom
316
294
  # Returns high-level object info string.
317
295
  #
318
296
  def inspect
319
- "#<#{self.class}: "\
297
+ "#<#{self.class} "\
320
298
  "@entries=#{@entries.inspect}, "\
321
299
  "@errors=#{@errors.inspect}, "\
322
300
  "@hosts=#{@hosts.inspect}, "\
323
- "@internal_pattern=#{@internal_pattern.inspect}, "\
324
301
  "@last_processed_at=#{@last_processed_at.inspect}, "\
325
302
  "@load_paths=#{@load_paths.inspect}, "\
326
303
  "@min_process_interval=#{@min_process_interval.inspect}, "\
@@ -342,8 +319,6 @@ class Darkroom
342
319
  def entry?(path)
343
320
  if @pristine.include?(path)
344
321
  true
345
- elsif @internal_pattern && @entries.empty?
346
- !path.match?(@internal_pattern)
347
322
  elsif @entries.empty?
348
323
  true
349
324
  else
@@ -35,8 +35,8 @@ class Darkroom
35
35
  # [regex] Regex for finding import statements. Must contain a named component called +path+ (e.g.
36
36
  # <tt>/^import (?<path>.*)/</tt>).
37
37
  # [&handler] Block for special handling of import statements (optional). Should
38
- # <tt>throw(:error, '...')</tt> on error. Passed three arguments:
39
- # * +parse_data:+ - Hash for storing data across calls to this and other parsing handlers.
38
+ # <tt>throw(:error, '...')</tt> on error. Passed three keyword arguments:
39
+ # * +parse_data:+ - Hash for storing data across calls to this and other parse handlers.
40
40
  # * +match:+ - MatchData object from the match against +regex+.
41
41
  # * +asset:+ - Asset object of the asset being imported.
42
42
  # Return value is used as the substitution for the import statement, with optional second and
@@ -54,10 +54,10 @@ class Darkroom
54
54
  # * +entity+ - Desired entity ('path' or 'content').
55
55
  # * +format+ - Format to use (see Asset::REFERENCE_FORMATS).
56
56
  # [&handler] Block for special handling of references (optional). Should <tt>throw(:error, '...')</tt>
57
- # on error. Passed four arguments:
58
- # * +parse_data:+ - Hash for storing data across calls to this and other parsing handlers.
57
+ # on error. Passed four keyword arguments:
58
+ # * +parse_data:+ - Hash for storing data across calls to this and other parse handlers.
59
59
  # * +match:+ - MatchData object from the match against +regex+.
60
- # * +asset:+ - Asset object of the asset being imported.
60
+ # * +asset:+ - Asset object of the asset being referenced.
61
61
  # * +format:+ - Format of the reference (see Asset::REFERENCE_FORMATS).
62
62
  # Return value is used as the substitution for the reference, with optional second and third
63
63
  # values as integers representing the start and end indexes of the match to replace.
@@ -73,8 +73,8 @@ class Darkroom
73
73
  # subclassing another Delegate, can be used to override the parent class's regex and handler.
74
74
  # [regex] Regex to match against.
75
75
  # [&handler] Block for handling matches of the regex. Should <tt>throw(:error, '...')</tt>
76
- # on error. Passed two arguments:
77
- # * +parse_data:+ - Hash for storing data across calls to this and other parsing handlers.
76
+ # on error. Passed two keyword arguments:
77
+ # * +parse_data:+ - Hash for storing data across calls to this and other parse handlers.
78
78
  # * +match:+ - MatchData object from the match against +regex+.
79
79
  # Return value is used as the substitution for the reference, with optional second and third
80
80
  # values as integers representing the start and end indexes of the match to replace.
@@ -90,10 +90,10 @@ class Darkroom
90
90
  # [lib:] Name of a library to +require+ that is needed by the handler (optional).
91
91
  # [delegate:] Another Delegate to be used after the asset is compiled (optional).
92
92
  # [&handler] Block to call that will return the compiled version of the asset's own content. Passed
93
- # three arguments when called:
94
- #. * +parse_data:+ - Hash of data collected during parsing.
95
- # * +path+ - Path of the asset being compiled.
96
- # * +own_content+ - Asset's own content.
93
+ # three keyword arguments when called:
94
+ # * +parse_data:+ - Hash of data collected during parsing.
95
+ # * +path:+ - Path of the asset being compiled.
96
+ # * +own_content:+ - Asset's own content (without imports).
97
97
  # Asset's own content is set to the value returned.
98
98
  #
99
99
  def self.compile(lib: nil, delegate: nil, &handler)
@@ -106,11 +106,11 @@ class Darkroom
106
106
  # Configures finalize behavior.
107
107
  #
108
108
  # [lib:] Name of a library to +require+ that is needed by the handler (optional).
109
- # [&handler] Block to call that will return the completed version of the asset's overall content. Passed
110
- # three arguments when called:
111
- #. * +parse_data:+ - Hash of data collected during parsing.
112
- # * +path+ - Path of the asset being finalized.
113
- # * +content+ - Asset's content (with imports prepended).
109
+ # [&handler] Block to call that will return the finalized version of the asset's compiled content (with
110
+ # imports prepended). Passed three keyword arguments when called:
111
+ # * +parse_data:+ - Hash of data collected during parsing.
112
+ # * +path:+ - Path of the asset being finalized.
113
+ # * +content:+ - Asset's compiled content (with imports prepended).
114
114
  # Asset's content is set to the value returned.
115
115
  #
116
116
  def self.finalize(lib: nil, &handler)
@@ -122,11 +122,11 @@ class Darkroom
122
122
  # Configures minification.
123
123
  #
124
124
  # [lib:] Name of a library to +require+ that is needed by the handler (optional).
125
- # [&handler] Block to call that will return the minified version of the asset's overall content. Passed
126
- # three arguments when called:
127
- #. * +parse_data:+ - Hash of data collected during parsing.
128
- # * +path+ - Path of the asset being finalized.
129
- # * +content+ - Finalized asset's content.
125
+ # [&handler] Block to call that will return the minified version of the asset's finalized content.
126
+ # Passed three keyword arguments when called:
127
+ # * +parse_data:+ - Hash of data collected during parsing.
128
+ # * +path:+ - Path of the asset being minified.
129
+ # * +content:+ - Asset's finalized content.
130
130
  # Asset's minified content is set to the value returned.
131
131
  #
132
132
  def self.minify(lib: nil, &handler)
@@ -169,69 +169,5 @@ class Darkroom
169
169
  yield(kind, regex, handler)
170
170
  end
171
171
  end
172
-
173
- ##
174
- # DEPRECATED: subclass Delegate and use its DSL instead. Returns a subclass of Delegate configured using
175
- # the supplied Hash.
176
- #
177
- def self.new(**params)
178
- Darkroom.deprecated("#{self.name}::new is deprecated: use the DSL inside a child class or a block "\
179
- 'passed to Darkroom.register')
180
-
181
- deprecated_from_hash(**params)
182
- end
183
-
184
- ##
185
- # DEPRECATED: subclass Delegate and use its DSL instead. Returns a subclass of Delegate configured using
186
- # the supplied Hash.
187
- #
188
- def self.deprecated_from_hash(content_type:, import_regex: nil, reference_regex: nil,
189
- validate_reference: nil, reference_content: nil, compile_lib: nil, compile: nil, compiled: nil,
190
- minify_lib: nil, minify: nil)
191
- Class.new(Delegate) do
192
- self.content_type(content_type)
193
-
194
- @import_regex = import_regex
195
- @reference_regex = reference_regex
196
-
197
- self.import(import_regex) if import_regex
198
-
199
- if validate_reference || reference_content
200
- @validate_reference = validate_reference
201
- @reference_content = reference_content
202
-
203
- self.reference(reference_regex) do |parse_data:, match:, asset:, format:|
204
- error_message = validate_reference&.call(asset, match, format)
205
- error(error_message) if error_message
206
-
207
- reference_content&.call(asset, match, format)
208
- end
209
- elsif reference_regex
210
- self.reference(reference_regex)
211
- end
212
-
213
- if compile
214
- self.compile(lib: compile_lib, delegate: compiled) do |parse_data:, path:, own_content:|
215
- compile.call(path, own_content)
216
- end
217
- elsif compile_lib || compiled
218
- self.compile(lib: compile_lib, delegate: compiled)
219
- end
220
-
221
- if minify
222
- self.minify(lib: minify_lib) do |parse_data:, path:, content:|
223
- minify.call(content)
224
- end
225
- end
226
- end
227
- end
228
-
229
- ##
230
- # DEPRECATED: subclass Delegate and use its DSL instead.
231
- #
232
- def self.import_regex() @import_regex end
233
- def self.reference_regex() @reference_regex end
234
- def self.validate_reference() @validate_reference end
235
- def self.reference_content() @reference_content end
236
172
  end
237
173
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Darkroom
4
- VERSION = '0.0.8'
4
+ VERSION = '0.0.9'
5
5
  end
data/lib/darkroom.rb CHANGED
@@ -10,7 +10,10 @@ require('darkroom/delegates/html')
10
10
  require('darkroom/delegates/htx')
11
11
  require('darkroom/delegates/javascript')
12
12
 
13
+ Darkroom.register('.apng', 'image/apng')
14
+ Darkroom.register('.avif', 'image/avif')
13
15
  Darkroom.register('.css', Darkroom::CSSDelegate)
16
+ Darkroom.register('.gif', 'image/gif')
14
17
  Darkroom.register('.htm', '.html', Darkroom::HTMLDelegate)
15
18
  Darkroom.register('.htx', Darkroom::HTXDelegate)
16
19
  Darkroom.register('.ico', 'image/x-icon')
@@ -20,5 +23,6 @@ Darkroom.register('.json', 'application/json')
20
23
  Darkroom.register('.png', 'image/png')
21
24
  Darkroom.register('.svg', 'image/svg+xml')
22
25
  Darkroom.register('.txt', 'text/plain')
26
+ Darkroom.register('.webp', 'image/webp')
23
27
  Darkroom.register('.woff', 'font/woff')
24
28
  Darkroom.register('.woff2', 'font/woff2')
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darkroom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Pickens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-16 00:00:00.000000000 Z
11
+ date: 2025-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base64
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -28,22 +42,16 @@ dependencies:
28
42
  name: minitest
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 5.11.2
34
- - - "<"
45
+ - - "~>"
35
46
  - !ruby/object:Gem::Version
36
- version: 6.0.0
47
+ version: '5.21'
37
48
  type: :development
38
49
  prerelease: false
39
50
  version_requirements: !ruby/object:Gem::Requirement
40
51
  requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 5.11.2
44
- - - "<"
52
+ - - "~>"
45
53
  - !ruby/object:Gem::Version
46
- version: 6.0.0
54
+ version: '5.21'
47
55
  description: Darkroom provides web asset compilation, bundling, and minification without
48
56
  any external tools, manifest files, or special comment syntax. CSS and JavaScript
49
57
  bundles are automatically generated based on import statements native to each language.
@@ -53,6 +61,7 @@ executables: []
53
61
  extensions: []
54
62
  extra_rdoc_files: []
55
63
  files:
64
+ - CHANGELOG.md
56
65
  - LICENSE
57
66
  - README.md
58
67
  - VERSION
@@ -77,9 +86,10 @@ homepage: https://github.com/npickens/darkroom
77
86
  licenses:
78
87
  - MIT
79
88
  metadata:
80
- allowed_push_host: https://rubygems.org
81
- homepage_uri: https://github.com/npickens/darkroom
82
- source_code_uri: https://github.com/npickens/darkroom
89
+ bug_tracker_uri: https://github.com/npickens/darkroom/issues
90
+ changelog_uri: https://github.com/npickens/darkroom/blob/master/CHANGELOG.md
91
+ documentation_uri: https://github.com/npickens/darkroom/blob/0.0.9/README.md
92
+ source_code_uri: https://github.com/npickens/darkroom/tree/0.0.9
83
93
  post_install_message:
84
94
  rdoc_options: []
85
95
  require_paths:
@@ -93,9 +103,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
103
  requirements:
94
104
  - - ">="
95
105
  - !ruby/object:Gem::Version
96
- version: '0'
106
+ version: 2.0.0
97
107
  requirements: []
98
- rubygems_version: 3.4.15
108
+ rubygems_version: 3.5.10
99
109
  signing_key:
100
110
  specification_version: 4
101
111
  summary: A fast, lightweight, and straightforward web asset management library.