bun_bun_bundle 0.1.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c51a065b214293e85467dfdd10f2c84bf763f65fe27d0b549b175ea7448563bf
4
- data.tar.gz: f2d86b5d3650797b617fa3ecee6d3dac119d97850749bc830172bfa12cb83dfe
3
+ metadata.gz: 11c05e743bc0de032fa96e01847eab10641fecb9cf59151b62f14fdfb4b8384f
4
+ data.tar.gz: 3d6f0044f6c3afcbb38dcc847ac2250eec8616c5a09bf51c7022468fff86e766
5
5
  SHA512:
6
- metadata.gz: c87530160b4f6829e32f7fc82c4f0d9180aa13b03089011fdb237d8daa4973453bd9cd074bd0402736f7fcab49e7d40c98f9b7fbb1a094aa07f718cd94ca9ac4
7
- data.tar.gz: 0cbad61b7eff7af29f38cab146f09941a8f3dbd9354a1c470d36a35f94bbb36484543f7a06ef27e4810f2a1f51becb00026c7206de30e6b0049334cd224127fd
6
+ metadata.gz: 9063a677cbb30db62e3162e7c0b753dd1f34b3b36ecc1c076efeba45ad3d3d09b7dac00ca575c6159cd0ea9ea13066963a053a7d2928501b9ebc879e4eecb52e
7
+ data.tar.gz: 8e9b0d27cb3f11dadfd88ac64deb1b52c54f20b63bb738d55dbb196d40b83049a1cb1285eb68ee379cc60cffbcb9f516d15864c74b9c2a05497c4fa1713f044c
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # BunBunBundle
1
+ # Bun, Bun, Bundle
2
2
 
