js_regex 3.11.1 → 3.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4152eef358fa4cdee93ac92b3a2b16590e49be305e81b53e88732ba11eade1f7
4
- data.tar.gz: 7e43bd8ec3ab16a2512769e49508e655f53960353646937b840777c71406af3f
3
+ metadata.gz: 054de046ea7e8e7244c80e8c8385270ffd821f0fdd690b8a15a79ea1c1d2a6f4
4
+ data.tar.gz: d6d3dbd4d191e16008bfd586ff22a10a8240275ac328c0a080af910d99b650a1
5
5
  SHA512:
6
- metadata.gz: e98d601411d6afd3b2ae10df1b18389109a647d4c2ca5e548cb12831badf20ca31de72a20c4fdc93c971aebf3b694623a586e9d1bdc6741084d46bc05a492bfd
7
- data.tar.gz: fd337def96701bc627d09bceb921ba50f341ace408533128b8f8fe4a806d1757805ac2437bc864c3a6c82567190f88c760924a1f0f2df23863cbc15c781ca0e1
6
+ metadata.gz: 93e7c0a574cadfb867c598a9adbcbd9b18585204912865e589691246ce879bec7b7d48a02872c599447db3f2feeda80236d08b1030aecd1d6bf0568792ac859a
7
+ data.tar.gz: e766f2ce0305ed0e6b10372d58f07fb0137e51a4a62a607b1f0272ac4b431c820fdb9ca2b8d2ce18de30d4bd33ca6cd8c49846d5c8efa79c081324342b990e87
@@ -37,7 +37,7 @@ class JsRegex
37
37
  end
38
38
 
39
39
  def convert_options(input, custom_options, required_options)
40
- options = custom_options.to_s.scan(/[gimsuy]/) + required_options
40
+ options = custom_options.to_s.scan(/[dgimsuvy]/) + required_options
41
41
  if input.is_a?(Regexp) && (input.options & Regexp::IGNORECASE).nonzero?
42
42
  options << 'i'
43
43
  end
@@ -10,8 +10,10 @@ class JsRegex
10
10
 
11
11
  def convert_data
12
12
  case subtype
13
- when :bol, :bos then '^'
14
- when :eol, :eos then '$'
13
+ when :bol then convert_bol
14
+ when :bos then '^'
15
+ when :eol then '(?=$|\n)'
16
+ when :eos then '$'
15
17
  when :eos_ob_eol then '(?=\n?$)'
16
18
  when :word_boundary then convert_boundary
17
19
  when :nonword_boundary then convert_nonboundary
@@ -20,6 +22,15 @@ class JsRegex
20
22
  end
21
23
  end
22
24
 
25
+ def convert_bol
26
+ if context.es_2018_or_higher?
27
+ '(?<=^|\n(?!$))'
28
+ else
29
+ # TODO: warn in v4.0.0, or drop ES2009 & ES2015 support
30
+ '^'
31
+ end
32
+ end
33
+
23
34
  def convert_boundary
24
35
  if context.es_2018_or_higher? && context.enable_u_option
25
36
  BOUNDARY_EXPANSION
@@ -29,7 +29,8 @@ class JsRegex
29
29
 
30
30
  def convert_to_plain_num_ref
31
31
  position = new_position
32
- Node.new("\\#{position}", reference: position, type: :backref)
32
+ text = "\\#{position}#{'(?:)' if expression.x?}"
33
+ Node.new(text, reference: position, type: :backref)
33
34
  end
34
35
 
35
36
  def new_position
@@ -43,7 +44,7 @@ class JsRegex
43
44
  def convert_call
44
45
  if context.recursions(expression) >= 5
45
46
  warn_of("Recursion for '#{expression}' curtailed at 5 levels")
46
- return ''
47
+ return drop
47
48
  end
48
49
 
49
50
  context.count_recursion(expression)
@@ -51,7 +52,9 @@ class JsRegex
51
52
  target_copy = expression.referenced_expression.unquantified_clone
52
53
  # avoid "Duplicate capture group name" error in JS
53
54
  target_copy.token = :capture if target_copy.is?(:named, :group)
55
+ context.start_subexp_recursion
54
56
  result = convert_expression(target_copy)
57
+ context.end_subexp_recursion
55
58
  # wrap in group if it is a full-pattern recursion
