opal-vite 0.2.5 → 0.2.6
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/lib/opal/vite/compiler.rb +229 -11
- data/lib/opal/vite/version.rb +1 -1
- 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: 19c908ad19445dc3ca95af2873ad94c885fd3546de85a78005cf3d45a10c2b7f
|
|
4
|
+
data.tar.gz: 10fd5fb0daf18cf0eeb466079307eb6f47f913c4e7e1e7031c7d7944beff4cb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0fca963918760eb442d55982d5de06875ed92293ad58a0d422f095f4e72bf2181ebbdb4ea99bdd978473d860b5f2948d98e92550938df0e7310fd707e94228b2
|
|
7
|
+
data.tar.gz: 6016bfab349b9b24af874cdf416bbc2dde40e6e106c30ce18157c609d7de418f1935c79d6fe782d75cb3ff03d49385f1c4b93994baf77e5e45aea90b834e3e1c
|
data/lib/opal/vite/compiler.rb
CHANGED
|
@@ -146,7 +146,7 @@ module Opal
|
|
|
146
146
|
return nil unless builder.respond_to?(:source_map) && builder.source_map
|
|
147
147
|
|
|
148
148
|
source_map = builder.source_map
|
|
149
|
-
map_hash = source_map.to_h
|
|
149
|
+
map_hash = deep_stringify_keys(source_map.to_h)
|
|
150
150
|
return nil unless map_hash
|
|
151
151
|
|
|
152
152
|
# If it's an index format with sections, merge all sections
|
|
@@ -156,16 +156,35 @@ module Opal
|
|
|
156
156
|
|
|
157
157
|
return nil unless map_hash
|
|
158
158
|
|
|
159
|
+
# Add sourceRoot for proper browser debugging
|
|
160
|
+
# This helps DevTools organize source files in a logical tree
|
|
161
|
+
map_hash['sourceRoot'] = ''
|
|
162
|
+
|
|
159
163
|
# Normalize source paths for browser debugging
|
|
164
|
+
# Prefix with /opal-sources/ so they appear in a dedicated folder in DevTools
|
|
160
165
|
if map_hash['sources']
|
|
161
166
|
map_hash['sources'] = map_hash['sources'].map do |source|
|
|
162
|
-
|
|
167
|
+
normalize_source_path_for_devtools(source, file_path)
|
|
163
168
|
end
|
|
164
169
|
end
|
|
165
170
|
|
|
166
171
|
map_hash.to_json
|
|
167
172
|
end
|
|
168
173
|
|
|
174
|
+
# Recursively convert all hash keys to strings
|
|
175
|
+
def deep_stringify_keys(obj)
|
|
176
|
+
case obj
|
|
177
|
+
when Hash
|
|
178
|
+
obj.each_with_object({}) do |(key, value), result|
|
|
179
|
+
result[key.to_s] = deep_stringify_keys(value)
|
|
180
|
+
end
|
|
181
|
+
when Array
|
|
182
|
+
obj.map { |item| deep_stringify_keys(item) }
|
|
183
|
+
else
|
|
184
|
+
obj
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
169
188
|
def merge_all_sections(index_map, file_path)
|
|
170
189
|
sections = index_map['sections']
|
|
171
190
|
return nil if sections.nil? || sections.empty?
|
|
@@ -186,6 +205,13 @@ module Opal
|
|
|
186
205
|
'mappings' => ''
|
|
187
206
|
}
|
|
188
207
|
|
|
208
|
+
# Track cumulative state for VLQ relative encoding
|
|
209
|
+
# These track the "previous" values across all sections
|
|
210
|
+
prev_source = 0
|
|
211
|
+
prev_orig_line = 0
|
|
212
|
+
prev_orig_col = 0
|
|
213
|
+
prev_name = 0
|
|
214
|
+
|
|
189
215
|
current_line = 0
|
|
190
216
|
|
|
191
217
|
sections.each_with_index do |section, idx|
|
|
@@ -202,7 +228,7 @@ module Opal
|
|
|
202
228
|
current_line = section_start_line
|
|
203
229
|
end
|
|
204
230
|
|
|
205
|
-
# Track source index
|
|
231
|
+
# Track source and name index offsets for this section
|
|
206
232
|
source_offset = merged['sources'].length
|
|
207
233
|
name_offset = merged['names'].length
|
|
208
234
|
|
|
@@ -223,25 +249,184 @@ module Opal
|
|
|
223
249
|
merged['names'].concat(section_map['names'])
|
|
224
250
|
end
|
|
225
251
|
|
|
226
|
-
#
|
|
252
|
+
# Process mappings from this section with index adjustment
|
|
227
253
|
if section_map['mappings'] && !section_map['mappings'].empty?
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
254
|
+
adjusted_mappings, prev_source, prev_orig_line, prev_orig_col, prev_name =
|
|
255
|
+
adjust_section_mappings(
|
|
256
|
+
section_map['mappings'],
|
|
257
|
+
source_offset,
|
|
258
|
+
name_offset,
|
|
259
|
+
prev_source,
|
|
260
|
+
prev_orig_line,
|
|
261
|
+
prev_orig_col,
|
|
262
|
+
prev_name
|
|
263
|
+
)
|
|
264
|
+
|
|
231
265
|
if idx > 0 && !merged['mappings'].empty? && !merged['mappings'].end_with?(';')
|
|
232
266
|
merged['mappings'] += ';'
|
|
233
267
|
end
|
|
234
|
-
merged['mappings'] +=
|
|
268
|
+
merged['mappings'] += adjusted_mappings
|
|
235
269
|
|
|
236
|
-
#
|
|
237
|
-
|
|
238
|
-
current_line
|
|
270
|
+
# Update current_line to the last line we wrote to
|
|
271
|
+
# section_start_line + number of semicolons in this section's mappings
|
|
272
|
+
current_line = section_start_line + section_map['mappings'].count(';')
|
|
239
273
|
end
|
|
240
274
|
end
|
|
241
275
|
|
|
242
276
|
merged
|
|
243
277
|
end
|
|
244
278
|
|
|
279
|
+
# Adjust mappings from a section by adding offsets to source/name indices
|
|
280
|
+
# VLQ mappings use relative deltas. When merging sections:
|
|
281
|
+
# - First section: no adjustment, just track final absolute state
|
|
282
|
+
# - Later sections: adjust first segment's source/name delta to bridge from previous section's end state
|
|
283
|
+
#
|
|
284
|
+
# Returns: [adjusted_mappings, new_prev_source, new_prev_orig_line, new_prev_orig_col, new_prev_name]
|
|
285
|
+
def adjust_section_mappings(mappings, source_offset, name_offset, prev_source, prev_orig_line, prev_orig_col, prev_name)
|
|
286
|
+
return ['', prev_source, prev_orig_line, prev_orig_col, prev_name] if mappings.nil? || mappings.empty?
|
|
287
|
+
|
|
288
|
+
result_lines = []
|
|
289
|
+
lines = mappings.split(';', -1)
|
|
290
|
+
|
|
291
|
+
# Track absolute state within this section (section-local, starting from 0)
|
|
292
|
+
section_abs_source = 0
|
|
293
|
+
section_abs_orig_line = 0
|
|
294
|
+
section_abs_orig_col = 0
|
|
295
|
+
section_abs_name = 0
|
|
296
|
+
|
|
297
|
+
first_segment_processed = false
|
|
298
|
+
|
|
299
|
+
lines.each do |line|
|
|
300
|
+
if line.empty?
|
|
301
|
+
result_lines << ''
|
|
302
|
+
next
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
result_segments = []
|
|
306
|
+
segments = line.split(',')
|
|
307
|
+
|
|
308
|
+
segments.each do |segment|
|
|
309
|
+
values = decode_vlq(segment)
|
|
310
|
+
next if values.empty?
|
|
311
|
+
|
|
312
|
+
# values[0] = generated column delta (always present, relative within line)
|
|
313
|
+
# values[1] = source index delta
|
|
314
|
+
# values[2] = original line delta
|
|
315
|
+
# values[3] = original column delta
|
|
316
|
+
# values[4] = name index delta
|
|
317
|
+
|
|
318
|
+
gen_col_delta = values[0]
|
|
319
|
+
|
|
320
|
+
if values.length > 1
|
|
321
|
+
# Update section-local absolute positions
|
|
322
|
+
section_abs_source += values[1]
|
|
323
|
+
section_abs_orig_line += values[2] if values.length > 2
|
|
324
|
+
section_abs_orig_col += values[3] if values.length > 3
|
|
325
|
+
section_abs_name += values[4] if values.length > 4
|
|
326
|
+
|
|
327
|
+
# Calculate global absolute positions (with offset)
|
|
328
|
+
global_abs_source = section_abs_source + source_offset
|
|
329
|
+
global_abs_name = section_abs_name + name_offset
|
|
330
|
+
|
|
331
|
+
if !first_segment_processed
|
|
332
|
+
# First segment: calculate delta from previous section's end state to this segment's global position
|
|
333
|
+
new_source_delta = global_abs_source - prev_source
|
|
334
|
+
new_orig_line_delta = section_abs_orig_line - prev_orig_line
|
|
335
|
+
new_orig_col_delta = section_abs_orig_col - prev_orig_col
|
|
336
|
+
new_name_delta = global_abs_name - prev_name
|
|
337
|
+
|
|
338
|
+
new_values = [gen_col_delta, new_source_delta, new_orig_line_delta, new_orig_col_delta]
|
|
339
|
+
new_values << new_name_delta if values.length > 4
|
|
340
|
+
|
|
341
|
+
first_segment_processed = true
|
|
342
|
+
else
|
|
343
|
+
# Subsequent segments: deltas are already correct (relative within section = relative within merged)
|
|
344
|
+
new_values = values.dup
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
result_segments << encode_vlq(new_values)
|
|
348
|
+
|
|
349
|
+
# Update tracking for next section
|
|
350
|
+
prev_source = global_abs_source
|
|
351
|
+
prev_orig_line = section_abs_orig_line
|
|
352
|
+
prev_orig_col = section_abs_orig_col
|
|
353
|
+
prev_name = global_abs_name if values.length > 4
|
|
354
|
+
else
|
|
355
|
+
# Only generated column, no source mapping
|
|
356
|
+
result_segments << encode_vlq([gen_col_delta])
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
result_lines << result_segments.join(',')
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
[result_lines.join(';'), prev_source, prev_orig_line, prev_orig_col, prev_name]
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# VLQ Base64 character set
|
|
367
|
+
VLQ_BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.freeze
|
|
368
|
+
VLQ_BASE64_VALUES = VLQ_BASE64_CHARS.each_char.with_index.to_h.freeze
|
|
369
|
+
VLQ_BASE_SHIFT = 5
|
|
370
|
+
VLQ_BASE = 1 << VLQ_BASE_SHIFT # 32
|
|
371
|
+
VLQ_BASE_MASK = VLQ_BASE - 1 # 31
|
|
372
|
+
VLQ_CONTINUATION_BIT = VLQ_BASE # 32
|
|
373
|
+
|
|
374
|
+
# Decode a VLQ-encoded segment into an array of integers
|
|
375
|
+
def decode_vlq(segment)
|
|
376
|
+
return [] if segment.nil? || segment.empty?
|
|
377
|
+
|
|
378
|
+
values = []
|
|
379
|
+
shift = 0
|
|
380
|
+
value = 0
|
|
381
|
+
|
|
382
|
+
segment.each_char do |char|
|
|
383
|
+
digit = VLQ_BASE64_VALUES[char]
|
|
384
|
+
return values if digit.nil? # Invalid character
|
|
385
|
+
|
|
386
|
+
continuation = (digit & VLQ_CONTINUATION_BIT) != 0
|
|
387
|
+
digit &= VLQ_BASE_MASK
|
|
388
|
+
value += digit << shift
|
|
389
|
+
|
|
390
|
+
if continuation
|
|
391
|
+
shift += VLQ_BASE_SHIFT
|
|
392
|
+
else
|
|
393
|
+
# Convert from VLQ signed representation
|
|
394
|
+
negative = (value & 1) == 1
|
|
395
|
+
value >>= 1
|
|
396
|
+
value = -value if negative
|
|
397
|
+
values << value
|
|
398
|
+
|
|
399
|
+
# Reset for next value
|
|
400
|
+
value = 0
|
|
401
|
+
shift = 0
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
values
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Encode an array of integers into a VLQ-encoded string
|
|
409
|
+
def encode_vlq(values)
|
|
410
|
+
return '' if values.nil? || values.empty?
|
|
411
|
+
|
|
412
|
+
result = ''
|
|
413
|
+
|
|
414
|
+
values.each do |value|
|
|
415
|
+
# Convert to VLQ signed representation
|
|
416
|
+
vlq = value < 0 ? ((-value) << 1) + 1 : value << 1
|
|
417
|
+
|
|
418
|
+
loop do
|
|
419
|
+
digit = vlq & VLQ_BASE_MASK
|
|
420
|
+
vlq >>= VLQ_BASE_SHIFT
|
|
421
|
+
digit |= VLQ_CONTINUATION_BIT if vlq > 0
|
|
422
|
+
result += VLQ_BASE64_CHARS[digit]
|
|
423
|
+
break if vlq == 0
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
result
|
|
428
|
+
end
|
|
429
|
+
|
|
245
430
|
def normalize_source_path(source, file_path)
|
|
246
431
|
# Convert absolute paths to relative for better browser debugging
|
|
247
432
|
return source if source.nil? || source.empty?
|
|
@@ -257,6 +442,39 @@ module Opal
|
|
|
257
442
|
source
|
|
258
443
|
end
|
|
259
444
|
end
|
|
445
|
+
|
|
446
|
+
def normalize_source_path_for_devtools(source, file_path)
|
|
447
|
+
# Format source paths so they appear properly in browser DevTools
|
|
448
|
+
# Chrome DevTools uses the source path to build the file tree
|
|
449
|
+
return source if source.nil? || source.empty?
|
|
450
|
+
|
|
451
|
+
# Remove any leading ./ for consistency
|
|
452
|
+
source = source.sub(/^\.\//, '')
|
|
453
|
+
|
|
454
|
+
# Handle absolute paths - make them relative to show properly
|
|
455
|
+
if source.start_with?('/')
|
|
456
|
+
# Extract just the relevant path (last few components)
|
|
457
|
+
parts = source.split('/')
|
|
458
|
+
# Keep last 3 path components for context (e.g., app/opal/controllers/...)
|
|
459
|
+
source = parts.last(3).join('/')
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Prefix paths for user code (controllers, services, etc.) to group them
|
|
463
|
+
# This ensures they appear under a dedicated folder in DevTools
|
|
464
|
+
if source.include?('controllers/') || source.include?('services/')
|
|
465
|
+
# Already has recognizable path, prefix with opal-sources for visibility
|
|
466
|
+
"/opal-sources/#{source}"
|
|
467
|
+
elsif source.start_with?('corelib/') || source.start_with?('opal/')
|
|
468
|
+
# Opal core library files - put under opal-core
|
|
469
|
+
"/opal-core/#{source}"
|
|
470
|
+
elsif source.include?('opal_stimulus/') || source.include?('opal_vite/')
|
|
471
|
+
# Opal library files
|
|
472
|
+
"/opal-libs/#{source}"
|
|
473
|
+
else
|
|
474
|
+
# Other files (native.rb, js/proxy.rb, etc.)
|
|
475
|
+
"/opal-other/#{source}"
|
|
476
|
+
end
|
|
477
|
+
end
|
|
260
478
|
end
|
|
261
479
|
end
|
|
262
480
|
end
|
data/lib/opal/vite/version.rb
CHANGED