3
3
  A self-contained asset bundler for Ruby powered by [Bun](https://bun.sh). No
4
- development dependencies, no complex configuration. Fast builds with CSS
5
- hot-reloading, fingerprinting, live reload, and a flexible plugin system. Works
6
- with Rails, Hanami, or any Rack app.
4
+ development dependencies, no complex configuration. Lightning fast builds with
5
+ CSS hot-reloading, fingerprinting, live reload, and a flexible plugin system.
6
+ Works with Rails, Hanami, or any Rack app.
7
7
 
8
8
  ## Why use BunBunBundle?
9
9
 
@@ -28,18 +28,18 @@ browsers always fetch the right version.
28
28
 
29
29
  Development and production builds go through the exact same pipeline. The only
30
30
  differences are fingerprinting and minification being enabled in production,
31
- but nothing is holding you back form them in development as well.
31
+ but nothing is holding you back from enabling them in development as well.
32
32
 
33
33
  ### Extensible plugin system
34
34
 
35
- Comes with built-in plugins for CSS glob imports, root aliases, and JS glob
36
- imports. Plugins are simple, plain JS files, so you can create your own JS/CSS
37
- transformers, and raw Bun plugins are supported as well.
35
+ BunBunBundle comes with built-in plugins for root aliases, CSS glob imports,
36
+ and JS glob imports. Plugins are simple, plain JS files, so you can create your
37
+ own JS/CSS transformers, and raw Bun plugins are supported as well.
38
38
 
39
39
  ### Just one dependency: Bun
40
40
 
41
- The bundler ships with the gem. Bun is the only external requirement, so there
42
- are zero dev dependencies.
41
+ The bundler ships with the gem. Bun is the only external requirement, so other
42
+ than that, there are no dev dependencies.
43
43
 
44
44
  ## Installation
45
45
 
@@ -59,6 +59,18 @@ are zero dev dependencies.
59
59
 
60
60
  ## Usage with Rails
61
61
 
62
+ BunBunBundle completely bypasses the Rails asset pipeline. If you're adding it
63
+ to an existing app, you can remove Sprockets/Propshaft:
64
+
65
+ - Remove `gem 'sprockets-rails'` or `gem 'propshaft'` from your `Gemfile`
66
+ - Delete `config/initializers/assets.rb` if present
67
+
68
+ For new apps, generate them without the asset pipeline:
69
+
70
+ ```sh
71
+ rails new myapp --minimal --skip-asset-pipeline --skip-javascript
72
+ ```
73
+
62
74
  The gem auto-configures itself through a Railtie. All helpers are available in
63
75
  your views immediately:
64
76
 
@@ -81,7 +93,13 @@ stale asset caching.
81
93
 
82
94
  ## Usage with Hanami
83
95
 
84
- 1. Require the Hanami integration:
96
+ Hanami ships with its own esbuild-based asset pipeline. Since BunBunBundle
97
+ replaces it entirely, you can clean up the default setup:
98
+
99
+ - Remove `gem 'hanami-assets'` from your `Gemfile`
100
+ - Delete `config/assets.js`, `package.json`, and `node_modules/`
101
+
102
+ 1. Set up the Hanami integration:
85
103
 
86
104
  ```ruby
87
105
  # config/app.rb
@@ -90,12 +108,15 @@ stale asset caching.
90
108
 
91
109
  module MyApp
92
110
  class App < Hanami::App
93
- BunBunBundle.setup(root: root)
94
- config.middleware.use BunBunBundle::DevCacheMiddleware if Hanami.env?(:development)
111
+ BunBunBundle.setup(root: root, hanami: config)
95
112
  end
96
113
  end
97
114
  ```
98
115
 
116
+ This loads the manifest, and in development automatically registers the
117
+ cache-busting middleware and configures the CSP to allow the live reload
118
+ script and WebSocket connection.
119
+
99
120
  2. Include the helpers in your views:
100
121
 
101
122
  ```ruby
@@ -111,7 +132,7 @@ stale asset caching.
111
132
  end
112
133
  ```
113
134
 
114
- 4. Use them in your templates:
135
+ 3. Use them in your templates:
115
136
 
116
137
  ```erb
117
138
  <%= bun_css_tag('css/app.css') %>
@@ -132,7 +153,8 @@ BunBunBundle.asset_host = 'https://cdn.example.com'
132
153
 
133
154
  ## Helpers
134
155
 
135
- All helpers are prefixed with `bun_` to avoid conflicts with framework helpers:
156
+ All helpers are prefixed with `bun_` to avoid conflicts with existing framework
157
+ helpers:
136
158
 
137
159
  | Helper | Description |
138
160
  | -------------------------------- | ------------------------------------------------ |
@@ -185,46 +207,144 @@ Place a `config/bun.json` in your project root:
185
207
  "secure": false
186
208
  },
187
209
  "plugins": {
188
- "css": ["cssAliases", "cssGlobs"],
189
- "js": ["jsGlobs"]
210
+ "css": ["aliases", "cssGlobs"],
211
+ "js": ["aliases", "jsGlobs"]
190
212
  }
191
213
  }
192
214
  ```
193
215
 
194
- All values shown above are defaults, you only need to specify what you want to
216
+ All values shown above are defaults. You only need to specify what you want to
195
217
  override.
196
218
 
197
219
  ## Plugins
198
220
 
199
- Three plugins are included out of the box:
221
+ Three plugins are included out of the box.
222
+
223
+ ### `aliases`
224
+
225
+ Resolves `$/` root aliases to absolute paths in both CSS and JS files. This
226
+ lets you reference assets and modules from the project root without worrying
227
+ about relative paths.
228
+
229
+ In CSS:
230
+
231
+ ```css
232
+ @import '$/app/assets/css/reset.css';
233
+
234
+ .logo {
235
+ background: url('$/app/assets/images/logo.png');
236
+ }
237
+ ```
238
+
239
+ In JS:
240
+
241
+ ```javascript
242
+ import utils from '$/lib/utils.js'
243
+ ```
244
+
245
+ All `$/` references are resolved to your project root.
246
+
247
+ ### `cssGlobs`
248
+
249
+ Expands glob patterns in CSS `@import` statements. Instead of manually listing
250
+ every file, you can import an entire directory at once:
251
+
252
+ ```css
253
+ @import './components/**/*.css';
254
+ ```
200
255
 
201
- | Plugin | Description |
202
- | ------------ | ---------------------------------------------------------------- |
203
- | `cssAliases` | Resolves `$/` root aliases in CSS `url()` references |
204
- | `cssGlobs` | Expands glob patterns in `@import` statements |
205
- | `jsGlobs` | Compiles `import x from 'glob:./path/*.js'` into object mappings |
256
+ This will be expanded into individual `@import` lines for each matching file,
257
+ sorted alphabetically.
258
+
259
+ > [!NOTE]
260
+ > A warning is logged if the pattern matches no files.
261
+
262
+ ### `jsGlobs`
263
+
264
+ Compiles glob imports into an object that maps file paths to their default
265
+ exports. Use the special `glob:` prefix in an import statement:
266
+
267
+ ```javascript
268
+ import components from 'glob:./components/**/*.js'
269
+ ```
270
+
271
+ This will generate individual imports and builds an object mapping. For
272
+ example:
273
+
274
+ ```javascript
275
+ import _glob_components_theme from './components/theme.js'
276
+ import _glob_components_shared_tooltip from './components/shared/tooltip.js'
277
+ const components = {
278
+ 'components/theme': _glob_components_theme,
279
+ 'components/shared/tooltip': _glob_components_shared_tooltip
280
+ }
281
+ ```
282
+
283
+ > [!NOTE]
284
+ > If no files match the pattern, an empty object is assigned.
206
285
 
207
286
  ### Custom plugins
208
287
 
209
- Create a JS file that exports a factory function:
288
+ Custom plugins are JS files referenced by their path in the config. Each file
289
+ must export a factory function that receives a context object with `root`
290
+ (project root path) and `prod` (boolean indicating production mode). What the
291
+ factory returns determines the plugin type.
292
+
293
+ #### Simple transform plugins
294
+
295
+ A simple transform plugin returns a function that receives the file content as
296
+ a string and an `args` object with the file's `path`. It should return the
297
+ transformed content. The transform can be synchronous or asynchronous.
298
+
299
+ Transforms are chained in the order they appear in the config, so each
300
+ transform receives the output of the previous one.
210
301
 
211
302
  ```javascript
212
303
  // config/bun/banner.js
213
304
 
214
- export default function banner({ prod }) {
215
- return (content) => {
216
- const stamp = prod ? "" : ` (dev build ${new Date().toISOString()})`;
217
- return `/* My App${stamp} */\n${content}`;
218
- };
305
+ export default function banner({prod}) {
306
+ return content => {
307
+ const stamp = prod ? '' : ` (dev build ${new Date().toISOString()})`
308
+ return `/* My App${stamp} */\n${content}`
309
+ }
310
+ }
311
+ ```
312
+
313
+ #### Raw Bun plugins
314
+
315
+ If the factory returns an object with a `setup` method instead of a function,
316
+ it is treated as a raw
317
+ [Bun plugin](https://bun.sh/docs/bundler/plugins). This gives you full access
318
+ to Bun's plugin API, including `onLoad`, `onResolve`, and custom loaders.
319
+
320
+ ```javascript
321
+ // config/bun/svg.js
322
+
323
+ export default function svg() {
324
+ return {
325
+ name: 'svg-loader',
326
+ setup(build) {
327
+ build.onLoad({filter: /\.svg$/}, async args => {
328
+ const text = await Bun.file(args.path).text()
329
+ return {
330
+ contents: `export default ${JSON.stringify(text)}`,
331
+ loader: 'js'
332
+ }
333
+ })
334
+ }
335
+ }
219
336
  }
220
337
  ```
221
338
 
222
- Then reference it in your config:
339
+ #### Registering custom plugins
340
+
341
+ Reference custom plugins by their file path in your config:
223
342
 
224
343
  ```json
225
344
  {
226
345
  "plugins": {
227
- "css": ["cssAliases", "cssGlobs", "config/bun/banner.js"]
346
+ "css": ["aliases", "cssGlobs", "config/bun/banner.js"],
347
+ "js": ["aliases", "jsGlobs", "config/bun/svg.js"]
228
348
  }
229
349
  }
230
350
  ```
@@ -252,8 +372,8 @@ your-app/
252
372
 
253
373
  BunBunBundle was originally built for [Fluck](https://fluck.site), a
254
374
  self-hostable website builder using [Lucky
255
- Framework](https://luckyframework.org/). I wanted to have a fast, comprehensive
256
- asset bundler that would not require too much maintenance in the long term.
375
+ Framework](https://luckyframework.org/). I wanted to have a fast and modern
376
+ asset bundler that would require minimal maintenance in the long term.
257
377
 
258
378
  Bun was the natural choice because it does almost everything:
259
379
 
@@ -265,10 +385,10 @@ Bun was the natural choice because it does almost everything:
265
385
  - Extendability with simple plugins
266
386
 
267
387
  It's also fast and reliable. We use this setup heavily in two Lucky apps and it
268
- is rock solid, and it has since been adopted by Lucky as the default builder.
388
+ is rock solid. It has since been adopted by Lucky as the default builder.
269
389
 
270
- I wanted to have the same setup in my Ruby apps as well, that's when this Gem
271
- was born. I hope you enjoy it too!
390
+ This Gem was born because I wanted to have the same setup in my Ruby apps as
391
+ well. I hope you enjoy it too!
272
392
 
273
393
  ## Contributing
274
394
 
@@ -42,7 +42,7 @@ export default {
42
42
  loadConfig() {
43
43
  const defaults = {
44
44
  entryPoints: {js: ['app/assets/js/app.js'], css: ['app/assets/css/app.css']},
45
- plugins: {css: ['cssAliases', 'cssGlobs'], js: ['jsGlobs']},
45
+ plugins: {css: ['aliases', 'cssGlobs'], js: ['aliases', 'jsGlobs']},
46
46
  staticDirs: ['app/assets/images', 'app/assets/fonts'],
47
47
  outDir: 'public/assets',
48
48
  publicPath: '/assets',
@@ -0,0 +1,9 @@
1
+ const REGEX = /(url\(\s*['"]?|(?<!\w)['"])\$\//g
2
+
3
+ // Resolves `$/` root aliases in CSS url() references and JS/CSS imports.
4
+ // e.g. url('$/app/assets/images/foo.png') → url('/absolute/root/app/assets/images/foo.png')
5
+ // import x from '$/lib/utils.js' → import x from '/absolute/root/lib/utils.js'
6
+ // @import '$/app/assets/css/reset.css' → @import '/absolute/root/app/assets/css/reset.css'
7
+ export default function aliases({root}) {
8
+ return content => content.replace(REGEX, `$1${root}/`)
9
+ }
@@ -1,9 +1,9 @@
1
1
  import {join} from 'path'
2
- import cssAliases from './cssAliases.js'
2
+ import aliases from './aliases.js'
3
3
  import cssGlobs from './cssGlobs.js'
4
4
  import jsGlobs from './jsGlobs.js'
5
5
 
6
- const builtins = {cssAliases, cssGlobs, jsGlobs}
6
+ const builtins = {aliases, cssGlobs, jsGlobs}
7
7
  const TYPE_REGEXES = {
8
8
  css: /\.css$/,
9
9
  js: /\.(js|ts|jsx|tsx)$/
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bun_bun_bundle'
4
+
3
5
  module BunBunBundle
4
6
  # Hanami integration for BunBunBundle.
5
7
  #
@@ -9,8 +11,7 @@ module BunBunBundle
9
11
  #
10
12
  # module MyApp
11
13
  # class App < Hanami::App
12
- # BunBunBundle.setup(root: root)
13
- # config.middleware.use BunBunBundle::DevCacheMiddleware if Hanami.env?(:development)
14
+ # BunBunBundle.setup(root: root, hanami: config)
14
15
  # end
15
16
  # end
16
17
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BunBunBundle
4
- VERSION = '0.1.2'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -38,10 +38,15 @@ module BunBunBundle
38
38
 
39
39
  # Loads config and manifest. Call this from Hanami's config/app.rb or
40
40
  # any Rack app's startup.
41
- def setup(root: Dir.pwd)
41
+ #
42
+ # Pass `hanami: config` from within a Hanami::App class body to
43
+ # automatically register middleware and configure CSP for development.
44
+ def setup(root: Dir.pwd, hanami: nil)
42
45
  self.config = Config.load(root: root.to_s)
43
46
  options = development? ? {} : { retries: 1, delay: 0 }
44
47
  self.manifest = Manifest.load(root: root.to_s, **options)
48
+
49
+ configure_hanami(hanami) if hanami
45
50
  end
46
51
 
47
52
  # Returns the path to the bundled JS files shipped with the gem.
@@ -56,6 +61,16 @@ module BunBunBundle
56
61
  @asset_host = nil
57
62
  @environment = nil
58
63
  end
64
+
65
+ private
66
+
67
+ def configure_hanami(hanami_config)
68
+ return unless development?
69
+
70
+ hanami_config.middleware.use DevCacheMiddleware
71
+ hanami_config.actions.content_security_policy[:script_src] += " 'unsafe-inline'"
72
+ hanami_config.actions.content_security_policy[:connect_src] += " #{config.dev_server.ws_url}"
73
+ end
59
74
  end
60
75
  end
61
76
 
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.1.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wout Fierens
@@ -36,7 +36,7 @@ files:
36
36
  - exe/bun_bun_bundle
37
37
  - lib/bun/bake.js
38
38
  - lib/bun/bun_bundle.js
39
- - lib/bun/plugins/cssAliases.js
39
+ - lib/bun/plugins/aliases.js
40
40
  - lib/bun/plugins/cssGlobs.js
41
41
  - lib/bun/plugins/index.js
42
42
  - lib/bun/plugins/jsGlobs.js
@@ -1,10 +0,0 @@
1
- import {join} from 'path'
2
-
3
- const REGEX = /url\(\s*['"]?\$\//g
4
-
5
- // Resolves `$` root aliases in CSS url() references.
6
- // e.g. url('$/images/foo.png') → url('/absolute/src/images/foo.png')
7
- export default function cssAliases({root}) {
8
- const srcDir = join(root, 'src')
9
- return content => content.replace(REGEX, `url('${srcDir}/`)
10
- }