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 +4 -4
- data/README.md +97 -4
- data/ext/rolldown/extconf.rb +123 -0
- data/ext/rolldown/include/rolldown.h +54 -0
- data/ext/rolldown/rolldown.c +130 -0
- data/lib/rolldown/asset.rb +33 -0
- data/lib/rolldown/backend.rb +36 -0
- data/lib/rolldown/build_result.rb +106 -0
- data/lib/rolldown/chunk.rb +70 -0
- data/lib/rolldown/diagnostic.rb +56 -0
- data/lib/rolldown/errors.rb +11 -0
- data/lib/rolldown/options.rb +152 -0
- data/lib/rolldown/version.rb +1 -1
- data/lib/rolldown.rb +45 -0
- data/licenses/README.md +8 -0
- data/licenses/rolldown-MIT.txt +25 -0
- data/licenses/rolldown-THIRD-PARTY.txt +33 -0
- data/rolldown.gemspec +13 -11
- data/rust/Cargo.lock +3086 -0
- data/rust/Cargo.toml +37 -0
- data/rust/build.rs +55 -0
- data/rust/cbindgen.toml +24 -0
- data/rust/rustfmt.toml +3 -0
- data/rust/src/build.rs +39 -0
- data/rust/src/lib.rs +100 -0
- data/rust/src/modules.rs +89 -0
- data/rust/src/options.rs +251 -0
- data/rust/src/payload.rs +104 -0
- data/rust/src/result.rs +49 -0
- data/sig/rolldown/asset.rbs +23 -0
- data/sig/rolldown/backend.rbs +26 -0
- data/sig/rolldown/build_result.rbs +47 -0
- data/sig/rolldown/chunk.rbs +39 -0
- data/sig/rolldown/diagnostic.rbs +35 -0
- data/sig/rolldown/errors.rbs +24 -0
- data/sig/rolldown/options.rbs +61 -0
- data/sig/rolldown/version.rbs +5 -0
- data/sig/rolldown.rbs +10 -1
- metadata +36 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48484d046263b114fc8c2c6cdcaaaa1bc85511f6d8ccc1de6b8dc9a12303e3a6
|
|
4
|
+
data.tar.gz: ed41d171fb77c02c67e469281d3dfaa6c6819a11841a38123ced8461a4cf487d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 80879741fe1dd7925e19c09e1abaaa7986248fef49a35eefffabd2d621b0ed8b5d4708d2af45c9d97ec9ad339a902ef00bedb77d38549b974c194b237c0b65e5
|
|
7
|
+
data.tar.gz: cc5fce1e3dba470a61762372a58fcf3cc8c0f6b9b39a8f2c98322da92f37575a6142745c128a135567f1fd6947a0bd6fd3607aed95a38fc70144f532f3ecdd6e
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<h2 align="center"
|
|
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
|
|
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
|
-
|
|
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
|