t-ruby 0.0.11 → 0.0.35
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 +66 -5
- data/lib/t_ruby/cli.rb +74 -18
- data/lib/t_ruby/compiler.rb +12 -7
- data/lib/t_ruby/config.rb +366 -24
- data/lib/t_ruby/version.rb +1 -1
- data/lib/t_ruby/watcher.rb +25 -15
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e0ef66816ac48ae4a0e21326270f31e44bf258bd12f28bc1e2822ef2f087e89
|
|
4
|
+
data.tar.gz: 3a16ea18d0a0aabc2b5add683642c5ef346343b8b988fb0b02d9942e1f95842f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2d1d004b7d7eac3a62c7a12487b9175d53bd688f47baec72d5f23c405a79943c4f33049e60cdfbb32924f530a8e88b4895c7738978754590d4e4898e34ffae9
|
|
7
|
+
data.tar.gz: 5079f36e6bff492eca9fb6aaff1ffa56379b42dc03d09dfc413236888adb6797ac791aa1839fd9ebf3b891f319333696d0af71966076a78088f8e900ee3668f7
|
data/README.md
CHANGED
|
@@ -152,10 +152,21 @@ trc --version
|
|
|
152
152
|
|
|
153
153
|
## Quick start
|
|
154
154
|
|
|
155
|
-
### 1.
|
|
155
|
+
### 1. Initialize project
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
trc --init
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This creates:
|
|
162
|
+
- `trbconfig.yml` — project configuration
|
|
163
|
+
- `src/` — source directory
|
|
164
|
+
- `build/` — output directory
|
|
165
|
+
|
|
166
|
+
### 2. Write `.trb`
|
|
156
167
|
|
|
157
168
|
```trb
|
|
158
|
-
# hello.trb
|
|
169
|
+
# src/hello.trb
|
|
159
170
|
def greet(name: String): String
|
|
160
171
|
"Hello, #{name}!"
|
|
161
172
|
end
|
|
@@ -163,19 +174,69 @@ end
|
|
|
163
174
|
puts greet("world")
|
|
164
175
|
```
|
|
165
176
|
|
|
166
|
-
###
|
|
177
|
+
### 3. Compile
|
|
167
178
|
|
|
168
179
|
```bash
|
|
169
|
-
trc hello.trb
|
|
180
|
+
trc src/hello.trb
|
|
170
181
|
```
|
|
171
182
|
|
|
172
|
-
###
|
|
183
|
+
### 4. Run
|
|
173
184
|
|
|
174
185
|
```bash
|
|
175
186
|
ruby build/hello.rb
|
|
176
187
|
# => Hello, world!
|
|
177
188
|
```
|
|
178
189
|
|
|
190
|
+
### 5. Watch mode
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
trc -w # Watch directories from trbconfig.yml (default: src/)
|
|
194
|
+
trc -w lib/ # Watch specific directory
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Files are automatically recompiled on change.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Configuration
|
|
202
|
+
|
|
203
|
+
`trc --init` generates a `trbconfig.yml` file with all available options:
|
|
204
|
+
|
|
205
|
+
```yaml
|
|
206
|
+
# T-Ruby configuration file
|
|
207
|
+
# See: https://type-ruby.github.io/docs/getting-started/project-configuration
|
|
208
|
+
|
|
209
|
+
source:
|
|
210
|
+
include:
|
|
211
|
+
- src
|
|
212
|
+
exclude: []
|
|
213
|
+
extensions:
|
|
214
|
+
- ".trb"
|
|
215
|
+
- ".rb"
|
|
216
|
+
|
|
217
|
+
output:
|
|
218
|
+
ruby_dir: build
|
|
219
|
+
# rbs_dir: sig # Optional: separate directory for .rbs files
|
|
220
|
+
preserve_structure: true
|
|
221
|
+
# clean_before_build: false
|
|
222
|
+
|
|
223
|
+
compiler:
|
|
224
|
+
strictness: standard # strict | standard | permissive
|
|
225
|
+
generate_rbs: true
|
|
226
|
+
target_ruby: "3.0"
|
|
227
|
+
# experimental: []
|
|
228
|
+
# checks:
|
|
229
|
+
# no_implicit_any: false
|
|
230
|
+
# no_unused_vars: false
|
|
231
|
+
# strict_nil: false
|
|
232
|
+
|
|
233
|
+
watch:
|
|
234
|
+
# paths: [] # Additional paths to watch
|
|
235
|
+
debounce: 100
|
|
236
|
+
# clear_screen: false
|
|
237
|
+
# on_success: "bundle exec rspec"
|
|
238
|
+
```
|
|
239
|
+
|
|
179
240
|
---
|
|
180
241
|
|
|
181
242
|
## Features
|
data/lib/t_ruby/cli.rb
CHANGED
|
@@ -9,6 +9,7 @@ module TRuby
|
|
|
9
9
|
trc <file.trb> Compile a .trb file to .rb
|
|
10
10
|
trc <file.rb> Copy .rb file to build/ and generate .rbs
|
|
11
11
|
trc --init Initialize a new t-ruby project
|
|
12
|
+
trc --config, -c <path> Use custom config file
|
|
12
13
|
trc --watch, -w Watch input files and recompile on change
|
|
13
14
|
trc --decl <file.trb> Generate .d.trb declaration file
|
|
14
15
|
trc --lsp Start LSP server (for IDE integration)
|
|
@@ -19,6 +20,7 @@ module TRuby
|
|
|
19
20
|
trc hello.trb Compile hello.trb to build/hello.rb
|
|
20
21
|
trc utils.rb Copy utils.rb to build/ and generate utils.rbs
|
|
21
22
|
trc --init Create trbconfig.yml and src/, build/ directories
|
|
23
|
+
trc -c custom.yml file.trb Compile with custom config file
|
|
22
24
|
trc -w Watch all .trb and .rb files in current directory
|
|
23
25
|
trc -w src/ Watch all .trb and .rb files in src/ directory
|
|
24
26
|
trc --watch hello.trb Watch specific file for changes
|
|
@@ -66,8 +68,12 @@ module TRuby
|
|
|
66
68
|
return
|
|
67
69
|
end
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
# Extract config path if --config or -c flag is present
|
|
72
|
+
config_path = extract_config_path
|
|
73
|
+
|
|
74
|
+
# Get input file (first non-flag argument)
|
|
75
|
+
input_file = find_input_file
|
|
76
|
+
compile(input_file, config_path: config_path)
|
|
71
77
|
end
|
|
72
78
|
|
|
73
79
|
private
|
|
@@ -80,24 +86,43 @@ module TRuby
|
|
|
80
86
|
created = []
|
|
81
87
|
skipped = []
|
|
82
88
|
|
|
83
|
-
# Create trbconfig.yml
|
|
89
|
+
# Create trbconfig.yml with new schema
|
|
84
90
|
if File.exist?(config_file)
|
|
85
91
|
skipped << config_file
|
|
86
92
|
else
|
|
87
93
|
File.write(config_file, <<~YAML)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
# T-Ruby configuration file
|
|
95
|
+
# See: https://type-ruby.github.io/docs/getting-started/project-configuration
|
|
96
|
+
|
|
97
|
+
source:
|
|
98
|
+
include:
|
|
99
|
+
- #{src_dir}
|
|
100
|
+
exclude: []
|
|
101
|
+
extensions:
|
|
102
|
+
- ".trb"
|
|
103
|
+
- ".rb"
|
|
104
|
+
|
|
105
|
+
output:
|
|
106
|
+
ruby_dir: #{build_dir}
|
|
107
|
+
# rbs_dir: sig # Optional: separate directory for .rbs files
|
|
108
|
+
preserve_structure: true
|
|
109
|
+
# clean_before_build: false
|
|
110
|
+
|
|
111
|
+
compiler:
|
|
112
|
+
strictness: standard # strict | standard | permissive
|
|
113
|
+
generate_rbs: true
|
|
114
|
+
target_ruby: "3.0"
|
|
115
|
+
# experimental: []
|
|
116
|
+
# checks:
|
|
117
|
+
# no_implicit_any: false
|
|
118
|
+
# no_unused_vars: false
|
|
119
|
+
# strict_nil: false
|
|
120
|
+
|
|
121
|
+
watch:
|
|
122
|
+
# paths: [] # Additional paths to watch
|
|
123
|
+
debounce: 100
|
|
124
|
+
# clear_screen: false
|
|
125
|
+
# on_success: "bundle exec rspec"
|
|
101
126
|
YAML
|
|
102
127
|
created << config_file
|
|
103
128
|
end
|
|
@@ -161,8 +186,8 @@ module TRuby
|
|
|
161
186
|
exit 1
|
|
162
187
|
end
|
|
163
188
|
|
|
164
|
-
def compile(input_file)
|
|
165
|
-
config = Config.new
|
|
189
|
+
def compile(input_file, config_path: nil)
|
|
190
|
+
config = Config.new(config_path)
|
|
166
191
|
compiler = Compiler.new(config)
|
|
167
192
|
|
|
168
193
|
output_path = compiler.compile(input_file)
|
|
@@ -171,5 +196,36 @@ module TRuby
|
|
|
171
196
|
puts "Error: #{e.message}"
|
|
172
197
|
exit 1
|
|
173
198
|
end
|
|
199
|
+
|
|
200
|
+
# Extract config path from --config or -c flag
|
|
201
|
+
def extract_config_path
|
|
202
|
+
config_index = @args.index("--config") || @args.index("-c")
|
|
203
|
+
return nil unless config_index
|
|
204
|
+
|
|
205
|
+
@args[config_index + 1]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Find the input file (first non-flag argument)
|
|
209
|
+
def find_input_file
|
|
210
|
+
skip_next = false
|
|
211
|
+
@args.each do |arg|
|
|
212
|
+
if skip_next
|
|
213
|
+
skip_next = false
|
|
214
|
+
next
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Skip known flags with arguments
|
|
218
|
+
if %w[--config -c --decl].include?(arg)
|
|
219
|
+
skip_next = true
|
|
220
|
+
next
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Skip flags without arguments
|
|
224
|
+
next if arg.start_with?("-")
|
|
225
|
+
|
|
226
|
+
return arg
|
|
227
|
+
end
|
|
228
|
+
nil
|
|
229
|
+
end
|
|
174
230
|
end
|
|
175
231
|
end
|
data/lib/t_ruby/compiler.rb
CHANGED
|
@@ -47,16 +47,19 @@ module TRuby
|
|
|
47
47
|
File.write(output_path, output)
|
|
48
48
|
|
|
49
49
|
# Generate .rbs file if enabled in config
|
|
50
|
-
if @config.
|
|
50
|
+
if @config.compiler["generate_rbs"]
|
|
51
|
+
rbs_dir = @config.rbs_dir
|
|
52
|
+
FileUtils.mkdir_p(rbs_dir)
|
|
51
53
|
if @use_ir && parser.ir_program
|
|
52
|
-
generate_rbs_from_ir(base_filename,
|
|
54
|
+
generate_rbs_from_ir(base_filename, rbs_dir, parser.ir_program)
|
|
53
55
|
else
|
|
54
|
-
generate_rbs_file(base_filename,
|
|
56
|
+
generate_rbs_file(base_filename, rbs_dir, parse_result)
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
58
|
-
# Generate .d.trb file if enabled in config
|
|
59
|
-
|
|
60
|
+
# Generate .d.trb file if enabled in config (legacy support)
|
|
61
|
+
# TODO: Add compiler.generate_dtrb option in future
|
|
62
|
+
if @config.compiler.key?("generate_dtrb") && @config.compiler["generate_dtrb"]
|
|
60
63
|
generate_dtrb_file(input_path, out_dir)
|
|
61
64
|
end
|
|
62
65
|
|
|
@@ -236,8 +239,10 @@ module TRuby
|
|
|
236
239
|
FileUtils.cp(input_path, output_path)
|
|
237
240
|
|
|
238
241
|
# Generate .rbs file if enabled in config
|
|
239
|
-
if @config.
|
|
240
|
-
|
|
242
|
+
if @config.compiler["generate_rbs"]
|
|
243
|
+
rbs_dir = @config.rbs_dir
|
|
244
|
+
FileUtils.mkdir_p(rbs_dir)
|
|
245
|
+
generate_rbs_from_ruby(base_filename, rbs_dir, input_path)
|
|
241
246
|
end
|
|
242
247
|
|
|
243
248
|
output_path
|
data/lib/t_ruby/config.rb
CHANGED
|
@@ -3,51 +3,393 @@
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
|
|
5
5
|
module TRuby
|
|
6
|
+
# Error raised when configuration is invalid
|
|
7
|
+
class ConfigError < StandardError; end
|
|
8
|
+
|
|
6
9
|
class Config
|
|
10
|
+
# Valid strictness levels
|
|
11
|
+
VALID_STRICTNESS = %w[strict standard permissive].freeze
|
|
12
|
+
# New schema structure (v0.0.12+)
|
|
7
13
|
DEFAULT_CONFIG = {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
14
|
+
"source" => {
|
|
15
|
+
"include" => ["src"],
|
|
16
|
+
"exclude" => [],
|
|
17
|
+
"extensions" => [".trb"]
|
|
18
|
+
},
|
|
19
|
+
"output" => {
|
|
20
|
+
"ruby_dir" => "build",
|
|
21
|
+
"rbs_dir" => nil,
|
|
22
|
+
"preserve_structure" => true,
|
|
23
|
+
"clean_before_build" => false
|
|
12
24
|
},
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
25
|
+
"compiler" => {
|
|
26
|
+
"strictness" => "standard",
|
|
27
|
+
"generate_rbs" => true,
|
|
28
|
+
"target_ruby" => "3.0",
|
|
29
|
+
"experimental" => [],
|
|
30
|
+
"checks" => {
|
|
31
|
+
"no_implicit_any" => false,
|
|
32
|
+
"no_unused_vars" => false,
|
|
33
|
+
"strict_nil" => false
|
|
34
|
+
}
|
|
16
35
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
36
|
+
"watch" => {
|
|
37
|
+
"paths" => [],
|
|
38
|
+
"debounce" => 100,
|
|
39
|
+
"clear_screen" => false,
|
|
40
|
+
"on_success" => nil
|
|
21
41
|
}
|
|
22
42
|
}.freeze
|
|
23
43
|
|
|
24
|
-
|
|
44
|
+
# Legacy keys for migration detection
|
|
45
|
+
LEGACY_KEYS = %w[emit paths strict include exclude].freeze
|
|
46
|
+
|
|
47
|
+
# Always excluded (not configurable)
|
|
48
|
+
AUTO_EXCLUDE = [".git"].freeze
|
|
49
|
+
|
|
50
|
+
attr_reader :source, :output, :compiler, :watch, :version_requirement
|
|
25
51
|
|
|
26
52
|
def initialize(config_path = nil)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@
|
|
53
|
+
raw_config = load_raw_config(config_path)
|
|
54
|
+
config = process_config(raw_config)
|
|
55
|
+
|
|
56
|
+
@source = config["source"]
|
|
57
|
+
@output = config["output"]
|
|
58
|
+
@compiler = config["compiler"]
|
|
59
|
+
@watch = config["watch"]
|
|
60
|
+
@version_requirement = raw_config["version"]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Get output directory for compiled Ruby files
|
|
64
|
+
# @return [String] output directory path
|
|
65
|
+
def ruby_dir
|
|
66
|
+
@output["ruby_dir"] || "build"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Get output directory for RBS files
|
|
70
|
+
# @return [String] RBS output directory (defaults to ruby_dir if not specified)
|
|
71
|
+
def rbs_dir
|
|
72
|
+
@output["rbs_dir"] || ruby_dir
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Check if source directory structure should be preserved in output
|
|
76
|
+
# @return [Boolean] true if structure should be preserved
|
|
77
|
+
def preserve_structure?
|
|
78
|
+
@output["preserve_structure"] != false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Check if output directory should be cleaned before build
|
|
82
|
+
# @return [Boolean] true if should clean before build
|
|
83
|
+
def clean_before_build?
|
|
84
|
+
@output["clean_before_build"] == true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Get compiler strictness level
|
|
88
|
+
# @return [String] one of: strict, standard, permissive
|
|
89
|
+
def strictness
|
|
90
|
+
@compiler["strictness"] || "standard"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Check if RBS files should be generated
|
|
94
|
+
# @return [Boolean] true if RBS files should be generated
|
|
95
|
+
def generate_rbs?
|
|
96
|
+
@compiler["generate_rbs"] != false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Get target Ruby version
|
|
100
|
+
# @return [String] target Ruby version (e.g., "3.0", "3.2")
|
|
101
|
+
def target_ruby
|
|
102
|
+
(@compiler["target_ruby"] || "3.0").to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Get list of enabled experimental features
|
|
106
|
+
# @return [Array<String>] list of experimental feature names
|
|
107
|
+
def experimental_features
|
|
108
|
+
@compiler["experimental"] || []
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Check if a specific experimental feature is enabled
|
|
112
|
+
# @param feature [String] feature name to check
|
|
113
|
+
# @return [Boolean] true if feature is enabled
|
|
114
|
+
def experimental_enabled?(feature)
|
|
115
|
+
experimental_features.include?(feature)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Check if no_implicit_any check is enabled
|
|
119
|
+
# @return [Boolean] true if check is enabled
|
|
120
|
+
def check_no_implicit_any?
|
|
121
|
+
@compiler.dig("checks", "no_implicit_any") == true
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Check if no_unused_vars check is enabled
|
|
125
|
+
# @return [Boolean] true if check is enabled
|
|
126
|
+
def check_no_unused_vars?
|
|
127
|
+
@compiler.dig("checks", "no_unused_vars") == true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Check if strict_nil check is enabled
|
|
131
|
+
# @return [Boolean] true if check is enabled
|
|
132
|
+
def check_strict_nil?
|
|
133
|
+
@compiler.dig("checks", "strict_nil") == true
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get additional watch paths
|
|
137
|
+
# @return [Array<String>] list of additional paths to watch
|
|
138
|
+
def watch_paths
|
|
139
|
+
@watch["paths"] || []
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Get watch debounce delay in milliseconds
|
|
143
|
+
# @return [Integer] debounce delay in milliseconds
|
|
144
|
+
def watch_debounce
|
|
145
|
+
@watch["debounce"] || 100
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Check if terminal should be cleared before rebuild
|
|
149
|
+
# @return [Boolean] true if terminal should be cleared
|
|
150
|
+
def watch_clear_screen?
|
|
151
|
+
@watch["clear_screen"] == true
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Get command to run after successful compilation
|
|
155
|
+
# @return [String, nil] command to run on success
|
|
156
|
+
def watch_on_success
|
|
157
|
+
@watch["on_success"]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Check if current T-Ruby version satisfies the version requirement
|
|
161
|
+
# @return [Boolean] true if version is satisfied or no requirement specified
|
|
162
|
+
def version_satisfied?
|
|
163
|
+
return true if @version_requirement.nil?
|
|
164
|
+
|
|
165
|
+
requirement = Gem::Requirement.new(@version_requirement)
|
|
166
|
+
current = Gem::Version.new(TRuby::VERSION)
|
|
167
|
+
requirement.satisfied_by?(current)
|
|
168
|
+
rescue ArgumentError
|
|
169
|
+
false
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Validate the configuration
|
|
173
|
+
# @raise [ConfigError] if configuration is invalid
|
|
174
|
+
def validate!
|
|
175
|
+
validate_strictness!
|
|
176
|
+
true
|
|
31
177
|
end
|
|
32
178
|
|
|
179
|
+
# Backwards compatible: alias for ruby_dir
|
|
33
180
|
def out_dir
|
|
34
|
-
|
|
181
|
+
ruby_dir
|
|
35
182
|
end
|
|
36
183
|
|
|
184
|
+
# Backwards compatible: first source.include directory
|
|
37
185
|
def src_dir
|
|
38
|
-
@
|
|
186
|
+
@source["include"].first || "src"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Get source include directories
|
|
190
|
+
# @return [Array<String>] list of include directories
|
|
191
|
+
def source_include
|
|
192
|
+
@source["include"] || ["src"]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Get source exclude patterns
|
|
196
|
+
# @return [Array<String>] list of exclude patterns
|
|
197
|
+
def source_exclude
|
|
198
|
+
@source["exclude"] || []
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Get source file extensions
|
|
202
|
+
# @return [Array<String>] list of file extensions (e.g., [".trb", ".truby"])
|
|
203
|
+
def source_extensions
|
|
204
|
+
@source["extensions"] || [".trb"]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Get include patterns for file discovery
|
|
208
|
+
def include_patterns
|
|
209
|
+
extensions = @source["extensions"] || [".trb"]
|
|
210
|
+
extensions.map { |ext| "**/*#{ext}" }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Get exclude patterns
|
|
214
|
+
def exclude_patterns
|
|
215
|
+
@source["exclude"] || []
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Find all source files matching include patterns, excluding exclude patterns
|
|
219
|
+
# @return [Array<String>] list of matching file paths
|
|
220
|
+
def find_source_files
|
|
221
|
+
files = []
|
|
222
|
+
|
|
223
|
+
@source["include"].each do |include_dir|
|
|
224
|
+
base_dir = File.expand_path(include_dir)
|
|
225
|
+
next unless Dir.exist?(base_dir)
|
|
226
|
+
|
|
227
|
+
include_patterns.each do |pattern|
|
|
228
|
+
full_pattern = File.join(base_dir, pattern)
|
|
229
|
+
files.concat(Dir.glob(full_pattern))
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Filter out excluded files
|
|
234
|
+
files.reject { |f| excluded?(f) }.uniq.sort
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Check if a file path should be excluded
|
|
238
|
+
# @param file_path [String] absolute or relative file path
|
|
239
|
+
# @return [Boolean] true if file should be excluded
|
|
240
|
+
def excluded?(file_path)
|
|
241
|
+
relative_path = relative_to_src(file_path)
|
|
242
|
+
all_exclude_patterns.any? { |pattern| matches_pattern?(relative_path, pattern) }
|
|
39
243
|
end
|
|
40
244
|
|
|
41
245
|
private
|
|
42
246
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
247
|
+
# Validate strictness value
|
|
248
|
+
def validate_strictness!
|
|
249
|
+
value = strictness
|
|
250
|
+
return if VALID_STRICTNESS.include?(value)
|
|
251
|
+
|
|
252
|
+
raise ConfigError, "Invalid compiler.strictness: '#{value}'. Must be one of: #{VALID_STRICTNESS.join(', ')}"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def load_raw_config(config_path)
|
|
256
|
+
raw = if config_path && File.exist?(config_path)
|
|
257
|
+
YAML.safe_load_file(config_path, permitted_classes: [Symbol]) || {}
|
|
258
|
+
elsif File.exist?("trbconfig.yml")
|
|
259
|
+
YAML.safe_load_file("trbconfig.yml", permitted_classes: [Symbol]) || {}
|
|
260
|
+
else
|
|
261
|
+
{}
|
|
262
|
+
end
|
|
263
|
+
expand_env_vars(raw)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Expand environment variables in config values
|
|
267
|
+
# Supports ${VAR} and ${VAR:-default} syntax
|
|
268
|
+
def expand_env_vars(obj)
|
|
269
|
+
case obj
|
|
270
|
+
when Hash
|
|
271
|
+
obj.transform_values { |v| expand_env_vars(v) }
|
|
272
|
+
when Array
|
|
273
|
+
obj.map { |v| expand_env_vars(v) }
|
|
274
|
+
when String
|
|
275
|
+
expand_env_string(obj)
|
|
276
|
+
else
|
|
277
|
+
obj
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Expand environment variables in a single string
|
|
282
|
+
def expand_env_string(str)
|
|
283
|
+
str.gsub(/\$\{([^}]+)\}/) do |_match|
|
|
284
|
+
var_expr = ::Regexp.last_match(1)
|
|
285
|
+
if var_expr.include?(":-")
|
|
286
|
+
var_name, default_value = var_expr.split(":-", 2)
|
|
287
|
+
ENV.fetch(var_name, default_value)
|
|
288
|
+
else
|
|
289
|
+
ENV.fetch(var_expr, "")
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def process_config(raw_config)
|
|
295
|
+
if legacy_config?(raw_config)
|
|
296
|
+
warn "DEPRECATED: trbconfig.yml uses legacy format. Please migrate to new schema (source/output/compiler/watch)."
|
|
297
|
+
migrate_legacy_config(raw_config)
|
|
298
|
+
else
|
|
299
|
+
merge_with_defaults(raw_config)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def legacy_config?(raw_config)
|
|
304
|
+
LEGACY_KEYS.any? { |key| raw_config.key?(key) }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def migrate_legacy_config(raw_config)
|
|
308
|
+
result = deep_dup(DEFAULT_CONFIG)
|
|
309
|
+
|
|
310
|
+
# Migrate emit -> compiler.generate_rbs
|
|
311
|
+
if raw_config["emit"]
|
|
312
|
+
result["compiler"]["generate_rbs"] = raw_config["emit"]["rbs"] if raw_config["emit"].key?("rbs")
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Migrate paths -> source.include and output.ruby_dir
|
|
316
|
+
if raw_config["paths"]
|
|
317
|
+
if raw_config["paths"]["src"]
|
|
318
|
+
src_path = raw_config["paths"]["src"].sub(%r{^\./}, "")
|
|
319
|
+
result["source"]["include"] = [src_path]
|
|
320
|
+
end
|
|
321
|
+
if raw_config["paths"]["out"]
|
|
322
|
+
out_path = raw_config["paths"]["out"].sub(%r{^\./}, "")
|
|
323
|
+
result["output"]["ruby_dir"] = out_path
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Migrate include/exclude patterns
|
|
328
|
+
if raw_config["include"]
|
|
329
|
+
# Keep legacy include patterns as-is for now
|
|
330
|
+
result["source"]["include"] = [result["source"]["include"].first || "src"]
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
if raw_config["exclude"]
|
|
334
|
+
result["source"]["exclude"] = raw_config["exclude"]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
result
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def merge_with_defaults(user_config)
|
|
341
|
+
result = deep_dup(DEFAULT_CONFIG)
|
|
342
|
+
deep_merge(result, user_config)
|
|
343
|
+
result
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def deep_dup(hash)
|
|
347
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
348
|
+
result[key] = value.is_a?(Hash) ? deep_dup(value) : (value.is_a?(Array) ? value.dup : value)
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def deep_merge(target, source)
|
|
353
|
+
source.each do |key, value|
|
|
354
|
+
if value.is_a?(Hash) && target[key].is_a?(Hash)
|
|
355
|
+
deep_merge(target[key], value)
|
|
356
|
+
elsif !value.nil?
|
|
357
|
+
target[key] = value
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Combine auto-excluded patterns with user-configured patterns
|
|
363
|
+
def all_exclude_patterns
|
|
364
|
+
patterns = AUTO_EXCLUDE.dup
|
|
365
|
+
patterns << out_dir.sub(%r{^\./}, "") # Add output directory
|
|
366
|
+
patterns.concat(exclude_patterns)
|
|
367
|
+
patterns.uniq
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Convert absolute path to relative path from first src_dir
|
|
371
|
+
def relative_to_src(file_path)
|
|
372
|
+
base_dir = File.expand_path(src_dir)
|
|
373
|
+
full_path = File.expand_path(file_path)
|
|
374
|
+
|
|
375
|
+
if full_path.start_with?(base_dir)
|
|
376
|
+
full_path.sub("#{base_dir}/", "")
|
|
48
377
|
else
|
|
49
|
-
|
|
378
|
+
file_path
|
|
50
379
|
end
|
|
51
380
|
end
|
|
381
|
+
|
|
382
|
+
# Check if path matches a glob/directory pattern
|
|
383
|
+
def matches_pattern?(path, pattern)
|
|
384
|
+
# Direct directory match (e.g., "node_modules" matches "node_modules/foo.rb")
|
|
385
|
+
return true if path.start_with?("#{pattern}/") || path == pattern
|
|
386
|
+
|
|
387
|
+
# Check if any path component matches
|
|
388
|
+
path_parts = path.split("/")
|
|
389
|
+
return true if path_parts.include?(pattern)
|
|
390
|
+
|
|
391
|
+
# Glob pattern match
|
|
392
|
+
File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_DOTMATCH)
|
|
393
|
+
end
|
|
52
394
|
end
|
|
53
395
|
end
|
data/lib/t_ruby/version.rb
CHANGED
data/lib/t_ruby/watcher.rb
CHANGED
|
@@ -91,7 +91,9 @@ module TRuby
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def handle_changes(modified, added, removed)
|
|
94
|
-
changed_files = (modified + added)
|
|
94
|
+
changed_files = (modified + added)
|
|
95
|
+
.select { |f| f.end_with?(".trb") || f.end_with?(".rb") }
|
|
96
|
+
.reject { |f| @config.excluded?(f) }
|
|
95
97
|
return if changed_files.empty? && removed.empty?
|
|
96
98
|
|
|
97
99
|
puts
|
|
@@ -231,26 +233,34 @@ module TRuby
|
|
|
231
233
|
end
|
|
232
234
|
|
|
233
235
|
def find_trb_files
|
|
234
|
-
|
|
235
|
-
@paths.each do |path|
|
|
236
|
-
if File.directory?(path)
|
|
237
|
-
files.concat(Dir.glob(File.join(path, "**", "*.trb")))
|
|
238
|
-
elsif File.file?(path) && path.end_with?(".trb")
|
|
239
|
-
files << path
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
files.uniq
|
|
236
|
+
find_source_files_by_extension(".trb")
|
|
243
237
|
end
|
|
244
238
|
|
|
245
239
|
def find_rb_files
|
|
240
|
+
find_source_files_by_extension(".rb")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def find_source_files_by_extension(ext)
|
|
246
244
|
files = []
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
|
|
246
|
+
# If watching specific paths, use those paths
|
|
247
|
+
# Otherwise, use Config's find_source_files which respects include/exclude patterns
|
|
248
|
+
if @paths == [File.expand_path(".")]
|
|
249
|
+
# Default case: use config's src_dir with include/exclude patterns
|
|
250
|
+
files = @config.find_source_files.select { |f| f.end_with?(ext) }
|
|
251
|
+
else
|
|
252
|
+
# Specific paths provided: search in those paths but still apply exclude patterns
|
|
253
|
+
@paths.each do |path|
|
|
254
|
+
if File.directory?(path)
|
|
255
|
+
Dir.glob(File.join(path, "**", "*#{ext}")).each do |file|
|
|
256
|
+
files << file unless @config.excluded?(file)
|
|
257
|
+
end
|
|
258
|
+
elsif File.file?(path) && path.end_with?(ext) && !@config.excluded?(path)
|
|
259
|
+
files << path
|
|
260
|
+
end
|
|
252
261
|
end
|
|
253
262
|
end
|
|
263
|
+
|
|
254
264
|
files.uniq
|
|
255
265
|
end
|
|
256
266
|
|