bun_bundler 0.1.0

A Rust-native programmable browser runtime built on Servo and SpiderMonkey
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
use std::io::Write as _;

use bstr::BStr;

use bun_core::fmt as bun_fmt;
use bun_core::strings;
use bun_paths::{self, MAX_PATH_BYTES, PathBuffer};
use bun_wyhash::{self, Wyhash11};

use crate::Transpiler;
use bun_js_parser as js_ast;

// PORT NOTE: `Path`/`PathName` come from the lower-tier `bun_paths::fs` shim
// (lifetime-erased `'static` slices) so `bun_ast::Source` field types line up;
// `FileSystem` is the real `bun_resolver::fs` singleton now that
// `bun_resolver` is in this crate's dep set.
pub mod Fs {
    pub use bun_paths::fs::{Path, PathName};
    pub use bun_resolver::fs::FileSystem;
}

pub struct FallbackEntryPoint {
    pub code_buffer: [u8; 8192],
    pub path_buffer: PathBuffer,
    pub source: bun_ast::Source,
    // Only ever assigned the literal "" (no writer anywhere in the tree); never freed.
    pub built_code: &'static [u8],
}

impl Default for FallbackEntryPoint {
    fn default() -> Self {
        Self {
            code_buffer: [0u8; 8192],
            path_buffer: PathBuffer::uninit(),
            source: bun_ast::Source::default(),
            built_code: b"",
        }
    }
}

impl FallbackEntryPoint {
    // TODO(port): crate::options::Framework / ClientCssInJs — `options`
    // module is still gated; body also touched `bun_resolver::fs` (see
    // PORTING.md §Forbidden) before un-gating.
    pub fn generate<TranspilerType>(
        entry: &mut FallbackEntryPoint,
        input_path: &[u8],
        transpiler: &mut TranspilerType,
    ) -> Result<(), bun_core::Error>
    // TODO(port): narrow error set
    where
        // TODO(port): TranspilerType trait bound — body reads `.options.framework` and `.arena`.
        TranspilerType: TranspilerLike,
    {
        // This is *extremely* naive.
        // The basic idea here is this:
        // --
        // import * as EntryPoint from 'entry-point';
        // import boot from 'framework';
        // boot(EntryPoint);
        // --
        // We go through the steps of printing the code -- only to then parse/transpile it because
        // we want it to go through the linker and the rest of the transpilation process

        let disable_css_imports = transpiler
            .options()
            .framework
            .as_ref()
            .unwrap()
            .client_css_in_js
            != ClientCssInJs::AutoOnImportCss;

        // PORT NOTE: the Source always OWNS its bytes (interned path label +
        // `Cow::Owned` contents) — the old shape re-borrowed the scratch
        // `entry.code_buffer` / caller's `input_path` through the
        // lifetime-erased `IntoStr` shim, a self-referential dangling hazard
        // (BCE: `Source::init_path_string` class). One copy of a tiny
        // generated wrapper beats a dangling borrow.
        // PORT NOTE: assemble bytes directly (not `write!`+`BStr`) so a
        // non-UTF-8 byte in `input_path` is emitted verbatim like Zig `{s}`,
        // not lossily replaced with U+FFFD by `BStr as Display`.
        macro_rules! render_into_entry {
            ($prefix:expr, $suffix:expr) => {{
                let prefix: &[u8] = $prefix;
                let suffix: &[u8] = $suffix;
                // PERF(port): was std.fmt.count + bufPrint/allocPrint stack-fallback — profile if hot.
                let count = prefix.len() + input_path.len() + suffix.len();
                if count < entry.code_buffer.len() {
                    let buf = &mut entry.code_buffer;
                    buf[..prefix.len()].copy_from_slice(prefix);
                    buf[prefix.len()..prefix.len() + input_path.len()]
                        .copy_from_slice(input_path);
                    buf[prefix.len() + input_path.len()..count].copy_from_slice(suffix);
                    entry.source = bun_ast::Source::init_path_string_interned_owned(
                        input_path,
                        entry.code_buffer[..count].to_vec(),
                    );
                } else {
                    let mut v: Vec<u8> = Vec::with_capacity(count);
                    v.extend_from_slice(prefix);
                    v.extend_from_slice(input_path);
                    v.extend_from_slice(suffix);
                    entry.source =
                        bun_ast::Source::init_path_string_interned_owned(input_path, v);
                }
            }};
        }

        if disable_css_imports {
            render_into_entry!(
                b"globalThis.Bun_disableCSSImports = true;\nimport boot from '",
                b"';\nboot(globalThis.__BUN_DATA__);"
            );
        } else {
            render_into_entry!(b"import boot from '", b"';\nboot(globalThis.__BUN_DATA__);");
        }

        entry.source.path.namespace = b"fallback-entry";
        Ok(())
    }
}

