rolldown 0.1.0-aarch64-linux-gnu

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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +152 -0
  4. data/ext/rolldown/extconf.rb +123 -0
  5. data/ext/rolldown/include/rolldown.h +54 -0
  6. data/ext/rolldown/rolldown.c +130 -0
  7. data/lib/rolldown/3.2/rolldown.so +0 -0
  8. data/lib/rolldown/3.3/rolldown.so +0 -0
  9. data/lib/rolldown/3.4/rolldown.so +0 -0
  10. data/lib/rolldown/4.0/rolldown.so +0 -0
  11. data/lib/rolldown/asset.rb +33 -0
  12. data/lib/rolldown/backend.rb +36 -0
  13. data/lib/rolldown/build_result.rb +106 -0
  14. data/lib/rolldown/chunk.rb +70 -0
  15. data/lib/rolldown/diagnostic.rb +56 -0
  16. data/lib/rolldown/errors.rb +11 -0
  17. data/lib/rolldown/options.rb +152 -0
  18. data/lib/rolldown/version.rb +5 -0
  19. data/lib/rolldown.rb +50 -0
  20. data/licenses/README.md +8 -0
  21. data/licenses/rolldown-MIT.txt +25 -0
  22. data/licenses/rolldown-THIRD-PARTY.txt +33 -0
  23. data/rolldown.gemspec +43 -0
  24. data/rust/Cargo.lock +3086 -0
  25. data/rust/Cargo.toml +37 -0
  26. data/rust/build.rs +55 -0
  27. data/rust/cbindgen.toml +24 -0
  28. data/rust/rustfmt.toml +3 -0
  29. data/rust/src/build.rs +39 -0
  30. data/rust/src/lib.rs +92 -0
  31. data/rust/src/modules.rs +89 -0
  32. data/rust/src/options.rs +251 -0
  33. data/rust/src/payload.rs +104 -0
  34. data/rust/src/result.rs +49 -0
  35. data/sig/rolldown/asset.rbs +23 -0
  36. data/sig/rolldown/backend.rbs +26 -0
  37. data/sig/rolldown/build_result.rbs +47 -0
  38. data/sig/rolldown/chunk.rbs +39 -0
  39. data/sig/rolldown/diagnostic.rbs +35 -0
  40. data/sig/rolldown/errors.rbs +24 -0
  41. data/sig/rolldown/options.rbs +61 -0
  42. data/sig/rolldown/version.rbs +5 -0
  43. data/sig/rolldown.rbs +12 -0
  44. metadata +92 -0
