rusteerio 1.0.0 → 1.1.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.
Files changed (65) hide show
  1. package/Cargo.lock +42 -1
  2. package/Cargo.toml +8 -1
  3. package/README.md +90 -51
  4. package/THIRD_PARTY_NOTICES.md +138 -2
  5. package/dist/{chunk-DMRD3FGC.mjs → chunk-FPRP6UIX.mjs} +2233 -751
  6. package/dist/chunk-FPRP6UIX.mjs.map +1 -0
  7. package/dist/index.cjs +2232 -750
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +1 -1
  12. package/dist/slim.cjs +2226 -745
  13. package/dist/slim.cjs.map +1 -1
  14. package/dist/slim.d.mts +2 -2
  15. package/dist/slim.d.ts +2 -2
  16. package/dist/slim.mjs +1 -1
  17. package/dist/{types-BP6TpxE5.d.mts → types-Cs9UP1Vv.d.mts} +3 -3
  18. package/dist/{types-BP6TpxE5.d.ts → types-Cs9UP1Vv.d.ts} +3 -3
  19. package/dist/utils.d.mts +1 -1
  20. package/dist/utils.d.ts +1 -1
  21. package/native/src/arena.rs +231 -602
  22. package/native/src/attributes.rs +11 -5
  23. package/native/src/codec.rs +62 -18
  24. package/native/src/document_order.rs +711 -0
  25. package/native/src/extraction.rs +15 -3
  26. package/native/src/gc.rs +12 -3
  27. package/native/src/hook_sink.rs +77 -3
  28. package/native/src/id_hash.rs +42 -0
  29. package/native/src/immutable.rs +256 -0
  30. package/native/src/lib.rs +1097 -172
  31. package/native/src/markup_tokenizer.rs +668 -0
  32. package/native/src/runtime.rs +189 -33
  33. package/native/src/selector.rs +346 -44
  34. package/native/src/sink.rs +14 -1
  35. package/native/src/snapshot.rs +509 -0
  36. package/native/src/stream.rs +2 -2
  37. package/native/src/tokenizer.rs +377 -204
  38. package/native/vendor/README.md +3 -2
  39. package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +79 -0
  40. package/native/vendor/html5ever-0.35.0/src/tokenizer/mod.rs +23 -13
  41. package/package.json +3 -1
  42. package/rusteerio.darwin-arm64.node +0 -0
  43. package/rusteerio.darwin-x64.node +0 -0
  44. package/rusteerio.linux-arm64-gnu.node +0 -0
  45. package/rusteerio.linux-arm64-musl.node +0 -0
  46. package/rusteerio.linux-x64-gnu.node +0 -0
  47. package/rusteerio.linux-x64-musl.node +0 -0
  48. package/rusteerio.win32-arm64-msvc.node +0 -0
  49. package/rusteerio.win32-x64-msvc.node +0 -0
  50. package/scripts/generate-notices.mjs +11 -0
  51. package/scripts/notices/htmlparser2-MIT.txt +18 -0
  52. package/scripts/notices/sources.json +6 -0
  53. package/src/attributes.ts +78 -44
  54. package/src/codec.ts +22 -3
  55. package/src/extract.ts +86 -2
  56. package/src/hooks.ts +148 -2
  57. package/src/index.ts +51 -40
  58. package/src/match.ts +207 -0
  59. package/src/native.ts +9 -1
  60. package/src/nodes.ts +531 -183
  61. package/src/router.ts +792 -229
  62. package/src/selection.ts +239 -125
  63. package/src/snapshot.ts +255 -0
  64. package/src/transport.ts +51 -42
  65. package/dist/chunk-DMRD3FGC.mjs.map +0 -1
@@ -173,7 +173,7 @@ fn set_css(arena: &mut DomArena, ids: &[u32], name: &str, value: &Value) {
173
173
  }
174
174
  let mut properties = parse_css(arena.attr(*id, "style").unwrap_or(""));
175
175
  if value.as_str() == Some("") {
176
- properties.remove(name);
176
+ properties.shift_remove(name);
177
177
  } else if !value.is_null() {
178
178
  properties.insert(name.into(), json!(js_string(value)));
179
179
  }
@@ -207,9 +207,15 @@ fn node_property(arena: &DomArena, id: u32, name: &str) -> bool {
207
207
  | "type"
208
208
  | "startIndex"
209
209
  | "endIndex"
210
- ) || arena
211
- .node(id)
212
- .is_some_and(|node| node.extras.contains_key(name))
210
+ ) || arena.node(id).is_some_and(|node| {
211
+ node.extras.contains_key(name)
212
+ || match name {
213
+ "namespace" => node.namespace.is_some(),
214
+ "attribsNamespace" => node.attribs_namespace.is_some(),
215
+ "attribsPrefix" => node.attribs_prefix.is_some(),
216
+ _ => false,
217
+ }
218
+ })
213
219
  }
214
220
 
