ruby_wasm_ui 0.8.3 → 0.9.0

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +0 -2
  3. data/Makefile +53 -0
  4. data/README.md +22 -4
  5. data/examples/.gitignore +3 -0
  6. data/examples/Gemfile.lock +14 -16
  7. data/examples/src/index.html +21 -0
  8. data/examples/src/index.rb +26 -0
  9. data/exe/ruby-wasm-ui +6 -0
  10. data/lib/ruby_wasm_ui/cli/command/base.rb +174 -0
  11. data/lib/ruby_wasm_ui/cli/command/dev.rb +206 -0
  12. data/lib/ruby_wasm_ui/cli/command/pack.rb +36 -0
  13. data/lib/ruby_wasm_ui/cli/command/rebuild.rb +38 -0
  14. data/lib/ruby_wasm_ui/cli/command/setup.rb +130 -0
  15. data/lib/ruby_wasm_ui/cli/command.rb +48 -0
  16. data/lib/ruby_wasm_ui/version.rb +1 -1
  17. data/lib/ruby_wasm_ui.rb +8 -8
  18. data/package-lock.json +2 -2
  19. data/package.json +1 -1
  20. data/packages/npm-packages/runtime/package-lock.json +2 -2
  21. data/packages/npm-packages/runtime/package.json +1 -1
  22. data/packages/npm-packages/runtime/rollup.config.mjs +1 -1
  23. data/spec/ruby_wasm_ui/cli/command/base_spec.rb +358 -0
  24. data/spec/ruby_wasm_ui/cli/command/dev_spec.rb +412 -0
  25. data/spec/ruby_wasm_ui/cli/command/pack_spec.rb +127 -0
  26. data/spec/ruby_wasm_ui/cli/command/rebuild_spec.rb +95 -0
  27. data/spec/ruby_wasm_ui/cli/command/setup_spec.rb +186 -0
  28. data/spec/ruby_wasm_ui/cli/command_spec.rb +118 -0
  29. data/{packages/npm-packages/runtime/spec → spec}/spec_helper.rb +1 -1
  30. metadata +96 -38
  31. data/packages/npm-packages/runtime/Gemfile +0 -3
  32. data/packages/npm-packages/runtime/Gemfile.lock +0 -26
  33. /data/lib/ruby_wasm_ui/{app.rb → runtime/app.rb} +0 -0
  34. /data/lib/ruby_wasm_ui/{component.rb → runtime/component.rb} +0 -0
  35. /data/lib/ruby_wasm_ui/{dispatcher.rb → runtime/dispatcher.rb} +0 -0
  36. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/attributes.rb +0 -0
  37. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/destroy_dom.rb +0 -0
  38. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/events.rb +0 -0
  39. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/mount_dom.rb +0 -0
  40. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/patch_dom.rb +0 -0
  41. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/scheduler.rb +0 -0
  42. /data/lib/ruby_wasm_ui/{dom.rb → runtime/dom.rb} +0 -0
  43. /data/lib/ruby_wasm_ui/{nodes_equal.rb → runtime/nodes_equal.rb} +0 -0
  44. /data/lib/ruby_wasm_ui/{template → runtime/template}/build_conditional_group.rb +0 -0
  45. /data/lib/ruby_wasm_ui/{template → runtime/template}/build_for_group.rb +0 -0
  46. /data/lib/ruby_wasm_ui/{template → runtime/template}/build_vdom.rb +0 -0
  47. /data/lib/ruby_wasm_ui/{template → runtime/template}/parser.rb +0 -0
  48. /data/lib/ruby_wasm_ui/{template.rb → runtime/template.rb} +0 -0
  49. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/arrays.rb +0 -0
  50. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/objects.rb +0 -0
  51. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/props.rb +0 -0
  52. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/strings.rb +0 -0
  53. /data/lib/ruby_wasm_ui/{utils.rb → runtime/utils.rb} +0 -0
  54. /data/lib/ruby_wasm_ui/{vdom.rb → runtime/vdom.rb} +0 -0
  55. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/component_spec.rb +0 -0
  56. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/dom/scheduler_spec.rb +0 -0
  57. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/nodes_equal_spec.rb +0 -0
  58. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_conditional_group_spec.rb +0 -0
  59. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_for_group_spec.rb +0 -0
  60. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_vdom_spec.rb +0 -0
  61. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/parser_spec.rb +0 -0
  62. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/arrays_spec.rb +0 -0
  63. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/objects_spec.rb +0 -0
  64. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/props_spec.rb +0 -0
  65. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/strings_spec.rb +0 -0
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module RubyWasmUi
6
+ module Cli
7
+ class Command
8
+ class Setup < Base
9
+ def self.description
10
+ "Set up the project for ruby-wasm-ui"
11
+ end
12
+
13
+ def run(_argv)
14
+ log_info("Setting up ruby-wasm-ui project...")
15
+ puts ""
16
+
17
+ # Check Ruby version
18
+ ruby_version_str = check_ruby_version
19
+
20
+ # Configure excluded gems for WASM build
21
+ log_info("Step 1/3: Configuring excluded gems...")
22
+ configure_excluded_gems
23
+ log_success("✓ Excluded gems configured")
24
+
25
+ # Build Ruby WASM
26
+ puts ""
27
+ log_info("Step 2/3: Building Ruby WASM...")
28
+ log_info("Running: rbwasm build --ruby-version #{ruby_version_str} -o ruby.wasm")
29
+ build_ruby_wasm(ruby_version_str)
30
+ log_success("✓ Ruby WASM build completed")
31
+
32
+ # Update .gitignore
33
+ puts ""
34
+ log_info("Step 3/4: Updating .gitignore...")
35
+ update_gitignore(["*.wasm", "/rubies", "/build"])
36
+ log_success("✓ .gitignore updated")
37
+
38
+ # Create initial files
39
+ puts ""
40
+ log_info("Step 4/4: Creating initial files...")
41
+ create_initial_files
42
+
43
+ puts ""
44
+ log_success("Setup completed successfully!")
45
+ end
46
+
47
+ private
48
+
49
+ def create_initial_files
50
+ # Skip if src directory exists
51
+ if Dir.exist?("src")
52
+ log_info("src directory already exists, skipping initial file creation")
53
+ return
54
+ end
55
+
56
+ # Skip if files already exist
57
+ if File.exist?("src/index.html")
58
+ log_info("src/index.html already exists, skipping initial file creation")
59
+ return
60
+ end
61
+
62
+ if File.exist?("src/index.rb")
63
+ log_info("src/index.rb already exists, skipping initial file creation")
64
+ return
65
+ end
66
+
67
+ # Create src directory
68
+ Dir.mkdir("src")
69
+
70
+ # Create index.html
71
+ File.write("src/index.html", <<~HTML)
72
+ <!DOCTYPE html>
73
+ <html lang="en">
74
+ <head>
75
+ <meta charset="UTF-8" />
76
+ <title>My App</title>
77
+ <script type="module">
78
+ import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.7.2/dist/browser/+esm";
79
+ const response = await fetch("../src.wasm");
80
+ const module = await WebAssembly.compileStreaming(response);
81
+ const { vm } = await DefaultRubyVM(module);
82
+ vm.evalAsync(`
83
+ require "ruby_wasm_ui"
84
+ require_relative './src/index.rb'
85
+ `);
86
+ </script>
87
+ </head>
88
+ <body>
89
+ <h1>My App</h1>
90
+ <div id="app"></div>
91
+ </body>
92
+ </html>
93
+ HTML
94
+
95
+ # Create index.rb
96
+ File.write("src/index.rb", <<~RUBY)
97
+ # Simple Hello World component
98
+ HelloComponent = RubyWasmUi.define_component(
99
+ state: ->(props) {
100
+ { message: props[:message] || "Hello, Ruby WASM UI!" }
101
+ },
102
+ template: ->() {
103
+ RubyWasmUi::Template::Parser.parse_and_eval(<<~HTML, binding)
104
+ <div>
105
+ <h2>{state[:message]}</h2>
106
+ <button on="{ click: -> { update_message } }">
107
+ Click me!
108
+ </button>
109
+ </div>
110
+ HTML
111
+ },
112
+ methods: {
113
+ update_message: ->() {
114
+ update_state(message: "You clicked the button!")
115
+ }
116
+ }
117
+ )
118
+
119
+ # Create and mount the app
120
+ app = RubyWasmUi::App.create(HelloComponent, message: "Hello, Ruby WASM UI!")
121
+ app_element = JS.global[:document].getElementById("app")
122
+ app.mount(app_element)
123
+ RUBY
124
+
125
+ log_success("✓ Initial files created: src/index.html, src/index.rb")
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "command/setup"
4
+ require_relative "command/dev"
5
+ require_relative "command/pack"
6
+ require_relative "command/rebuild"
7
+
8
+ module RubyWasmUi
9
+ module Cli
10
+ class Command
11
+ COMMANDS = {
12
+ "setup" => Command::Setup,
13
+ "dev" => Command::Dev,
14
+ "pack" => Command::Pack,
15
+ "rebuild" => Command::Rebuild
16
+ }.freeze
17
+
18
+ def self.run(argv)
19
+ command_name = argv[0]
20
+
21
+ if command_name.nil?
22
+ show_usage
23
+ raise SystemExit.new(1)
24
+ end
25
+
26
+ command_class = COMMANDS[command_name]
27
+ if command_class.nil?
28
+ puts "Unknown command: #{command_name}"
29
+ puts ""
30
+ show_usage
31
+ raise SystemExit.new(1)
32
+ end
33
+
34
+ command_class.new.run(argv[1..-1])
35
+ end
36
+
37
+ def self.show_usage
38
+ puts "Usage: ruby-wasm-ui <command>"
39
+ puts ""
40
+ puts "Commands:"
41
+ COMMANDS.each do |name, klass|
42
+ description = klass.description || ""
43
+ puts " #{name.ljust(12)}#{description}"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyWasmUi
4
- VERSION = "0.8.3"
4
+ VERSION = "0.9.0"
5
5
  end
