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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 589d2115317b97d0c2c1452001b128f4ac3269d8e05c390e60e87d0446c8ebf0
4
- data.tar.gz: 2811c208a9e64dfd800d2f1d4540a052f348b82b03f255ff6cae820895af8098
3
+ metadata.gz: 19c908ad19445dc3ca95af2873ad94c885fd3546de85a78005cf3d45a10c2b7f
4
+ data.tar.gz: 10fd5fb0daf18cf0eeb466079307eb6f47f913c4e7e1e7031c7d7944beff4cb2
5
5
  SHA512:
6
- metadata.gz: a85ead98a10e206d858a508ef808f9410af6bf6597c6f167b93560da2cb706cbffb252b90ac5144eb9003ef45291bcdbdd84f8dab7c56425d08933be952bbbe8
7
- data.tar.gz: 117923f00c860592866c31fba50371b84a8c47007c822ce240fa377c510119b1f640443cfa338b78298e5fe59ec1c7d44137340c71503634a71f9b26d9813783
6
+ metadata.gz: 0fca963918760eb442d55982d5de06875ed92293ad58a0d422f095f4e72bf2181ebbdb4ea99bdd978473d860b5f2948d98e92550938df0e7310fd707e94228b2
7
+ data.tar.gz: 6016bfab349b9b24af874cdf416bbc2dde40e6e106c30ce18157c609d7de418f1935c79d6fe782d75cb3ff03d49385f1c4b93994baf77e5e45aea90b834e3e1c
@@ -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
- normalize_source_path(source, file_path)
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 offset for this section
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
- # Add mappings from this section
252
+ # Process mappings from this section with index adjustment
227
253
  if section_map['mappings'] && !section_map['mappings'].empty?
228
- # If we need to adjust source/name indices, we'd need to decode/re-encode VLQ
229
- # For now, append as-is (works when each section has its own source indices starting at 0)
230
- # This is a simplification - proper implementation would adjust indices
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'] += section_map['mappings']
268
+ merged['mappings'] += adjusted_mappings
235
269
 
236
- # Count lines in this section's mappings
237
- lines_in_section = section_map['mappings'].count(';') + 1
238
- current_line += lines_in_section
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
@@ -1,5 +1,5 @@
1
1
  module Opal
2
2
  module Vite
3
- VERSION = "0.2.5"
3
+ VERSION = "0.2.6"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opal-vite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - stofu1234