pub struct ClientEntryPoint {
    pub code_buffer: [u8; 8192],
    pub path_buffer: PathBuffer,
    pub source: bun_ast::Source,
}

impl Default for ClientEntryPoint {
    fn default() -> Self {
        Self {
            code_buffer: [0u8; 8192],
            path_buffer: PathBuffer::uninit(),
            source: bun_ast::Source::default(),
        }
    }
}

impl ClientEntryPoint {
    pub fn is_entry_point_path(extname: &[u8]) -> bool {
        strings::starts_with(b"entry.", extname)
    }

    // PORT NOTE: takes the lifetime-generic `bun_paths::fs::PathName<'_>` (not the
    // `'static`-field `bun_paths::fs::PathName<'static>`) so callers with a borrowed path
    // (e.g. `bun_runtime::filesystem_router::get_script_src_string`) needn't forge
    // `'static`. The body only copies `dir`/`base`/`ext` into `outbuffer`.
    pub fn generate_entry_point_path<'a>(
        outbuffer: &'a mut [u8],
        original_path: &bun_paths::fs::PathName<'_>,
    ) -> &'a [u8] {
        let joined_base_and_dir_parts: [&[u8]; 2] = [original_path.dir, original_path.base];
        // SAFETY: FileSystem singleton is initialized before bundling.
        let mut generated_path =
            Fs::FileSystem::get().abs_buf(&joined_base_and_dir_parts, outbuffer);

        // PORT NOTE: reshaped for borrowck — capture len, drop borrow, re-borrow outbuffer.
        let mut len = generated_path.len();
        outbuffer[len..len + b".entry".len()].copy_from_slice(b".entry");
        len += b".entry".len();
        generated_path = &outbuffer[..len];
        let _ = generated_path;
        outbuffer[len..len + original_path.ext.len()].copy_from_slice(original_path.ext);
        &outbuffer[..len + original_path.ext.len()]
    }

    pub fn decode_entry_point_path<'a>(
        outbuffer: &'a mut [u8],
        original_path: &Fs::PathName,
    ) -> &'a [u8] {
        let joined_base_and_dir_parts: [&[u8]; 2] = [original_path.dir, original_path.base];
        // SAFETY: FileSystem singleton is initialized before bundling.
        let generated_path = Fs::FileSystem::get().abs_buf(&joined_base_and_dir_parts, outbuffer);
        let len = generated_path.len();
        let mut original_ext = original_path.ext;
        if let Some(entry_i) = strings::index_of(original_path.ext, b"entry") {
            original_ext = &original_path.ext[entry_i + b"entry".len()..];
        }

        outbuffer[len..len + original_ext.len()].copy_from_slice(original_ext);

        &outbuffer[..len + original_ext.len()]
    }

    pub fn generate<TranspilerType>(
        &mut self,
        transpiler: &mut TranspilerType,
        original_path: &Fs::PathName,
        client: &[u8],
    ) -> Result<(), bun_core::Error>
    // TODO(port): narrow error set
    where
        // TODO(port): TranspilerType trait bound — body reads `.options.framework`.
        TranspilerType: TranspilerLike,
    {
        let entry = self;
        // This is *extremely* naive.
        // The basic idea here is this:
        // --
        // import * as EntryPoint from 'entry-point';
        // import boot from 'framework';
        // boot(EntryPoint);
        // --
        // We go through the steps of printing the code -- only to then parse/transpile it because
        // we want it to go through the linker and the rest of the transpilation process

        let dir_to_use: &[u8] = original_path.dir_with_trailing_slash();
        let disable_css_imports = transpiler
            .options()
            .framework
            .as_ref()
            .unwrap()
            .client_css_in_js
            != ClientCssInJs::AutoOnImportCss;

        // TODO(port): self-referential — `code` borrows `entry.code_buffer` and is stored into
        // `entry.source`. See note in FallbackEntryPoint::generate.
        let code: &[u8] = if disable_css_imports {
            let mut cursor = std::io::Cursor::new(&mut entry.code_buffer[..]);
            write!(
                &mut cursor,
                "globalThis.Bun_disableCSSImports = true;\n\
                 import boot from '{}';\n\
                 import * as EntryPoint from '{}{}';\n\
                 boot(EntryPoint);",
                BStr::new(client),
                BStr::new(dir_to_use),
                BStr::new(original_path.filename),
            )
            .map_err(|_| bun_core::err!("NoSpaceLeft"))?;
            let n = cursor.position() as usize;
            &entry.code_buffer[..n]
        } else {
            let mut cursor = std::io::Cursor::new(&mut entry.code_buffer[..]);
            write!(
                &mut cursor,
                "import boot from '{}';\n\
                 if ('setLoaded' in boot) boot.setLoaded(loaded);\n\
                 import * as EntryPoint from '{}{}';\n\
                 boot(EntryPoint);",
                BStr::new(client),
                BStr::new(dir_to_use),
                BStr::new(original_path.filename),
            )
            .map_err(|_| bun_core::err!("NoSpaceLeft"))?;
            let n = cursor.position() as usize;
            &entry.code_buffer[..n]
        };

        // `bun_paths::fs::PathName<'static>` → `bun_paths::fs::PathName<'static>`: field-identical
        // mirrors (see `#[repr(C)]` note on both); spell out the copy instead of a cast.
        let original_path_borrowed = bun_paths::fs::PathName {
            dir: original_path.dir,
            base: original_path.base,
            ext: original_path.ext,
            filename: original_path.filename,
        };
        // The generated label is written into the REUSED `entry.path_buffer`
        // scratch (overwritten by the next entry) and `code` re-borrows
        // `entry.code_buffer` — the Source owns copies of both (interned
        // path + `Cow::Owned` contents) so it never dangles.
        entry.source = bun_ast::Source::init_path_string_interned_owned(
            Self::generate_entry_point_path(&mut entry.path_buffer.0, &original_path_borrowed),
            code.to_vec(),
        );
        entry.source.path.namespace = b"client-entry";
        Ok(())
    }
}