56
59
  expression.reference == 0 ? Node.new('(?:', result, ')') : result
57
60
  end
@@ -79,8 +79,9 @@ class JsRegex
79
79
  number = context.capturing_group_count + 1
80
80
  backref_node = Node.new("\\#{number}", reference: number, type: :backref)
81
81
  context.increment_local_capturing_group_count
82
- # an empty passive group (?:) is appended as literal digits may follow
83
- Node.new('(?=(', *content, '))', backref_node, '(?:)')
82
+ # The surrounding group is added so that quantifiers apply to the whole.
83
+ # Without it, `(?:)` would need to be appended as literal digits may follow.
84
+ Node.new('(?:(?=(', *content, '))', backref_node, ')')
84
85
  end
85
86
  end
86
87
  end
@@ -10,6 +10,7 @@ class JsRegex
10
10
  :case_insensitive_root,
11
11
  :fail_fast,
12
12
  :in_atomic_group,
13
+ :in_subexp_recursion,
13
14
  :warnings
14
15
 
15
16
  def initialize(case_insensitive_root: false, fail_fast: false, target: nil)
@@ -81,6 +82,14 @@ class JsRegex
81
82
  [exp.class, exp.starts_at]
82
83
  end
83
84
 
85
+ def start_subexp_recursion
86
+ self.in_subexp_recursion = true
87
+ end
88
+
89
+ def end_subexp_recursion
90
+ self.in_subexp_recursion = false
91
+ end
92
+
84
93
  # takes and returns 1-indexed group positions.
85
94
  # new is different from old if capturing groups were added in between.
86
95
  def new_capturing_group_position(old_position)
@@ -106,6 +115,7 @@ class JsRegex
106
115
  :case_insensitive_root,
107
116
  :fail_fast,
108
117
  :in_atomic_group,
118
+ :in_subexp_recursion,
109
119
  :warnings
110
120
 
111
121
  def total_added_capturing_groups
@@ -42,10 +42,10 @@ class JsRegex
42
42
  unicode_escape_codepoint
43
43
  when :literal
44
44
  LiteralConverter.convert_data(expression.char, context)
45
+ when :bell, :escape, :hex, :octal
46
+ hex_escape_codepoint
45
47
  when *ESCAPES_SHARED_BY_RUBY_AND_JS
46
48
  pass_through
47
- when :bell, :escape, :octal
48
- hex_escape_codepoint
49
49
  else
50
50
  warn_of_unsupported_feature
51
51
  end
@@ -84,7 +84,7 @@ class JsRegex
84
84
  tail = opts[:tail] || ')'
85
85
  return Node.new(*wrap(head, tail)) if opts[:capturing].equal?(false)
86
86
 
87
- context.capture_group
87
+ context.capture_group unless context.in_subexp_recursion
88
88
  ref = expression.number
89
89
  Node.new(*wrap(head, tail), reference: ref, type: :captured_group)
90
90
  end
@@ -41,11 +41,7 @@ class JsRegex
41
41
  def simple_convert_child(exp)
42
42
  case exp.type
43
43
  when :literal
44
- return false if !context.u? &&
45
- exp.text =~ LiteralConverter::ASTRAL_PLANE_CODEPOINT_PATTERN &&
46
- !context.enable_u_option
47
-
48
- LiteralConverter.escape_incompatible_bmp_literals(exp.text)
44
+ simple_convert_literal_child(exp)
49
45
  when :set
50
46
  # full conversion is needed for nested sets and intersections
51
47
  exp.token.equal?(:range) && exp.expressions.map do |op|
@@ -67,6 +63,19 @@ class JsRegex
67
63
  end
68
64
  end
69
65
 
66
+ def simple_convert_literal_child(exp)
67
+ if !context.u? &&
68
+ exp.text =~ LiteralConverter::ASTRAL_PLANE_CODEPOINT_PATTERN &&
69
+ !context.enable_u_option
70
+ false
71
+ elsif SET_LITERALS_REQUIRING_ESCAPE_PATTERN.match?(exp.text)
72
+ "\\#{exp.text}"
73
+ else
74
+ LiteralConverter.escape_incompatible_bmp_literals(exp.text)
75
+ end
76
+ end
77
+
78
+ SET_LITERALS_REQUIRING_ESCAPE_PATTERN = Regexp.union(%w<( ) [ ] { } / - |>)
70
79
  SET_SPECIFIC_ESCAPES_PATTERN = /[\^\-]/
