rusteerio 0.0.0-stage → 0.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 (206) hide show
  1. package/CONTRIBUTING.md +73 -0
  2. package/Cargo.lock +1051 -0
  3. package/Cargo.toml +36 -0
  4. package/LICENSE +21 -0
  5. package/README.md +252 -2
  6. package/RELEASING.md +138 -0
  7. package/THIRD_PARTY_NOTICES.md +7443 -0
  8. package/build.rs +3 -0
  9. package/dist/chunk-DKULX6VO.mjs +3185 -0
  10. package/dist/chunk-DKULX6VO.mjs.map +1 -0
  11. package/dist/chunk-OHPGEY3E.mjs +32 -0
  12. package/dist/chunk-OHPGEY3E.mjs.map +1 -0
  13. package/dist/index.cjs +3230 -0
  14. package/dist/index.cjs.map +1 -0
  15. package/dist/index.d.mts +113 -0
  16. package/dist/index.d.ts +113 -0
  17. package/dist/index.mjs +38 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/slim.cjs +2441 -0
  20. package/dist/slim.cjs.map +1 -0
  21. package/dist/slim.d.mts +11 -0
  22. package/dist/slim.d.ts +11 -0
  23. package/dist/slim.mjs +19 -0
  24. package/dist/slim.mjs.map +1 -0
  25. package/dist/types-BP6TpxE5.d.mts +532 -0
  26. package/dist/types-BP6TpxE5.d.ts +532 -0
  27. package/dist/utils.cjs +61 -0
  28. package/dist/utils.cjs.map +1 -0
  29. package/dist/utils.d.mts +13 -0
  30. package/dist/utils.d.ts +13 -0
  31. package/dist/utils.mjs +17 -0
  32. package/dist/utils.mjs.map +1 -0
  33. package/native/src/arena.rs +1781 -0
  34. package/native/src/attributes.rs +923 -0
  35. package/native/src/codec.rs +172 -0
  36. package/native/src/diagnostics.rs +1249 -0
  37. package/native/src/extraction.rs +226 -0
  38. package/native/src/gc.rs +166 -0
  39. package/native/src/hook_sink.rs +727 -0
  40. package/native/src/hook_stream.rs +513 -0
  41. package/native/src/lib.rs +2095 -0
  42. package/native/src/location.rs +392 -0
  43. package/native/src/runtime.rs +189 -0
  44. package/native/src/selector.rs +1287 -0
  45. package/native/src/sink.rs +261 -0
  46. package/native/src/stream.rs +334 -0
  47. package/native/src/tokenizer.rs +355 -0
  48. package/native/src/tracker.rs +232 -0
  49. package/native/vendor/README.md +25 -0
  50. package/native/vendor/html5ever-0.35.0/.cargo_vcs_info.json +6 -0
  51. package/native/vendor/html5ever-0.35.0/Cargo.lock +828 -0
  52. package/native/vendor/html5ever-0.35.0/Cargo.toml +85 -0
  53. package/native/vendor/html5ever-0.35.0/LICENSE-APACHE +201 -0
  54. package/native/vendor/html5ever-0.35.0/LICENSE-MIT +25 -0
  55. package/native/vendor/html5ever-0.35.0/README.md +60 -0
  56. package/native/vendor/html5ever-0.35.0/benches/html5ever.rs +86 -0
  57. package/native/vendor/html5ever-0.35.0/data/bench/lipsum-zh.html +19 -0
  58. package/native/vendor/html5ever-0.35.0/data/bench/lipsum.html +40 -0
  59. package/native/vendor/html5ever-0.35.0/data/bench/medium-fragment.html +24 -0
  60. package/native/vendor/html5ever-0.35.0/data/bench/small-fragment.html +7 -0
  61. package/native/vendor/html5ever-0.35.0/data/bench/strong.html +1 -0
  62. package/native/vendor/html5ever-0.35.0/data/bench/tiny-fragment.html +1 -0
  63. package/native/vendor/html5ever-0.35.0/examples/arena.rs +356 -0
  64. package/native/vendor/html5ever-0.35.0/examples/capi/tokenize.c +74 -0
  65. package/native/vendor/html5ever-0.35.0/examples/noop-tokenize.rs +47 -0
  66. package/native/vendor/html5ever-0.35.0/examples/noop-tree-builder.rs +132 -0
  67. package/native/vendor/html5ever-0.35.0/examples/print-tree-actions.rs +185 -0
  68. package/native/vendor/html5ever-0.35.0/examples/tokenize.rs +111 -0
  69. package/native/vendor/html5ever-0.35.0/src/driver.rs +148 -0
  70. package/native/vendor/html5ever-0.35.0/src/lib.rs +30 -0
  71. package/native/vendor/html5ever-0.35.0/src/macros.rs +34 -0
  72. package/native/vendor/html5ever-0.35.0/src/serialize/mod.rs +255 -0
  73. package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +451 -0
  74. package/native/vendor/html5ever-0.35.0/src/tokenizer/interface.rs +99 -0
  75. package/native/vendor/html5ever-0.35.0/src/tokenizer/mod.rs +2305 -0
  76. package/native/vendor/html5ever-0.35.0/src/tokenizer/states.rs +97 -0
  77. package/native/vendor/html5ever-0.35.0/src/tree_builder/data.rs +170 -0
  78. package/native/vendor/html5ever-0.35.0/src/tree_builder/mod.rs +1848 -0
  79. package/native/vendor/html5ever-0.35.0/src/tree_builder/rules.rs +1494 -0
  80. package/native/vendor/html5ever-0.35.0/src/tree_builder/tag_sets.rs +114 -0
  81. package/native/vendor/html5ever-0.35.0/src/tree_builder/types.rs +91 -0
  82. package/native/vendor/html5ever-0.35.0/src/util/str.rs +52 -0
  83. package/native/vendor/html5gum-0.8.4/Cargo.lock +1006 -0
  84. package/native/vendor/html5gum-0.8.4/Cargo.toml +109 -0
  85. package/native/vendor/html5gum-0.8.4/LICENSE +19 -0
  86. package/native/vendor/html5gum-0.8.4/README.md +110 -0
  87. package/native/vendor/html5gum-0.8.4/benches/files/0050.html +2131 -0
  88. package/native/vendor/html5gum-0.8.4/benches/files/6D30.html +1004 -0
  89. package/native/vendor/html5gum-0.8.4/benches/files.rs +32 -0
  90. package/native/vendor/html5gum-0.8.4/benches/patterns.rs +31 -0
  91. package/native/vendor/html5gum-0.8.4/src/arrayvec.rs +29 -0
  92. package/native/vendor/html5gum-0.8.4/src/char_validator.rs +134 -0
  93. package/native/vendor/html5gum-0.8.4/src/emitters/callback.rs +676 -0
  94. package/native/vendor/html5gum-0.8.4/src/emitters/default.rs +207 -0
  95. package/native/vendor/html5gum-0.8.4/src/emitters/emitter.rs +344 -0
  96. package/native/vendor/html5gum-0.8.4/src/emitters/html5ever.rs +306 -0
  97. package/native/vendor/html5gum-0.8.4/src/emitters/mod.rs +25 -0
  98. package/native/vendor/html5gum-0.8.4/src/entities.rs +3342 -0
  99. package/native/vendor/html5gum-0.8.4/src/error.rs +96 -0
  100. package/native/vendor/html5gum-0.8.4/src/htmlstring.rs +130 -0
  101. package/native/vendor/html5gum-0.8.4/src/lib.rs +66 -0
  102. package/native/vendor/html5gum-0.8.4/src/machine.rs +2136 -0
  103. package/native/vendor/html5gum-0.8.4/src/machine_helper.rs +277 -0
  104. package/native/vendor/html5gum-0.8.4/src/read_helper.rs +244 -0
  105. package/native/vendor/html5gum-0.8.4/src/reader.rs +440 -0
  106. package/native/vendor/html5gum-0.8.4/src/span.rs +98 -0
  107. package/native/vendor/html5gum-0.8.4/src/state.rs +16 -0
  108. package/native/vendor/html5gum-0.8.4/src/testutils.rs +71 -0
  109. package/native/vendor/html5gum-0.8.4/src/tokenizer.rs +189 -0
  110. package/native/vendor/html5gum-0.8.4/src/utils.rs +88 -0
  111. package/native/vendor/markup5ever-0.35.0/.cargo_vcs_info.json +6 -0
  112. package/native/vendor/markup5ever-0.35.0/Cargo.toml +45 -0
  113. package/native/vendor/markup5ever-0.35.0/LICENSE-APACHE +201 -0
  114. package/native/vendor/markup5ever-0.35.0/LICENSE-MIT +25 -0
  115. package/native/vendor/markup5ever-0.35.0/interface/mod.rs +422 -0
  116. package/native/vendor/markup5ever-0.35.0/interface/tree_builder.rs +280 -0
  117. package/native/vendor/markup5ever-0.35.0/lib.rs +59 -0
  118. package/native/vendor/markup5ever-0.35.0/serialize.rs +77 -0
  119. package/native/vendor/markup5ever-0.35.0/util/buffer_queue.rs +333 -0
  120. package/native/vendor/markup5ever-0.35.0/util/smallcharset.rs +88 -0
  121. package/package.json +137 -4
  122. package/rusteerio.darwin-arm64.node +0 -0
  123. package/rusteerio.darwin-x64.node +0 -0
  124. package/rusteerio.linux-arm64-gnu.node +0 -0
  125. package/rusteerio.linux-arm64-musl.node +0 -0
  126. package/rusteerio.linux-x64-gnu.node +0 -0
  127. package/rusteerio.linux-x64-musl.node +0 -0
  128. package/rusteerio.win32-arm64-msvc.node +0 -0
  129. package/rusteerio.win32-x64-msvc.node +0 -0
  130. package/scripts/check-native.mjs +15 -0
  131. package/scripts/check-release.mjs +235 -0
  132. package/scripts/fix-declarations.mjs +27 -0
  133. package/scripts/generate-notices.mjs +176 -0
  134. package/scripts/notices/README.md +13 -0
  135. package/scripts/notices/cheerio-MIT.txt +21 -0
  136. package/scripts/notices/html5ever-AUTHORS.txt +20 -0
  137. package/scripts/notices/html5ever-COPYRIGHT.txt +8 -0
  138. package/scripts/notices/html5ever-LICENSE-APACHE.txt +201 -0
  139. package/scripts/notices/html5ever-LICENSE-MIT.txt +25 -0
  140. package/scripts/notices/napi-rs-MIT.txt +43 -0
  141. package/scripts/notices/neon-MIT.txt +19 -0
  142. package/scripts/notices/runtime-01c266bced4a434da005.txt +3 -0
  143. package/scripts/notices/runtime-0a54e647fe54104658b5.txt +14 -0
  144. package/scripts/notices/runtime-0b74dfa0bcee5c420c6b.txt +25 -0
  145. package/scripts/notices/runtime-0d542e0c8804e39aa7f3.txt +177 -0
  146. package/scripts/notices/runtime-0f96a83840e146e43c0e.txt +21 -0
  147. package/scripts/notices/runtime-123a331b5dbf04c30097.txt +25 -0
  148. package/scripts/notices/runtime-172020dbfd5b53a226df.txt +34 -0
  149. package/scripts/notices/runtime-209fbbe0ad52d9235e37.txt +26 -0
  150. package/scripts/notices/runtime-23f18e03dc49df91622f.txt +23 -0
  151. package/scripts/notices/runtime-268872b9816f90fd8e85.txt +220 -0
  152. package/scripts/notices/runtime-35242e7a83f69875e6ed.txt +176 -0
  153. package/scripts/notices/runtime-378f5840b258e2779c39.txt +25 -0
  154. package/scripts/notices/runtime-4108245a1f2df9d4e94d.txt +25 -0
  155. package/scripts/notices/runtime-4455bf75a91154108304.txt +21 -0
  156. package/scripts/notices/runtime-553fffcd9b1cb158bc3e.txt +13 -0
  157. package/scripts/notices/runtime-62c7a1e35f56406896d7.txt +176 -0
  158. package/scripts/notices/runtime-6485b8ed310d3f0340bf.txt +25 -0
  159. package/scripts/notices/runtime-69fef7b0f322a6555415.txt +201 -0
  160. package/scripts/notices/runtime-6df43f6f4b5d4587f3d8.txt +187 -0
  161. package/scripts/notices/runtime-799e9ca9d179295ef372.txt +24 -0
  162. package/scripts/notices/runtime-7b63ecd5f1902af1b637.txt +25 -0
  163. package/scripts/notices/runtime-7bc7d5de2a0b793d4e1a.txt +21 -0
  164. package/scripts/notices/runtime-7e12e5df4bae12cb2158.txt +24 -0
  165. package/scripts/notices/runtime-861399f8c21c042b1105.txt +12 -0
  166. package/scripts/notices/runtime-8ada45cd9f843acf64e4.txt +201 -0
  167. package/scripts/notices/runtime-90eb64f0279b0d9432ac.txt +12 -0
  168. package/scripts/notices/runtime-a60eea817514531668d7.txt +201 -0
  169. package/scripts/notices/runtime-a7c936ff1ed8fa340172.txt +25 -0
  170. package/scripts/notices/runtime-ab6eec6caf0fa5775e41.txt +275 -0
  171. package/scripts/notices/runtime-b1181a40b2a7b25cf66f.txt +19 -0
  172. package/scripts/notices/runtime-b71bd43a069ca0641a9e.txt +25 -0
  173. package/scripts/notices/runtime-c16f8dcf1a368b83be78.txt +201 -0
  174. package/scripts/notices/runtime-c2cfccb812fe482101a8.txt +21 -0
  175. package/scripts/notices/runtime-e99d88d232bf57d70f0f.txt +25 -0
  176. package/scripts/notices/runtime-f32fb3b417a194167cfa.txt +9 -0
  177. package/scripts/notices/runtime-f5062c9a188d81dfe66b.txt +39 -0
  178. package/scripts/notices/runtime-f9070a832833fa7488fd.txt +4 -0
  179. package/scripts/notices/runtime-fab3dd6bdab226f1c086.txt +373 -0
  180. package/scripts/notices/runtime-fba3318d0c600380177e.txt +62 -0
  181. package/scripts/notices/runtime-ff8f68cb076caf8cefe7.txt +25 -0
  182. package/scripts/notices/runtime-ff92bed461f50338dd70.txt +74 -0
  183. package/scripts/notices/runtime.json +1632 -0
  184. package/scripts/notices/sources.json +555 -0
  185. package/scripts/notices/xray-MIT.txt +21 -0
  186. package/src/attributes.ts +271 -0
  187. package/src/codec.ts +74 -0
  188. package/src/dom.ts +87 -0
  189. package/src/extract-plan.ts +97 -0
  190. package/src/extract.ts +130 -0
  191. package/src/hooks.ts +220 -0
  192. package/src/index.ts +223 -0
  193. package/src/io.ts +491 -0
  194. package/src/native.ts +135 -0
  195. package/src/nodes.ts +290 -0
  196. package/src/parse.ts +53 -0
  197. package/src/parser-types.ts +157 -0
  198. package/src/router.ts +327 -0
  199. package/src/selection.ts +566 -0
  200. package/src/slim.ts +12 -0
  201. package/src/transport.ts +104 -0
  202. package/src/types.ts +186 -0
  203. package/src/utils.ts +29 -0
  204. package/src/workers.ts +21 -0
  205. package/tsconfig.json +15 -0
  206. package/tsup.config.ts +16 -0