#[derive(Default)]
pub struct ServerEntryPoint {
    /// The generated wrapper source for `bun:main`. Always a valid slice
    /// (either empty or owned by `bun.default_allocator`) so readers never
    /// see `undefined` memory regardless of the `generated` flag's state.
    pub contents: Box<[u8]>,
    pub generated: bool,
}

// `deinit` only freed `contents` and reset flags; with `Box<[u8]>` this is the
// auto-generated `Drop`, so no explicit impl is needed.

impl ServerEntryPoint {
    pub fn generate(
        entry: &mut ServerEntryPoint,
        is_hot_reload_enabled: bool,
        path_to_use: &[u8],
    ) -> Result<(), bun_core::Error> {
        // TODO(port): narrow error set
        // Use the global arena so this buffer's lifetime is decoupled
        // from whichever arena the caller's VM happens to be using; the
        // slice is read later from `getHardcodedModule` which outlives any
        // per-transpile arena.
        let code: Vec<u8> = 'brk: {
            if is_hot_reload_enabled {
                let mut v: Vec<u8> = Vec::new();
                write!(
                    &mut v,
                    "// @bun\n\
                     import * as start from '{}';\n\
                     var hmrSymbol = Symbol(\"BunServerHMR\");\n\
                     var entryNamespace = start;\n\
                     function isServerConfig(def) {{\n\
                     \x20  return def && def !== globalThis && (typeof def.fetch === 'function' || def.app != undefined) && typeof def.stop !== 'function';\n\
                     }}\n\
                     if (typeof entryNamespace?.then === 'function') {{\n\
                     \x20  entryNamespace = entryNamespace.then((entryNamespace) => {{\n\
                     \x20     var def = entryNamespace?.default;\n\
                     \x20     if (isServerConfig(def))  {{\n\
                     \x20       var server = globalThis[hmrSymbol];\n\
                     \x20       if (server) {{\n\
                     \x20          server.reload(def);\n\
                     \x20          console.debug(`Reloaded ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);\n\
                     \x20       }} else {{\n\
                     \x20          server = globalThis[hmrSymbol] = Bun.serve(def);\n\
                     \x20          console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);\n\
                     \x20       }}\n\
                     \x20     }}\n\
                     \x20  }}, reportError);\n\
                     }} else if (isServerConfig(entryNamespace?.default)) {{\n\
                     \x20  var server = globalThis[hmrSymbol];\n\
                     \x20  if (server) {{\n\
                     \x20     server.reload(entryNamespace.default);\n\
                     \x20     console.debug(`Reloaded ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);\n\
                     \x20  }} else {{\n\
                     \x20     server = globalThis[hmrSymbol] = Bun.serve(entryNamespace.default);\n\
                     \x20     console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);\n\
                     \x20  }}\n\
                     }}\n",
                    strings::format_escapes(path_to_use, strings::QuoteEscapeFormatFlags { quote_char: b'\'', ..Default::default() }),
                )
                .map_err(|_| bun_core::err!("FormatError"))?;
                break 'brk v;
            }
            let mut v: Vec<u8> = Vec::new();
            write!(
                &mut v,
                "// @bun\n\
                 import * as start from \"{}\";\n\
                 var entryNamespace = start;\n\
                 function isServerConfig(def) {{\n\
                 \x20  return def && def !== globalThis && (typeof def.fetch === 'function' || def.app != undefined) && typeof def.stop !== 'function';\n\
                 }}\n\
                 if (typeof entryNamespace?.then === 'function') {{\n\
                 \x20  entryNamespace = entryNamespace.then((entryNamespace) => {{\n\
                 \x20     if (isServerConfig(entryNamespace?.default))  {{\n\
                 \x20       const server = Bun.serve(entryNamespace.default);\n\
                 \x20       console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);\n\
                 \x20     }}\n\
                 \x20  }}, reportError);\n\
                 }} else if (isServerConfig(entryNamespace?.default)) {{\n\
                 \x20  const server = Bun.serve(entryNamespace.default);\n\
                 \x20  console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);\n\
                 }}\n",
                strings::format_escapes(path_to_use, strings::QuoteEscapeFormatFlags { quote_char: b'"', ..Default::default() }),
            )
            .map_err(|_| bun_core::err!("FormatError"))?;
            v
        };