71
80
  CONVERTIBLE_ESCAPE_TOKENS = %i[control meta_sequence bell escape octal] +
72
81
  EscapeConverter::ESCAPES_SHARED_BY_RUBY_AND_JS
@@ -8,6 +8,8 @@ class JsRegex
8
8
  class TypeConverter < JsRegex::Converter::Base
9
9
  HEX_EXPANSION = '[0-9A-Fa-f]'
10
10
  NONHEX_EXPANSION = '[^0-9A-Fa-f]'
11
+ I_MODE_HEX_EXPANSION = '[0-9A-F]'
12
+ I_MODE_NONHEX_EXPANSION = '[^0-9A-F]'
11
13
  ES2018_HEX_EXPANSION = '\p{AHex}'
12
14
  ES2018_NONHEX_EXPANSION = '\P{AHex}'
13
15
  ES2018_XGRAPHEME_EXPANSION = '[\P{M}\P{Lm}](?:(?:[\u035C\u0361]\P{M}\p{M}*)|\u200d|\p{M}|\p{Lm}|\p{Emoji_Modifier})*'
@@ -28,7 +30,7 @@ class JsRegex
28
30
  case subtype
29
31
  when :hex then hex_expansion
30
32
  when :nonhex then nonhex_expansion
31
- when :linebreak then LINEBREAK_EXPANSION
33
+ when :linebreak then linebreak_expansion
32
34
  when :xgrapheme then xgrapheme
33
35
  when :digit, :space, :word
34
36
  return pass_through if self.class.directly_compatible?(expression)
@@ -44,6 +46,8 @@ class JsRegex
44
46
  def hex_expansion
45
47
  if context.es_2018_or_higher? && context.enable_u_option
46
48
  ES2018_HEX_EXPANSION
49
+ elsif context.case_insensitive_root
50
+ I_MODE_HEX_EXPANSION
47
51
  else
48
52
  HEX_EXPANSION
49
53
  end
@@ -52,11 +56,17 @@ class JsRegex
52
56
  def nonhex_expansion
53
57
  if context.es_2018_or_higher? && context.enable_u_option
54
58
  ES2018_NONHEX_EXPANSION
59
+ elsif context.case_insensitive_root
60
+ I_MODE_NONHEX_EXPANSION
55
61
  else
56
62
  NONHEX_EXPANSION
57
63
  end
58
64
  end
59
65
 
66
+ def linebreak_expansion
67
+ wrap_in_backrefed_lookahead(LINEBREAK_EXPANSION)
68
+ end
69
+
60
70
  def negative_set_substitution
61
71
  # ::of_expression returns an inverted set for negative expressions,
62
72
  # so we need to un-invert before wrapping in [^ and ]. Kinda lame.
@@ -73,7 +83,7 @@ class JsRegex
73
83
 
74
84
  def xgrapheme
75
85
  if context.es_2018_or_higher? && context.enable_u_option
76
- ES2018_XGRAPHEME_EXPANSION
86
+ wrap_in_backrefed_lookahead(ES2018_XGRAPHEME_EXPANSION)
77
87
  else
78
88
  warn_of_unsupported_feature
79
89
  end
@@ -1,3 +1,3 @@
1
1
  class JsRegex
2
- VERSION = '3.11.1'
2
+ VERSION = '3.13.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js_regex
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.11.1
4
+ version: 3.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janosch Müller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-07 00:00:00.000000000 Z
11
+ date: 2025-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: character_set
@@ -28,22 +28,16 @@ dependencies:
28
28
  name: regexp_parser
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 2.6.2
34
- - - "<"
31
+ - - "~>"
35
32
  - !ruby/object:Gem::Version
36
- version: 3.0.0
33
+ version: '2.10'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 2.6.2
44
- - - "<"
38
+ - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: 3.0.0
40
+ version: '2.10'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: regexp_property_values
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -111,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
105
  - !ruby/object:Gem::Version
112
106
  version: '0'
113
107
  requirements: []
114
- rubygems_version: 3.5.0.dev
108
+ rubygems_version: 3.5.22
115
109
  signing_key:
116
110
  specification_version: 4
117
111
  summary: Converts Ruby regexes to JavaScript regexes.