rolldown 0.0.1 → 0.1.1

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: 28f0f4512b0121ad746f54c1df63f3e4f94a2f96136f347dc9ceebe9f0ede04d
4
- data.tar.gz: 9e437fefb81181be710087a7f9432eccd558fcc2b1145030a58612b03130147c
3
+ metadata.gz: 48484d046263b114fc8c2c6cdcaaaa1bc85511f6d8ccc1de6b8dc9a12303e3a6
4
+ data.tar.gz: ed41d171fb77c02c67e469281d3dfaa6c6819a11841a38123ced8461a4cf487d
5
5
  SHA512:
6
- metadata.gz: 4c7a4aa427585fd0674547a2c91993c8023c5292b26ca1d19794f1529ccaf7b45c6b09d5d7046f64271a988fcc775d2fcc5b0f9901a241ad0b16f41c17f33d26
7
- data.tar.gz: 10cfc4fc8876b6e6ea42edfbf8325cddcaf44443835517c5ff2d329e3d799eff65517d5b3fc2d47790e7711ac1c970effe5c402bdf5b25e858285010666bd964
6
+ metadata.gz: 80879741fe1dd7925e19c09e1abaaa7986248fef49a35eefffabd2d621b0ed8b5d4708d2af45c9d97ec9ad339a902ef00bedb77d38549b974c194b237c0b65e5
7
+ data.tar.gz: cc5fce1e3dba470a61762372a58fcf3cc8c0f6b9b39a8f2c98322da92f37575a6142745c128a135567f1fd6947a0bd6fd3607aed95a38fc70144f532f3ecdd6e
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- <h2 align="center">Rolldown for Ruby</h2>
1
+ <h2 align="center">⬇️ Rolldown for Ruby</h2>
2
2
 
3
3
  <h4 align="center">Blazing Fast Rust-based bundler for JavaScript.</h4>
4
4
 
5
- <div align="center">Ruby bindings for <a href="https://rolldown.rs">Rolldown</a>, the Rust-based bundler for JavaScript.</div><br/>
5
+ <div align="center">Ruby bindings for <a href="https://rolldown.rs">Rolldown</a>, the Rust bundler behind Vite.</div><br/>
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://rubygems.org/gems/rolldown"><img alt="Gem Version" src="https://img.shields.io/gem/v/rolldown"></a>
@@ -29,7 +29,100 @@ Anywhere a precompiled gem is not published, the gem builds from source and need
29
29
 
30
30
  ### Usage
31
31
 