data/lib/ruby_wasm_ui.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "ruby_wasm_ui/app"
4
- require_relative "ruby_wasm_ui/component"
5
- require_relative "ruby_wasm_ui/dispatcher"
6
- require_relative "ruby_wasm_ui/dom"
7
- require_relative "ruby_wasm_ui/nodes_equal"
8
- require_relative "ruby_wasm_ui/template"
9
- require_relative "ruby_wasm_ui/utils"
10
- require_relative "ruby_wasm_ui/vdom"
3
+ require_relative "ruby_wasm_ui/runtime/app"
4
+ require_relative "ruby_wasm_ui/runtime/component"
5
+ require_relative "ruby_wasm_ui/runtime/dispatcher"
6
+ require_relative "ruby_wasm_ui/runtime/dom"
7
+ require_relative "ruby_wasm_ui/runtime/nodes_equal"
8
+ require_relative "ruby_wasm_ui/runtime/template"
9
+ require_relative "ruby_wasm_ui/runtime/utils"
10
+ require_relative "ruby_wasm_ui/runtime/vdom"
11
11
  require_relative "ruby_wasm_ui/version"
12
12
 
13
13
  module RubyWasmUi
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ruby-wasm-ui-project",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "ruby-wasm-ui-project",
9
- "version": "0.8.2",
9
+ "version": "0.9.0",
10
10
  "license": "MIT",