data/rust/Cargo.toml ADDED
@@ -0,0 +1,37 @@
1
+ [workspace]
2
+
3
+ [package]
4
+ name = "rolldown-ruby-ffi"
5
+ version = "0.1.0"
6
+ edition = "2021"
7
+ authors = ["Marco Roth <marco.roth@intergga.ch>"]
8
+ description = "C FFI bindings for rolldown, used by the rolldown gem"
9
+ license = "MIT"
10
+ repository = "https://github.com/marcoroth/rolldown-ruby"
11
+ publish = false
12
+
13
+ [lib]
14
+ name = "rolldown_ffi"
15
+ path = "src/lib.rs"
16
+ crate-type = ["cdylib", "staticlib", "rlib"]
17
+
18
+ [dependencies]
19
+ rolldown = "=1.2.6"
20
+ rolldown_common = "=1.2.6"
21
+ rolldown_error = "=1.2.6"
22
+ rolldown_plugin = "=1.2.6"
23
+ arcstr = "1"
24
+ rolldown_utils = "=1.2.6"
25
+ serde = { version = "1", features = ["derive"] }
26
+ serde_json = "1"
27
+ tokio = { version = "1", features = ["rt-multi-thread"] }
28
+
29
+ [build-dependencies]
30
+ cbindgen = "0.28"
31
+
32
+ [profile.release]
33
+ opt-level = 3
34
+ lto = "fat"
35
+ codegen-units = 1
36
+ strip = "symbols"
37
+ panic = "unwind"
data/rust/build.rs ADDED
@@ -0,0 +1,55 @@
1
+ use std::env;
2
+ use std::fs;
3
+ use std::path::PathBuf;
4
+
5
+ fn main() {
6
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
7
+ let header_path = PathBuf::from(&crate_dir).join("../ext/rolldown/include/rolldown.h");
8
+
9
+ if let Ok(bindings) = cbindgen::generate(&crate_dir) {
10
+ if let Some(parent) = header_path.parent() {
11
+ let _ = std::fs::create_dir_all(parent);
12
+ }
13
+
14
+ bindings.write_to_file(&header_path);
15
+ }
16
+
17
+ let lock_path = PathBuf::from(&crate_dir).join("Cargo.lock");
18
+
19
+ println!("cargo:rerun-if-changed={}", lock_path.display());
20
+
21
+ println!(
22
+ "cargo:rerun-if-changed={}",
23
+ PathBuf::from(&crate_dir).join("src").display()
24
+ );
25
+
26
+ println!(
27
+ "cargo:rerun-if-changed={}",
28
+ PathBuf::from(&crate_dir).join("cbindgen.toml").display()
29
+ );
30
+
31
+ println!(
32
+ "cargo:rustc-env=ROLLDOWN_VERSION={}",
33
+ locked_version(&lock_path, "rolldown")
34
+ );
35
+ }
36
+
37
+ fn locked_version(lock_path: &PathBuf, package: &str) -> String {
38
+ let Ok(lock) = fs::read_to_string(lock_path) else {
39
+ return "unknown".to_string();
40
+ };
41
+
42
+ let mut lines = lock.lines();
43
+
44
+ while let Some(line) = lines.next() {
45
+ if line.trim() != format!("name = \"{package}\"") {
46
+ continue;
47
+ }
48
+
49
+ if let Some(version) = lines.next().and_then(|next| next.trim().strip_prefix("version = ")) {
50
+ return version.trim_matches('"').to_string();
51
+ }
52
+ }
53
+
54
+ "unknown".to_string()
55
+ }
@@ -0,0 +1,24 @@
1
+ language = "C"
2
+ header = """/* Generated by cbindgen, do not edit manually */
3
+
4
+ #include <stdbool.h>
5
+ #include <stdint.h>
6
+ #include <stddef.h>"""
7
+ include_guard = "ROLLDOWN_H"
8
+ no_includes = true
9
+
10
+ [export]
11
+ include = [
12
+ "RolldownResult",
13
+ "RolldownErrorCode",
14
+ ]
15
+
16
+ [enum]
17
+ rename_variants = "ScreamingSnakeCase"
18
+ prefix_with_name = true
19
+
20
+ [fn]
21
+ sort_by = "None"
22
+
23
+ [parse]
24
+ parse_deps = false
data/rust/rustfmt.toml ADDED
@@ -0,0 +1,3 @@
1
+ tab_spaces = 2
2
+ max_width = 120
3
+ edition = "2021"
data/rust/src/build.rs ADDED
@@ -0,0 +1,39 @@
1
+ use rolldown::Bundler;
2
+
3
+ use crate::options::Options;
4
+ use crate::payload::BuildPayload;
5
+ use crate::result::{RolldownErrorCode, RolldownResult};
6
+
7
+ pub fn build(options_json: &str) -> RolldownResult {
8
+ let (options, plugins) = match Options::parse(options_json).and_then(Options::into_parts) {
9
+ Ok(parts) => parts,
10
+ Err(failure) => return failure,
11
+ };
12
+
13
+ let mut bundler = match Bundler::with_plugins(options, plugins) {
14
+ Ok(bundler) => bundler,
15
+ Err(batched) => return payload(BuildPayload::from_failure(batched)),
16
+ };
17
+
18
+ let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
19
+ Ok(runtime) => runtime,
20
+ Err(error) => {
21
+ return RolldownResult::error(
22
+ RolldownErrorCode::Internal,
23
+ format!("could not start a runtime: {error}"),
24
+ )
25
+ }
26
+ };
27
+
28
+ match runtime.block_on(bundler.generate()) {
29
+ Ok(output) => payload(BuildPayload::from_output(output)),
30
+ Err(batched) => payload(BuildPayload::from_failure(batched)),
31
+ }
32
+ }
33
+
34
+ fn payload(payload: BuildPayload) -> RolldownResult {
35
+ match serde_json::to_string(&payload) {
36
+ Ok(json) => RolldownResult::value(json),
37
+ Err(error) => RolldownResult::error(RolldownErrorCode::Internal, error.to_string()),
38
+ }
39
+ }
data/rust/src/lib.rs ADDED
@@ -0,0 +1,92 @@
1
+ use std::ffi::{CStr, CString};
2
+ use std::os::raw::c_char;
3
+ use std::panic::{catch_unwind, AssertUnwindSafe};
4
+
5
+ mod build;
6
+ mod modules;
7
+ mod options;
8
+ mod payload;
9
+ mod result;
10
+
11
+ use result::{RolldownErrorCode, RolldownResult};
12
+
13
+ /// # Safety
14
+ ///
15
+ /// `options_json` must be a valid, nul-terminated UTF-8 string.
16
+ #[no_mangle]
17
+ pub unsafe extern "C" fn rolldown_build(options_json: *const c_char) -> RolldownResult {
18
+ let Some(options) = borrow(options_json) else {
19
+ return RolldownResult::error(RolldownErrorCode::Encoding, "options were not valid UTF-8");
20
+ };
21
+
22
+ match catch_unwind(AssertUnwindSafe(|| build::build(options))) {
23
+ Ok(result) => result,
24
+ Err(payload) => RolldownResult::error(RolldownErrorCode::Panic, panic_message(&payload)),
25
+ }
26
+ }
27
+
28
+ #[no_mangle]
29
+ pub extern "C" fn rolldown_version() -> *mut c_char {
30
+ into_c_string(env!("CARGO_PKG_VERSION"))
31
+ }
32
+
33
+ #[no_mangle]
34
+ pub extern "C" fn rolldown_rolldown_version() -> *mut c_char {
35
+ into_c_string(env!("ROLLDOWN_VERSION"))
36
+ }
37
+
38
+ /// # Safety
39
+ ///
40
+ /// `value` must be a pointer this library returned, and must not be used afterwards.
41
+ #[no_mangle]
42
+ pub unsafe extern "C" fn rolldown_string_free(value: *mut c_char) {
43
+ if value.is_null() {
44
+ return;
45
+ }
46
+
47
+ drop(CString::from_raw(value));
48
+ }
49
+
50
+ /// # Safety
51
+ ///
52
+ /// `result` must be one this library returned, and must not be used afterwards.
53
+ #[no_mangle]
54
+ pub unsafe extern "C" fn rolldown_result_free(result: RolldownResult) {
55
+ if !result.value.is_null() {
56
+ drop(CString::from_raw(result.value));
57
+ }
58
+
59
+ if !result.error.is_null() {
60
+ drop(CString::from_raw(result.error));
61
+ }
62
+ }
63
+
64
+ #[no_mangle]
65
+ pub extern "C" fn rolldown_panic_for_test() -> RolldownResult {
66
+ match catch_unwind(AssertUnwindSafe(|| -> RolldownResult { panic!("a deliberate panic") })) {
67
+ Ok(result) => result,
68
+ Err(payload) => RolldownResult::error(RolldownErrorCode::Panic, panic_message(&payload)),
69
+ }
70
+ }
71
+
72
+ unsafe fn borrow<'a>(value: *const c_char) -> Option<&'a str> {
73
+ if value.is_null() {
74
+ return None;
75
+ }
76
+
77
+ CStr::from_ptr(value).to_str().ok()
78
+ }
79
+
80
+ fn panic_message(payload: &Box<dyn std::any::Any + Send>) -> String {
81
+ payload
82
+ .downcast_ref::<&str>()
83
+ .map(|message| (*message).to_string())
84
+ .or_else(|| payload.downcast_ref::<String>().cloned())
85
+ .unwrap_or_else(|| "rolldown panicked".to_string())
86
+ }
87
+
88
+ fn into_c_string(value: &str) -> *mut c_char {
89
+ CString::new(value)
90
+ .unwrap_or_else(|_| CString::new("unknown").expect("a string with no interior nul"))
91
+ .into_raw()
92
+ }
@@ -0,0 +1,89 @@
1
+ use std::borrow::Cow;
2
+ use std::collections::BTreeMap;
3
+ use std::path::{Component, Path, PathBuf};
4
+
5
+ use arcstr::ArcStr;
6
+ use rolldown_plugin::{
7
+ HookLoadArgs, HookLoadOutput, HookLoadReturn, HookResolveIdArgs, HookResolveIdOutput, HookResolveIdReturn, HookUsage,
8
+ Plugin, PluginContext, SharedLoadPluginContext,
9
+ };
10
+
11
+ #[derive(Debug)]
12
+ pub struct VirtualModules {
13
+ sources: BTreeMap<String, ArcStr>,
14
+ }
15
+
16
+ impl VirtualModules {
17
+ pub fn new(sources: BTreeMap<String, String>) -> Self {
18
+ Self {
19
+ sources: sources.into_iter().map(|(id, code)| (id, ArcStr::from(code))).collect(),
20
+ }
21
+ }
22
+ }
23
+
24
+ impl Plugin for VirtualModules {
25
+ fn name(&self) -> Cow<'static, str> {
26
+ Cow::Borrowed("rolldown-ruby:modules")
27
+ }
28
+
29
+ fn register_hook_usage(&self) -> HookUsage {
30
+ HookUsage::ResolveId | HookUsage::Load
31
+ }
32
+
33
+ async fn resolve_id(&self, _ctx: &PluginContext, args: &HookResolveIdArgs<'_>) -> HookResolveIdReturn {
34
+ if self.sources.contains_key(args.specifier) {
35
+ return Ok(Some(HookResolveIdOutput {
36
+ id: ArcStr::from(args.specifier),
37
+ ..Default::default()
38
+ }));
39
+ }
40
+
41
+ let Some(importer) = args.importer else {
42
+ return Ok(None);
43
+ };
44
+
45
+ if !self.sources.contains_key(importer) || !args.specifier.starts_with('.') {
46
+ return Ok(None);
47
+ }
48
+
49
+ let Some(directory) = Path::new(importer).parent() else {
50
+ return Ok(None);
51
+ };
52
+
53
+ let Some(resolved) = normalize(&directory.join(args.specifier)) else {
54
+ return Ok(None);
55
+ };
56
+
57
+ Ok(Some(HookResolveIdOutput {
58
+ id: ArcStr::from(resolved),
59
+ ..Default::default()
60
+ }))
61
+ }
62
+
63
+ async fn load(&self, _ctx: SharedLoadPluginContext, args: &HookLoadArgs<'_>) -> HookLoadReturn {
64
+ let Some(code) = self.sources.get(args.id) else {
65
+ return Ok(None);
66
+ };
67
+
68
+ Ok(Some(HookLoadOutput {
69
+ code: code.clone(),
70
+ ..Default::default()
71
+ }))
72
+ }
73
+ }
74
+
75
+ fn normalize(path: &Path) -> Option<String> {
76
+ let mut normalized = PathBuf::new();
77
+
78
+ for component in path.components() {
79
+ match component {
80
+ Component::CurDir => {}
81
+ Component::ParentDir => {
82
+ normalized.pop();
83
+ }
84
+ other => normalized.push(other),
85
+ }
86
+ }
87
+
88
+ normalized.to_str().map(ToOwned::to_owned)
89
+ }
@@ -0,0 +1,251 @@
1
+ use std::path::PathBuf;
2
+
3
+ use std::collections::BTreeMap;
4
+ use std::sync::Arc;
5
+
6
+ use rolldown::{
7
+ AddonOutputOption, AssetFilenamesOutputOption, BundlerOptions, ChunkFilenamesOutputOption, InputItem, IsExternal,
8
+ LegalComments, OutputExports, OutputFormat, Platform, RawMinifyOptions, SourceMapType, TreeshakeOptions,
9
+ };
10
+
11
+ use rolldown_common::ModuleType;
12
+ use rolldown_plugin::Pluginable;
13
+ use rolldown_utils::pattern_filter::StringOrRegex;
14
+ use serde::Deserialize;
15
+
16
+ use crate::modules::VirtualModules;
17
+ use crate::result::{RolldownErrorCode, RolldownResult};
18
+
19
+ #[derive(Deserialize, Default)]
20
+ #[serde(default, deny_unknown_fields)]
21
+ pub struct Options {
22
+ pub input: Vec<Input>,
23
+ pub cwd: Option<String>,
24
+ pub external: Vec<String>,
25
+ pub platform: Option<String>,
26
+ pub treeshake: Option<bool>,
27
+ pub module_types: BTreeMap<String, String>,
28
+ pub modules: BTreeMap<String, String>,
29
+ pub transform: Transform,
30
+ pub shim_missing_exports: Option<bool>,
31
+ pub output: Output,
32
+ }
33
+
34
+ #[derive(Deserialize, Default)]
35
+ #[serde(default, deny_unknown_fields)]
36
+ pub struct Transform {
37
+ pub define: BTreeMap<String, String>,
38
+ }
39
+
40
+ #[derive(Deserialize, Default)]
41
+ #[serde(default, deny_unknown_fields)]
42
+ pub struct Output {
43
+ pub dir: Option<String>,
44
+ pub file: Option<String>,
45
+ pub format: Option<String>,
46
+ pub name: Option<String>,
47
+ pub exports: Option<String>,
48
+ pub sourcemap: Option<SourceMap>,
49
+ pub minify: Option<Minify>,
50
+ pub banner: Option<String>,
51
+ pub footer: Option<String>,
52
+ pub intro: Option<String>,
53
+ pub outro: Option<String>,
54
+ pub entry_file_names: Option<String>,
55
+ pub chunk_file_names: Option<String>,
56
+ pub asset_file_names: Option<String>,
57
+ pub keep_names: Option<bool>,
58
+ pub legal_comments: Option<String>,
59
+ pub es_module: Option<bool>,
60
+ }
61
+
62
+ #[derive(Deserialize)]
63
+ #[serde(untagged)]
64
+ pub enum Input {
65
+ Bare(String),
66
+ Named { name: Option<String>, import: String },
67
+ }
68
+
69
+ #[derive(Deserialize)]
70
+ #[serde(untagged)]
71
+ pub enum SourceMap {
72
+ Toggle(bool),
73
+ Kind(String),
74
+ }
75
+
76
+ #[derive(Deserialize)]
77
+ #[serde(untagged)]
78
+ pub enum Minify {
79
+ Toggle(bool),
80
+ Kind(String),
81
+ }
82
+
83
+ impl Options {
84
+ pub fn parse(json: &str) -> Result<Self, RolldownResult> {
85
+ serde_json::from_str(json).map_err(|error| RolldownResult::error(RolldownErrorCode::Option, error.to_string()))
86
+ }
87
+
88
+ pub fn into_parts(mut self) -> Result<(BundlerOptions, Vec<Arc<dyn Pluginable>>), RolldownResult> {
89
+ let modules = std::mem::take(&mut self.modules);
90
+
91
+ let plugins: Vec<Arc<dyn Pluginable>> = if modules.is_empty() {
92
+ Vec::new()
93
+ } else {
94
+ vec![Arc::new(VirtualModules::new(modules))]
95
+ };
96
+
97
+ Ok((self.into_bundler_options()?, plugins))
98
+ }
99
+
100
+ fn into_bundler_options(self) -> Result<BundlerOptions, RolldownResult> {
101
+ if self.input.is_empty() {
102
+ return Err(RolldownResult::error(RolldownErrorCode::Option, "input is required"));
103
+ }
104
+
105
+ let input = self
106
+ .input
107
+ .into_iter()
108
+ .map(|entry| match entry {
109
+ Input::Bare(import) => InputItem { name: None, import },
110
+ Input::Named { name, import } => InputItem { name, import },
111
+ })
112
+ .collect();
113
+
114
+ let output = self.output;
115
+
116
+ Ok(BundlerOptions {
117
+ input: Some(input),
118
+ cwd: self.cwd.map(PathBuf::from),
119
+ platform: self.platform.as_deref().map(platform).transpose()?,
120
+ external: external(self.external),
121
+ define: (!self.transform.define.is_empty()).then(|| self.transform.define.into_iter().collect()),
122
+ treeshake: self
123
+ .treeshake
124
+ .map_or(TreeshakeOptions::Boolean(true), TreeshakeOptions::Boolean),
125
+ shim_missing_exports: self.shim_missing_exports,
126
+ module_types: module_types(self.module_types)?.map(|pairs| pairs.into_iter().collect()),
127
+ dir: output.dir,
128
+ file: output.file,
129
+ format: output.format.as_deref().map(format).transpose()?,
130
+ name: output.name,
131
+ exports: output.exports.as_deref().map(exports).transpose()?,
132
+ sourcemap: sourcemap(output.sourcemap)?,
133
+ minify: minify(output.minify)?,
134
+ banner: addon(output.banner),
135
+ footer: addon(output.footer),
136
+ intro: addon(output.intro),
137
+ outro: addon(output.outro),
138
+ entry_filenames: output.entry_file_names.map(ChunkFilenamesOutputOption::String),
139
+ chunk_filenames: output.chunk_file_names.map(ChunkFilenamesOutputOption::String),
140
+ asset_filenames: output.asset_file_names.map(AssetFilenamesOutputOption::String),
141
+ keep_names: output.keep_names,
142
+ legal_comments: output.legal_comments.as_deref().map(legal_comments).transpose()?,
143
+ es_module: output.es_module.map(Into::into),
144
+ ..Default::default()
145
+ })
146
+ }
147
+ }
148
+
149
+ fn module_types(given: BTreeMap<String, String>) -> Result<Option<Vec<(String, ModuleType)>>, RolldownResult> {
150
+ if given.is_empty() {
151
+ return Ok(None);
152
+ }
153
+
154
+ given
155
+ .into_iter()
156
+ .map(|(extension, name)| {
157
+ let module_type = ModuleType::from_known_str(&name).map_err(|_| {
158
+ unknown(
159
+ "module type",
160
+ &name,
161
+ "js, jsx, ts, tsx, json, text, base64, dataurl, binary, empty, css, asset or copy",
162
+ )
163
+ })?;
164
+
165
+ Ok((extension, module_type))
166
+ })
167
+ .collect::<Result<Vec<_>, _>>()
168
+ .map(Some)
169
+ }
170
+
171
+ fn external(patterns: Vec<String>) -> Option<IsExternal> {
172
+ if patterns.is_empty() {
173
+ return None;
174
+ }
175
+
176
+ Some(IsExternal::StringOrRegex(
177
+ patterns.into_iter().map(StringOrRegex::String).collect(),
178
+ ))
179
+ }
180
+
181
+ fn addon(value: Option<String>) -> Option<AddonOutputOption> {
182
+ value.map(|text| AddonOutputOption::String(Some(text)))
183
+ }
184
+
185
+ fn sourcemap(value: Option<SourceMap>) -> Result<Option<SourceMapType>, RolldownResult> {
186
+ match value {
187
+ None | Some(SourceMap::Toggle(false)) => Ok(None),
188
+ Some(SourceMap::Toggle(true)) => Ok(Some(SourceMapType::File)),
189
+ Some(SourceMap::Kind(kind)) => match kind.as_str() {
190
+ "file" => Ok(Some(SourceMapType::File)),
191
+ "inline" => Ok(Some(SourceMapType::Inline)),
192
+ "hidden" => Ok(Some(SourceMapType::Hidden)),
193
+ other => Err(unknown("sourcemap", other, "true, false, file, inline or hidden")),
194
+ },
195
+ }
196
+ }
197
+
198
+ fn minify(value: Option<Minify>) -> Result<Option<RawMinifyOptions>, RolldownResult> {
199
+ match value {
200
+ None => Ok(None),
201
+ Some(Minify::Toggle(toggle)) => Ok(Some(RawMinifyOptions::Bool(toggle))),
202
+ Some(Minify::Kind(kind)) => match kind.as_str() {
203
+ "dce-only" => Ok(Some(RawMinifyOptions::DeadCodeEliminationOnly)),
204
+ other => Err(unknown("minify", other, "true, false or dce-only")),
205
+ },
206
+ }
207
+ }
208
+
209
+ fn format(value: &str) -> Result<OutputFormat, RolldownResult> {
210
+ match value {
211
+ "esm" => Ok(OutputFormat::Esm),
212
+ "cjs" => Ok(OutputFormat::Cjs),
213
+ "iife" => Ok(OutputFormat::Iife),
214
+ "umd" => Ok(OutputFormat::Umd),
215
+ other => Err(unknown("format", other, "esm, cjs, iife or umd")),
216
+ }
217
+ }
218
+
219
+ fn platform(value: &str) -> Result<Platform, RolldownResult> {
220
+ match value {
221
+ "browser" => Ok(Platform::Browser),
222
+ "node" => Ok(Platform::Node),
223
+ "neutral" => Ok(Platform::Neutral),
224
+ other => Err(unknown("platform", other, "browser, node or neutral")),
225
+ }
226
+ }
227
+
228
+ fn exports(value: &str) -> Result<OutputExports, RolldownResult> {
229
+ match value {
230
+ "auto" => Ok(OutputExports::Auto),
231
+ "named" => Ok(OutputExports::Named),
232
+ "default" => Ok(OutputExports::Default),
233
+ "none" => Ok(OutputExports::None),
234
+ other => Err(unknown("exports", other, "auto, named, default or none")),
235
+ }
236
+ }
237
+
238
+ fn legal_comments(value: &str) -> Result<LegalComments, RolldownResult> {
239
+ match value {
240
+ "none" => Ok(LegalComments::None),
241
+ "inline" => Ok(LegalComments::Inline),
242
+ other => Err(unknown("legal_comments", other, "none or inline")),
243
+ }
244
+ }
245
+
246
+ fn unknown(option: &str, given: &str, expected: &str) -> RolldownResult {
247
+ RolldownResult::error(
248
+ RolldownErrorCode::Option,
249
+ format!("Unknown {option}: {given}. Expected {expected}."),
250
+ )
251
+ }
@@ -0,0 +1,104 @@
1
+ use rolldown::BundleOutput;
2
+ use rolldown_common::Output;
3
+ use rolldown_error::{BatchedBuildDiagnostic, BuildDiagnostic};
4
+ use serde::Serialize;
5
+
6
+ #[derive(Serialize, Default)]
7
+ pub struct BuildPayload {
8
+ pub chunks: Vec<Chunk>,
9
+ pub assets: Vec<Asset>,
10
+ pub warnings: Vec<Diagnostic>,
11
+ pub errors: Vec<Diagnostic>,
12
+ pub failed: bool,
13
+ }
14
+
15
+ #[derive(Serialize)]
16
+ pub struct Chunk {
17
+ pub filename: String,
18
+ pub name: String,
19
+ pub code: String,
20
+ pub map: Option<String>,
21
+ pub is_entry: bool,
22
+ pub is_dynamic_entry: bool,
23
+ pub imports: Vec<String>,
24
+ pub dynamic_imports: Vec<String>,
25
+ pub exports: Vec<String>,
26
+ pub module_ids: Vec<String>,
27
+ }
28
+
29
+ #[derive(Serialize)]
30
+ pub struct Asset {
31
+ pub filename: String,
32
+ pub names: Vec<String>,
33
+ pub source: Option<String>,
34
+ }
35
+
36
+ #[derive(Serialize)]
37
+ pub struct Diagnostic {
38
+ pub kind: String,
39
+ pub severity: String,
40
+ pub message: String,
41
+ pub file: Option<String>,
42
+ pub line: Option<usize>,
43
+ pub column: Option<usize>,
44
+ }
45
+
46
+ impl BuildPayload {
47
+ pub fn from_output(output: BundleOutput) -> Self {
48
+ let mut payload = Self {
49
+ warnings: convert(&output.warnings),
50
+ ..Default::default()
51
+ };
52
+
53
+ for asset in output.assets {
54
+ match asset {
55
+ Output::Chunk(chunk) => payload.chunks.push(Chunk {
56
+ filename: chunk.filename.to_string(),
57
+ name: chunk.name.to_string(),
58
+ code: chunk.code.clone(),
59
+ map: chunk.map.as_ref().map(|map| map.to_json_string()),
60
+ is_entry: chunk.is_entry,
61
+ is_dynamic_entry: chunk.is_dynamic_entry,
62
+ imports: chunk.imports.iter().map(ToString::to_string).collect(),
63
+ dynamic_imports: chunk.dynamic_imports.iter().map(ToString::to_string).collect(),
64
+ exports: chunk.exports.iter().map(ToString::to_string).collect(),
65
+ module_ids: chunk.module_ids.iter().map(ToString::to_string).collect(),
66
+ }),
67
+ Output::Asset(asset) => payload.assets.push(Asset {
68
+ filename: asset.filename.to_string(),
69
+ names: asset.names.clone(),
70
+ source: asset.source.clone().try_into_string().ok(),
71
+ }),
72
+ }
73
+ }
74
+
75
+ payload
76
+ }
77
+
78
+ pub fn from_failure(batched: BatchedBuildDiagnostic) -> Self {
79
+ Self {
80
+ errors: convert(&batched.into_vec()),
81
+ failed: true,
82
+ ..Default::default()
83
+ }
84
+ }
85
+ }
86
+
87
+ fn convert(diagnostics: &[BuildDiagnostic]) -> Vec<Diagnostic> {
88
+ diagnostics
89
+ .iter()
90
+ .map(|diagnostic| {
91
+ let rendered = diagnostic.to_diagnostic();
92
+ let location = rendered.get_primary_location();
93
+
94
+ Diagnostic {
95
+ kind: diagnostic.kind().to_string(),
96
+ severity: format!("{:?}", diagnostic.severity()).to_lowercase(),
97
+ message: rendered.convert_to_string(false),
98
+ file: location.as_ref().map(|(file, ..)| file.clone()),
99
+ line: location.as_ref().map(|(_, line, ..)| *line),
100
+ column: location.as_ref().map(|(_, _, column, _)| *column),
101
+ }
102
+ })
103
+ .collect()
104
+ }