        // Free the previous buffer on regenerate (hot reload) instead of
        // leaking it. `contents` is either "" or a prior allocPrint result.
        // (Handled implicitly: assigning to `Box<[u8]>` drops the old one.)
        entry.contents = code.into_boxed_slice();
        entry.generated = true;
        Ok(())
    }
}

// This is not very fast. The idea is: we want to generate a unique entry point
// per macro function export that registers the macro Registering the macro
// happens in VirtualMachine We "register" it which just marks the JSValue as
// protected. This is mostly a workaround for being unable to call ESM exported
// functions from C++. When that is resolved, we should remove this.
pub struct MacroEntryPoint {
    pub code_buffer: [u8; MAX_PATH_BYTES * 2 + 500],
    pub output_code_buffer: [u8; MAX_PATH_BYTES * 8 + 500],
    pub source: bun_ast::Source,
}

impl Default for MacroEntryPoint {
    fn default() -> Self {
        Self {
            code_buffer: [0u8; MAX_PATH_BYTES * 2 + 500],
            output_code_buffer: [0u8; MAX_PATH_BYTES * 8 + 500],
            source: bun_ast::Source::default(),
        }
    }
}

impl MacroEntryPoint {
    pub fn generate_id(
        entry_path: &[u8],
        function_name: &[u8],
        buf: &mut [u8],
        len: &mut u32,
    ) -> i32 {
        let mut hasher = Wyhash11::init(0);
        hasher.update(js_ast::Macro::NAMESPACE_WITH_COLON);
        hasher.update(entry_path);
        hasher.update(function_name);
        let hash = hasher.final_();
        let fmt = bun_fmt::hex_int_lower::<16>(hash);

        // PORT NOTE: reshaped for borrowck — capture cursor position, drop &mut
        // borrow, then re-borrow `buf` immutably.
        let n = {
            let mut cursor = std::io::Cursor::new(&mut buf[..]);
            write!(
                &mut cursor,
                concat!("{}", "//{}.js"),
                BStr::new(js_ast::Macro::NAMESPACE_WITH_COLON),
                fmt,
            )
            .expect("unreachable");
            cursor.position() as usize
        };
        let specifier: &[u8] = &buf[..n];
        *len = specifier.len() as u32;

        Self::generate_id_from_specifier(specifier)
    }

    pub fn generate_id_from_specifier(specifier: &[u8]) -> i32 {
        // Same-size bitcast u32 → i32 (matches Zig `@bitCast`).
        (bun_wyhash::hash(specifier) as u32) as i32
    }