32
- TODO
32
+ The options follow [Rolldown's JavaScript API](https://rolldown.rs/reference/), so a `rolldown.config.js` ports across as it reads.
33
+
34
+ ```ruby
35
+ result = Rolldown.build(
36
+ input: "src/main.js",
37
+ output: {
38
+ file: "bundle.js"
39
+ }
40
+ )
41
+
42
+ result.entry.code
43
+ ```
44
+
45
+ Everything comes back in memory. `write` puts it on disk, chunks and source maps together, creating the directory if it is missing.
46
+
47
+ ```ruby
48
+ result = Rolldown.build(
49
+ input: "app/javascript/application.js",
50
+ output: {
51
+ dir: "app/assets/builds",
52
+ format: "esm",
53
+ sourcemap: true,
54
+ minify: true
55
+ }
56
+ )
57
+
58
+ result.write
59
+ #=> ["app/assets/builds/application.js", "app/assets/builds/application.js.map"]
60
+ ```
61
+
62
+ `write` goes to `output.dir`, or to whatever directory `output.file` names, and takes an argument to go somewhere else. `output.dir` also decides what the source map's relative paths look like, so it is worth setting even when writing elsewhere.
63
+
64
+ Writing happens in Ruby. Rolldown can do it, but it hands the whole bundle back either way, so writing from Rust measured slower while giving the caller less say over where files land.
65
+
66
+ #### Entry points
67
+
68
+ An entry point is a string, a list, or a hash of names, matching `string | string[] | Record<string, string>`.
69
+
70
+ ```ruby
71
+ Rolldown.build(input: "src/main.js")
72
+ Rolldown.build(input: ["src/main.js", "src/admin.js"])
73
+ Rolldown.build(input: { app: "src/main.js", admin: "src/admin.js" })
74
+ ```
75
+
76
+ #### Bundling something that is not a file
77
+
78
+ `modules` hands the bundler sources it holds in memory, keyed by whatever name you want to import them under.
79
+
80
+ ```ruby
81
+ Rolldown.build(
82
+ input: "app:entry",
83
+ modules: {
84
+ "app:entry" => %(import { card } from "app:users/card"\ncard()),
85
+ "app:users/card" => %(export function card() { ... })
86
+ }
87
+ )
88
+ ```
89
+
90
+ They mix freely with what is on disk. A module in memory can import an npm package or a relative file, and a file on disk can import a module in memory. A relative import out of a module in memory resolves against that module's own name, so give it a path-shaped one when it needs to reach real files next to it.
91
+
92
+ This exists so a generator can transform sources in Ruby first and bundle the result without writing anything out. There are no plugins, and no Ruby runs while the bundle does.
93
+
94
+ #### What comes back
95
+
96
+ ```ruby
97
+ result.chunks # every chunk, each with filename, name, code, map, imports, exports and module_ids
98
+ result.assets # source maps and anything else emitted alongside
99
+ result.entry # the first chunk that is an entry point
100
+ result.warnings # diagnostics that did not stop the build
101
+ result.errors # diagnostics that did
102
+ result.failed?
103
+ ```
104
+
105
+ A build that produces nothing usable raises `Rolldown::BuildError` carrying what went wrong. Pass `strict: true` to raise on warnings too.
106
+
107
+ #### Bundling, not installing
108
+
109
+ Bare imports resolve with no plugin and no configuration whenever the files are on disk. Putting them there is a package manager, and this gem is not one. An app whose JavaScript is its own code plus a few vendored libraries needs nothing else.
110
+
111
+ #### Which options are bound
112
+
113
+ Of the JavaScript API's 22 input options and 49 output options, these are bound so far.
114
+
115
+ At the top level, `input`, `cwd`, `external`, `platform`, `treeshake`, `shim_missing_exports`, `module_types` and `modules`, plus `transform` for `define`.
116
+
117
+ Under `output`, `dir`, `file`, `format`, `name`, `exports`, `sourcemap`, `minify`, `banner`, `footer`, `intro`, `outro`, `entry_file_names`, `chunk_file_names`, `asset_file_names`, `keep_names`, `legal_comments` and `es_module`.
118
+
119
+ Names follow the JavaScript spelling in snake_case, so `entryFileNames` is `entry_file_names`. Where JavaScript and Rust disagree, JavaScript wins, and `define` sits under `transform` for the same reason.
120
+
121
+ #### What cannot cross
122
+
123
+ Options that take a JavaScript function in the JavaScript API have nowhere to go in Ruby. `plugins`, `on_log`, `manual_chunks` and `sourcemap_path_transform` are refused by name, and anything else Rolldown does not recognise is refused too.
124
+
125
+ Watch mode, hot module replacement and the dev server are not bound.
33
126
 
34
127
  ### Development
35
128
 
@@ -56,4 +149,4 @@ Issues with parsing, transforming or minifying itself belong [upstream](https://
56
149
 
57
150
  The Ruby, C, and Rust code in this gem is available under the terms of the [MIT License](https://opensource.org/licenses/MIT).
58
151
 
59
- It builds against [Rolldown](https://github.com/rolldown/rolldown), which is MIT licensed and carries some MIT code of its own.
152
+ It builds against [Rolldown](https://github.com/rolldown/rolldown), which is MIT licensed and carries some MIT code of its own. A copy of both travels with the gem in [`licenses/`](licenses).
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+ require "fileutils"
5
+
6
+ ext_dir = __dir__
7
+ root_dir = File.expand_path("../..", ext_dir)
8
+
9
+ rust_dir = File.join(root_dir, "rust")
10
+
11
+ unless File.exist?(File.join(rust_dir, "Cargo.toml"))
12
+ abort <<~MESSAGE
13
+
14
+ ERROR: Rust sources not found at #{rust_dir}.
15
+
16
+ MESSAGE
17
+ end
18
+
19
+ unless system("cargo --version > /dev/null 2>&1")
20
+ abort <<~MESSAGE
21
+
22
+ ERROR: Rust toolchain not found.
23
+
24
+ rolldown requires the Rust toolchain to compile from source.
25
+
26
+ Install Rust: https://rustup.rs
27
+
28
+ MESSAGE
29
+ end
30
+
31
+ RUST_TARGETS = {
32
+ "aarch64-linux-gnu" => "aarch64-unknown-linux-gnu",
33
+ "aarch64-linux-musl" => "aarch64-unknown-linux-musl",
34
+ "arm-linux-gnu" => "armv7-unknown-linux-gnueabihf",
35
+ "arm-linux-musl" => "armv7-unknown-linux-musleabihf",
36
+ "arm64-darwin" => "aarch64-apple-darwin",
37
+ "x86_64-darwin" => "x86_64-apple-darwin",
38
+ "x86_64-linux-gnu" => "x86_64-unknown-linux-gnu",
39
+ "x86_64-linux-musl" => "x86_64-unknown-linux-musl",
40
+ "x86-linux-gnu" => "i686-unknown-linux-gnu",
41
+ "x86-linux-musl" => "i686-unknown-linux-musl",
42
+ }.freeze
43
+
44
+ cross_compiling = ENV.key?("RUBY_CC_VERSION")
45
+ target_platform = ENV.fetch("CARGO_BUILD_TARGET", nil)
46
+
47
+ if cross_compiling && target_platform.nil?
48
+ rcd_platform = ENV.fetch("RCD_PLATFORM", "")
49
+ target_platform = RUST_TARGETS[rcd_platform]
50
+
51
+ if target_platform.nil?
52
+ ruby_platform = RbConfig::CONFIG["arch"]
53
+ target_platform = RUST_TARGETS.values.find { |target| ruby_platform.include?(target.split("-").first) }
54
+ end
55
+ end
56
+
57
+ header_path = File.join(ext_dir, "include", "rolldown.h")
58
+
59
+ FileUtils.mkdir_p(File.dirname(header_path))
60
+
61
+ target_dir = File.join(rust_dir, "target")
62
+
63
+ if target_platform
64
+ puts "rolldown: Cross-compiling Rust for target: #{target_platform}"
65
+
66
+ system("rustup target add #{target_platform}") || warn("rolldown: Failed to add Rust target #{target_platform}")
67
+
68
+ cargo_args = "--release --locked --target #{target_platform}"
69
+ lib_dir = File.join(target_dir, target_platform, "release")
70
+ else
71
+ puts "rolldown: Compiling Rust library for native platform..."
72
+
73
+ cargo_args = "--release --locked"
74
+ lib_dir = File.join(target_dir, "release")
75
+ end
76
+
77
+ unless system("cd #{rust_dir} && cargo build #{cargo_args}")
78
+ abort "ERROR: Failed to compile rolldown from Rust source."
79
+ end
80
+
81
+ unless File.exist?(header_path)
82
+ abort "ERROR: cbindgen did not generate #{header_path}. Try `cargo clean` in #{rust_dir} and reinstall."
83
+ end
84
+
85
+ static_lib = File.join(lib_dir, "librolldown_ffi.a")
86
+
87
+ developing = File.exist?(File.join(root_dir, ".git"))
88
+
89
+ if File.exist?(static_lib) && !developing
90
+ vendored = File.join(ext_dir, "librolldown_ffi.a")
91
+
92
+ FileUtils.cp(static_lib, vendored)
93
+ FileUtils.rm_rf(target_dir)
94
+
95
+ puts "rolldown: Static library vendored at #{vendored}, Rust build directory removed"
96
+
97
+ $LDFLAGS << " #{vendored}"
98
+ elsif File.exist?(static_lib)
99
+ puts "rolldown: Static library found at #{static_lib}"
100
+
101
+ $LDFLAGS << " #{static_lib}"
102
+ else
103
+ host_os = target_platform || RbConfig::CONFIG["host_os"]
104
+
105
+ lib_name = case host_os
106
+ when /darwin/ then "librolldown_ffi.dylib"
107
+ when /mingw|mswin|windows/ then "rolldown_ffi.dll"
108
+ else "librolldown_ffi.so"
109
+ end
110
+
111
+ lib_path = File.join(lib_dir, lib_name)
112
+
113
+ abort "ERROR: Shared library not found at #{lib_path}" unless File.exist?(lib_path)
114
+
115
+ puts "rolldown: Shared library found at #{lib_path} (dynamic)"
116
+
117
+ $LDFLAGS << " -L#{lib_dir} -lrolldown_ffi"
118
+ $LDFLAGS << " -Wl,-rpath,#{lib_dir}" if RbConfig::CONFIG["host_os"].match?(/darwin|linux/)
119
+ end
120
+
121
+ $CFLAGS << " -I#{ext_dir}"
122
+
123
+ create_makefile("rolldown/rolldown")
@@ -0,0 +1,54 @@
1
+ /* Generated by cbindgen, do not edit manually */
2
+
3
+ #include <stdbool.h>
4
+ #include <stdint.h>
5
+ #include <stddef.h>
6
+
7
+ #ifndef ROLLDOWN_H
8
+ #define ROLLDOWN_H
9
+
10
+ typedef enum RolldownErrorCode {
11
+ ROLLDOWN_ERROR_CODE_NONE = 0,
12
+ ROLLDOWN_ERROR_CODE_OPTION,
13
+ ROLLDOWN_ERROR_CODE_ENCODING,
14
+ ROLLDOWN_ERROR_CODE_BUILD,
15
+ ROLLDOWN_ERROR_CODE_IO,
16
+ ROLLDOWN_ERROR_CODE_INTERNAL,
17
+ ROLLDOWN_ERROR_CODE_PANIC,
18
+ } RolldownErrorCode;
19
+
20
+ typedef struct RolldownResult {
21
+ char *value;
22
+ uintptr_t value_len;
23
+ char *error;
24
+ enum RolldownErrorCode code;
25
+ } RolldownResult;
26
+
27
+ /**
28
+ * # Safety
29
+ *
30
+ * `options_json` must be a valid, nul-terminated UTF-8 string.
31
+ */
32
+ struct RolldownResult rolldown_build(const char *options_json);
33
+
34
+ char *rolldown_version(void);
35
+
36
+ char *rolldown_rolldown_version(void);
37
+
38
+ /**
39
+ * # Safety
40
+ *
41
+ * `value` must be a pointer this library returned, and must not be used afterwards.
42
+ */
43
+ void rolldown_string_free(char *value);
44
+
45
+ /**
46
+ * # Safety
47
+ *
48
+ * `result` must be one this library returned, and must not be used afterwards.
49
+ */
50
+ void rolldown_result_free(struct RolldownResult result);
51
+
52
+ struct RolldownResult rolldown_panic_for_test(void);
53
+
54
+ #endif /* ROLLDOWN_H */
@@ -0,0 +1,130 @@
1
+ #include <ruby.h>
2
+ #include <ruby/encoding.h>
3
+ #include <ruby/thread.h>
4
+ #include "include/rolldown.h"
5
+
6
+ static VALUE rb_mRolldown;
7
+ static VALUE rb_mBackend;
8
+ static VALUE rb_eError;
9
+ static VALUE rb_eOptionError;
10
+ static VALUE rb_eRolldownEncodingError;
11
+ static VALUE rb_eBuildError;
12
+ static VALUE rb_eRolldownIOError;
13
+ static VALUE rb_eInternalError;
14
+ static VALUE rb_ePanicError;
15
+
16
+ typedef struct RolldownResult (*rolldown_function)(const char *);
17
+
18
+ struct call_arguments {
19
+ rolldown_function function;
20
+ const char *options;
21
+ struct RolldownResult result;
22
+ };
23
+
24
+ static VALUE make_utf8_string(const char *cstring) {
25
+ return rb_enc_str_new_cstr(cstring, rb_utf8_encoding());
26
+ }
27
+
28
+ static VALUE take_utf8_string(char *cstring) {
29
+ if (!cstring) return Qnil;
30
+
31
+ VALUE string = make_utf8_string(cstring);
32
+ rolldown_string_free(cstring);
33
+
34
+ return string;
35
+ }
36
+
37
+ static VALUE error_class_for(enum RolldownErrorCode code) {
38
+ switch (code) {
39
+ case ROLLDOWN_ERROR_CODE_OPTION: return rb_eOptionError;
40
+ case ROLLDOWN_ERROR_CODE_ENCODING: return rb_eRolldownEncodingError;
41
+ case ROLLDOWN_ERROR_CODE_BUILD: return rb_eBuildError;
42
+ case ROLLDOWN_ERROR_CODE_IO: return rb_eRolldownIOError;
43
+ case ROLLDOWN_ERROR_CODE_PANIC: return rb_ePanicError;
44
+ default: return rb_eInternalError;
45
+ }
46
+ }
47
+
48
+ static VALUE unwrap(struct RolldownResult result) {
49
+ if (result.error) {
50
+ VALUE message = make_utf8_string(result.error);
51
+ VALUE error_class = error_class_for(result.code);
52
+
53
+ rolldown_result_free(result);
54
+
55
+ rb_raise(error_class, "%s", StringValueCStr(message));
56
+ }
57
+
58
+ if (!result.value) {
59
+ rolldown_result_free(result);
60
+
61
+ rb_raise(rb_eInternalError, "rolldown returned no result");
62
+ }
63
+
64
+ VALUE value = rb_enc_str_new(result.value, (long) result.value_len, rb_utf8_encoding());
65
+
66
+ rolldown_result_free(result);
67
+
68
+ return value;
69
+ }
70
+
71
+ static void *without_gvl(void *data) {
72
+ struct call_arguments *arguments = (struct call_arguments *) data;
73
+
74
+ arguments->result = arguments->function(arguments->options);
75
+
76
+ return NULL;
77
+ }
78
+
79
+ static VALUE call(rolldown_function function, VALUE options) {
80
+ struct call_arguments arguments;
81
+
82
+ arguments.function = function;
83
+ arguments.options = StringValueCStr(options);
84
+
85
+ rb_thread_call_without_gvl(without_gvl, &arguments, NULL, NULL);
86
+
87
+ return unwrap(arguments.result);
88
+ }
89
+
90
+ static VALUE rb_build(VALUE self, VALUE options) {
91
+ (void) self;
92
+
93
+ return call(rolldown_build, options);
94
+ }
95
+
96
+ static VALUE rb_panic_for_test(VALUE self) {
97
+ (void) self;
98
+
99
+ return unwrap(rolldown_panic_for_test());
100
+ }
101
+
102
+ static VALUE rb_native_version(VALUE self) {
103
+ (void) self;
104
+
105
+ return take_utf8_string(rolldown_version());
106
+ }
107
+
108
+ static VALUE rb_rolldown_version(VALUE self) {
109
+ (void) self;
110
+
111
+ return take_utf8_string(rolldown_rolldown_version());
112
+ }
113
+
114
+ void Init_rolldown(void) {
115
+ rb_mRolldown = rb_define_module("Rolldown");
116
+ rb_mBackend = rb_define_module_under(rb_mRolldown, "Backend");
117
+
118
+ rb_eError = rb_define_class_under(rb_mRolldown, "Error", rb_eStandardError);
119
+ rb_eOptionError = rb_define_class_under(rb_mRolldown, "OptionError", rb_eError);
120
+ rb_eRolldownEncodingError = rb_define_class_under(rb_mRolldown, "EncodingError", rb_eError);
121
+ rb_eBuildError = rb_define_class_under(rb_mRolldown, "BuildError", rb_eError);
122
+ rb_eRolldownIOError = rb_define_class_under(rb_mRolldown, "IOError", rb_eError);
123
+ rb_eInternalError = rb_define_class_under(rb_mRolldown, "InternalError", rb_eError);
124
+ rb_ePanicError = rb_define_class_under(rb_mRolldown, "PanicError", rb_eInternalError);
125
+
126
+ rb_define_singleton_method(rb_mBackend, "build", rb_build, 1);
127
+ rb_define_singleton_method(rb_mBackend, "panic_for_test", rb_panic_for_test, 0);
128
+ rb_define_singleton_method(rb_mBackend, "version", rb_native_version, 0);
129
+ rb_define_singleton_method(rb_mBackend, "rolldown_version", rb_rolldown_version, 0);
130
+ }
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolldown
4
+ class Asset
5
+ attr_reader :filename #: String
6
+ attr_reader :names #: Array[String]
7
+ attr_reader :source #: String?
8
+
9
+ #: (Hash[String, untyped]) -> Rolldown::Asset
10
+ def self.from_hash(hash)
11
+ new(filename: hash.fetch("filename"), names: hash.fetch("names"), source: hash["source"])
12
+ end
13
+
14
+ #: (filename: String, names: Array[String], source: String?) -> void
15
+ def initialize(filename:, names:, source:)
16
+ @filename = filename.freeze
17
+ @names = names.freeze
18
+ @source = source&.freeze
19
+
20
+ freeze
21
+ end
22
+
23
+ #: () -> String
24
+ def to_s
25
+ source.to_s
26
+ end
27
+
28
+ #: () -> String
29
+ def inspect
30
+ "#<#{self.class.name} #{filename.inspect}#{" #{source.bytesize} bytes" if source}>"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolldown
4
+ module Backend
5
+ module Unavailable
6
+ #: (String) -> String
7
+ def build(_options_json)
8
+ unavailable(__method__)
9
+ end
10
+
11
+ #: () -> String
12
+ def panic_for_test
13
+ unavailable(__method__)
14
+ end
15
+
16
+ #: () -> String
17
+ def version
18
+ unavailable(__method__)
19
+ end
20
+
21
+ #: () -> String
22
+ def rolldown_version
23
+ unavailable(__method__)
24
+ end
25
+
26
+ private
27
+
28
+ #: (Symbol?) -> bot
29
+ def unavailable(name)
30
+ raise NotImplementedError, "Rolldown::Backend.#{name} is defined by the native extension, which did not load"
31
+ end
32
+ end
33
+
34
+ extend Unavailable
35
+ end
36
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "pathname"
5
+ require "fileutils"
6
+
7
+ module Rolldown
8
+ class BuildResult
9
+ attr_reader :chunks #: Array[Rolldown::Chunk]
10
+ attr_reader :assets #: Array[Rolldown::Asset]
11
+ attr_reader :warnings #: Array[Rolldown::Diagnostic]
12
+ attr_reader :errors #: Array[Rolldown::Diagnostic]
13
+ attr_reader :dir #: String?
14
+ attr_reader :cwd #: String?
15
+
16
+ #: (String, ?String?, ?String?) -> Rolldown::BuildResult
17
+ def self.from_json(payload, dir = nil, cwd = nil)
18
+ parsed = JSON.parse(payload)
19
+
20
+ new(
21
+ dir: dir,
22
+ cwd: cwd,
23
+ chunks: parsed.fetch("chunks").map { |chunk| Chunk.from_hash(chunk) },
24
+ assets: parsed.fetch("assets").map { |asset| Asset.from_hash(asset) },
25
+ warnings: parsed.fetch("warnings").map { |warning| Diagnostic.from_hash(warning) },
26
+ errors: parsed.fetch("errors").map { |error| Diagnostic.from_hash(error) },
27
+ failed: parsed.fetch("failed")
28
+ )
29
+ end #: String?
30
+
31
+ #: (chunks: Array[Rolldown::Chunk], assets: Array[Rolldown::Asset], warnings: Array[Rolldown::Diagnostic], errors: Array[Rolldown::Diagnostic], failed: bool, ?dir: String?, ?cwd: String?) -> void
32
+ def initialize(chunks:, assets:, warnings:, errors:, failed:, dir: nil, cwd: nil)
33
+ @dir = dir
34
+ @cwd = cwd
35
+ @chunks = chunks.freeze
36
+ @assets = assets.freeze
37
+ @warnings = warnings.freeze
38
+ @errors = errors.freeze
39
+ @failed = failed
40
+
41
+ freeze
42
+ end
43
+
44
+ #: () -> bool
45
+ def failed?
46
+ @failed
47
+ end
48
+
49
+ #: () -> bool
50
+ def errors?
51
+ !errors.empty?
52
+ end
53
+
54
+ #: () -> bool
55
+ def warnings?
56
+ !warnings.empty?
57
+ end
58
+
59
+ #: (?String?) -> Array[String]
60
+ def write(into = dir)
61
+ raise IOError, "no directory to write to, pass one or set output.dir or output.file" unless into
62
+
63
+ root = cwd ? File.expand_path(into, cwd) : into
64
+
65
+ FileUtils.mkdir_p(root)
66
+
67
+ (chunks + assets).map do |file|
68
+ path = Pathname.new(root).join(file.filename).cleanpath.to_s
69
+
70
+ FileUtils.mkdir_p(File.dirname(path))
71
+ File.write(path, file.to_s)
72
+
73
+ path
74
+ end
75
+ end
76
+
77
+ #: () -> Rolldown::Chunk?
78
+ def entry
79
+ chunks.find(&:entry?)
80
+ end
81
+
82
+ #: (?strict: bool) -> Rolldown::BuildResult
83
+ def validate!(strict: false)
84
+ raise BuildError, errors.map(&:message).join("\n\n") if failed? || errors?
85
+ raise BuildError, warnings.map(&:message).join("\n\n") if strict && warnings?
86
+
87
+ self
88
+ end
89
+
90
+ #: () -> String
91
+ def to_s
92
+ chunks.map(&:code).join("\n")
93
+ end
94
+
95
+ #: () -> String
96
+ def inspect
97
+ parts = ["chunks=#{chunks.length}"]
98
+ parts << "assets=#{assets.length}" unless assets.empty?
99
+ parts << "warnings=#{warnings.length}" unless warnings.empty?
100
+ parts << "errors=#{errors.length}" unless errors.empty?
101
+ parts << "failed" if failed?
102
+
103
+ "#<#{self.class.name} #{parts.join(" ")}>"
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolldown
4
+ class Chunk
5
+ attr_reader :filename #: String
6
+ attr_reader :name #: String
7
+ attr_reader :code #: String
8
+ attr_reader :map #: String?
9
+ attr_reader :imports #: Array[String]
10
+ attr_reader :dynamic_imports #: Array[String]
11
+ attr_reader :exports #: Array[String]
12
+ attr_reader :module_ids #: Array[String]
13
+
14
+ #: (Hash[String, untyped]) -> Rolldown::Chunk
15
+ def self.from_hash(hash)
16
+ new(
17
+ filename: hash.fetch("filename"),
18
+ name: hash.fetch("name"),
19
+ code: hash.fetch("code"),
20
+ map: hash["map"],
21
+ entry: hash.fetch("is_entry"),
22
+ dynamic_entry: hash.fetch("is_dynamic_entry"),
23
+ imports: hash.fetch("imports"),
24
+ dynamic_imports: hash.fetch("dynamic_imports"),
25
+ exports: hash.fetch("exports"),
26
+ module_ids: hash.fetch("module_ids")
27
+ )
28
+ end
29
+
30
+ #: (filename: String, name: String, code: String, map: String?, entry: bool, dynamic_entry: bool, imports: Array[String], dynamic_imports: Array[String], exports: Array[String], module_ids: Array[String]) -> void
31
+ def initialize(filename:, name:, code:, map:, entry:, dynamic_entry:, imports:, dynamic_imports:, exports:, module_ids:)
32
+ @filename = filename.freeze
33
+ @name = name.freeze
34
+ @code = code.freeze
35
+ @map = map&.freeze
36
+ @entry = entry
37
+ @dynamic_entry = dynamic_entry
38
+ @imports = imports.freeze
39
+ @dynamic_imports = dynamic_imports.freeze
40
+ @exports = exports.freeze
41
+ @module_ids = module_ids.freeze
42
+
43
+ freeze
44
+ end
45
+
46
+ #: () -> bool
47
+ def entry?
48
+ @entry
49
+ end
50
+
51
+ #: () -> bool
52
+ def dynamic_entry?
53
+ @dynamic_entry
54
+ end
55
+
56
+ #: () -> String
57
+ def to_s
58
+ code
59
+ end
60
+
61
+ #: () -> String
62
+ def inspect
63
+ parts = [filename.inspect, "#{code.bytesize} bytes"]
64
+ parts << "entry" if entry?
65
+ parts << "map" if map
66
+
67
+ "#<#{self.class.name} #{parts.join(" ")}>"
68
+ end
69
+ end
70
+ end