11
11
  "workspaces": [
12
12
  "packages/*"
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruby-wasm-ui-project",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "private": true,
5
5
  "description": "A project to ruby.wasm ui framework",
6
6
  "scripts": {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ruby-wasm-ui",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "ruby-wasm-ui",
9
- "version": "0.8.3",
9
+ "version": "0.9.0",
10
10
  "license": "MIT",
11
11
  "devDependencies": {
12
12
  "@rollup/plugin-replace": "^6.0.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruby-wasm-ui",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "description": "",
5
5
  "main": "dist/ruby-wasm-ui.js",
6
6
  "files": [
@@ -18,7 +18,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
18
18
 
19
19
  // Find all Ruby files in ruby_wasm_ui directory
20
20
  const rubyFiles = glob
21
- .sync("src/ruby_wasm_ui/**/*.rb")
21
+ .sync("src/ruby_wasm_ui/runtime/**/*.rb")
22
22
  .map((file) => file.replace("src/", ""))
23
23
  .sort((a, b) => {
24
24
  // Files in root directory should be loaded first
@@ -0,0 +1,358 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ RSpec.describe RubyWasmUi::Cli::Command::Base do
8
+ let(:base_instance) { described_class.new }
9
+
10
+ describe '#run' do
11
+ it 'raises NotImplementedError' do
12
+ expect { base_instance.run([]) }.to raise_error(NotImplementedError, /Subclasses must implement #run/)
13
+ end
14
+ end
15
+
16
+ describe '#update_gitignore' do
17
+ let(:temp_dir) { Dir.mktmpdir }
18
+ let(:original_dir) { Dir.pwd }
19
+
20
+ around do |example|
21
+ Dir.chdir(temp_dir) do
22
+ example.run
23
+ end
24
+ ensure
25
+ FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
26
+ end
27
+
28
+ context 'when .gitignore does not exist' do
29
+ it 'creates .gitignore with new entries' do
30
+ base_instance.send(:update_gitignore, ['*.wasm', '/rubies'])
31
+ content = File.read('.gitignore')
32
+ expect(content).to include('*.wasm')
33
+ expect(content).to include('/rubies')
34
+ end
35
+ end
36
+
37
+ context 'when .gitignore exists' do
38
+ before do
39
+ File.write('.gitignore', "node_modules/\n*.log\n")
40
+ end
41
+
42
+ it 'adds new entries to existing .gitignore' do
43
+ base_instance.send(:update_gitignore, ['*.wasm', '/rubies'])
44
+ content = File.read('.gitignore')
45
+ expect(content).to include('node_modules/')
46
+ expect(content).to include('*.log')
47
+ expect(content).to include('*.wasm')
48
+ expect(content).to include('/rubies')
49
+ end
50
+
51
+ it 'does not add duplicate entries' do
52
+ File.write('.gitignore', "*.wasm\n")
53
+ base_instance.send(:update_gitignore, ['*.wasm', '/rubies'])
54
+ content = File.read('.gitignore')
55
+ expect(content.scan(/^\*\.wasm$/).count).to eq(1)
56
+ end
57
+
58
+ it 'outputs message when entries are added' do
59
+ expect { base_instance.send(:update_gitignore, ['*.wasm']) }.to output(
60
+ /Added to .gitignore: \*\.wasm/
61
+ ).to_stdout
62
+ end
63
+
64
+ it 'outputs message when no entries are added' do
65
+ File.write('.gitignore', "*.wasm\n")
66
+ expect { base_instance.send(:update_gitignore, ['*.wasm']) }.to output(
67
+ /No new entries added to .gitignore/
68
+ ).to_stdout
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#run_command' do
74
+ context 'when command succeeds' do
75
+ it 'executes the command and outputs stdout' do
76
+ expect { base_instance.send(:run_command, 'echo "test output"') }.to output(
77
+ /test output/
78
+ ).to_stdout
79
+ end
80
+
81
+ it 'does not raise error' do
82
+ expect { base_instance.send(:run_command, 'echo "test"') }.not_to raise_error
83
+ end
84
+
85
+ it 'returns true' do
86
+ result = base_instance.send(:run_command, 'echo "test"')
87
+ expect(result).to be true
88
+ end
89
+ end
90
+
91
+ context 'when command fails' do
92
+ it 'outputs error message and exits by default' do
93
+ expect { base_instance.send(:run_command, 'false') }.to output(
94
+ /Command failed: false/
95
+ ).to_stdout.and raise_error(SystemExit)
96
+ end
97
+
98
+ it 'exits with correct status code' do
99
+ expect { base_instance.send(:run_command, 'false') }.to raise_error(SystemExit) do |error|
100
+ expect(error.status).to eq(1)
101
+ end
102
+ end
103
+
104
+ context 'when exit_on_error is false' do
105
+ it 'outputs error message but does not exit' do
106
+ expect { base_instance.send(:run_command, 'false', exit_on_error: false) }.to output(
107
+ /Command failed: false/
108
+ ).to_stdout
109
+ expect { base_instance.send(:run_command, 'false', exit_on_error: false) }.not_to raise_error
110
+ end
111
+
112
+ it 'returns false' do
113
+ result = base_instance.send(:run_command, 'false', exit_on_error: false)
114
+ expect(result).to be false
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '#ensure_src_directory' do
121
+ let(:temp_dir) { Dir.mktmpdir }
122
+ let(:original_dir) { Dir.pwd }
123
+
124
+ around do |example|
125
+ Dir.chdir(temp_dir) do
126
+ example.run
127
+ end
128
+ ensure
129
+ FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
130
+ end
131
+
132
+ context 'when src directory does not exist' do
133
+ it 'outputs error message and exits' do
134
+ expect { base_instance.send(:ensure_src_directory) }.to output(
135
+ /src directory not found. Please run 'ruby-wasm-ui setup' first./
136
+ ).to_stdout.and raise_error(SystemExit)
137
+ end
138
+
139
+ it 'exits with status 1' do
140
+ expect { base_instance.send(:ensure_src_directory) }.to raise_error(SystemExit) do |error|
141
+ expect(error.status).to eq(1)
142
+ end
143
+ end
144
+ end
145
+
146
+ context 'when src directory exists' do
147
+ before do
148
+ FileUtils.mkdir_p('src')
149
+ end
150
+
151
+ it 'does not raise error' do
152
+ expect { base_instance.send(:ensure_src_directory) }.not_to raise_error
153
+ end
154
+
155
+ it 'does not output error message' do
156
+ expect { base_instance.send(:ensure_src_directory) }.not_to output(
157
+ /src directory not found/
158
+ ).to_stdout
159
+ end
160
+ end
161
+ end
162
+
163
+ describe '#ensure_ruby_wasm' do
164
+ let(:temp_dir) { Dir.mktmpdir }
165
+ let(:original_dir) { Dir.pwd }
166
+
167
+ around do |example|
168
+ Dir.chdir(temp_dir) do
169
+ example.run
170
+ end
171
+ ensure
172
+ FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
173
+ end
174
+
175
+ context 'when ruby.wasm does not exist' do
176
+ it 'outputs error message and exits' do
177
+ expect { base_instance.send(:ensure_ruby_wasm) }.to output(
178
+ /ruby.wasm not found. Please run 'ruby-wasm-ui setup' first./
179
+ ).to_stdout.and raise_error(SystemExit)
180
+ end
181
+
182
+ it 'exits with status 1' do
183
+ expect { base_instance.send(:ensure_ruby_wasm) }.to raise_error(SystemExit) do |error|
184
+ expect(error.status).to eq(1)
185
+ end
186
+ end
187
+ end
188
+
189
+ context 'when ruby.wasm exists' do
190
+ before do
191
+ FileUtils.touch('ruby.wasm')
192
+ end
193
+
194
+ it 'does not raise error' do
195
+ expect { base_instance.send(:ensure_ruby_wasm) }.not_to raise_error
196
+ end
197
+
198
+ it 'does not output error message' do
199
+ expect { base_instance.send(:ensure_ruby_wasm) }.not_to output(
200
+ /ruby.wasm not found/
201
+ ).to_stdout
202
+ end
203
+ end
204
+ end
205
+
206
+ describe '#check_ruby_version' do
207
+ context 'when Ruby version is 3.2 or higher' do
208
+ before do
209
+ stub_const('RUBY_VERSION', '3.2.0')
210
+ end
211
+
212
+ it 'returns the version string' do
213
+ result = base_instance.send(:check_ruby_version)
214
+ expect(result).to eq('3.2')
215
+ end
216
+
217
+ it 'does not exit' do
218
+ expect { base_instance.send(:check_ruby_version) }.not_to raise_error
219
+ end
220
+ end
221
+
222
+ context 'when Ruby version is 3.4' do
223
+ before do
224
+ stub_const('RUBY_VERSION', '3.4.0')
225
+ end
226
+
227
+ it 'returns the version string' do
228
+ result = base_instance.send(:check_ruby_version)
229
+ expect(result).to eq('3.4')
230
+ end
231
+ end
232
+
233
+ context 'when Ruby version is less than 3.2' do
234
+ context 'when Ruby version is 3.1' do
235
+ before do
236
+ stub_const('RUBY_VERSION', '3.1.5')
237
+ end
238
+
239
+ it 'outputs error message and exits' do
240
+ expect { base_instance.send(:check_ruby_version) }.to output(
241
+ /\[ERROR\] Ruby WASM requires Ruby 3.2 or higher. Current version: 3.1.5/
242
+ ).to_stdout.and raise_error(SystemExit) do |error|
243
+ expect(error.status).to eq(1)
244
+ end
245
+ end
246
+ end
247
+
248
+ context 'when Ruby version is 2.7' do
249
+ before do
250
+ stub_const('RUBY_VERSION', '2.7.8')
251
+ end
252
+
253
+ it 'outputs error message and exits' do
254
+ expect { base_instance.send(:check_ruby_version) }.to output(
255
+ /\[ERROR\] Ruby WASM requires Ruby 3.2 or higher. Current version: 2.7.8/
256
+ ).to_stdout.and raise_error(SystemExit) do |error|
257
+ expect(error.status).to eq(1)
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#configure_excluded_gems' do
265
+ before do
266
+ # Clear EXCLUDED_GEMS before each test
267
+ RubyWasm::Packager::EXCLUDED_GEMS.clear if defined?(RubyWasm::Packager::EXCLUDED_GEMS)
268
+ end
269
+
270
+ it 'excludes rack, puma, nio4r, listen, and ffi gems' do
271
+ allow(Bundler).to receive(:definition).and_return(
272
+ double(
273
+ resolve: double(
274
+ materialize: [
275
+ double(name: 'rack'),
276
+ double(name: 'puma'),
277
+ double(name: 'nio4r'),
278
+ double(name: 'listen'),
279
+ double(name: 'ffi'),
280
+ double(name: 'js'),
281
+ double(name: 'ruby_wasm')
282
+ ]
283
+ ),
284
+ requested_dependencies: []
285
+ )
286
+ )
287
+
288
+ base_instance.send(:configure_excluded_gems)
289
+
290
+ expect(RubyWasm::Packager::EXCLUDED_GEMS).to include('rack', 'puma', 'nio4r', 'listen', 'ffi')
291
+ expect(RubyWasm::Packager::EXCLUDED_GEMS).not_to include('js', 'ruby_wasm')
292
+ end
293
+
294
+ it 'always excludes nio4r, puma, rack, listen, and ffi even if not in dependencies' do
295
+ allow(Bundler).to receive(:definition).and_return(
296
+ double(
297
+ resolve: double(
298
+ materialize: [
299
+ double(name: 'js'),
300
+ double(name: 'ruby_wasm')
301
+ ]
302
+ ),
303
+ requested_dependencies: []
304
+ )
305
+ )
306
+
307
+ base_instance.send(:configure_excluded_gems)
308
+
309
+ # Always excluded gems should be in the list even if not in dependencies
310
+ expect(RubyWasm::Packager::EXCLUDED_GEMS).to include('nio4r', 'puma', 'rack', 'listen', 'ffi')
311
+ expect(RubyWasm::Packager::EXCLUDED_GEMS).not_to include('js', 'ruby_wasm')
312
+ end
313
+
314
+ it 'excludes development/test gems' do
315
+ allow(Bundler).to receive(:definition).and_return(
316
+ double(
317
+ resolve: double(
318
+ materialize: [
319
+ double(name: 'rspec'),
320
+ double(name: 'rubocop'),
321
+ double(name: 'rake'),
322
+ double(name: 'js')
323
+ ]
324
+ ),
325
+ requested_dependencies: []
326
+ )
327
+ )
328
+
329
+ base_instance.send(:configure_excluded_gems)
330
+
331
+ expect(RubyWasm::Packager::EXCLUDED_GEMS).to include('rspec', 'rubocop', 'rake')
332
+ expect(RubyWasm::Packager::EXCLUDED_GEMS).not_to include('js')
333
+ end
334
+ end
335
+
336
+ describe '#build_ruby_wasm' do
337
+ let(:cli_instance) { instance_double(RubyWasm::CLI) }
338
+
339
+ before do
340
+ allow(RubyWasm::CLI).to receive(:new).and_return(cli_instance)
341
+ allow(cli_instance).to receive(:run)
342
+ end
343
+
344
+ it 'executes rbwasm build command with correct arguments' do
345
+ expect(RubyWasm::CLI).to receive(:new).with(stdout: $stdout, stderr: $stderr).and_return(cli_instance)
346
+ expect(cli_instance).to receive(:run).with(['build', '--ruby-version', '3.2', '-o', 'ruby.wasm'])
347
+
348
+ base_instance.send(:build_ruby_wasm, '3.2')
349
+ end
350
+
351
+ it 'executes rbwasm build command with different version' do
352
+ expect(RubyWasm::CLI).to receive(:new).with(stdout: $stdout, stderr: $stderr).and_return(cli_instance)
353
+ expect(cli_instance).to receive(:run).with(['build', '--ruby-version', '3.4', '-o', 'ruby.wasm'])
354
+
355
+ base_instance.send(:build_ruby_wasm, '3.4')
356
+ end
357
+ end
358
+ end