    // TODO(port): bun_ast::Macro + bun_resolver::fs::PathName —
    // see `generate_id`.
    pub fn generate(
        entry: &mut MacroEntryPoint,
        _: &mut Transpiler,
        import_path: &Fs::PathName,
        function_name: &[u8],
        macro_id: i32,
        macro_label_: &[u8],
    ) -> Result<(), bun_core::Error> {
        // TODO(port): narrow error set
        let dir_to_use: &[u8] = if import_path.dir.is_empty() {
            b""
        } else {
            import_path.dir_with_trailing_slash()
        };
        // PORT NOTE: reshaped for borrowck — capture the label length, write the
        // body via a scoped &mut borrow, then re-borrow `code_buffer` immutably
        // for the (label, code) slices passed to `init_path_string`.
        let label_len = macro_label_.len();
        entry.code_buffer[..label_len].copy_from_slice(macro_label_);

        let code_len: usize = 'brk: {
            if import_path.base == b"bun" {
                let mut cursor = std::io::Cursor::new(&mut entry.code_buffer[label_len..]);
                write!(
                    &mut cursor,
                    "//Auto-generated file\n\
                     var Macros;\n\
                     try {{\n\
                     \x20 Macros = globalThis.Bun;\n\
                     }} catch (err) {{\n\
                     \x20  console.error(\"Error importing macro\");\n\
                     \x20  throw err;\n\
                     }}\n\
                     const macro = Macros['{}'];\n\
                     if (!macro) {{\n\
                     \x20 throw new Error(\"Macro '{}' not found in 'bun'\");\n\
                     }}\n\
                     \n\
                     Bun.registerMacro({}, macro);",
                    BStr::new(function_name),
                    BStr::new(function_name),
                    macro_id,
                )
                .map_err(|_| bun_core::err!("NoSpaceLeft"))?;
                break 'brk cursor.position() as usize;
            }

            let mut cursor = std::io::Cursor::new(&mut entry.code_buffer[label_len..]);
            write!(
                &mut cursor,
                "//Auto-generated file\n\
                 var Macros;\n\
                 try {{\n\
                 \x20 Macros = await import('{}{}');\n\
                 }} catch (err) {{\n\
                 \x20  console.error(\"Error importing macro\");\n\
                 \x20  throw err;\n\
                 }}\n\
                 if (!('{}' in Macros)) {{\n\
                 \x20 throw new Error(\"Macro '{}' not found in '{}{}'\");\n\
                 }}\n\
                 \n\
                 Bun.registerMacro({}, Macros['{}']);",
                bun_fmt::fmt_path_u8(
                    dir_to_use,
                    bun_fmt::PathFormatOptions {
                        escape_backslashes: true,
                        ..Default::default()
                    }
                ),
                bun_fmt::fmt_path_u8(
                    import_path.filename,
                    bun_fmt::PathFormatOptions {
                        escape_backslashes: true,
                        ..Default::default()
                    }
                ),
                BStr::new(function_name),
                BStr::new(function_name),
                bun_fmt::fmt_path_u8(
                    dir_to_use,
                    bun_fmt::PathFormatOptions {
                        escape_backslashes: true,
                        ..Default::default()
                    }
                ),
                bun_fmt::fmt_path_u8(
                    import_path.filename,
                    bun_fmt::PathFormatOptions {
                        escape_backslashes: true,
                        ..Default::default()
                    }
                ),
                macro_id,
                BStr::new(function_name),
            )
            .map_err(|_| bun_core::err!("NoSpaceLeft"))?;
            cursor.position() as usize
        };

        // `macro_label`/`code` re-borrow the reused `entry.code_buffer` scratch —
        // the Source owns copies of both (interned path + `Cow::Owned` contents)
        // so it never dangles (resolves the old self-referential TODO).
        let macro_label: &[u8] = &entry.code_buffer[..label_len];
        let code: &[u8] = &entry.code_buffer[label_len..label_len + code_len];
        entry.source = bun_ast::Source::init_path_string_interned_owned(macro_label, code.to_vec());
        // `Path::init` already set `text = macro_label`; only override namespace.
        entry.source.path.namespace = js_ast::Macro::NAMESPACE;
        Ok(())
    }
}

// TODO(port): `TranspilerLike` is a placeholder for the duck-typed
// `comptime TranspilerType: type` param used by FallbackEntryPoint/ClientEntryPoint.
// Replace with the concrete `Transpiler` type or a real trait.
pub trait TranspilerLike {
    fn options(&self) -> &crate::options::Options<'_>;
}

use crate::options::ClientCssInJs;

// ported from: src/bundler/entry_points.zig