bun_bun_bundle 0.9.1 → 0.11.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: 32717f75c661fee2dfaaec58b6a41c6af8c10e29b44a10c193a9d7f953a28ce8
4
- data.tar.gz: 827508488f3c5e7e212c832570e904bd1072a9b595e5a2c6675b01774fc1462f
3
+ metadata.gz: d45723ba4d7826f7a860af793c4acf6fbceda366e442f9b9858ec2c1f340e5ec
4
+ data.tar.gz: 13a119627c68cd7009975c40e0caa22f91adc35797b74f7604c35237960a69c8
5
5
  SHA512:
6
- metadata.gz: 12ec0f0fe4952c46718f33f3a4599c6056222948d452e800e1015219d58332be31241d49bf62111e372dcdeff163dd6a9b0cc68c1001f0545490ba64a4de5106
7
- data.tar.gz: fb6875aae889f86d030905721b74eeca6b293684c8076577728f1f2bc00002763d7cc6e35d2dc3522494526194c28d09d7dd92cdc4d3ec528d1fe0c1773bfa89
6
+ metadata.gz: 7e7f845bfaff50c0c3a3b18e5dfcac5900929d9d2f261b3931a5bbac47775629d77a834c9e581a6c0dbea04004cbfd4d7a561a7d013524a55a67837e5b3cf42a
7
+ data.tar.gz: 3d17dbcfe26e03538d349bb2a8ba48ec5ecd52b203712ab0296ebaa4502485c7b4275e0a4cfd8764a1d8e6dd2cb56651430ed42ac05c621151d807439eb54e11
data/README.md CHANGED
@@ -174,13 +174,30 @@ bun_bun_bundle dev
174
174
  # Build assets
175
175
  bun_bun_bundle build
176
176
 
177
- # Build with fingerprinting and minification
177
+ # Build with fingerprinting and minification (shortcut)
178
178
  bun_bun_bundle build --prod
179
179
 
180
+ # Fingerprint without minifying (e.g. for staging diagnostics)
181
+ bun_bun_bundle build --fingerprint
182
+
183
+ # Strip sourcemaps from a prod build
184
+ bun_bun_bundle build --prod --sourcemap=none
185
+
180
186
  # Development with verbose WebSocket logging
181
187
  bun_bun_bundle dev --debug
182
188
  ```
183
189
 
190
+ ### Flags
191
+
192
+ - `--prod`: shortcut for `--fingerprint --minify`
193
+ - `--fingerprint`: hash asset filenames for cache busting
194
+ - `--minify`: minify JS and CSS output
195
+ - `--sourcemap[=KIND]`: `inline`, `linked`, `external`, or `none`. Defaults
196
+ to `inline` in `dev` and `linked` for builds, so production stack traces
197
+ and browser devtools stay debuggable. Pass `--sourcemap=none` when you
198
+ explicitly do not want maps shipped.
199
+ - `--debug`: verbose WebSocket logging
200
+
184
201
  > [!NOTE]
185
202
  > When running from a Procfile (e.g. with Overmind or Foreman), use
186
203
  > `bundle exec bun_bun_bundle` to ensure the correct gem version is loaded.
@@ -320,7 +337,11 @@ The context object has the following properties:
320
337
  - `root`: absolute path to the project root
321
338
  - `config`: the resolved `bun.json` configuration object
322
339
  - `dev`: `true` when running in development mode
323
- - `prod`: `true` when running in production mode
340
+ - `prod`: `true` when `--prod` was passed (shortcut flag)
341
+ - `fingerprint`: `true` when asset filenames will be content-hashed
342
+ - `minify`: `true` when output will be minified (use this to strip
343
+ comments, banners, or dev-only branches in your plugin)
344
+ - `sourcemap`: the sourcemap kind being produced (or `null` for default)
324
345
  - `manifest`: the current asset manifest object
325
346
 
326
347
  #### Simple transform plugins
@@ -337,9 +358,9 @@ transform receives the output of the previous one.
337
358
  ```javascript
338
359
  // config/bun/banner.js
339
360
 
