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 +4 -4
- data/CHANGELOG.md +83 -0
- data/LICENSE +1 -1
- data/README.md +347 -75
- data/VERSION +1 -1
- data/lib/darkroom/asset.rb +4 -14
- data/lib/darkroom/darkroom.rb +2 -27
- data/lib/darkroom/delegate.rb +21 -85
- data/lib/darkroom/version.rb +1 -1
- data/lib/darkroom.rb +4 -0
- metadata +27 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec78b55ac1e82e265e1f5546a929ed3b12ab72585bc448d5f3c3d06c8804ec10
|
4
|
+
data.tar.gz: 2df42517847826a826ebe32d90b8bbbe41fe242872e56db06672e5b5c1d4fb3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
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: [
|
60
|
-
'https://cname1.cdn.com',
|
61
|
-
'https://cname2.cdn.com',
|
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',
|
65
|
-
pristine: ['/google-verify.html'],
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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') # =>
|
102
|
-
integrity = darkroom.asset_integrity('/js/app.js') # =>
|
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
|
108
|
-
|
109
|
-
assest.
|
110
|
-
assest.
|
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.
|
116
|
-
|
117
|
-
asset.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
asset.
|
122
|
-
asset.
|
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
|
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
|
133
|
-
|
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
|
-
|
182
|
+
API()
|
183
|
+
```
|
140
184
|
|
185
|
+
```javascript
|
141
186
|
// Processed /app.js
|
142
|
-
function
|
143
|
-
console.log('API called!')
|
144
|
-
}
|
187
|
+
function API() { console.log('API called!') }
|
145
188
|
|
189
|
+
API()
|
190
|
+
```
|
146
191
|
|
147
|
-
|
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
|
-
|
201
|
+
```javascript
|
202
|
+
// Unprocessed /app.js
|
203
|
+
import {API} from '/api.js'
|
151
204
|
|
152
|
-
|
153
|
-
|
154
|
-
header {
|
155
|
-
background: #f1f1f1;
|
156
|
-
}
|
205
|
+
API()
|
206
|
+
```
|
157
207
|
|
158
|
-
|
159
|
-
|
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
|
-
|
162
|
-
|
163
|
-
}
|
216
|
+
for (const [name, def] of bundle)
|
217
|
+
modules[name] = def($import)
|
164
218
|
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
172
|
-
|
173
|
-
}
|
174
|
-
```
|
230
|
+
['/app.js', $import => {
|
231
|
+
let API; $import('/api.js', m => API = m.API)
|
175
232
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
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
|
233
|
-
|
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
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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.
|
1
|
+
0.0.9
|
data/lib/darkroom/asset.rb
CHANGED
@@ -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 ||=
|
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 =
|
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? }
|
data/lib/darkroom/darkroom.rb
CHANGED
@@ -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,
|
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
|
data/lib/darkroom/delegate.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
-
|
95
|
-
# * +path
|
96
|
-
# * +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
|
110
|
-
# three arguments when called:
|
111
|
-
|
112
|
-
# * +path
|
113
|
-
# * +content
|
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
|
126
|
-
# three arguments when called:
|
127
|
-
|
128
|
-
# * +path
|
129
|
-
# * +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
|
data/lib/darkroom/version.rb
CHANGED
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.
|
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:
|
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:
|
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:
|
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
|
-
|
81
|
-
|
82
|
-
|
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:
|
106
|
+
version: 2.0.0
|
97
107
|
requirements: []
|
98
|
-
rubygems_version: 3.
|
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.
|