bun_bun_bundle 0.12.1 → 0.14.0
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/README.md +15 -0
- data/exe/bun_bun_bundle +4 -0
- data/lib/bun/bun_bundle.js +115 -36
- data/lib/bun_bun_bundle/helpers.rb +29 -6
- data/lib/bun_bun_bundle/manifest.rb +20 -1
- data/lib/bun_bun_bundle/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39edf3123436dc20c093f42cf59baa94d7cfa12b80cb1024af6fac74d872a2e9
|
|
4
|
+
data.tar.gz: 170ca35cd0f7fa6e81ac9e156ea0e2a2830edef61b54393e2c1eb8b8dd6d36c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f25361191c1840f4642af90d0cac4e6836a069d6d7850fa4ebff0721b22c5044742776d8b6719efee4c604d09e6c39406f4a0cfeaf8733402680e42bea71b98
|
|
7
|
+
data.tar.gz: 852f04b4ff3ba7cd7110bb083e8effb1d9d0f7e38584e7686afa64ac42158dec178517bce103711f385e07166bebda6b3e71fa22fba368083f9251e84b313fa5
|
data/README.md
CHANGED
|
@@ -183,6 +183,9 @@ bun_bun_bundle build --fingerprint
|
|
|
183
183
|
# Strip sourcemaps from a prod build
|
|
184
184
|
bun_bun_bundle build --prod --sourcemap=none
|
|
185
185
|
|
|
186
|
+
# Production build with Subresource Integrity digests
|
|
187
|
+
bun_bun_bundle build --prod --sri=sha384
|
|
188
|
+
|
|
186
189
|
# Development with verbose WebSocket logging
|
|
187
190
|
bun_bun_bundle dev --debug
|
|
188
191
|
```
|
|
@@ -196,8 +199,16 @@ bun_bun_bundle dev --debug
|
|
|
196
199
|
to `inline` in `dev` and `linked` for builds, so production stack traces
|
|
197
200
|
and browser devtools stay debuggable. Pass `--sourcemap=none` when you
|
|
198
201
|
explicitly do not want maps shipped.
|
|
202
|
+
- `--sri[=ALGOS]`: compute [Subresource Integrity][sri] digests for each
|
|
203
|
+
asset. Pass a comma-separated list of `sha256`, `sha384`, or `sha512`
|
|
204
|
+
(bare `--sri` defaults to `sha384`). When digests are present, the
|
|
205
|
+
`bun_js_tag` and `bun_css_tag` helpers automatically render
|
|
206
|
+
`integrity="..." crossorigin="anonymous"` so browsers verify the response
|
|
207
|
+
before executing it
|
|
199
208
|
- `--debug`: verbose WebSocket logging
|
|
200
209
|
|
|
210
|
+
[sri]: https://developer.mozilla.org/docs/Web/Security/Subresource_Integrity
|
|
211
|
+
|
|
201
212
|
> [!NOTE]
|
|
202
213
|
> When running from a Procfile (e.g. with Overmind or Foreman), use
|
|
203
214
|
> `bundle exec bun_bun_bundle` to ensure the correct gem version is loaded.
|
|
@@ -233,6 +244,10 @@ Place a `config/bun.json` in your project root:
|
|
|
233
244
|
> Creating a `bun.json` file is entirely optional. All values shown above are
|
|
234
245
|
> defaults, you only need to specify what you want to override.
|
|
235
246
|
|
|
247
|
+
`watchDirs` entries may be glob patterns. For example, in a Hanami app with
|
|
248
|
+
multiple slices, `"slices/*/assets"` will watch every slice's assets directory
|
|
249
|
+
without having to list them explicitly.
|
|
250
|
+
|
|
236
251
|
If you're developing inside a Docker container, set `listenHost` so the
|
|
237
252
|
WebSocket server accepts connections from the host machine:
|
|
238
253
|
|
data/exe/bun_bun_bundle
CHANGED
|
@@ -27,6 +27,9 @@ else
|
|
|
27
27
|
--minify Minify JS and CSS output
|
|
28
28
|
--sourcemap[=KIND] Sourcemap kind: inline, linked, external, none
|
|
29
29
|
(dev defaults to inline, builds default to linked)
|
|
30
|
+
--sri[=ALGOS] Compute Subresource Integrity digests for each
|
|
31
|
+
asset (comma-separated: sha256, sha384, sha512;
|
|
32
|
+
defaults to sha384)
|
|
30
33
|
--debug Enable verbose WebSocket logging
|
|
31
34
|
|
|
32
35
|
Examples:
|
|
@@ -34,6 +37,7 @@ else
|
|
|
34
37
|
bun_bun_bundle build # plain build
|
|
35
38
|
bun_bun_bundle build --prod # fingerprint + minify
|
|
36
39
|
bun_bun_bundle build --fingerprint # fingerprint only
|
|
40
|
+
bun_bun_bundle build --prod --sri=sha384 # add integrity digests
|
|
37
41
|
bun_bun_bundle build --prod --sourcemap=none # strip sourcemaps
|
|
38
42
|
|
|
39
43
|
Configuration:
|
data/lib/bun/bun_bundle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {mkdirSync, readFileSync, existsSync, rmSync, watch} from 'fs'
|
|
1
|
+
import {mkdirSync, readFileSync, existsSync, rmSync, statSync, watch} from 'fs'
|
|
2
2
|
import {join, dirname, basename, extname} from 'path'
|
|
3
3
|
import {Glob} from 'bun'
|
|
4
4
|
import {resolvePlugins} from './plugins/index.js'
|
|
@@ -24,16 +24,17 @@ export default {
|
|
|
24
24
|
fingerprint: false,
|
|
25
25
|
minify: false,
|
|
26
26
|
sourcemap: null,
|
|
27
|
+
sri: [],
|
|
27
28
|
wsClients: new Set(),
|
|
28
29
|
watchTimers: new Map(),
|
|
30
|
+
watchers: [],
|
|
29
31
|
plugins: [],
|
|
30
32
|
|
|
33
|
+
SRI_ALGORITHMS: ['sha256', 'sha384', 'sha512'],
|
|
34
|
+
|
|
31
35
|
flags(input) {
|
|
32
|
-
const {debug, dev, prod, fingerprint, minify, sourcemap} =
|
|
33
|
-
input
|
|
34
|
-
)
|
|
35
|
-
? this.parseArgv(input)
|
|
36
|
-
: input
|
|
36
|
+
const {debug, dev, prod, fingerprint, minify, sourcemap, sri} =
|
|
37
|
+
Array.isArray(input) ? this.parseArgv(input) : input
|
|
37
38
|
if (debug != null) this.debug = debug
|
|
38
39
|
if (dev != null) this.dev = dev
|
|
39
40
|
if (prod != null) this.prod = prod
|
|
@@ -42,31 +43,74 @@ export default {
|
|
|
42
43
|
if (minify != null) this.minify = minify
|
|
43
44
|
else if (prod === true) this.minify = true
|
|
44
45
|
if (sourcemap != null) this.sourcemap = sourcemap
|
|
46
|
+
if (sri != null) this.sri = sri
|
|
45
47
|
},
|
|
46
48
|
|
|
47
49
|
SOURCEMAP_KINDS: ['inline', 'linked', 'external', 'none'],
|
|
50
|
+
BOOLEAN_FLAGS: ['debug', 'dev', 'prod', 'fingerprint', 'minify'],
|
|
48
51
|
|
|
49
52
|
parseArgv(argv) {
|
|
53
|
+
return {
|
|
54
|
+
...this.parseBooleanFlags(argv),
|
|
55
|
+
...this.parseSourcemapFlag(argv),
|
|
56
|
+
...this.parseSriFlag(argv)
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
parseBooleanFlags(argv) {
|
|
50
61
|
const opts = {}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (argv.includes('--prod')) opts.prod = true
|
|
54
|
-
if (argv.includes('--fingerprint')) opts.fingerprint = true
|
|
55
|
-
if (argv.includes('--minify')) opts.minify = true
|
|
56
|
-
const sm = argv.find(
|
|
57
|
-
a => a === '--sourcemap' || a.startsWith('--sourcemap=')
|
|
58
|
-
)
|
|
59
|
-
if (sm) {
|
|
60
|
-
const value = sm.includes('=') ? sm.split('=')[1] : 'linked'
|
|
61
|
-
if (this.SOURCEMAP_KINDS.includes(value)) opts.sourcemap = value
|
|
62
|
-
else
|
|
63
|
-
console.warn(
|
|
64
|
-
` ▸ Ignoring --sourcemap=${value} (valid: ${this.SOURCEMAP_KINDS.join(', ')})`
|
|
65
|
-
)
|
|
62
|
+
for (const name of this.BOOLEAN_FLAGS) {
|
|
63
|
+
if (argv.includes(`--${name}`)) opts[name] = true
|
|
66
64
|
}
|
|
67
65
|
return opts
|
|
68
66
|
},
|
|
69
67
|
|
|
68
|
+
findValueFlag(argv, name, defaultValue) {
|
|
69
|
+
const flag = argv.find(a => a === `--${name}` || a.startsWith(`--${name}=`))
|
|
70
|
+
if (!flag) return null
|
|
71
|
+
return flag.includes('=') ? flag.split('=')[1] : defaultValue
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
parseSourcemapFlag(argv) {
|
|
75
|
+
const value = this.findValueFlag(argv, 'sourcemap', 'linked')
|
|
76
|
+
if (value === null) return {}
|
|
77
|
+
if (this.SOURCEMAP_KINDS.includes(value)) return {sourcemap: value}
|
|
78
|
+
console.warn(
|
|
79
|
+
` ▸ Ignoring --sourcemap=${value} (valid: ${this.SOURCEMAP_KINDS.join(', ')})`
|
|
80
|
+
)
|
|
81
|
+
return {}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
parseSriFlag(argv) {
|
|
85
|
+
const value = this.findValueFlag(argv, 'sri', 'sha384')
|
|
86
|
+
if (value === null) return {}
|
|
87
|
+
const algos = value.split(',').map(a => a.trim()).filter(Boolean)
|
|
88
|
+
const valid = algos.filter(a => this.SRI_ALGORITHMS.includes(a))
|
|
89
|
+
const invalid = algos.filter(a => !this.SRI_ALGORITHMS.includes(a))
|
|
90
|
+
if (invalid.length) {
|
|
91
|
+
console.warn(
|
|
92
|
+
` ▸ Ignoring --sri=${invalid.join(',')} (valid: ${this.SRI_ALGORITHMS.join(', ')})`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
return valid.length ? {sri: valid} : {}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
computeSri(content) {
|
|
99
|
+
if (!this.sri.length) return null
|
|
100
|
+
return this.sri.map(algo => {
|
|
101
|
+
const hasher = new Bun.CryptoHasher(algo)
|
|
102
|
+
hasher.update(content)
|
|
103
|
+
return `${algo}-${hasher.digest('base64')}`
|
|
104
|
+
})
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
manifestEntry(url, content) {
|
|
108
|
+
const entry = {url}
|
|
109
|
+
const sri = this.computeSri(content)
|
|
110
|
+
if (sri) entry.sri = sri
|
|
111
|
+
return entry
|
|
112
|
+
},
|
|
113
|
+
|
|
70
114
|
deepMerge(target, source) {
|
|
71
115
|
const result = {...target}
|
|
72
116
|
for (const k of Object.keys(source))
|
|
@@ -186,7 +230,10 @@ export default {
|
|
|
186
230
|
}
|
|
187
231
|
|
|
188
232
|
await Bun.write(join(outDir, fileName), content)
|
|
189
|
-
this.manifest[`${type}/${entryName}${ext}`] =
|
|
233
|
+
this.manifest[`${type}/${entryName}${ext}`] = this.manifestEntry(
|
|
234
|
+
`${type}/${fileName}`,
|
|
235
|
+
content
|
|
236
|
+
)
|
|
190
237
|
}
|
|
191
238
|
},
|
|
192
239
|
|
|
@@ -215,20 +262,20 @@ export default {
|
|
|
215
262
|
for await (const file of glob.scan({cwd: fullDir, onlyFiles: true})) {
|
|
216
263
|
const srcPath = join(fullDir, file)
|
|
217
264
|
const content = await Bun.file(srcPath).arrayBuffer()
|
|
265
|
+
const bytes = new Uint8Array(content)
|
|
218
266
|
|
|
219
267
|
const ext = extname(file)
|
|
220
268
|
const name = file.slice(0, -ext.length) || file
|
|
221
|
-
const fileName = this.fingerprintName(
|
|
222
|
-
name,
|
|
223
|
-
ext,
|
|
224
|
-
new Uint8Array(content)
|
|
225
|
-
)
|
|
269
|
+
const fileName = this.fingerprintName(name, ext, bytes)
|
|
226
270
|
const destPath = join(destDir, fileName)
|
|
227
271
|
|
|
228
272
|
mkdirSync(dirname(destPath), {recursive: true})
|
|
229
273
|
await Bun.write(destPath, content)
|
|
230
274
|
|
|
231
|
-
this.manifest[`${assetType}/${file}`] =
|
|
275
|
+
this.manifest[`${assetType}/${file}`] = this.manifestEntry(
|
|
276
|
+
`${assetType}/${fileName}`,
|
|
277
|
+
bytes
|
|
278
|
+
)
|
|
232
279
|
}
|
|
233
280
|
}
|
|
234
281
|
},
|
|
@@ -260,7 +307,10 @@ export default {
|
|
|
260
307
|
|
|
261
308
|
prettyManifest() {
|
|
262
309
|
const lines = Object.entries(this.manifest)
|
|
263
|
-
.map(([key, value]) =>
|
|
310
|
+
.map(([key, value]) => {
|
|
311
|
+
const url = value && typeof value === 'object' ? value.url : value
|
|
312
|
+
return ` ${key} → ${url}`
|
|
313
|
+
})
|
|
264
314
|
.join('\n')
|
|
265
315
|
return `\n${lines}\n\n`
|
|
266
316
|
},
|
|
@@ -332,18 +382,38 @@ export default {
|
|
|
332
382
|
})()
|
|
333
383
|
}
|
|
334
384
|
|
|
335
|
-
for (const
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
385
|
+
for (const pattern of this.config.watchDirs) {
|
|
386
|
+
const dirs = pattern.includes('*')
|
|
387
|
+
? await Array.fromAsync(new Glob(pattern).scan({cwd: this.root, onlyFiles: false}))
|
|
388
|
+
: [pattern]
|
|
389
|
+
for (const dir of dirs) {
|
|
390
|
+
const fullDir = join(this.root, dir)
|
|
391
|
+
if (!existsSync(fullDir) || !statSync(fullDir).isDirectory()) {
|
|
392
|
+
console.warn(` ▸ Watch directory ${dir} does not exist, skipping...`)
|
|
393
|
+
continue
|
|
394
|
+
}
|
|
395
|
+
this.watchers.push(watch(fullDir, {recursive: true}, handler))
|
|
340
396
|
}
|
|
341
|
-
watch(fullDir, {recursive: true}, handler)
|
|
342
397
|
}
|
|
343
398
|
|
|
344
399
|
console.log('Beginning to watch your project')
|
|
345
400
|
},
|
|
346
401
|
|
|
402
|
+
shutdown() {
|
|
403
|
+
for (const w of this.watchers) {
|
|
404
|
+
try {
|
|
405
|
+
w.close()
|
|
406
|
+
} catch {}
|
|
407
|
+
}
|
|
408
|
+
this.watchers = []
|
|
409
|
+
for (const client of this.wsClients) {
|
|
410
|
+
try {
|
|
411
|
+
client.close()
|
|
412
|
+
} catch {}
|
|
413
|
+
}
|
|
414
|
+
this.wsClients.clear()
|
|
415
|
+
},
|
|
416
|
+
|
|
347
417
|
async serve() {
|
|
348
418
|
await this.build()
|
|
349
419
|
await this.watch()
|
|
@@ -353,7 +423,7 @@ export default {
|
|
|
353
423
|
const debug = this.debug
|
|
354
424
|
const wsClients = this.wsClients
|
|
355
425
|
|
|
356
|
-
Bun.serve({
|
|
426
|
+
const server = Bun.serve({
|
|
357
427
|
hostname,
|
|
358
428
|
port,
|
|
359
429
|
fetch(req, server) {
|
|
@@ -376,6 +446,15 @@ export default {
|
|
|
376
446
|
|
|
377
447
|
const protocol = secure ? 'wss' : 'ws'
|
|
378
448
|
console.log(`\n\n 🔌 Live reload at ${protocol}://${host}:${port}\n\n`)
|
|
449
|
+
|
|
450
|
+
process.on('SIGINT', () => {
|
|
451
|
+
console.log('\n ▸ Shutting down...')
|
|
452
|
+
this.shutdown()
|
|
453
|
+
try {
|
|
454
|
+
server.stop(true)
|
|
455
|
+
} catch {}
|
|
456
|
+
process.exit(0)
|
|
457
|
+
})
|
|
379
458
|
},
|
|
380
459
|
|
|
381
460
|
async bake() {
|
|
@@ -15,6 +15,8 @@ module BunBunBundle
|
|
|
15
15
|
module Helpers
|
|
16
16
|
include SafeHtml
|
|
17
17
|
|
|
18
|
+
FINGERPRINTED_CSS_REGEX = /-[0-9a-f]{8}\.css$/
|
|
19
|
+
|
|
18
20
|
# Returns the public path to an asset from the manifest.
|
|
19
21
|
#
|
|
20
22
|
# Prepends the configured public_path and asset_host:
|
|
@@ -23,8 +25,7 @@ module BunBunBundle
|
|
|
23
25
|
# bun_asset("images/logo.png") # => "/assets/images/logo-abc123.png" (production)
|
|
24
26
|
#
|
|
25
27
|
def bun_asset(path)
|
|
26
|
-
|
|
27
|
-
"#{BunBunBundle.asset_host}#{BunBunBundle.config.public_path}/#{fingerprinted}"
|
|
28
|
+
bun_asset_url(bun_entry(path))
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
# Generates a <script> tag for a JS entry point.
|
|
@@ -33,8 +34,11 @@ module BunBunBundle
|
|
|
33
34
|
# # => '<script src="/assets/js/app.js" type="text/javascript"></script>'
|
|
34
35
|
#
|
|
35
36
|
def bun_js_tag(source, **options)
|
|
36
|
-
|
|
37
|
-
attrs = { type: 'text/javascript' }
|
|
37
|
+
entry = bun_entry(source)
|
|
38
|
+
attrs = { type: 'text/javascript' }
|
|
39
|
+
.merge(bun_sri_attrs(entry))
|
|
40
|
+
.merge(options)
|
|
41
|
+
.merge(src: bun_asset_url(entry))
|
|
38
42
|
bun_safe(%(<script #{bun_html_attrs(attrs)}></script>))
|
|
39
43
|
end
|
|
40
44
|
|
|
@@ -44,14 +48,19 @@ module BunBunBundle
|
|
|
44
48
|
# # => '<link href="/assets/css/app.css" type="text/css" rel="stylesheet">'
|
|
45
49
|
#
|
|
46
50
|
def bun_css_tag(source, **options)
|
|
51
|
+
entry = bun_entry(source)
|
|
47
52
|
attrs = { type: 'text/css', rel: 'stylesheet' }
|
|
53
|
+
.merge(bun_sri_attrs(entry))
|
|
48
54
|
.merge(options)
|
|
49
|
-
.merge(href: bun_href_with_timestamp(
|
|
55
|
+
.merge(href: bun_href_with_timestamp(bun_asset_url(entry)))
|
|
50
56
|
bun_safe(%(<link #{bun_html_attrs(attrs)}>))
|
|
51
57
|
end
|
|
52
58
|
|
|
53
59
|
# Generates an <img> tag for an image asset.
|
|
54
60
|
#
|
|
61
|
+
# Subresource Integrity is intentionally not rendered on images: browsers
|
|
62
|
+
# do not enforce SRI for <img> elements.
|
|
63
|
+
#
|
|
55
64
|
# bun_img_tag("images/logo.png", alt: "Logo")
|
|
56
65
|
# # => '<img src="/assets/images/logo.png" alt="Logo">'
|
|
57
66
|
#
|
|
@@ -64,6 +73,20 @@ module BunBunBundle
|
|
|
64
73
|
|
|
65
74
|
private
|
|
66
75
|
|
|
76
|
+
def bun_entry(source)
|
|
77
|
+
BunBunBundle.manifest[source]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def bun_asset_url(entry)
|
|
81
|
+
"#{BunBunBundle.asset_host}#{BunBunBundle.config.public_path}/#{entry.url}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def bun_sri_attrs(entry)
|
|
85
|
+
return {} if entry.sri.empty?
|
|
86
|
+
|
|
87
|
+
{ integrity: entry.sri.join(' '), crossorigin: 'anonymous' }
|
|
88
|
+
end
|
|
89
|
+
|
|
67
90
|
def bun_html_attrs(hash)
|
|
68
91
|
bun_flatten_attrs(hash).compact.map do |k, v|
|
|
69
92
|
k = k.to_s.tr('_', '-')
|
|
@@ -89,7 +112,7 @@ module BunBunBundle
|
|
|
89
112
|
def bun_href_with_timestamp(href)
|
|
90
113
|
config = BunBunBundle.config
|
|
91
114
|
return href unless href.start_with?(config.public_path)
|
|
92
|
-
return href if href.match?(
|
|
115
|
+
return href if href.match?(FINGERPRINTED_CSS_REGEX)
|
|
93
116
|
|
|
94
117
|
file_path = href.sub(config.public_path, config.out_dir)
|
|
95
118
|
mtime = File.exist?(file_path) ? File.mtime(file_path).to_i : Time.now.to_i
|
|
@@ -5,11 +5,30 @@ require 'json'
|
|
|
5
5
|
module BunBunBundle
|
|
6
6
|
class Manifest
|
|
7
7
|
class MissingAssetError < StandardError; end
|
|
8
|
+
class MigrationError < StandardError; end
|
|
9
|
+
class InvalidEntryError < StandardError; end
|
|
10
|
+
|
|
11
|
+
Entry = Data.define(:url, :sri) do
|
|
12
|
+
def self.from(value)
|
|
13
|
+
value.is_a?(Hash) || raise(
|
|
14
|
+
MigrationError,
|
|
15
|
+
'Manifest predates bun_bun_bundle 0.13. Run: bun_bun_bundle build',
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if (url = value['url']).nil? || url.empty?
|
|
19
|
+
raise InvalidEntryError, "Manifest entry is missing 'url'"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
new(url: url, sri: Array(value['sri']).freeze)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
8
25
|
|
|
9
26
|
attr_reader :entries
|
|
10
27
|
|
|
11
28
|
def initialize(entries = {})
|
|
12
|
-
@entries = entries.
|
|
29
|
+
@entries = entries.transform_values do |value|
|
|
30
|
+
value.is_a?(Entry) ? value : Entry.from(value)
|
|
31
|
+
end.freeze
|
|
13
32
|
end
|
|
14
33
|
|
|
15
34
|
# Loads the manifest from a JSON file.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bun_bun_bundle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wout Fierens
|
|
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: '0'
|
|
75
75
|
requirements: []
|
|
76
|
-
rubygems_version: 4.0.
|
|
76
|
+
rubygems_version: 4.0.11
|
|
77
77
|
specification_version: 4
|
|
78
78
|
summary: A self-contained asset bundler powered by Bun
|
|
79
79
|
test_files: []
|