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.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +0 -2
- data/Makefile +53 -0
- data/README.md +22 -4
- data/examples/.gitignore +3 -0
- data/examples/Gemfile.lock +14 -16
- data/examples/src/index.html +21 -0
- data/examples/src/index.rb +26 -0
- data/exe/ruby-wasm-ui +6 -0
- data/lib/ruby_wasm_ui/cli/command/base.rb +174 -0
- data/lib/ruby_wasm_ui/cli/command/dev.rb +206 -0
- data/lib/ruby_wasm_ui/cli/command/pack.rb +36 -0
- data/lib/ruby_wasm_ui/cli/command/rebuild.rb +38 -0
- data/lib/ruby_wasm_ui/cli/command/setup.rb +130 -0
- data/lib/ruby_wasm_ui/cli/command.rb +48 -0
- data/lib/ruby_wasm_ui/version.rb +1 -1
- data/lib/ruby_wasm_ui.rb +8 -8
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/packages/npm-packages/runtime/package-lock.json +2 -2
- data/packages/npm-packages/runtime/package.json +1 -1
- data/packages/npm-packages/runtime/rollup.config.mjs +1 -1
- data/spec/ruby_wasm_ui/cli/command/base_spec.rb +358 -0
- data/spec/ruby_wasm_ui/cli/command/dev_spec.rb +412 -0
- data/spec/ruby_wasm_ui/cli/command/pack_spec.rb +127 -0
- data/spec/ruby_wasm_ui/cli/command/rebuild_spec.rb +95 -0
- data/spec/ruby_wasm_ui/cli/command/setup_spec.rb +186 -0
- data/spec/ruby_wasm_ui/cli/command_spec.rb +118 -0
- data/{packages/npm-packages/runtime/spec → spec}/spec_helper.rb +1 -1
- metadata +96 -38
- data/packages/npm-packages/runtime/Gemfile +0 -3
- data/packages/npm-packages/runtime/Gemfile.lock +0 -26
- /data/lib/ruby_wasm_ui/{app.rb → runtime/app.rb} +0 -0
- /data/lib/ruby_wasm_ui/{component.rb → runtime/component.rb} +0 -0
- /data/lib/ruby_wasm_ui/{dispatcher.rb → runtime/dispatcher.rb} +0 -0
- /data/lib/ruby_wasm_ui/{dom → runtime/dom}/attributes.rb +0 -0
- /data/lib/ruby_wasm_ui/{dom → runtime/dom}/destroy_dom.rb +0 -0
- /data/lib/ruby_wasm_ui/{dom → runtime/dom}/events.rb +0 -0
- /data/lib/ruby_wasm_ui/{dom → runtime/dom}/mount_dom.rb +0 -0
- /data/lib/ruby_wasm_ui/{dom → runtime/dom}/patch_dom.rb +0 -0
- /data/lib/ruby_wasm_ui/{dom → runtime/dom}/scheduler.rb +0 -0
- /data/lib/ruby_wasm_ui/{dom.rb → runtime/dom.rb} +0 -0
- /data/lib/ruby_wasm_ui/{nodes_equal.rb → runtime/nodes_equal.rb} +0 -0
- /data/lib/ruby_wasm_ui/{template → runtime/template}/build_conditional_group.rb +0 -0
- /data/lib/ruby_wasm_ui/{template → runtime/template}/build_for_group.rb +0 -0
- /data/lib/ruby_wasm_ui/{template → runtime/template}/build_vdom.rb +0 -0
- /data/lib/ruby_wasm_ui/{template → runtime/template}/parser.rb +0 -0
- /data/lib/ruby_wasm_ui/{template.rb → runtime/template.rb} +0 -0
- /data/lib/ruby_wasm_ui/{utils → runtime/utils}/arrays.rb +0 -0
- /data/lib/ruby_wasm_ui/{utils → runtime/utils}/objects.rb +0 -0
- /data/lib/ruby_wasm_ui/{utils → runtime/utils}/props.rb +0 -0
- /data/lib/ruby_wasm_ui/{utils → runtime/utils}/strings.rb +0 -0
- /data/lib/ruby_wasm_ui/{utils.rb → runtime/utils.rb} +0 -0
- /data/lib/ruby_wasm_ui/{vdom.rb → runtime/vdom.rb} +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/component_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/dom/scheduler_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/nodes_equal_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_conditional_group_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_for_group_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_vdom_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/parser_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/arrays_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/objects_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/props_spec.rb +0 -0
- /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/strings_spec.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba3eb2a44a8b73913fb511fe95407173c865a57890f3622915b8e6eb6ef42c87
|
|
4
|
+
data.tar.gz: '00526093db65a601dc375cd3c9944dde4d9c04bee00cf33701e656914e89e2c1'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3529c934f5bef222cdcbb85b3b83e8ca323b36da9ea0d150c5977c4ebe1dfe282d8e97e7933eb8ae2bf90e632373d731928e14e48e105bede36239f782ab7fd5
|
|
7
|
+
data.tar.gz: e883ae7b71653376ccd06de8fe760aa909116614c1b0ad7a8357e9427b0bd9709dfa5de43be241c0ba1630a37a95ed02f099eb5515880a07b6acf4afde3f8cf5
|
data/.github/workflows/rspec.yml
CHANGED
data/Makefile
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.PHONY: bump help
|
|
2
|
+
|
|
3
|
+
help:
|
|
4
|
+
@echo "Usage: make bump <version>"
|
|
5
|
+
@echo " make bump VERSION=<version>"
|
|
6
|
+
@echo "Example: make bump 0.9.0"
|
|
7
|
+
@echo ""
|
|
8
|
+
@echo "This command updates the version number in the following files:"
|
|
9
|
+
@echo " - lib/ruby_wasm_ui/version.rb"
|
|
10
|
+
@echo " - package.json"
|
|
11
|
+
@echo " - packages/npm-packages/runtime/package.json"
|
|
12
|
+
@echo " - README.md"
|
|
13
|
+
@echo " - package-lock.json (via npm install)"
|
|
14
|
+
@echo " - packages/npm-packages/runtime/package-lock.json (via npm install)"
|
|
15
|
+
|
|
16
|
+
# Get version from position argument (if VERSION is not already set)
|
|
17
|
+
VERSION_ARG := $(word 2,$(MAKECMDGOALS))
|
|
18
|
+
ifndef VERSION
|
|
19
|
+
ifneq ($(VERSION_ARG),)
|
|
20
|
+
VERSION := $(VERSION_ARG)
|
|
21
|
+
endif
|
|
22
|
+
endif
|
|
23
|
+
|
|
24
|
+
bump:
|
|
25
|
+
@if [ -z "$(VERSION)" ]; then \
|
|
26
|
+
echo "Error: VERSION is required. Usage: make bump 0.9.0"; \
|
|
27
|
+
exit 1; \
|
|
28
|
+
fi
|
|
29
|
+
@echo "Bumping version to $(VERSION)..."
|
|
30
|
+
@# Update lib/ruby_wasm_ui/version.rb
|
|
31
|
+
@sed -i '' 's/VERSION = ".*"/VERSION = "$(VERSION)"/' lib/ruby_wasm_ui/version.rb
|
|
32
|
+
@echo "✓ Updated lib/ruby_wasm_ui/version.rb"
|
|
33
|
+
@# Update root package.json
|
|
34
|
+
@sed -i '' 's/"version": ".*"/"version": "$(VERSION)"/' package.json
|
|
35
|
+
@echo "✓ Updated package.json"
|
|
36
|
+
@# Update packages/npm-packages/runtime/package.json
|
|
37
|
+
@sed -i '' 's/"version": ".*"/"version": "$(VERSION)"/' packages/npm-packages/runtime/package.json
|
|
38
|
+
@echo "✓ Updated packages/npm-packages/runtime/package.json"
|
|
39
|
+
@# Update README.md (unpkg.com URL)
|
|
40
|
+
@sed -i '' 's|unpkg.com/ruby-wasm-ui@[0-9.]*|unpkg.com/ruby-wasm-ui@$(VERSION)|' README.md
|
|
41
|
+
@echo "✓ Updated README.md"
|
|
42
|
+
@echo ""
|
|
43
|
+
@echo "Updating package-lock.json files..."
|
|
44
|
+
@npm install --package-lock-only
|
|
45
|
+
@echo "✓ Updated root package-lock.json"
|
|
46
|
+
@cd packages/npm-packages/runtime && npm install --package-lock-only && cd ../../..
|
|
47
|
+
@echo "✓ Updated packages/npm-packages/runtime/package-lock.json"
|
|
48
|
+
@echo ""
|
|
49
|
+
@echo "Version bumped to $(VERSION) successfully!"
|
|
50
|
+
|
|
51
|
+
# Prevent make from treating the version argument as a target
|
|
52
|
+
%:
|
|
53
|
+
@:
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Create an HTML file:
|
|
|
21
21
|
<!DOCTYPE html>
|
|
22
22
|
<html>
|
|
23
23
|
<head>
|
|
24
|
-
<script src="https://unpkg.com/ruby-wasm-ui@0.
|
|
24
|
+
<script src="https://unpkg.com/ruby-wasm-ui@0.9.0"></script>
|
|
25
25
|
<script defer type="text/ruby" src="app.rb"></script>
|
|
26
26
|
</head>
|
|
27
27
|
<body>
|
|
@@ -112,20 +112,38 @@ bundle install
|
|
|
112
112
|
|
|
113
113
|
### Building Your Application
|
|
114
114
|
|
|
115
|
-
1.
|
|
115
|
+
1. Set up your project (first time only):
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
bundle exec
|
|
118
|
+
bundle exec ruby-wasm-ui setup
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
This command will:
|
|
122
|
+
- Configure excluded gems for WASM build
|
|
123
|
+
- Build Ruby WASM file
|
|
124
|
+
- Update `.gitignore`
|
|
125
|
+
- Create initial `src/index.html` and `src/index.rb` files
|
|
126
|
+
|
|
121
127
|
2. Pack your application files:
|
|
122
128
|
|
|
123
129
|
```bash
|
|
124
|
-
bundle exec
|
|
130
|
+
bundle exec ruby-wasm-ui pack
|
|
125
131
|
```
|
|
126
132
|
|
|
127
133
|
This command packs your Ruby files from the `./src` directory into the WASM file.
|
|
128
134
|
|
|
135
|
+
**Additional Commands:**
|
|
136
|
+
|
|
137
|
+
- **Development server**: Start a development server with file watching and auto-build:
|
|
138
|
+
```bash
|
|
139
|
+
bundle exec ruby-wasm-ui dev
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **Rebuild Ruby WASM**: Rebuild the Ruby WASM file when you add new gems:
|
|
143
|
+
```bash
|
|
144
|
+
bundle exec ruby-wasm-ui rebuild
|
|
145
|
+
```
|
|
146
|
+
|
|
129
147
|
### Creating Your HTML File
|
|
130
148
|
|
|
131
149
|
Create an HTML file in the `src` directory that loads the WASM file:
|
data/examples/.gitignore
ADDED
data/examples/Gemfile.lock
CHANGED
|
@@ -3,36 +3,34 @@ PATH
|
|
|
3
3
|
specs:
|
|
4
4
|
ruby_wasm_ui (0.8.3)
|
|
5
5
|
js (~> 2.7)
|
|
6
|
+
listen (~> 3.8)
|
|
7
|
+
puma (~> 6.0)
|
|
8
|
+
rack (~> 3.0)
|
|
6
9
|
ruby_wasm (~> 2.7)
|
|
7
10
|
|
|
8
11
|
GEM
|
|
9
12
|
remote: https://rubygems.org/
|
|
10
13
|
specs:
|
|
14
|
+
ffi (1.17.2-arm64-darwin)
|
|
11
15
|
js (2.7.2)
|
|
16
|
+
listen (3.9.0)
|
|
17
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
18
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
|
12
19
|
logger (1.7.0)
|
|
20
|
+
nio4r (2.7.5)
|
|
21
|
+
puma (6.6.1)
|
|
22
|
+
nio4r (~> 2.0)
|
|
23
|
+
rack (3.2.4)
|
|
24
|
+
rb-fsevent (0.11.2)
|
|
25
|
+
rb-inotify (0.11.1)
|
|
26
|
+
ffi (~> 1.0)
|
|
13
27
|
ruby_wasm (2.7.2)
|
|
14
28
|
logger
|
|
15
|
-
ruby_wasm (2.7.2-aarch64-linux)
|
|
16
|
-
logger
|
|
17
|
-
ruby_wasm (2.7.2-aarch64-linux-musl)
|
|
18
|
-
logger
|
|
19
29
|
ruby_wasm (2.7.2-arm64-darwin)
|
|
20
30
|
logger
|
|
21
|
-
ruby_wasm (2.7.2-x86_64-darwin)
|
|
22
|
-
logger
|
|
23
|
-
ruby_wasm (2.7.2-x86_64-linux)
|
|
24
|
-
logger
|
|
25
|
-
ruby_wasm (2.7.2-x86_64-linux-musl)
|
|
26
|
-
logger
|
|
27
31
|
|
|
28
32
|
PLATFORMS
|
|
29
|
-
aarch64-linux
|
|
30
|
-
aarch64-linux-musl
|
|
31
33
|
arm64-darwin
|
|
32
|
-
ruby
|
|
33
|
-
x86_64-darwin
|
|
34
|
-
x86_64-linux
|
|
35
|
-
x86_64-linux-musl
|
|
36
34
|
|
|
37
35
|
DEPENDENCIES
|
|
38
36
|
ruby_wasm_ui!
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>My App</title>
|
|
6
|
+
<script type="module">
|
|
7
|
+
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.7.2/dist/browser/+esm";
|
|
8
|
+
const response = await fetch("../src.wasm");
|
|
9
|
+
const module = await WebAssembly.compileStreaming(response);
|
|
10
|
+
const { vm } = await DefaultRubyVM(module);
|
|
11
|
+
vm.evalAsync(`
|
|
12
|
+
require "ruby_wasm_ui"
|
|
13
|
+
require_relative './src/index.rb'
|
|
14
|
+
`);
|
|
15
|
+
</script>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<h1>My App</h1>
|
|
19
|
+
<div id="app"></div>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Simple Hello World component
|
|
2
|
+
HelloComponent = RubyWasmUi.define_component(
|
|
3
|
+
state: ->(props) {
|
|
4
|
+
{ message: props[:message] || "Hello, Ruby WASM UI!" }
|
|
5
|
+
},
|
|
6
|
+
template: ->() {
|
|
7
|
+
RubyWasmUi::Template::Parser.parse_and_eval(<<~HTML, binding)
|
|
8
|
+
<div>
|
|
9
|
+
<h2>{state[:message]}</h2>
|
|
10
|
+
<button on="{ click: -> { update_message } }">
|
|
11
|
+
Click me!
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
HTML
|
|
15
|
+
},
|
|
16
|
+
methods: {
|
|
17
|
+
update_message: ->() {
|
|
18
|
+
update_state(message: "You clicked the button!")
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Create and mount the app
|
|
24
|
+
app = RubyWasmUi::App.create(HelloComponent, message: "Hello, Ruby WASM UI!")
|
|
25
|
+
app_element = JS.global[:document].getElementById("app")
|
|
26
|
+
app.mount(app_element)
|
data/exe/ruby-wasm-ui
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "ruby_wasm"
|
|
6
|
+
require "ruby_wasm/cli"
|
|
7
|
+
|
|
8
|
+
module RubyWasmUi
|
|
9
|
+
module Cli
|
|
10
|
+
class Command
|
|
11
|
+
class Base
|
|
12
|
+
def self.description
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run(_argv)
|
|
17
|
+
raise NotImplementedError, "Subclasses must implement #run"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
protected
|
|
21
|
+
|
|
22
|
+
def run_command(command, exit_on_error: true)
|
|
23
|
+
log_debug("Executing command: #{command}")
|
|
24
|
+
|
|
25
|
+
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
|
26
|
+
stdin.close
|
|
27
|
+
|
|
28
|
+
# Read stdout and stderr in separate threads for real-time output
|
|
29
|
+
stdout_thread = Thread.new do
|
|
30
|
+
stdout.each_line do |line|
|
|
31
|
+
print line
|
|
32
|
+
$stdout.flush
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
stderr_thread = Thread.new do
|
|
37
|
+
stderr.each_line do |line|
|
|
38
|
+
$stderr.print line
|
|
39
|
+
$stderr.flush
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Wait for both threads to finish reading
|
|
44
|
+
stdout_thread.join
|
|
45
|
+
stderr_thread.join
|
|
46
|
+
|
|
47
|
+
# Wait for the process to finish
|
|
48
|
+
exit_status = wait_thr.value
|
|
49
|
+
|
|
50
|
+
unless exit_status.success?
|
|
51
|
+
log_error("Command failed: #{command}")
|
|
52
|
+
if exit_on_error
|
|
53
|
+
raise SystemExit.new(exit_status.exitstatus)
|
|
54
|
+
else
|
|
55
|
+
return false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ensure_src_directory
|
|
64
|
+
unless Dir.exist?("src")
|
|
65
|
+
log_error("src directory not found. Please run 'ruby-wasm-ui setup' first.")
|
|
66
|
+
raise SystemExit.new(1)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def ensure_ruby_wasm
|
|
71
|
+
unless File.exist?("ruby.wasm")
|
|
72
|
+
log_error("ruby.wasm not found. Please run 'ruby-wasm-ui setup' first.")
|
|
73
|
+
raise SystemExit.new(1)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_gitignore(entries_to_add)
|
|
78
|
+
gitignore_path = ".gitignore"
|
|
79
|
+
|
|
80
|
+
# Read existing .gitignore or create new content
|
|
81
|
+
if File.exist?(gitignore_path)
|
|
82
|
+
content = File.read(gitignore_path)
|
|
83
|
+
lines = content.lines.map(&:chomp)
|
|
84
|
+
else
|
|
85
|
+
lines = []
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Add entries that don't already exist
|
|
89
|
+
added_entries = []
|
|
90
|
+
entries_to_add.each do |entry|
|
|
91
|
+
unless lines.include?(entry)
|
|
92
|
+
lines << entry
|
|
93
|
+
added_entries << entry
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Write back to .gitignore
|
|
98
|
+
File.write(gitignore_path, lines.join("\n") + "\n")
|
|
99
|
+
if added_entries.any?
|
|
100
|
+
log_info("Added to .gitignore: #{added_entries.join(', ')}")
|
|
101
|
+
else
|
|
102
|
+
log_info("No new entries added to .gitignore (all entries already exist)")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
protected
|
|
107
|
+
|
|
108
|
+
def log_info(message)
|
|
109
|
+
puts "[INFO] #{message}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def log_success(message)
|
|
113
|
+
puts "[SUCCESS] #{message}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def log_error(message)
|
|
117
|
+
puts "[ERROR] #{message}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def log_debug(message)
|
|
121
|
+
puts "[DEBUG] #{message}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def check_ruby_version
|
|
125
|
+
ruby_version = RUBY_VERSION.split('.').map(&:to_i)
|
|
126
|
+
major, minor = ruby_version[0], ruby_version[1]
|
|
127
|
+
|
|
128
|
+
if major < 3 || (major == 3 && minor < 2)
|
|
129
|
+
log_error("Ruby WASM requires Ruby 3.2 or higher. Current version: #{RUBY_VERSION}")
|
|
130
|
+
raise SystemExit.new(1)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
"#{major}.#{minor}"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def configure_excluded_gems
|
|
137
|
+
# Get all gems from Bundler definition including all dependencies
|
|
138
|
+
definition = Bundler.definition
|
|
139
|
+
resolved = definition.resolve
|
|
140
|
+
|
|
141
|
+
# Get all resolved specs (including transitive dependencies)
|
|
142
|
+
all_specs = resolved.materialize(definition.requested_dependencies)
|
|
143
|
+
gem_names = all_specs.map(&:name).uniq
|
|
144
|
+
|
|
145
|
+
# Always exclude gems that cause WASM build errors
|
|
146
|
+
# These gems have native extensions that don't work in WASM environment
|
|
147
|
+
always_excluded = %w[nio4r puma rack listen ffi]
|
|
148
|
+
|
|
149
|
+
# Exclude gems that cause build errors or are unnecessary for WASM
|
|
150
|
+
# Keep essential gems like 'js' for WASM
|
|
151
|
+
excluded_gems = gem_names.select do |gem_name|
|
|
152
|
+
# Exclude gems that cause WASM build errors
|
|
153
|
+
always_excluded.include?(gem_name) ||
|
|
154
|
+
# Exclude development/test gems
|
|
155
|
+
gem_name.start_with?("rspec", "rubocop", "rake")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Always add always_excluded gems to ensure they're excluded even if not in dependencies
|
|
159
|
+
excluded_gems.concat(always_excluded)
|
|
160
|
+
excluded_gems.uniq!
|
|
161
|
+
|
|
162
|
+
# Add to EXCLUDED_GEMS
|
|
163
|
+
RubyWasm::Packager::EXCLUDED_GEMS.concat(excluded_gems)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def build_ruby_wasm(ruby_version_str)
|
|
167
|
+
command = ["build", "--ruby-version", ruby_version_str, "-o", "ruby.wasm"]
|
|
168
|
+
cli = RubyWasm::CLI.new(stdout: $stdout, stderr: $stderr)
|
|
169
|
+
cli.run(command)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require "listen"
|
|
5
|
+
require "rack"
|
|
6
|
+
require "thread"
|
|
7
|
+
require "rbconfig"
|
|
8
|
+
|
|
9
|
+
module RubyWasmUi
|
|
10
|
+
module Cli
|
|
11
|
+
class Command
|
|
12
|
+
class Dev < Base
|
|
13
|
+
def self.description
|
|
14
|
+
"Start development server with file watching and auto-build"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run(_argv)
|
|
18
|
+
log_info("Starting development server...")
|
|
19
|
+
puts ""
|
|
20
|
+
|
|
21
|
+
ensure_src_directory
|
|
22
|
+
|
|
23
|
+
# Initial build
|
|
24
|
+
log_info("Performing initial build...")
|
|
25
|
+
build
|
|
26
|
+
log_success("✓ Initial build completed")
|
|
27
|
+
puts ""
|
|
28
|
+
|
|
29
|
+
# Start file watcher in a separate thread
|
|
30
|
+
@build_lock = Mutex.new
|
|
31
|
+
@build_queue = Queue.new
|
|
32
|
+
@listener = nil
|
|
33
|
+
@watcher_thread = nil
|
|
34
|
+
|
|
35
|
+
@watcher_thread = Thread.new do
|
|
36
|
+
start_file_watcher
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Register cleanup hook to ensure cleanup runs on exit
|
|
40
|
+
@cleanup_done = false
|
|
41
|
+
at_exit do
|
|
42
|
+
cleanup unless @cleanup_done
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Start development server
|
|
46
|
+
begin
|
|
47
|
+
start_server
|
|
48
|
+
ensure
|
|
49
|
+
cleanup
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def open_browser(url)
|
|
56
|
+
case RbConfig::CONFIG["host_os"]
|
|
57
|
+
when /darwin/
|
|
58
|
+
system("open", url)
|
|
59
|
+
when /linux/
|
|
60
|
+
system("xdg-open", url)
|
|
61
|
+
when /mswin|mingw|cygwin/
|
|
62
|
+
system("start", url)
|
|
63
|
+
else
|
|
64
|
+
log_info("Please open #{url} in your browser")
|
|
65
|
+
end
|
|
66
|
+
rescue => e
|
|
67
|
+
log_info("Could not open browser automatically: #{e.message}")
|
|
68
|
+
log_info("Please open #{url} in your browser")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def cleanup
|
|
72
|
+
return if @cleanup_done
|
|
73
|
+
@cleanup_done = true
|
|
74
|
+
@listener&.stop
|
|
75
|
+
@watcher_thread&.kill
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def build
|
|
79
|
+
command = "bundle exec rbwasm pack ruby.wasm --dir ./src::./src -o src.wasm"
|
|
80
|
+
log_info("Building: #{command}")
|
|
81
|
+
|
|
82
|
+
success = run_command(command, exit_on_error: false)
|
|
83
|
+
if success
|
|
84
|
+
log_success("✓ Build completed")
|
|
85
|
+
else
|
|
86
|
+
log_error("Build failed")
|
|
87
|
+
end
|
|
88
|
+
success
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def start_file_watcher
|
|
92
|
+
@listener = Listen.to("src") do |modified, added, removed|
|
|
93
|
+
files_changed = (modified + added + removed).reject do |file|
|
|
94
|
+
# Ignore temporary files and common build artifacts
|
|
95
|
+
file.end_with?(".swp", ".tmp", "~", ".wasm")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if files_changed.any?
|
|
99
|
+
log_info("Files changed: #{files_changed.join(', ')}")
|
|
100
|
+
|
|
101
|
+
# Debounce: add to queue
|
|
102
|
+
@build_queue << :build
|
|
103
|
+
|
|
104
|
+
# Process queue with debouncing
|
|
105
|
+
Thread.new do
|
|
106
|
+
sleep 0.5 # Debounce delay
|
|
107
|
+
begin
|
|
108
|
+
if @build_queue.pop(true)
|
|
109
|
+
@build_queue.clear
|
|
110
|
+
@build_lock.synchronize do
|
|
111
|
+
build
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
rescue ThreadError
|
|
115
|
+
# Queue is empty, ignore
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
@listener.start
|
|
122
|
+
rescue => e
|
|
123
|
+
log_error("File watcher error: #{e.message}")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def start_server
|
|
127
|
+
port = 8080
|
|
128
|
+
log_info("Starting development server on http://localhost:#{port}")
|
|
129
|
+
log_info("Press Ctrl+C to stop")
|
|
130
|
+
puts ""
|
|
131
|
+
|
|
132
|
+
# Static file server application
|
|
133
|
+
static_app = lambda do |env|
|
|
134
|
+
path = env["PATH_INFO"]
|
|
135
|
+
if path == "/"
|
|
136
|
+
file_path = File.join(Dir.pwd, "src", "index.html")
|
|
137
|
+
else
|
|
138
|
+
file_path = File.join(Dir.pwd, path)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if File.exist?(file_path) && File.file?(file_path)
|
|
142
|
+
content_type = Rack::Mime.mime_type(File.extname(file_path), "text/html")
|
|
143
|
+
[200, { "Content-Type" => content_type }, [File.read(file_path)]]
|
|
144
|
+
else
|
|
145
|
+
[404, { "Content-Type" => "text/plain" }, ["File not found: #{path}"]]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# CORS middleware
|
|
150
|
+
cors_middleware = lambda do |env|
|
|
151
|
+
if env["REQUEST_METHOD"] == "OPTIONS"
|
|
152
|
+
[
|
|
153
|
+
200,
|
|
154
|
+
{
|
|
155
|
+
"Access-Control-Allow-Origin" => "*",
|
|
156
|
+
"Access-Control-Allow-Methods" => "GET, POST, OPTIONS",
|
|
157
|
+
"Access-Control-Allow-Headers" => "Content-Type"
|
|
158
|
+
},
|
|
159
|
+
[]
|
|
160
|
+
]
|
|
161
|
+
else
|
|
162
|
+
status, headers, body = static_app.call(env)
|
|
163
|
+
headers["Access-Control-Allow-Origin"] = "*"
|
|
164
|
+
headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
|
|
165
|
+
headers["Access-Control-Allow-Headers"] = "Content-Type"
|
|
166
|
+
[status, headers, body]
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Create Rack application
|
|
171
|
+
app = Rack::Builder.new do
|
|
172
|
+
run cors_middleware
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Handle shutdown
|
|
176
|
+
trap("INT") do
|
|
177
|
+
log_info("\nShutting down server...")
|
|
178
|
+
cleanup
|
|
179
|
+
raise SystemExit.new(0)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Use Puma handler (Rack 3.0 compatible)
|
|
183
|
+
begin
|
|
184
|
+
require "rack/handler/puma"
|
|
185
|
+
log_info("Using handler: puma")
|
|
186
|
+
|
|
187
|
+
# Open browser after a short delay to ensure server is ready
|
|
188
|
+
Thread.new do
|
|
189
|
+
sleep 1
|
|
190
|
+
open_browser("http://localhost:#{port}/src/index.html")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
Rack::Handler::Puma.run(app, Port: port, Host: "0.0.0.0")
|
|
194
|
+
rescue LoadError => e
|
|
195
|
+
log_error("Puma handler not available: #{e.message}")
|
|
196
|
+
log_error("Please ensure puma gem is installed: gem install puma")
|
|
197
|
+
raise SystemExit.new(1)
|
|
198
|
+
end
|
|
199
|
+
rescue => e
|
|
200
|
+
log_error("Server error: #{e.message}")
|
|
201
|
+
raise SystemExit.new(1)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module RubyWasmUi
|
|
6
|
+
module Cli
|
|
7
|
+
class Command
|
|
8
|
+
class Pack < Base
|
|
9
|
+
def self.description
|
|
10
|
+
"Pack WASM file by packing Ruby source files"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(_argv)
|
|
14
|
+
log_info("Packing WASM file...")
|
|
15
|
+
puts ""
|
|
16
|
+
|
|
17
|
+
ensure_src_directory
|
|
18
|
+
ensure_ruby_wasm
|
|
19
|
+
|
|
20
|
+
# Pack WASM file
|
|
21
|
+
pack
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def pack
|
|
27
|
+
command = "bundle exec rbwasm pack ruby.wasm --dir ./src::./src -o src.wasm"
|
|
28
|
+
log_info("Packing: #{command}")
|
|
29
|
+
|
|
30
|
+
run_command(command)
|
|
31
|
+
log_success("✓ Pack completed")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module RubyWasmUi
|
|
6
|
+
module Cli
|
|
7
|
+
class Command
|
|
8
|
+
class Rebuild < Base
|
|
9
|
+
def self.description
|
|
10
|
+
"Rebuild Ruby WASM file (useful when gems are added)"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(_argv)
|
|
14
|
+
log_info("Rebuilding Ruby WASM...")
|
|
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/2: 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/2: 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
|
+
puts ""
|
|
33
|
+
log_success("Rebuild completed successfully!")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|