215
221
  fn get_prop(arena: &DomArena, id: u32, name: &str) -> napi::Result<Value> {
@@ -682,7 +688,7 @@ pub(crate) fn execute(
682
688
  .and_then(|node| node.extras.get_mut("data"))
683
689
  .and_then(Value::as_object_mut)
684
690
  {
685
- data.remove(name);
691
+ data.shift_remove(name);
686
692
  }
687
693
  }
688
694
  }
@@ -10,6 +10,9 @@ pub(crate) fn has_encoding(value: &str) -> bool {
10
10
  value.chars().any(is_reserved)
11
11
  }
12
12
  pub(crate) fn from_units(units: &[u16]) -> String {
13
+ if units.iter().all(|unit| !(0xd800..=0xe800).contains(unit)) {
14
+ return String::from_utf16_lossy(units);
15
+ }
13
16
  let mut output = String::with_capacity(units.len());
14
17
  let mut index = 0;
15
18
  while index < units.len() {
@@ -38,28 +41,69 @@ pub(crate) fn from_units(units: &[u16]) -> String {
38
41
  }
39
42
  output
40
43
  }
41
- pub(crate) fn to_units(value: &str) -> Vec<u16> {
42
- let mut output = Vec::with_capacity(value.len());
43
- let mut chars = value.chars().peekable();
44
- while let Some(ch) = chars.next() {
45
- if ch == ESCAPE && chars.peek().copied().is_some_and(is_reserved) {
46
- output.push(chars.next().unwrap() as u16);
47
- } else if ('\u{e000}'..'\u{e800}').contains(&ch) {
48
- output.push(0xd800 + (ch as u16 - 0xe000));
49
- } else {
50
- let mut encoded = [0; 2];
51
- output.extend_from_slice(ch.encode_utf16(&mut encoded));
52
- }
44
+ /// Receives the UTF-16 units of a string from [`visit_units`].
45
+ pub(crate) trait UnitSink {
46
+ /// A run of ASCII bytes starting at byte offset `start`, one unit each.
47
+ fn ascii(&mut self, start: usize, run: &[u8]);
48
+ /// One unit and the byte offset of the character that starts there, or
49
+ /// `None` for the low half of a surrogate pair.
50
+ fn unit(&mut self, offset: Option<usize>, unit: u16);
51
+ }
52
+
53
+ impl UnitSink for Vec<u16> {
54
+ fn ascii(&mut self, _: usize, run: &[u8]) {
55
+ self.extend(run.iter().map(|value| u16::from(*value)));
56
+ }
57
+ fn unit(&mut self, _: Option<usize>, unit: u16) {
58
+ self.push(unit);
53
59
  }
54
- output
55
60
  }
56
- pub(crate) fn logical_len(value: &str) -> usize {
57
- if has_encoding(value) {
58
- to_units(value).len()
59
- } else {
60
- value.encode_utf16().count()
61
+
62
+ /// Feeds the UTF-16 units of `value` to `sink`. With `framed`, lossless frames
63
+ /// decode to their original units.
64
+ pub(crate) fn visit_units(value: &str, framed: bool, sink: &mut impl UnitSink) {
65
+ let bytes = value.as_bytes();
66
+ let mut byte = 0;
67
+ while byte < bytes.len() {
68
+ let ascii = bytes[byte..]
69
+ .iter()
70
+ .position(|value| !value.is_ascii())
71
+ .unwrap_or(bytes.len() - byte);
72
+ sink.ascii(byte, &bytes[byte..byte + ascii]);
73
+ byte += ascii;
74
+ let Some(ch) = value[byte..].chars().next() else {
75
+ break;
76
+ };
77
+ let start = byte;
78
+ byte += ch.len_utf8();
79
+ if framed && ch == ESCAPE {
80
+ if let Some(next) = value[byte..]
81
+ .chars()
82
+ .next()
83
+ .filter(|next| is_reserved(*next))
84
+ {
85
+ sink.unit(Some(start), next as u16);
86
+ byte += next.len_utf8();
87
+ continue;
88
+ }
89
+ }
90
+ if framed && ('\u{e000}'..ESCAPE).contains(&ch) {
91
+ sink.unit(Some(start), 0xd800 + (ch as u16 - 0xe000));
92
+ continue;
93
+ }
94
+ let mut encoded = [0; 2];
95
+ let encoded = ch.encode_utf16(&mut encoded);
96
+ sink.unit(Some(start), encoded[0]);
97
+ if let Some(low) = encoded.get(1) {
98
+ sink.unit(None, *low);
99
+ }
61
100
  }
62
101
  }
102
+ pub(crate) fn to_units(value: &str) -> Vec<u16> {
103
+ let mut output = Vec::with_capacity(value.len());
104
+ visit_units(value, has_encoding(value), &mut output);
105
+ output
106
+ }
63
107
  pub(crate) fn to_usv(value: &str) -> String {
64
108
  if has_encoding(value) {
65
109
  String::from_utf16_lossy(&to_units(value))