340
- export default function banner({prod}) {
361
+ export default function banner({minify}) {
341
362
  return (content, args) => {
342
- const stamp = prod ? '' : ` (dev ${args.path})`
363
+ const stamp = minify ? '' : ` (dev ${args.path})`
343
364
  return `/* My App${stamp} */\n${content}`
344
365
  }
345
366
  }
data/exe/bun_bun_bundle CHANGED
@@ -22,13 +22,19 @@ else
22
22
  build Build assets
23
23
 
24
24
  Flags:
25
- --prod Enable minification and fingerprinting
26
- --debug Enable verbose WebSocket logging
25
+ --prod Shortcut for --fingerprint --minify
26
+ --fingerprint Hash asset filenames for cache busting
27
+ --minify Minify JS and CSS output
28
+ --sourcemap[=KIND] Sourcemap kind: inline, linked, external, none
29
+ (dev defaults to inline, builds default to linked)
30
+ --debug Enable verbose WebSocket logging
27
31
 
28
32
  Examples:
29
- bun_bun_bundle dev # development with live reload
30
- bun_bun_bundle build # build assets
31
- bun_bun_bundle build --prod # build with fingerprinting and minification
33
+ bun_bun_bundle dev # live reload
34
+ bun_bun_bundle build # plain build
35
+ bun_bun_bundle build --prod # fingerprint + minify
36
+ bun_bun_bundle build --fingerprint # fingerprint only
37
+ bun_bun_bundle build --prod --sourcemap=none # strip sourcemaps
32
38
 
33
39
  Configuration:
34
40
  Place a config/bun.json in your project root to customize settings.
data/lib/bun/bake.js CHANGED
@@ -1,9 +1,5 @@
1
1
  import BunBunBundle from "./bun_bundle.js";
2
2
 
3
- BunBunBundle.flags({
4
- debug: process.argv.includes("--debug"),
5
- dev: process.argv.includes("--dev"),
6
- prod: process.argv.includes("--prod"),
7
- });
3
+ BunBunBundle.flags(process.argv);
8
4
 
9
5
  await BunBunBundle.bake();
@@ -21,14 +21,50 @@ export default {
21
21
  debug: false,
22
22
  dev: false,
23
23
  prod: false,
24
+ fingerprint: false,
25
+ minify: false,
26
+ sourcemap: null,
24
27
  wsClients: new Set(),
25
28
  watchTimers: new Map(),
26
29
  plugins: [],
27
30
 
28
- flags({debug, dev, prod}) {
31
+ flags(input) {
32
+ const {debug, dev, prod, fingerprint, minify, sourcemap} = Array.isArray(
33
+ input
34
+ )
35
+ ? this.parseArgv(input)
36
+ : input
29
37
  if (debug != null) this.debug = debug
30
38
  if (dev != null) this.dev = dev
31
39
  if (prod != null) this.prod = prod
40
+ if (fingerprint != null) this.fingerprint = fingerprint
41
+ else if (prod === true) this.fingerprint = true
42
+ if (minify != null) this.minify = minify
43
+ else if (prod === true) this.minify = true
44
+ if (sourcemap != null) this.sourcemap = sourcemap
45
+ },
46
+
47
+ SOURCEMAP_KINDS: ['inline', 'linked', 'external', 'none'],
48
+
49
+ parseArgv(argv) {
50
+ const opts = {}
51
+ if (argv.includes('--debug')) opts.debug = true
52
+ if (argv.includes('--dev')) opts.dev = true
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
+ )
66
+ }
67
+ return opts
32
68
  },
33
69
 
34
70
  deepMerge(target, source) {
@@ -43,7 +79,10 @@ export default {
43
79
 
44
80
  loadConfig() {
45
81
  const defaults = {
46
- entryPoints: {js: ['app/assets/js/app.js'], css: ['app/assets/css/app.css']},
82
+ entryPoints: {
83
+ js: ['app/assets/js/app.js'],
84
+ css: ['app/assets/css/app.css']
85
+ },
47
86
  plugins: {css: ['aliases', 'cssGlobs'], js: ['aliases', 'jsGlobs']},
48
87
  watchDirs: ['app/assets'],
49
88
  staticDirs: ['app/assets/images', 'app/assets/fonts'],
@@ -69,6 +108,9 @@ export default {
69
108
  config: this.config,
70
109
  dev: this.dev,
71
110
  prod: this.prod,
111
+ fingerprint: this.fingerprint,
112
+ minify: this.minify,
113
+ sourcemap: this.sourcemap,
72
114
  manifest: this.manifest
73
115
  })
74
116
  },
@@ -79,8 +121,8 @@ export default {
79
121
  return join(this.root, this.config.outDir)
80
122
  },
81
123
 
82
- fingerprint(name, ext, content) {
83
- if (!this.prod) return `${name}${ext}`
124
+ fingerprintName(name, ext, content) {
125
+ if (!this.fingerprint) return `${name}${ext}`
84
126
 
85
127
  const hash = Bun.hash(content).toString(16).slice(0, 8)
86
128
  return `${name}-${hash}${ext}`
@@ -107,7 +149,7 @@ export default {
107
149
  try {
108
150
  result = await Bun.build({
109
151
  entrypoints: [entryPath],
110
- minify: this.prod,
152
+ minify: this.minify,
111
153
  plugins: this.plugins,
112
154
  ...options
113
155
  })
@@ -124,16 +166,26 @@ export default {
124
166
  continue
125
167
  }
126
168
 
127
- const output = result.outputs.find(o => o.path.endsWith(ext))
128
- if (!output) {
169
+ const mainOutput = result.outputs.find(o => o.path.endsWith(ext))
170
+ if (!mainOutput) {
129
171
  console.error(` ▸ No ${type.toUpperCase()} output for ${entry}`)
130
172
  continue
131
173
  }
174
+ const mapOutput = result.outputs.find(o => o.kind === 'sourcemap')
175
+
176
+ let content = await mainOutput.text()
177
+ const fileName = this.fingerprintName(entryName, ext, content)
178
+
179
+ if (mapOutput) {
180
+ const mapFileName = `${fileName}.map`
181
+ content = content.replace(
182
+ /\/\/# sourceMappingURL=\S+/,
183
+ () => `//# sourceMappingURL=${mapFileName}`
184
+ )
185
+ await Bun.write(join(outDir, mapFileName), await mapOutput.text())
186
+ }
132
187
 
133
- const content = await output.text()
134
- const fileName = this.fingerprint(entryName, ext, content)
135
188
  await Bun.write(join(outDir, fileName), content)
136
-
137
189
  this.manifest[`${type}/${entryName}${ext}`] = `${type}/${fileName}`
138
190
  }
139
191
  },
@@ -142,7 +194,7 @@ export default {
142
194
  await this.buildAssets('js', {
143
195
  target: 'browser',
144
196
  format: 'iife',
145
- sourcemap: this.dev ? 'inline' : 'none'
197
+ sourcemap: this.sourcemap || (this.dev ? 'inline' : 'linked')
146
198
  })
147
199
  },
148
200
 
@@ -166,7 +218,11 @@ export default {
166
218
 
167
219
  const ext = extname(file)
168
220
  const name = file.slice(0, -ext.length) || file
169
- const fileName = this.fingerprint(name, ext, new Uint8Array(content))
221
+ const fileName = this.fingerprintName(
222
+ name,
223
+ ext,
224
+ new Uint8Array(content)
225
+ )
170
226
  const destPath = join(destDir, fileName)
171
227
 
172
228
  mkdirSync(dirname(destPath), {recursive: true})
@@ -223,6 +279,10 @@ export default {
223
279
  },
224
280
 
225
281
  async watch() {
282
+ const extras = this.config.watchExtensions || {}
283
+ const cssExts = ['css', ...(extras.css || [])]
284
+ const jsExts = ['js', 'ts', 'jsx', 'tsx', ...(extras.js || [])]
285
+
226
286
  const handler = (event, filename) => {
227
287
  if (!filename) return
228
288
 
@@ -239,16 +299,18 @@ export default {
239
299
 
240
300
  // Debounce: multiple events for the same file (e.g. actual save + backup)
241
301
  if (this.watchTimers.has(normalizedFilename)) return
242
- this.watchTimers.set(normalizedFilename, setTimeout(() => {
243
- this.watchTimers.delete(normalizedFilename)
244
- }, 100))
302
+ this.watchTimers.set(
303
+ normalizedFilename,
304
+ setTimeout(() => {
305
+ this.watchTimers.delete(normalizedFilename)
306
+ }, 100)
307
+ )
245
308
 
246
309
  console.log(` ▸ ${normalizedFilename} changed`)
247
-
248
310
  ;(async () => {
249
311
  try {
250
- if (ext === 'css') await this.buildCSS()
251
- else if (['js', 'ts', 'jsx', 'tsx'].includes(ext)) await this.buildJS()
312
+ if (cssExts.includes(ext)) await this.buildCSS()
313
+ else if (jsExts.includes(ext)) await this.buildJS()
252
314
  else if (base.includes('.')) await this.copyStaticAssets()
253
315
 
254
316
  await this.writeManifest()
@@ -295,7 +357,8 @@ export default {
295
357
  },
296
358
  close(ws) {
297
359
  wsClients.delete(ws)
298
- if (debug) console.log(` ▸ Client disconnected (${wsClients.size})\n\n`)
360
+ if (debug)
361
+ console.log(` ▸ Client disconnected (${wsClients.size})\n\n`)
299
362
  },
300
363
  message() {}
301
364
  }
@@ -1,9 +1,14 @@
1
- const REGEX = /(url\(\s*['"]?|(?<!\w)['"](?:glob:)?)\$\//g
1
+ const CSS_REGEX = /((?:url\(\s*|@import\s+)['"]?(?:glob:)?)\$\//g
2
+ const JS_REGEX =
3
+ /((?:from\s+|require\s*\(\s*|import\s*\(\s*)['"](?:glob:)?)\$\//g
2
4
 
3
5
  // Resolves `$/` root aliases in CSS url() references and JS/CSS imports.
4
6
  // e.g. url('$/app/assets/images/foo.png') → url('/absolute/root/app/assets/images/foo.png')
5
7
  // import x from '$/lib/utils.js' → import x from '/absolute/root/lib/utils.js'
6
8
  // @import '$/app/assets/css/reset.css' → @import '/absolute/root/app/assets/css/reset.css'
7
9
  export default function aliases({root}) {
8
- return content => content.replace(REGEX, `$1${root}/`)
10
+ return (content, args) => {
11
+ const regex = args.path.endsWith('.css') ? CSS_REGEX : JS_REGEX
12
+ return content.replace(regex, `$1${root}/`)
13
+ }
9
14
  }
@@ -33,6 +33,19 @@ module BunBunBundle
33
33
  const ws = new WebSocket('#{config.dev_server.ws_url}')
34
34
  let connected = false
35
35
 
36
+ const scrollKey = 'bun-scroll:' + location.pathname
37
+ addEventListener('load', () => {
38
+ const saved = sessionStorage.getItem(scrollKey)
39
+ if (saved !== null) {
40
+ sessionStorage.removeItem(scrollKey)
41
+ scrollTo(0, parseInt(saved, 10))
42
+ }
43
+ })
44
+ const reload = () => {
45
+ sessionStorage.setItem(scrollKey, String(scrollY))
46
+ location.reload()
47
+ }
48
+
36
49
  ws.onmessage = (event) => {
37
50
  const data = JSON.parse(event.data)
38
51
 
@@ -50,7 +63,7 @@ module BunBunBundle
50
63
  console.error('\\u2716 Build error:', data.message)
51
64
  } else {
52
65
  console.log('\\u25b8 Reloading...')
53
- location.reload()
66
+ reload()
54
67
  }
55
68
  }
56
69
 
@@ -59,7 +72,7 @@ module BunBunBundle
59
72
  console.log('\\u25b8 Live reload connected')
60
73
  }
61
74
  ws.onclose = () => {
62
- if (connected) setTimeout(() => location.reload(), 2000)
75
+ if (connected) setTimeout(reload, 2000)
63
76
  }
64
77
  })()
65
78
  </script>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BunBunBundle
4
- VERSION = '0.9.1'
4
+ VERSION = '0.11.0'
5
5
  end
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.9.1
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wout Fierens