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
use crate::mal_prelude::*;
use bun_collections::MultiArrayList;
use bun_core::string_joiner::{StringJoiner, Watcher};
use bun_sourcemap::{LineColumnOffset, LineColumnOffsetOptional};
use crate::chunk::IntermediateOutput;
use crate::linker_context_mod::{GenerateChunkCtx, LinkerOptionsMode};
use crate::thread_pool;
use crate::{Chunk, CompileResultForSourceMap, Index, options};
/// This runs after we've already populated the compile results
pub fn post_process_css_chunk(
ctx: GenerateChunkCtx,
worker: &mut thread_pool::Worker,
chunk: &mut Chunk,
) -> Result<(), bun_core::Error> {
// TODO(port): narrow error set
let c = ctx.c();
// TODO(port): worker.arena is a per-worker arena — thread `&'bump Bump`.
// PORT NOTE: avoid FRU `..Default::default()` — StringJoiner impls Drop (E0509).
let mut j = StringJoiner::default();
j.watcher = Watcher {
input: chunk.unique_key,
..Default::default()
};
let mut line_offset: LineColumnOffsetOptional =
if c.options.source_maps != options::SourceMapOption::None {
LineColumnOffsetOptional::Value(LineColumnOffset::default())
} else {
LineColumnOffsetOptional::Null
};
let mut newline_before_comment = false;
// TODO: css banner
// if len(c.options.CSSBanner) > 0 {
// prevOffset.AdvanceString(c.options.CSSBanner)
// j.AddString(c.options.CSSBanner)
// prevOffset.AdvanceString("\n")
// j.AddString("\n")
// }
// TODO: (this is where we would put the imports)
// Generate any prefix rules now
// (THIS SHOULD BE SET WHEN GENERATING PREFIX RULES!)
// newline_before_comment = true;
// TODO: meta
// Concatenate the generated CSS chunks together
let compile_results = &chunk.compile_results_for_chunk;
let mut compile_results_for_source_map: MultiArrayList<CompileResultForSourceMap> =
MultiArrayList::default();
bun_core::handle_oom(compile_results_for_source_map.set_capacity(compile_results.len()));
let sources: &[bun_ast::Source] = c.parse_graph().input_files.items_source();
for compile_result in compile_results.iter() {
let source_index = compile_result.source_index();
if c.options.mode == LinkerOptionsMode::Bundle
&& !c.options.minify_whitespace
&& Index::init(source_index).is_valid()
{
if newline_before_comment {
j.push_static(b"\n");
line_offset.advance(b"\n");
}
let pretty: &[u8] = sources[source_index as usize].path.pretty;
j.push_static(b"/* ");
line_offset.advance(b"/* ");
// A `*/` in the path would terminate the comment early and let the
// rest of the path be parsed as CSS in the bundled output.
if bun_core::strings::contains(pretty, b"*/") {
let mut escaped = Vec::with_capacity(pretty.len() + 1);
for &byte in pretty {
if byte == b'/' && escaped.last() == Some(&b'*') {
escaped.push(b'\\');
}
escaped.push(byte);
}
line_offset.advance(&escaped);
j.push_owned(escaped.into_boxed_slice());
} else {
j.push_static(pretty);
line_offset.advance(pretty);
}
j.push_static(b" */\n");
line_offset.advance(b" */\n");
}
if !compile_result.code().is_empty() {
newline_before_comment = true;
}
// Save the offset to the start of the stored JavaScript
// PORT NOTE: Zig `j.push(.., bun.default_allocator)` — code() borrows from
// compile_results which outlives the joiner; treat as static (no copy/free).
j.push_static(compile_result.code());
if let Some(source_map_chunk) = compile_result.source_map_chunk() {
if c.options.source_maps != options::SourceMapOption::None {
bun_core::handle_oom(compile_results_for_source_map.append(
CompileResultForSourceMap {
// SAFETY: bitwise alias of `chunk.compile_results_for_chunk`
// (read-only and outlives this fn); see `postProcessJSChunk.rs`.
source_map_chunk: unsafe { source_map_chunk.alias() },
// Zig reads `.value` payload directly — guaranteed `Value` here
// because `source_maps != None` implies `line_offset` was
// initialised to `Value(_)` above.
generated_offset: match line_offset {
LineColumnOffsetOptional::Value(v) => v,
LineColumnOffsetOptional::Null => unreachable!(),
},
source_index: compile_result.source_index(),
},
));
}
line_offset.reset();
} else {
line_offset.advance(compile_result.code());
}
}
// Make sure the file ends with a newline
j.ensure_newline_at_end();
// if c.options.UnsupportedCSSFeatures.Has(compat.InlineStyle) {
// slashTag = ""
// }
// c.maybeAppendLegalComments(c.options.LegalComments, legalCommentList, chunk, &j, slashTag)
// if len(c.options.CSSFooter) > 0 {
// j.AddString(c.options.CSSFooter)
// j.AddString("\n")
// }
// SAFETY: `worker.arena` set by `Worker::create`, outlives the worker step.
let alloc = worker.arena();
// SAFETY: every borrowed node in `j` points into `chunk.compile_results_for_chunk`
// (filled in place before post-processing, never reassigned afterwards), graph
// source paths (`Path<'static>`), or `'static` literals; `watcher.input` is
// `chunk.unique_key` (`&'static`). All of these outlive the joiner stored in
// `chunk.intermediate_output`, which is only read while the chunk and the linker
// graph are alive.
let mut j = unsafe { j.detach_lifetime() };
chunk.intermediate_output =
bun_core::handle_oom(c.break_output_into_pieces(alloc, &mut j, ctx.chunks.len() as u32));
// TODO: meta contents
chunk.isolated_hash = c.generate_isolated_hash(chunk);
// chunk.flags.is_executable = is_executable;
if c.options.source_maps != options::SourceMapOption::None {
let can_have_shifts = matches!(chunk.intermediate_output, IntermediateOutput::Pieces(_));
// Copy the `ParentRef` out (not `c.resolver()`) so `output_dir`
// borrows the local, not `c`, avoiding the split-borrow with
// `c.generate_source_map_for_chunk(&mut self, …)` below.
let resolver = c.resolver.expect("resolver set in load()");
let output_dir = &resolver.opts.output_dir;
chunk.output_source_map = c.generate_source_map_for_chunk(
chunk.isolated_hash,
worker,
&compile_results_for_source_map,
output_dir,
can_have_shifts,
)?;
}
Ok(())
}
// ported from: src/bundler/linker_context/postProcessCSSChunk.zig