@@ -0,0 +1,355 @@
1
+ //! Consume events from an explicitly supplied JavaScript htmlparser2 Tokenizer.
2
+ //! No markup is retokenized here: user event edits directly determine the DOM.
3
+ use crate::arena::{append_parsed, html_parser_void, implied_close, DomArena, DomNode};
4
+ use serde_json::{json, Map, Value};
5
+
6
+ struct Pending {
7
+ name: String,
8
+ attrs: Map<String, Value>,
9
+ start: usize,
10
+ }
11
+ struct Builder {
12
+ arena: DomArena,
13
+ source: Vec<u16>,
14
+ parents: Vec<u32>,
15
+ foreign: Vec<bool>,
16
+ pending: Option<Pending>,
17
+ attribute_name: String,
18
+ attribute_value: String,
19
+ start: usize,
20
+ end: usize,
21
+ xml: bool,
22
+ lower_tags: bool,
23
+ }
24
+ impl Builder {
25
+ fn slice(&self, start: usize, end: usize) -> String {
26
+ crate::codec::from_units(
27
+ self.source
28
+ .get(
29
+ start.min(self.source.len())
30
+ ..end.min(self.source.len()).max(start.min(self.source.len())),
31
+ )
32
+ .unwrap_or_default(),
33
+ )
34
+ }
35
+ fn name(&self, start: usize, end: usize) -> String {
36
+ let name = self.slice(start, end);
37
+ if self.lower_tags {
38
+ name.to_lowercase()
39
+ } else {
40
+ name
41
+ }
42
+ }
43
+ fn close(&mut self) {
44
+ if let Some(id) = (self.parents.len() > 1).then(|| self.parents.pop().unwrap()) {
45
+ if self.arena.options.get("withEndIndices") == Some(&Value::Bool(true)) {
46
+ self.arena.nodes[id as usize]
47
+ .extras
48
+ .insert("endIndex".into(), json!(self.end));
49
+ }
50
+ }
51
+ }
52
+ fn open(&mut self, name: String) {
53
+ if !self.xml {
54
+ while self
55
+ .parents
56
+ .last()
57
+ .and_then(|id| self.arena.node(*id))
58
+ .and_then(|node| node.name.as_deref())
59
+ .is_some_and(|open| implied_close(&name, open))
60
+ {
61
+ self.close();
62
+ }
63
+ }
64
+ if !(!self.xml && html_parser_void(&name)) {
65
+ if matches!(name.as_str(), "svg" | "math") {
66
+ self.foreign.push(true);
67
+ } else if matches!(
68
+ name.as_str(),
69
+ "mi" | "mo"
70
+ | "mn"
71
+ | "ms"
72
+ | "mtext"
73
+ | "annotation-xml"
74
+ | "foreignobject"
75
+ | "desc"
76
+ | "title"
77
+ ) {
78
+ self.foreign.push(false);
79
+ }
80
+ }
81
+ self.pending = Some(Pending {
82
+ name,
83
+ attrs: Map::new(),
84
+ start: self.start,
85
+ });
86
+ }
87
+ fn end_open(&mut self, self_closing: bool) {
88
+ if let Some(pending) = self.pending.take() {
89
+ let is_void = !self.xml && html_parser_void(&pending.name);
90
+ let kind = if !self.xml && matches!(pending.name.as_str(), "script" | "style") {
91
+ pending.name.as_str()
92
+ } else {
93
+ "tag"
94
+ };
95
+ let mut node = DomNode::new(kind);
96
+ node.name = Some(pending.name);
97
+ node.attribs = pending.attrs;
98
+ let id = append_parsed(
99
+ &mut self.arena,
100
+ *self.parents.last().unwrap(),
101
+ node,
102
+ pending.start,
103
+ self.end,
104
+ );
105
+ if !is_void && !self_closing {
106
+ self.parents.push(id);
107
+ }
108
+ }
109
+ }
110
+ fn text(&mut self, data: String, end: usize) {
111
+ self.end = end.saturating_sub(1);
112
+ let mut node = DomNode::new("text");
113
+ node.data = Some(data);
114
+ append_parsed(
115
+ &mut self.arena,
116
+ *self.parents.last().unwrap(),
117
+ node,
118
+ self.start,
119
+ self.end,
120
+ );
121
+ self.start = end;
122
+ }
123
+ fn instruction(&mut self, start: usize, end: usize, prefix: char) {
124
+ let value = self.slice(start, end);
125
+ let boundary = value
126
+ .find(|ch: char| ch.is_whitespace() || ch == '/')
127
+ .unwrap_or(value.len());
128
+ let name = if self.lower_tags {
129
+ value[..boundary].to_lowercase()
130
+ } else {
131
+ value[..boundary].to_owned()
132
+ };
133
+ let mut node = DomNode::new("directive");
134
+ node.name = Some(format!("{prefix}{name}"));
135
+ node.data = Some(format!("{prefix}{value}"));
136
+ append_parsed(
137
+ &mut self.arena,
138
+ *self.parents.last().unwrap(),
139
+ node,
140
+ self.start,
141
+ end,
142
+ );
143
+ self.end = end;
144
+ self.start = end + 1;
145
+ }
146
+ }
147
+
148
+ pub(crate) fn parse(source: &str, events: &Value, options: Value) -> DomArena {
149
+ let xml = crate::arena::xml_mode(&options);
150
+ let utf16_codec = options.get("__utf16Codec") == Some(&Value::Bool(true));
151
+ let lower_tags = options
152
+ .get("lowerCaseTags")
153
+ .and_then(Value::as_bool)
154
+ .unwrap_or(!xml);
155
+ let lower_attrs = options
156
+ .get("lowerCaseAttributeNames")
157
+ .and_then(Value::as_bool)
158
+ .unwrap_or(!xml);
159
+ let mut builder = Builder {
160
+ arena: DomArena::from_records(vec![DomNode::new("root")], options),
161
+ source: if utf16_codec {
162
+ crate::codec::to_units(source)
163
+ } else {
164
+ source.encode_utf16().collect()
165
+ },
166
+ parents: vec![0],
167
+ foreign: vec![xml],
168
+ pending: None,
169
+ attribute_name: String::new(),
170
+ attribute_value: String::new(),
171
+ start: 0,
172
+ end: 0,
173
+ xml,
174
+ lower_tags,
175
+ };
176
+ for event in events.as_array().into_iter().flatten() {
177
+ let Some(tuple) = event.as_array() else {
178
+ continue;
179
+ };
180
+ let Some(event) = tuple.first().and_then(Value::as_str) else {
181
+ continue;
182
+ };
183
+ let arg = |index: usize| tuple.get(index + 1).and_then(Value::as_u64).unwrap_or(0) as usize;
184
+ let (a, b, c) = (arg(0), arg(1), arg(2));
185
+ match event {
186
+ "ontext" => {
187
+ let text = builder.slice(a, b);
188
+ builder.text(text, b);
189
+ }
190
+ "ontextentity" => {
191
+ let text = if a <= 0xffff {
192
+ crate::codec::from_units(&[a as u16])
193
+ } else {
194
+ crate::codec::encode_scalar(char::from_u32(a as u32).unwrap_or('\u{fffd}'))
195
+ };
196
+ builder.text(text, b);
197
+ }
198
+ "onopentagname" => {
199
+ builder.end = b;
200
+ let name = builder.name(a, b);
201
+ builder.open(name);
202
+ }
203
+ "onopentagend" => {
204
+ builder.end = a;
205
+ builder.end_open(false);
206
+ builder.start = a + 1;
207
+ }
208
+ "onselfclosingtag" => {
209
+ builder.end = a;
210
+ let self_closing = builder
211
+ .arena
212
+ .options
213
+ .get("recognizeSelfClosing")
214
+ .and_then(Value::as_bool)
215
+ .unwrap_or(xml)
216
+ || *builder.foreign.last().unwrap_or(&xml);
217
+ builder.end_open(self_closing);
218
+ builder.start = a + 1;
219
+ }
220
+ "onattribname" => {
221
+ builder.start = a;
222
+ let name = builder.slice(a, b);
223
+ builder.attribute_name = if lower_attrs {
224
+ name.to_lowercase()
225
+ } else {
226
+ name
227
+ };
228
+ }
229
+ "onattribdata" => {
230
+ let data = builder.slice(a, b);
231
+ builder.attribute_value.push_str(&data);
232
+ }
233
+ "onattribentity" => {
234
+ builder.attribute_value.push_str(&if a <= 0xffff {
235
+ crate::codec::from_units(&[a as u16])
236
+ } else {
237
+ crate::codec::encode_scalar(char::from_u32(a as u32).unwrap_or('\u{fffd}'))
238
+ });
239
+ }
240
+ "onattribend" => {
241
+ builder.end = b;
242
+ if let Some(pending) = builder.pending.as_mut() {
243
+ pending
244
+ .attrs
245
+ .entry(builder.attribute_name.clone())
246
+ .or_insert_with(|| Value::String(builder.attribute_value.clone()));
247
+ }
248
+ builder.attribute_value.clear();
249
+ }
250
+ "onclosetag" => {
251
+ builder.end = b;
252
+ let name = builder.name(a, b);
253
+ if !xml
254
+ && matches!(
255
+ name.as_str(),
256
+ "svg"
257
+ | "math"
258
+ | "mi"
259
+ | "mo"
260
+ | "mn"
261
+ | "ms"
262
+ | "mtext"
263
+ | "annotation-xml"
264
+ | "foreignobject"
265
+ | "desc"
266
+ | "title"
267
+ )
268
+ {
269
+ builder.foreign.pop();
270
+ }
271
+ if !(!xml && html_parser_void(&name)) {
272
+ if let Some(position) = builder.parents.iter().rposition(|id| {
273
+ builder
274
+ .arena
275
+ .node(*id)
276
+ .and_then(|node| node.name.as_deref())
277
+ == Some(name.as_str())
278
+ }) {
279
+ while builder.parents.len() > position {
280
+ builder.close();
281
+ }
282
+ } else if !xml && name == "p" {
283
+ builder.open(name);
284
+ builder.end_open(true);
285
+ }
286
+ } else if !xml && name == "br" {
287
+ builder.open(name);
288
+ builder.end_open(true);
289
+ }
290
+ builder.start = b + 1;
291
+ }
292
+ "ondeclaration" => builder.instruction(a, b, '!'),
293
+ "onprocessinginstruction" => builder.instruction(a, b, '?'),
294
+ "oncomment" => {
295
+ let mut node = DomNode::new("comment");
296
+ node.data = Some(builder.slice(a, b.saturating_sub(c)));
297
+ append_parsed(
298
+ &mut builder.arena,
299
+ *builder.parents.last().unwrap(),
300
+ node,
301
+ builder.start,
302
+ b,
303
+ );
304
+ builder.end = b;
305
+ builder.start = b + 1;
306
+ }
307
+ "oncdata" => {
308
+ let data = builder.slice(a, b.saturating_sub(c));
309
+ if xml || builder.arena.options.get("recognizeCDATA") == Some(&Value::Bool(true)) {
310
+ let id = append_parsed(
311
+ &mut builder.arena,
312
+ *builder.parents.last().unwrap(),
313
+ DomNode::new("cdata"),
314
+ builder.start,
315
+ b,
316
+ );
317
+ let mut text = DomNode::new("text");
318
+ text.data = Some(data);
319
+ text.parent = Some(id);
320
+ text.extras.insert("startIndex".into(), Value::Null);
321
+ text.extras.insert(
322
+ "endIndex".into(),
323
+ if builder.arena.options.get("withEndIndices") == Some(&Value::Bool(true)) {
324
+ json!(b)
325
+ } else {
326
+ Value::Null
327
+ },
328
+ );
329
+ let child = builder.arena.push(text);
330
+ builder.arena.nodes[id as usize].children.push(child);
331
+ } else {
332
+ let mut node = DomNode::new("comment");
333
+ node.data = Some(format!("[CDATA[{data}]]"));
334
+ append_parsed(
335
+ &mut builder.arena,
336
+ *builder.parents.last().unwrap(),
337
+ node,
338
+ builder.start,
339
+ b,
340
+ );
341
+ }
342
+ builder.end = b;
343
+ builder.start = b + 1;
344
+ }
345
+ "onend" => {
346
+ builder.end = builder.start;
347
+ while builder.parents.len() > 1 {
348
+ builder.close();
349
+ }
350
+ }
351
+ _ => {}
352
+ }
353
+ }
354
+ builder.arena
355
+ }
@@ -0,0 +1,232 @@
1
+ //! Attach source spans to actual HTML5 sink operations. Scanner metadata is
2
+ //! retained only as a fallback for deferred table text and parser-only closures.
3
+ use crate::diagnostics::DiagnosticContext;
4
+ use serde_json::{Map, Value};
5
+ use std::cell::RefCell;
6
+ use std::collections::{HashMap, HashSet};
7
+ use std::hash::Hash;
8
+ use std::rc::Rc;
9
+
10
+ pub(crate) struct Locations<K> {
11
+ context: Rc<DiagnosticContext>,
12
+ records: RefCell<HashMap<K, Value>>,
13
+ names: RefCell<HashMap<K, String>>,
14
+ closed: RefCell<HashSet<K>>,
15
+ }
16
+ impl<K: Copy + Eq + Hash> Locations<K> {
17
+ pub(crate) fn new(context: Rc<DiagnosticContext>) -> Self {
18
+ Self {
19
+ context,
20
+ records: RefCell::new(HashMap::new()),
21
+ names: RefCell::new(HashMap::new()),
22
+ closed: RefCell::new(HashSet::new()),
23
+ }
24
+ }
25
+ pub(crate) fn created(&self, id: K, name: &str) {
26
+ self.names.borrow_mut().insert(id, name.to_owned());
27
+ self.closed.borrow_mut().remove(&id);
28
+ let token = self.context.current_token();
29
+ let value = if token.kind == "startTag"
30
+ && token
31
+ .name
32
+ .as_deref()
33
+ .is_some_and(|token| token.eq_ignore_ascii_case(name))
34
+ {
35
+ let mut range = self.context.location(token.span.0, token.span.1);
36
+ if !token.attributes.is_empty() {
37
+ let attrs: Map<String, Value> = token
38
+ .attributes
39
+ .into_iter()
40
+ .map(|(name, start, _end)| {
41
+ let raw = self.context.source_between(start, token.span.1);
42
+ let bytes = raw.as_bytes();
43
+ let mut end = 0;
44
+ while end < bytes.len()
45
+ && !bytes[end].is_ascii_whitespace()
46
+ && !matches!(bytes[end], b'=' | b'>' | b'/')
47
+ {
48
+ end += 1;
49
+ }
50
+ let name_end = end;
51
+ while end < bytes.len() && bytes[end].is_ascii_whitespace() {
52
+ end += 1;
53
+ }
54
+ if bytes.get(end) == Some(&b'=') {
55
+ end += 1;
56
+ while end < bytes.len() && bytes[end].is_ascii_whitespace() {
57
+ end += 1;
58
+ }
59
+ if let Some(q @ (b'\'' | b'"')) = bytes.get(end).copied() {
60
+ end += 1;
61
+ while end < bytes.len() && bytes[end] != q {
62
+ end += 1;
63
+ }
64
+ end += (end < bytes.len()) as usize;
65
+ } else {
66
+ while end < bytes.len()
67
+ && !bytes[end].is_ascii_whitespace()
68
+ && bytes[end] != b'>'
69
+ {
70
+ end += 1;
71
+ }
72
+ }
73
+ } else {
74
+ end = name_end;
75
+ }
76
+ (name, self.context.location(start, start + end))
77
+ })
78
+ .collect();
79
+ range["attrs"] = Value::Object(attrs);
80
+ }
81
+ range["startTag"] = range.clone();
82
+ if crate::void_name(name) {
83
+ self.closed.borrow_mut().insert(id);
84
+ }
85
+ range
86
+ } else if matches!(
87
+ name,
88
+ "a" | "b"
89
+ | "big"
90
+ | "code"
91
+ | "em"
92
+ | "font"
93
+ | "i"
94
+ | "nobr"
95
+ | "s"
96
+ | "small"
97
+ | "strike"
98
+ | "strong"
99
+ | "tt"
100
+ | "u"
101
+ ) {
102
+ let names = self.names.borrow();
103
+ let records = self.records.borrow();
104
+ records
105
+ .iter()
106
+ .filter(|(other, value)| {
107
+ names.get(other).is_some_and(|other| other == name)
108
+ && value.get("startTag").is_some()
109
+ })
110
+ .max_by_key(|(_, value)| value["startOffset"].as_u64().unwrap_or(0))
111
+ .map(|(_, value)| {
112
+ let mut value = value.clone();
113
+ if let Some(object) = value.as_object_mut() {
114
+ object.remove("endTag");
115
+ }
116
+ let tag = value["startTag"].clone();
117
+ for field in ["endLine", "endCol", "endOffset"] {
118
+ value[field] = tag[field].clone();
119
+ }
120
+ value
121
+ })
122
+ .unwrap_or(Value::Null)
123
+ } else {
124
+ Value::Null
125
+ };
126
+ self.records.borrow_mut().insert(id, value);
127
+ }
128
+ pub(crate) fn closed(&self, id: K) {
129
+ let token = self.context.current_token();
130
+ if !matches!(token.kind, "startTag" | "endTag" | "eof") {
131
+ return;
132
+ }
133
+ if self.closed.borrow().contains(&id) {
134
+ return;
135
+ }
136
+ let names = self.names.borrow();
137
+ let Some(name) = names.get(&id) else {
138
+ return;
139
+ };
140
+ let mut records = self.records.borrow_mut();
141
+ let Some(Value::Object(record)) = records.get_mut(&id) else {
142
+ return;
143
+ };
144
+ if record.contains_key("endTag") {
145
+ return;
146
+ }
147
+ self.closed.borrow_mut().insert(id);
148
+ let explicit = token.kind == "endTag"
149
+ && token
150
+ .name
151
+ .as_deref()
152
+ .is_some_and(|token| token.eq_ignore_ascii_case(name));
153
+ let end = if explicit { token.span.1 } else { token.span.0 };
154
+ let point = self.context.location(end, end);
155
+ for field in ["endLine", "endCol", "endOffset"] {
156
+ record.insert(field.into(), point[field].clone());
157
+ }
158
+ if explicit {
159
+ record.insert(
160
+ "endTag".into(),
161
+ self.context.location(token.span.0, token.span.1),
162
+ );
163
+ }
164
+ }
165
+ pub(crate) fn closed_as(&self, id: K, name: &str) {
166
+ let previous = self.names.borrow_mut().insert(id, name.to_owned());
167
+ self.closed(id);
168
+ if let Some(previous) = previous {
169
+ self.names.borrow_mut().insert(id, previous);
170
+ }
171
+ }
172
+ pub(crate) fn token(&self, id: K, kind: &str) {
173
+ let token = self.context.current_token();
174
+ if token.kind == kind {
175
+ self.records
176
+ .borrow_mut()
177
+ .insert(id, self.context.location(token.span.0, token.span.1));
178
+ }
179
+ }
180
+ pub(crate) fn text(&self, id: K, data: &str) {
181
+ let token = self.context.current_token();
182
+ if token.kind != "text" {
183
+ return;
184
+ }
185
+ let (mut start, end) = self.context.current_span();
186
+ let raw = self.context.source_between(start, end);
187
+ let normalized = raw
188
+ .replace("\r\n", "\n")
189
+ .replace('\r', "\n")
190
+ .replace('\0', "\u{fffd}");
191
+ let decoded = crate::arena::decode_entities_mode(
192
+ &normalized,
193
+ false,
194
+ false,
195
+ self.context.codec_enabled(),
196
+ );
197
+ if normalized != data && decoded != data {
198
+ if normalized.starts_with('\n')
199
+ && (&normalized[1..] == data
200
+ || crate::arena::decode_entities_mode(
201
+ &normalized[1..],
202
+ false,
203
+ false,
204
+ self.context.codec_enabled(),
205
+ ) == data)
206
+ {
207
+ start += if raw.starts_with("\r\n") { 2 } else { 1 };
208
+ } else {
209
+ return;
210
+ }
211
+ }
212
+ let range = self.context.location(start, end);
213
+ let mut records = self.records.borrow_mut();
214
+ if let Some(Value::Object(record)) = records.get_mut(&id) {
215
+ for field in ["endLine", "endCol", "endOffset"] {
216
+ record.insert(field.into(), range[field].clone());
217
+ }
218
+ } else {
219
+ records.insert(id, range);
220
+ }
221
+ }
222
+ pub(crate) fn get(&self, id: K) -> Option<Value> {
223
+ self.records.borrow().get(&id).cloned()
224
+ }
225
+ pub(crate) fn take(&self) -> HashMap<K, Value> {
226
+ let ids: Vec<_> = self.names.borrow().keys().copied().collect();
227
+ for id in ids {
228
+ self.closed(id);
229
+ }
230
+ std::mem::take(&mut *self.records.borrow_mut())
231
+ }
232
+ }
@@ -0,0 +1,25 @@
1
+ # Vendored HTML5 integration hooks
2
+
3
+ These directories contain html5ever 0.35.0, markup5ever 0.35.0 and html5gum 0.8.4,
4
+ with their original license files (MIT OR Apache-2.0 for html5ever/markup5ever;
5
+ MIT for html5gum). Their exact patched sources ship in
6
+ the npm package and are selected by the root Cargo manifest's
7
+ `[patch.crates-io]` entries.
8
+
9
+ Rusteerio adds optional TreeSink observers for the parser's actual open-element
10
+ stack. Stack push, pop, replacement and truncation operations notify the sink
11
+ as they happen. These hooks allow a caller's parse5-compatible tree adapter to
12
+ observe the same construction process used by the Rust HTML5 tree builder.
13
+ Ordinary parsing uses the default observer methods.
14
+
15
+ An opt-in tokenizer flag also escapes private-use characters produced by
16
+ character references. This preserves the distinction between literal Unicode
17
+ and Rusteerio's internal representation of lone JavaScript UTF-16 surrogates.
18
+ The flag is disabled for upstream users of these crates. The original HTML5
19
+ character-reference decoding rules remain in place.
20
+
21
+ The patches preserve the HTML5 tree-building algorithms. Review changes against
22
+ the original [html5ever archive](https://crates.io/api/v1/crates/html5ever/0.35.0/download)
23
+ and [markup5ever archive](https://crates.io/api/v1/crates/markup5ever/0.35.0/download),
24
+ and the [html5gum archive](https://crates.io/api/v1/crates/html5gum/0.8.4/download).
25
+ Do not remove or edit the original third-party notices when updating these sources.
@@ -0,0 +1,6 @@
1
+ {
2
+ "git": {
3
+ "sha1": "6db7bbf1c4dfaecd28731950d427e40c8b911bef"
4
+ },
5
+ "path_in_vcs": "html5ever"
6
+ }