darkroom 0.0.8 → 0.0.10
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 +92 -0
- data/LICENSE +1 -1
- data/README.md +430 -114
- data/VERSION +1 -1
- data/lib/darkroom/asset.rb +176 -164
- data/lib/darkroom/darkroom.rb +140 -149
- data/lib/darkroom/delegate.rb +204 -161
- data/lib/darkroom/delegates/{css.rb → css_delegate.rb} +1 -0
- data/lib/darkroom/delegates/{html.rb → html_delegate.rb} +4 -3
- data/lib/darkroom/delegates/{htx.rb → htx_delegate.rb} +3 -2
- data/lib/darkroom/delegates/{javascript.rb → javascript_delegate.rb} +9 -8
- data/lib/darkroom/errors/asset_error.rb +6 -17
- data/lib/darkroom/errors/asset_not_found_error.rb +4 -8
- data/lib/darkroom/errors/circular_reference_error.rb +4 -8
- data/lib/darkroom/errors/duplicate_asset_error.rb +7 -16
- data/lib/darkroom/errors/invalid_path_error.rb +5 -14
- data/lib/darkroom/errors/missing_library_error.rb +7 -16
- data/lib/darkroom/errors/processing_error.rb +13 -20
- data/lib/darkroom/errors/unrecognized_extension_error.rb +4 -8
- data/lib/darkroom/version.rb +1 -1
- data/lib/darkroom.rb +8 -6
- metadata +36 -30
data/README.md
CHANGED
@@ -1,28 +1,24 @@
|
|
1
1
|
# Darkroom
|
2
2
|
|
3
|
-
Darkroom is a
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
each language's native import statement
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
| SVG | image/svg+xml | .svg |
|
23
|
-
| Text | text/plain | .txt |
|
24
|
-
| WOFF | font/woff | .woff |
|
25
|
-
| WOFF2 | font/woff2 | .woff2 |
|
3
|
+
Darkroom is a simple and straightforward web asset management and bundling library written entirely in Ruby.
|
4
|
+
It is designed to be used directly within a Ruby web server process—no external dependencies or process
|
5
|
+
management is needed.
|
6
|
+
|
7
|
+
* **Asset Bundling** — CSS and JavaScript assets are bundled using each language's native import statement
|
8
|
+
syntax.
|
9
|
+
* `@import '/header.css';` includes the contents of header.css in a CSS asset.
|
10
|
+
* `import '/api.js'` includes the contents of api.js in a JavaScript asset.
|
11
|
+
* **Asset References** — Asset paths are versioned with a URL query parameter.
|
12
|
+
* `<img src='/logo.svg?asset-path'>` gets replaced with `<img src='/logo-[fingerprint].svg'>`.
|
13
|
+
* **Asset Inlining** — Certain kinds of assets (e.g. images in HTML documents) can be inlined using a URL
|
14
|
+
query parameter.
|
15
|
+
* `<img src='/logo.svg?asset-content=utf8'>` gets replaced with
|
16
|
+
`<img src='data:image/svg+xml;utf8,<svg>[...]</svg>'>`.
|
17
|
+
* **JavaScript Modules** — JavaScript bundles can (optionally) encapsulate the content of each file with
|
18
|
+
imports defined as local variables to mimic native ES6 modules within a single file.
|
19
|
+
* **In-Memory** — Processed assets are all stored in and served directly from memory to avoid the issues
|
20
|
+
that generally ensue from writing to and managing files on disk. Assets can however be dumped to disk for
|
21
|
+
upload to a CDN or proxy server in production environments.
|
26
22
|
|
27
23
|
## Installation
|
28
24
|
|
@@ -55,21 +51,26 @@ To create and start using a Darkroom instance, specify one or more load paths (a
|
|
55
51
|
optional):
|
56
52
|
|
57
53
|
```ruby
|
54
|
+
Darkroom.javascript_iife = true # Use IIFEs to emulate ES6-style JavaScript modules
|
55
|
+
|
58
56
|
darkroom = Darkroom.new('app/assets', 'vendor/assets', '...',
|
59
|
-
hosts: [
|
60
|
-
'https://cname1.cdn.com',
|
61
|
-
'https://cname2.cdn.com',
|
57
|
+
hosts: [ # Hosts to prepend to asset paths (useful in production
|
58
|
+
'https://cname1.cdn.com', # when assets are served from a CDN with multiple
|
59
|
+
'https://cname2.cdn.com', # cnames); hosts are chosen round-robin per thread
|
62
60
|
'...',
|
63
61
|
],
|
64
|
-
prefix: '/static',
|
65
|
-
pristine: ['/google-verify.html'],
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
62
|
+
prefix: '/static', # Prefix to add to all asset paths
|
63
|
+
pristine: ['/google-verify.html'], # Paths with no prefix or versioning (assets such as
|
64
|
+
# /favicon.ico and /robots.txt are included
|
65
|
+
# automatically)
|
66
|
+
entries: /^\/controllers\//, # Assets that will be directly accessed (fewer means
|
67
|
+
# better performance); can be a string, regex, or
|
68
|
+
# array of such
|
69
|
+
minify: true, # Minify assets that can be minified
|
70
|
+
minified: /(\.|-)min\.\w+$/, # Files to skip minification on when minify: true; can
|
71
|
+
# be a string, regex, or array of such
|
72
|
+
min_process_interval: 1, # Minimum seconds that must elapse between process
|
73
|
+
# calls (otherwise processing is skipped)
|
73
74
|
)
|
74
75
|
```
|
75
76
|
|
@@ -98,86 +99,158 @@ To work with assets:
|
|
98
99
|
|
99
100
|
```ruby
|
100
101
|
# 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') # =>
|
102
|
+
path = darkroom.asset_path('/js/app.js') # => "/static/js/app-[fingerprint].js"
|
103
|
+
integrity = darkroom.asset_integrity('/js/app.js') # => "sha384-[hash]"
|
103
104
|
|
104
105
|
# Retrieve the Asset object associated with a path.
|
105
106
|
asset = darkroom.asset(path)
|
106
107
|
|
107
|
-
# Prefix (if set on the Darkroom instance) is included in the unversioned and versioned
|
108
|
-
|
109
|
-
assest.
|
110
|
-
assest.
|
108
|
+
# Prefix (if set on the Darkroom instance) is included in the unversioned and versioned
|
109
|
+
# paths.
|
110
|
+
assest.path # => "/js/app.js"
|
111
|
+
assest.path_unversioned # => "/static/js/app.js"
|
112
|
+
assest.path_versioned # => "/static/js/app-[fingerprint].js"
|
111
113
|
|
112
|
-
asset.content_type # => 'text/javascript'
|
113
114
|
asset.content # Content of processed /js/app.js file
|
114
115
|
|
115
|
-
asset.
|
116
|
-
|
117
|
-
asset.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
asset.
|
122
|
-
asset.
|
116
|
+
asset.content_type # => "text/javascript"
|
117
|
+
asset.binary? # => false
|
118
|
+
asset.font? # => false
|
119
|
+
asset.image? # => false
|
120
|
+
asset.entry? # => true
|
121
|
+
|
122
|
+
asset.error? # => true
|
123
|
+
asset.errors # => [#<Darkroom::AssetError ...>, ...]
|
124
|
+
|
125
|
+
asset.fingerprint # => "[MD5 hash of asset content]"
|
126
|
+
asset.headers # => {"Content-Type" => "text/javascript",
|
127
|
+
# "Cache-Control" => "public, max-age=31536000"}
|
128
|
+
asset.headers(versioned: false) # => {"Content-Type" => "text/javascript",
|
129
|
+
# "ETag" => "[fingerprint]"}
|
130
|
+
|
131
|
+
asset.integrity # => "sha384-[hash]"
|
132
|
+
asset.integrity(:sha256) # => "sha256-[hash]"
|
133
|
+
asset.integrity(:sha384) # => "sha384-[hash]"
|
134
|
+
asset.integrity(:sha512) # => "sha512-[hash]"
|
123
135
|
```
|
124
136
|
|
125
137
|
## Asset Bundling
|
126
138
|
|
127
|
-
|
128
|
-
import statement is replaced with content of the
|
129
|
-
|
130
|
-
```javascript
|
131
|
-
// Unprocessed /api.js
|
132
|
-
function api() {
|
133
|
-
console.log('API called!')
|
134
|
-
}
|
135
|
-
|
136
|
-
// Unprocessed /app.js
|
137
|
-
import '/api.js'
|
138
|
-
|
139
|
-
api()
|
140
|
-
|
141
|
-
// Processed /app.js
|
142
|
-
function api() {
|
143
|
-
console.log('API called!')
|
144
|
-
}
|
145
|
-
|
146
|
-
|
147
|
-
api()
|
148
|
-
```
|
149
|
-
|
150
|
-
The same applies for CSS files. Example:
|
151
|
-
|
152
|
-
```css
|
153
|
-
/* Unprocessed /header.css */
|
154
|
-
header {
|
155
|
-
background: #f1f1f1;
|
156
|
-
}
|
157
|
-
|
158
|
-
/* Unprocessed /app.css */
|
159
|
-
@import '/header.css';
|
160
|
-
|
161
|
-
body {
|
162
|
-
background: #fff;
|
163
|
-
}
|
164
|
-
|
165
|
-
/* Processed /app.css */
|
166
|
-
header {
|
167
|
-
background: #f1f1f1;
|
168
|
-
}
|
169
|
-
|
170
|
-
|
171
|
-
body {
|
172
|
-
background: #fff;
|
173
|
-
}
|
174
|
-
```
|
139
|
+
JavaScript and CSS assets specify their dependencies by way of each language's native import statement. Each
|
140
|
+
import statement is replaced with the content of the imported asset.
|
175
141
|
|
176
142
|
Imported assets can also contain import statements, and those assets are all included in the base asset.
|
177
143
|
Imports can even be cyclical. If `asset-a.css` imports `asset-b.css` and vice-versa, each asset will simply
|
178
144
|
contain the content of both of those assets (though order will be different as an asset's own content always
|
179
145
|
comes after any imported assets' contents).
|
180
146
|
|
147
|
+
### JavaScript - Single Scope
|
148
|
+
|
149
|
+
By default, JavaScript bundles are a simple concatenation of files. Imports should all be "side effect"
|
150
|
+
style and result in all code using one shared scope:
|
151
|
+
|
152
|
+
* **/api.js** (raw file)
|
153
|
+
```javascript
|
154
|
+
function API() {
|
155
|
+
console.log('API called!')
|
156
|
+
}
|
157
|
+
```
|
158
|
+
* **/app.js** (raw file)
|
159
|
+
```javascript
|
160
|
+
import '/api.js'
|
161
|
+
|
162
|
+
API()
|
163
|
+
```
|
164
|
+
* **/app.js** (processed)
|
165
|
+
```javascript
|
166
|
+
function API() {
|
167
|
+
console.log('API called!')
|
168
|
+
}
|
169
|
+
|
170
|
+
API()
|
171
|
+
```
|
172
|
+
|
173
|
+
### JavaScript - Modules
|
174
|
+
|
175
|
+
Alternatively, setting `Darkroom.javascript_iife = true` will cause JavaScript assets to be compiled to a
|
176
|
+
series of IIFEs that provide the same encapsulation as native ES6 modules. In this case, objects must be
|
177
|
+
explicitly exported to be importable and named (rather than side effect) imports used:
|
178
|
+
|
179
|
+
* **/api.js** (raw file)
|
180
|
+
```javascript
|
181
|
+
export function API() {
|
182
|
+
console.log('API called!')
|
183
|
+
}
|
184
|
+
```
|
185
|
+
* **/app.js** (raw file)
|
186
|
+
```javascript
|
187
|
+
import {API} from '/api.js'
|
188
|
+
|
189
|
+
API()
|
190
|
+
```
|
191
|
+
* **/app.js** (processed)
|
192
|
+
```javascript
|
193
|
+
((...bundle) => {
|
194
|
+
const modules = {}
|
195
|
+
const setters = []
|
196
|
+
const $import = (name, setter) =>
|
197
|
+
modules[name] ? setter(modules[name]) : setters.push([setter, name])
|
198
|
+
|
199
|
+
for (const [name, def] of bundle)
|
200
|
+
modules[name] = def($import)
|
201
|
+
|
202
|
+
for (const [setter, name] of setters)
|
203
|
+
setter(modules[name])
|
204
|
+
})(
|
205
|
+
['/api.js', $import => {
|
206
|
+
function API() {
|
207
|
+
console.log('API called!')
|
208
|
+
}
|
209
|
+
|
210
|
+
return Object.seal({
|
211
|
+
API: API,
|
212
|
+
})
|
213
|
+
}],
|
214
|
+
|
215
|
+
['/app.js', $import => {
|
216
|
+
let API; $import('/api.js', m => API = m.API)
|
217
|
+
|
218
|
+
API()
|
219
|
+
|
220
|
+
return Object.seal({})
|
221
|
+
}],
|
222
|
+
)
|
223
|
+
```
|
224
|
+
|
225
|
+
### CSS
|
226
|
+
|
227
|
+
CSS imports are always just a simple concatenation.
|
228
|
+
|
229
|
+
* **/header.css** (raw file)
|
230
|
+
```css
|
231
|
+
header {
|
232
|
+
background: #f1f1f1;
|
233
|
+
}
|
234
|
+
```
|
235
|
+
* **/app.css** (raw file)
|
236
|
+
```css
|
237
|
+
@import '/header.css';
|
238
|
+
|
239
|
+
body {
|
240
|
+
background: #fff;
|
241
|
+
}
|
242
|
+
```
|
243
|
+
* **/app.css** (processed)
|
244
|
+
```css
|
245
|
+
header {
|
246
|
+
background: #f1f1f1;
|
247
|
+
}
|
248
|
+
|
249
|
+
body {
|
250
|
+
background: #fff;
|
251
|
+
}
|
252
|
+
```
|
253
|
+
|
181
254
|
## Asset References
|
182
255
|
|
183
256
|
Asset paths and content can be inserted into an asset by referencing an asset's path and including a query
|
@@ -212,9 +285,11 @@ replaced appropriately.
|
|
212
285
|
</head>
|
213
286
|
|
214
287
|
<body>
|
215
|
-
<img src='/logo.svg?asset-content
|
288
|
+
<img src='/logo.svg?asset-content=displace'>
|
216
289
|
</body>
|
290
|
+
```
|
217
291
|
|
292
|
+
```html
|
218
293
|
<!-- Result -->
|
219
294
|
<head>
|
220
295
|
<title>My App</title>
|
@@ -229,23 +304,264 @@ replaced appropriately.
|
|
229
304
|
|
230
305
|
## Extending
|
231
306
|
|
232
|
-
|
233
|
-
|
307
|
+
The following file types are supported out of the box:
|
308
|
+
|
309
|
+
| Name | Content Type | Extension(s) |
|
310
|
+
|------------|------------------|--------------|
|
311
|
+
| APNG | image/apng | .apng |
|
312
|
+
| AVIF | image/avif | .avif |
|
313
|
+
| CSS | text/css | .css |
|
314
|
+
| GIF | image/gif | .gif |
|
315
|
+
| HTML | text/html | .htm, .html |
|
316
|
+
| HTX | text/javascript | .htx |
|
317
|
+
| ICO | image/x-icon | .ico |
|
318
|
+
| JavaScript | text/javascript | .js |
|
319
|
+
| JPEG | image/jpeg | .jpg, .jpeg |
|
320
|
+
| JSON | application/json | .json |
|
321
|
+
| PNG | image/png | .png |
|
322
|
+
| SVG | image/svg+xml | .svg |
|
323
|
+
| Text | text/plain | .txt |
|
324
|
+
| WebP | image/webp | .webp |
|
325
|
+
| WOFF | font/woff | .woff |
|
326
|
+
| WOFF2 | font/woff2 | .woff2 |
|
327
|
+
|
328
|
+
But Darkroom is extensible, allowing support for any kind of asset to be added. This is done most simply by
|
329
|
+
specifying one or more extensions and a content type:
|
234
330
|
|
235
331
|
```ruby
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
332
|
+
Darkroom.register('.ext1', '.ext2', '...', 'content/type')
|
333
|
+
```
|
334
|
+
|
335
|
+
### DSL
|
336
|
+
|
337
|
+
For more advanced functionality, a DSL is provided which can be used one of three ways. With a block:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
341
|
+
# ...
|
342
|
+
end
|
343
|
+
```
|
344
|
+
|
345
|
+
Or with a class that extends `Darkroom::Delegate`:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
class MyDelegate < Darkroom::Delegate
|
349
|
+
# ...
|
350
|
+
end
|
351
|
+
|
352
|
+
Darkroom.register('.ext1', '.ext2', '...', MyDelegate)
|
353
|
+
```
|
354
|
+
|
355
|
+
Or with both:
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
class MyDelegate < Darkroom::Delegate
|
359
|
+
# ...
|
360
|
+
end
|
361
|
+
|
362
|
+
Darkroom.register('.ext1', '.ext2', '...', MyDelegate) do
|
363
|
+
# Extend MyDelegate
|
364
|
+
end
|
365
|
+
```
|
366
|
+
|
367
|
+
The DSL supports basic parsing via regular expressions, with special behavior for import statements and
|
368
|
+
references. Compilation, finalization, and minification behavior can also be configured.
|
369
|
+
|
370
|
+
#### Content Type
|
371
|
+
|
372
|
+
```ruby
|
373
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
374
|
+
content_type('content/type') # HTTP MIME type string.
|
375
|
+
|
376
|
+
# ...
|
377
|
+
end
|
378
|
+
```
|
379
|
+
|
380
|
+
#### Imports
|
381
|
+
|
382
|
+
Imports are references to other assets, identified via regex, which get prepended to an asset's own content.
|
383
|
+
The regex requires a named component, `path`, as it is used internally to determine the asset being imported
|
384
|
+
(leveraging `Asset::QUOTED_PATH_REGEX` within one's own regex is helpful).
|
385
|
+
|
386
|
+
A block is optional, but can be used to accumulate parse data and/or override the default behavior of
|
387
|
+
removing an import statement altogether by returning a string to replace it with.
|
388
|
+
|
389
|
+
```ruby
|
390
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
391
|
+
# ...
|
392
|
+
|
393
|
+
# The (optional) block is passed three keyword arguments:
|
394
|
+
# parse_data: - Hash for storing arbitrary data across calls to this and other handlers.
|
395
|
+
# match: - MatchData object from the match against the regex.
|
396
|
+
# asset: - Asset object of the asset being imported.
|
397
|
+
import(/import '(?<path>[^']+)';/) do |parse_data:, match:, asset:|
|
398
|
+
parse_data[:imports] ||= [] # Accumulate and use arbitrary parse data.
|
399
|
+
parse_data[:imports] << match[:path] # Use the MatchData object of the regex match.
|
400
|
+
|
401
|
+
if asset.binary? # Access the Asset object of the imported asset.
|
402
|
+
error('Binary asset not allowed!') # Halt execution of the block and record an error.
|
403
|
+
end
|
404
|
+
|
405
|
+
# Return nil for default behavior (import statement is removed).
|
406
|
+
nil
|
407
|
+
|
408
|
+
# ...Or return a string as the replacement for the import statement.
|
409
|
+
"/* [#{asset.path}] */"
|
410
|
+
end
|
411
|
+
end
|
412
|
+
```
|
413
|
+
|
414
|
+
#### References
|
415
|
+
|
416
|
+
References are non-import references to other assets, identified via regex, which result in either the
|
417
|
+
asset's path or content being inserted in place. The regex requires named components `quote`, `quoted`,
|
418
|
+
`path`, `entity`, and `format`, as these are used internally to determine the asset being referenced and how
|
419
|
+
it should be treated (leveraging `Asset::REFERENCE_REGEX` within one's own regex is helpful). See the [Asset
|
420
|
+
References](#asset-references) section for more detail.
|
421
|
+
|
422
|
+
* `quote` - The type of quote used (e.g. `'` or `"`)
|
423
|
+
* `quoted` - The portion of text within the `quote`s
|
424
|
+
* `path` - The path of the asset
|
425
|
+
* `entity` - Either 'path' or 'content'
|
426
|
+
* `format` - Format of the path or content
|
427
|
+
* If `entity` == 'path' - Either 'versioned' or 'unversioned'
|
428
|
+
* If `entity` == 'content' - One of 'base64', 'utf8', or 'displace'
|
429
|
+
|
430
|
+
A block is optional, but can be used to accumulate parse data and/or override the default substitution
|
431
|
+
behavior.
|
432
|
+
|
433
|
+
```ruby
|
434
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
435
|
+
# ...
|
436
|
+
|
437
|
+
reference_regex = /ref=#{Asset::REFERENCE_REGEX.source}/x
|
438
|
+
|
439
|
+
# The (optional) block is passed four keyword arguments:
|
440
|
+
#
|
441
|
+
# parse_data: - Hash for storing arbitrary data across calls to this and other handlers.
|
442
|
+
# match: - MatchData object from the match against the regex.
|
443
|
+
# asset: - Asset object of the asset being referenced.
|
444
|
+
# format: - String format of the reference (see Asset::REFERENCE_FORMATS).
|
445
|
+
reference(reference_regex) do |parse_data:, match:, asset:, format:|
|
446
|
+
parse_data[:refs] ||= [] # Accumulate and use arbitrary parse data.
|
447
|
+
parse_data[:refs] << match[:path] # Use the MatchData object of the regex match.
|
448
|
+
|
449
|
+
if format == 'base64' # See Asset References section for format details.
|
450
|
+
error('Format must be utf8!') # Halt execution of the block and register an error.
|
451
|
+
end
|
452
|
+
|
453
|
+
# Return nil for default behavior (path or content is substituted based on format).
|
454
|
+
nil
|
455
|
+
|
456
|
+
# ...Or return a string to use in lieu of default substitution.
|
457
|
+
asset.content.gsub('#', '%23') if format == 'utf8'
|
458
|
+
|
459
|
+
# ...Or return nil or a string, a start index, and an end index of text to substitute.
|
460
|
+
["[ref]#{asset.content.gsub('#', '%23')}[/ref]", match.begin(0), match.end(0)]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
```
|
464
|
+
|
465
|
+
#### Parsing
|
466
|
+
|
467
|
+
More generalized parsing of any asset-specific text of interest can be performed with `parse` calls, which
|
468
|
+
take a name, regex, and block that returns the substitution for the matched text.
|
469
|
+
|
470
|
+
|
471
|
+
```ruby
|
472
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
473
|
+
# ...
|
474
|
+
|
475
|
+
# The block is passed two keyword arguments:
|
476
|
+
#
|
477
|
+
# parse_data: - Hash for storing arbitrary data across calls to this and other handlers.
|
478
|
+
# match: - MatchData object from the match against the regex.
|
479
|
+
parse(:exports, /export (?<name>.+)/) do |parse_data:, match:|
|
480
|
+
parse_data[:exports] ||= [] # Accumulate and use arbitrary parse data.
|
481
|
+
parse_data[:exports] << match[:name] # Use the MatchData object of the regex match.
|
482
|
+
|
483
|
+
# Return nil for default behavior (matched text is removed).
|
484
|
+
nil
|
485
|
+
|
486
|
+
# ...Or return a string as the replacement for the matched text.
|
487
|
+
"exports.#{match[:name]} = "
|
488
|
+
|
489
|
+
# ...Or return a string, a start index, and an end index of text to substitute.
|
490
|
+
[match[:name].upcase, match.begin(:name), match.end(:name)]
|
491
|
+
end
|
492
|
+
|
493
|
+
# Any number of parse statements are allowed and are run in the order they are declared.
|
494
|
+
parse(:something_else, /.../) do |parse_data:, match:|
|
495
|
+
# ...
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
```
|
500
|
+
|
501
|
+
#### Compile
|
502
|
+
|
503
|
+
Compilation allows for a library to require (optional), a delegate to use after compilation (optional), and
|
504
|
+
a block that returns the compiled version of the asset's own content.
|
505
|
+
|
506
|
+
Passing a delegate will cause the asset to be treated as if it is that type once it has been compiled. For
|
507
|
+
example, HTX templates use `delegate: JavaScriptDelegate` because they get compiled to JavaScript and thus
|
508
|
+
should be treated as JavaScript files post compilation.
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
512
|
+
# ...
|
513
|
+
|
514
|
+
# The block is passed three keyword arguments:
|
515
|
+
#
|
516
|
+
# parse_data: - Hash for storing arbitrary data across calls to this and other handlers.
|
517
|
+
# path: - String path of the asset.
|
518
|
+
# own_content: - String own content (without imports) of the asset.
|
519
|
+
compile(lib: 'compile_lib', delegate: SomeDelegate) do |parse_data:, path:, own_content:|
|
520
|
+
CompileLib.compile(own_content)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
```
|
524
|
+
|
525
|
+
#### Finalize
|
526
|
+
|
527
|
+
Finalization happens once an asset is fully processed and compiled (though before minification). A library
|
528
|
+
can be provided to require (optional) and the block should return the finalized version of the asset's
|
529
|
+
compiled content.
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
533
|
+
# ...
|
534
|
+
|
535
|
+
# The block is passed three keyword arguments:
|
536
|
+
#
|
537
|
+
# parse_data: - Hash for storing arbitrary data across calls to this and other handlers.
|
538
|
+
# path: - String path of the asset.
|
539
|
+
# content: - String content of the compiled asset (with imports prepended).
|
540
|
+
finalize(lib: 'finalize_lib') do |parse_data:, path:, content:|
|
541
|
+
FinalizeLib.finalize(content)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
```
|
545
|
+
|
546
|
+
#### Minify
|
547
|
+
|
548
|
+
Minification is the very last thing that happens to an asset's content, though it will only happen if
|
549
|
+
minification is enabled on the Darkroom instance. A library can be provided to require (optional) and the
|
550
|
+
block should return the minified version of the asset's finalized content.
|
551
|
+
|
552
|
+
```ruby
|
553
|
+
Darkroom.register('.ext1', '.ext2', '...') do
|
554
|
+
# ...
|
555
|
+
|
556
|
+
# The block is passed three keyword arguments:
|
557
|
+
#
|
558
|
+
# parse_data: - Hash for storing arbitrary data across calls to this and other handlers.
|
559
|
+
# path: - String oath of the asset being minified.
|
560
|
+
# content: - string content of the finalized asset.
|
561
|
+
minify(lib: 'minify_lib') do |parse_data:, path:, content:|
|
562
|
+
MinifyLib.compress(content)
|
563
|
+
end
|
564
|
+
end
|
249
565
|
|
250
566
|
```
|
251
567
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.10
|