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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +152 -0
- 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/3.2/rolldown.so +0 -0
- data/lib/rolldown/3.3/rolldown.so +0 -0
- data/lib/rolldown/3.4/rolldown.so +0 -0
- data/lib/rolldown/4.0/rolldown.so +0 -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 +5 -0
- data/lib/rolldown.rb +50 -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 +43 -0
- 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 +92 -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 +12 -0
- metadata +92 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6faef1b2d5b61f2c1c3a6f454f15a0e8544367cf8d2718bd937a62bea9cc5dae
|
|
4
|
+
data.tar.gz: e62a70aebbcb2633066a69bb9c4f90c8db53be271b17c618c97783b932ce3907
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e5e3e724c1af07e1596bd2ac1028a4e5a820ee305e9762dba027f0234c876bbec9f51b0ae8bddd1e7afb0e9e399ecc7c36d4e08d562168eb63f68042e901d628
|
|
7
|
+
data.tar.gz: 4d67f8aea9e6f609b94e2b20b78ed01b05e2361009ab3dd97ab11c4c3a2a436a6198d48ae2a1f2856ac2953ac63ee1fc47246b4cd916befb41be04b7093a103f
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marco Roth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<h2 align="center">⬇️ Rolldown for Ruby</h2>
|
|
2
|
+
|
|
3
|
+
<h4 align="center">Blazing Fast Rust-based bundler for JavaScript.</h4>
|
|
4
|
+
|
|
5
|
+
<div align="center">Ruby bindings for <a href="https://rolldown.rs">Rolldown</a>, the Rust bundler behind Vite.</div><br/>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://rubygems.org/gems/rolldown"><img alt="Gem Version" src="https://img.shields.io/gem/v/rolldown"></a>
|
|
9
|
+
<a href="https://rolldown.rs"><img alt="Documentation" src="https://img.shields.io/badge/rolldown.rs-documentation-green"></a>
|
|
10
|
+
<a href="https://github.com/marcoroth/rolldown-ruby/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/rolldown-ruby"></a>
|
|
11
|
+
<a href="https://github.com/marcoroth/rolldown-ruby/issues"><img alt="Issues" src="https://img.shields.io/github/issues/marcoroth/rolldown-ruby"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<br/>
|
|
15
|
+
|
|
16
|
+
### What is Rolldown for Ruby?
|
|
17
|
+
|
|
18
|
+
Ruby bindings for [Rolldown](https://rolldown.rs), the Rust-based bundler for JavaScript. Bundle JavaScript from Ruby, without the need for a JavaScript runtime.
|
|
19
|
+
|
|
20
|
+
Everything here is Rolldown doing the work. For what the options mean and what it can do, [rolldown.rs](https://rolldown.rs) is the reference.
|
|
21
|
+
|
|
22
|
+
### Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle add rolldown
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Anywhere a precompiled gem is not published, the gem builds from source and needs the [Rust toolchain](https://rustup.rs) at 1.96 or newer.
|
|
29
|
+
|
|
30
|
+
### Usage
|
|
31
|
+
|
|
32
|
+
The options follow [Rolldown's JavaScript API](https://rolldown.rs/reference/config-options), 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.
|
|
126
|
+
|
|
127
|
+
### Development
|
|
128
|
+
|
|
129
|
+
The gem is a C extension over a Rust crate. `rust/` builds a static library and generates the C header with [cbindgen](https://github.com/mozilla/cbindgen), `ext/rolldown/` wraps it, and `lib/` is the Ruby API over that.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
bin/setup
|
|
133
|
+
bundle exec rake
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`sig/` is generated from the `#:` annotations next to the code. Regenerate it with `rake rbs` after changing a signature, and CI checks that it matches.
|
|
137
|
+
|
|
138
|
+
### Acknowledgements
|
|
139
|
+
|
|
140
|
+
[Rolldown](https://rolldown.rs) is maintained at [rolldown/rolldown](https://github.com/rolldown/rolldown) and is part of [VoidZero](https://voidzero.dev)'s toolchain for JavaScript. This gem only calls into it. Thank you for building these tools!
|
|
141
|
+
|
|
142
|
+
### Contributing
|
|
143
|
+
|
|
144
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/rolldown-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/marcoroth/rolldown-ruby/blob/main/CODE_OF_CONDUCT.md).
|
|
145
|
+
|
|
146
|
+
Issues with parsing, transforming or minifying itself belong [upstream](https://github.com/rolldown/rolldown/issues), since this gem does none of that. Issues with the Ruby API, the build, or the bindings belong here.
|
|
147
|
+
|
|
148
|
+
### License
|
|
149
|
+
|
|
150
|
+
The Ruby, C, and Rust code in this gem is available under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
151
|
+
|
|
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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|