htmljs-parser 3.1.0 → 3.2.2

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.
package/README.md CHANGED
@@ -1,591 +1,397 @@
1
- # htmljs-parser
1
+ <h1 align="center">
2
+ <!-- Logo -->
3
+ <br/>
4
+ htmljs-parser
5
+ <br/>
6
+
7
+ <!-- Format -->
8
+ <a href="https://github.com/prettier/prettier">
9
+ <img src="https://img.shields.io/badge/styled_with-prettier-ff69b4.svg" alt="Styled with prettier"/>
10
+ </a>
11
+ <!-- CI -->
12
+ <a href="https://github.com/marko-js/htmljs-parser/actions/workflows/ci.yml">
13
+ <img src="https://github.com/marko-js/htmljs-parser/actions/workflows/ci.yml/badge.svg" alt="Build status"/>
14
+ </a>
15
+ <!-- Coverage -->
16
+ <a href="https://codecov.io/gh/marko-js/htmljs-parser">
17
+ <img src="https://codecov.io/gh/marko-js/htmljs-parser/branch/main/graph/badge.svg?token=Sv8ePs16ix" alt="Code Coverage"/>
18
+ </a>
19
+ <!-- NPM Version -->
20
+ <a href="https://npmjs.org/package/htmljs-parser">
21
+ <img src="https://img.shields.io/npm/v/htmljs-parser.svg" alt="NPM version"/>
22
+ </a>
23
+ <!-- Downloads -->
24
+ <a href="https://npmjs.org/package/htmljs-parser">
25
+ <img src="https://img.shields.io/npm/dm/htmljs-parser.svg" alt="Downloads"/>
26
+ </a>
27
+ </h1>
28
+
29
+ An HTML parser with super powers used by [Marko](https://markojs.com/docs/syntax/).
2
30
 
3
- HTML parsers written according to the HTML spec will interpret all
4
- attribute values as strings which makes it challenging to properly
5
- describe a value's type (boolean, string, number, array, etc.)
6
- or to provide a complex JavaScript expression as a value.
7
- The ability to describe JavaScript expressions within attributes
8
- is important for HTML-based template compilers.
9
-
10
- For example, consider a HTML-based template that wishes to
11
- support a custom tag named `<say-hello>` that supports an
12
- attribute named `message` that can be a string literal or a JavaScript expression.
13
-
14
- Ideally, the template compiler should be able to handle any of the following:
15
-
16
- ```html
17
- <say-hello message="Hello world!" />
18
- <say-hello message=("Hello " + personName + "!") />
19
- <say-hello message="Hello ${personName}!" />
20
- ```
21
-
22
- This parser extends the HTML grammar to add these important features:
23
-
24
- - JavaScript expressions as attribute values
25
-
26
- ```html
27
- <say-hello message=("Hello " + personName) count=2+2 large=true />
28
- ```
29
-
30
- - Placeholders in the content of an element
31
-
32
- ```html
33
- <div>Hello ${personName}</div>
34
- ```
35
-
36
- - Placeholders within attribute value strings
31
+ # Installation
37
32
 
38
- ```html
39
- <div data-message="Hello ${personName}!"></div>
33
+ ```console
34
+ npm install htmljs-parser
40
35
  ```
41
36
 
42
- - JavaScript flow-control statements within HTML elements
37
+ # Creating A Parser
43
38
 
44
- ```html
45
- <div for(a in b) />
46
- <div if(a === b) />
47
- ```
39
+ First we must create a parser instance and pass it some handlers for the various parse events shown below.
48
40
 
49
- - JavaScript flow-control statements as elements
41
+ Each parse event is called a `Range` and is an object with start and end properties which are zero-based offsets from the beginning of th parsed code.
50
42
 
51
- ```html
52
- <for (a in b)> <if (a in b)></if></for>
53
- ```
43
+ Additional meta data and nested ranges are exposed on some events shown below.
54
44
 
55
- # Installation
56
-
57
- ```bash
58
- npm install htmljs-parser
59
- ```
60
-
61
- # Usage
45
+ You can get the raw string from any range using `parser.read(range)`.
62
46
 
63
47
  ```javascript
64
- var parser = require("htmljs-parser").createParser({
65
- onText: function (event) {
66
- // Text within an HTML element
67
- var value = event.value;
48
+ import { createParser, ErrorCode, TagType } from "htmljs-parser";
49
+
50
+ const parser = createParser({
51
+ /**
52
+ * Called when the parser encounters an error.
53
+ *
54
+ * @example
55
+ * 1╭─ <a><b
56
+ * ╰─ ╰─ error(code: 19, message: "EOF reached while parsing open tag")
57
+ */
58
+ onError(range) {
59
+ range.code; // An error code id. You can see the list of error codes in ErrorCode imported above.
60
+ range.message; // A human readable (hopefully) error message.
68
61
  },
69
62
 
70
- onPlaceholder: function (event) {
71
- // ${<value>]} // escape = true
72
- // $!{<value>]} // escape = false
73
- var value = event.value; // String
74
- var escaped = event.escaped; // boolean
75
- var withinBody = event.withinBody; // boolean
76
- var withinAttribute = event.withinAttribute; // boolean
77
- var withinOpenTag = event.withinOpenTag; // boolean
78
- var pos = event.pos; // Integer
63
+ /**
64
+ * Called when some static text is parsed within some body content.
65
+ *
66
+ * @example
67
+ * 1╭─ <div>Hi</div>
68
+ * ╰─ ╰─ text "Hi"
69
+ */
70
+ onText(range) {},
71
+
72
+ /**
73
+ * Called after parsing a placeholder within body content.
74
+ *
75
+ * @example
76
+ * 1╭─ <div>${hello} $!{world}</div>
77
+ * │ │ │ │ ╰─ placeholder.value "world"
78
+ * │ │ │ ╰─ placeholder "$!{world}"
79
+ * │ │ ╰─ placeholder:escape.value "hello"
80
+ * ╰─ ╰─ placeholder:escape "${hello}"
81
+ */
82
+ onPlaceholder(range) {
83
+ range.escape; // true for ${} placeholders and false for $!{} placeholders.
84
+ range.value; // Another range that includes only the placeholder value itself without the wrapping braces.
79
85
  },
80
86
 
81
- onString: function (event) {
82
- // Text within ""
83
- var value = event.value; // String
84
- var pos = event.pos; // Integer
87
+ /**
88
+ * Called when we find a comment at the root of the document or within a tags contents.
89
+ * It will not be fired for comments within expressions, such as attribute values.
90
+ *
91
+ * @example
92
+ * 1╭─ <!-- hi -->
93
+ * │ │ ╰─ comment.value " hi "
94
+ * ╰─ ╰─ comment "<!-- hi -->"
95
+ * 2╭─ // hi
96
+ * │ │ ╰─ comment.value " hi"
97
+ * ╰─ ╰─ comment "// hi"
98
+ */
99
+ onComment(range) {
100
+ range.value; // Another range that only includes the contents of the comment.
85
101
  },
86
102
 
87
- onCDATA: function (event) {
88
- // <![CDATA[<value>]]>
89
- var value = event.value; // String
90
- var pos = event.pos; // Integer
103
+ /**
104
+ * Called after parsing a CDATA section.
105
+ * // https://developer.mozilla.org/en-US/docs/Web/API/CDATASection
106
+ *
107
+ * @example
108
+ * 1╭─ <![CDATA[hi]]>
109
+ * │ │ ╰─ cdata.value "hi"
110
+ * ╰─ ╰─ cdata "<![CDATA[hi]]>"
111
+ */
112
+ onCDATA(range) {
113
+ range.value; // Another range that only includes the contents of the CDATA.
91
114
  },
92
115
 
93
- onOpenTag: function (event) {
94
- var tagName = event.tagName; // String
95
- var attributes = event.attributes; // Array
96
- var argument = event.argument; // Object
97
- var pos = event.pos; // Integer
116
+ /**
117
+ * Called after parsing a DocType comment.
118
+ * https://developer.mozilla.org/en-US/docs/Web/API/DocumentType
119
+ *
120
+ * @example
121
+ * 1╭─ <!DOCTYPE html>
122
+ * │ │ ╰─ doctype.value "DOCTYPE html"
123
+ * ╰─ ╰─ doctype "<!DOCTYPE html>"
124
+ */
125
+ onDoctype(range) {
126
+ range.value; // Another range that only includes the contents of the DocType.
98
127
  },
99
128
 
100
- onCloseTag: function (event) {
101
- // close tag
102
- var tagName = event.tagName; // String
103
- var pos = event.pos; // Integer
129
+ /**
130
+ * Called after parsing an XML declaration.
131
+ * https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction#xml_declaration
132
+ *
133
+ * @example
134
+ * 1╭─ <?xml version="1.0" encoding="UTF-8"?>
135
+ * │ │ ╰─ declaration.value "xml version=\"1.0\" encoding=\"UTF-8\""
136
+ * ╰─ ╰─ declaration "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
137
+ */
138
+ onDeclaration(range) {
139
+ range.value; // Another range that only includes the contents of the declaration.
104
140
  },
105
141
 
106
- onDocumentType: function (event) {
107
- // Document Type/DTD
108
- // <!<value>>
109
- // Example: <!DOCTYPE html>
110
- var value = event.value; // String
111
- var pos = event.pos; // Integer
142
+ /**
143
+ * Called after parsing a scriptlet (new line followed by a $).
144
+ *
145
+ * @example
146
+ * 1╭─ $ foo();
147
+ * │ │╰─ scriptlet.value "foo();"
148
+ * ╰─ ╰─ scriptlet " foo();"
149
+ * 2╭─ $ { bar(); }
150
+ * │ │ ╰─ scriptlet:block.value " bar(); "
151
+ * ╰─ ╰─ scriptlet:block " { bar(); }"
152
+ */
153
+ onScriptlet(range) {
154
+ range.block; // true if the scriptlet was contained within braces.
155
+ range.value; // Another range that includes only the value itself without the leading $ or surrounding braces (if applicable).
112
156
  },
113
157
 
114
- onDeclaration: function (event) {
115
- // Declaration
116
- // <?<value>?>
117
- // Example: <?xml version="1.0" encoding="UTF-8" ?>
118
- var value = event.value; // String
119
- var pos = event.pos; // Integer
158
+ /**
159
+ * Called when a tag name, which can include placeholders, has been parsed.
160
+ *
161
+ * @example
162
+ * 1╭─ <div/>
163
+ * ╰─ ╰─ tagName "div"
164
+ * 2╭─ <hello-${test}-again/>
165
+ * │ │ │ ╰─ tagName.quasis[1] "-again"
166
+ * │ │ ╰─ tagName.expressions[0] "${test}"
167
+ * │ ├─ tagName.quasis[0] "hello-"
168
+ * ╰─ ╰─ tagName "hello-${test}-again"
169
+ */
170
+ onTagName(range) {
171
+ range.concise; // true if this tag is a concise mode tag.
172
+ range.quasis; // An array of ranges that indicate the string literal parts of the tag name.
173
+ range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder).
174
+
175
+ // Return a different tag type enum value to enter a different parse mode.
176
+ // Below is approximately what Marko uses:
177
+ switch (parser.read(range)) {
178
+ case "area":
179
+ case "base":
180
+ case "br":
181
+ case "col":
182
+ case "embed":
183
+ case "hr":
184
+ case "img":
185
+ case "input":
186
+ case "link":
187
+ case "meta":
188
+ case "param":
189
+ case "source":
190
+ case "track":
191
+ case "wbr":
192
+ // TagType.void makes this a void element (cannot have children).
193
+ return TagType.void;
194
+ case "html-comment":
195
+ case "script":
196
+ case "style":
197
+ case "textarea":
198
+ // TagType.text makes the child content text only (with placeholders).
199
+ return TagType.text;
200
+ case "class":
201
+ case "export":
202
+ case "import":
203
+ case "static":
204
+ // TagType.statement makes this a statement tag where the content following the tag name will be parsed as script code until we reach a new line, eg for `import x from "y"`).
205
+ return TagType.statement;
206
+ }
207
+
208
+ // TagType.html is the default which allows child content as html with placeholders.
209
+ return TagType.html;
120
210
  },
121
211
 
122
- onComment: function (event) {
123
- // Text within XML comment
124
- var value = event.value; // String
125
- var pos = event.pos; // Integer
212
+ /**
213
+ * Called when a shorthand id, which can include placeholders, has been parsed.
214
+ *
215
+ * @example
216
+ * 1╭─ <div#hello-${test}-again/>
217
+ * │ ││ │ ╰─ tagShorthandId.quasis[1] "-again"
218
+ * │ ││ ╰─ tagShorthandId.expressions[0] "${test}"
219
+ * │ │╰─ tagShorthandId.quasis[0] "hello-"
220
+ * ╰─ ╰─ tagShorthandId "#hello-${test}-again"
221
+ */
222
+ onTagShorthandId(range) {
223
+ range.quasis; // An array of ranges that indicate the string literal parts of the shorthand id name.
224
+ range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder).
126
225
  },
127
226
 
128
- onScriptlet: function (event) {
129
- // Text within <% %>
130
- var value = event.value; // String
131
- var pos = event.pos; // Integer
227
+ /**
228
+ * Called when a shorthand class name, which can include placeholders, has been parsed.
229
+ * Note there can be multiple of these.
230
+ *
231
+ * @example
232
+ * 1╭─ <div.hello-${test}-again/>
233
+ * │ ││ │ ╰─ tagShorthandClassName.quasis[1] "-again"
234
+ * │ ││ ╰─ tagShorthandClassName.expressions[0] "${test}"
235
+ * │ │╰─ tagShorthandClassName.quasis[0] "hello-"
236
+ * ╰─ ╰─ tagShorthandClassName "#hello-${test}-again"
237
+ */
238
+ onTagShorthandClass(range) {
239
+ range.quasis; // An array of ranges that indicate the string literal parts of the shorthand id name.
240
+ range.expressions; // A list of placeholder ranges (similar to whats emitted via onPlaceholder).
132
241
  },
133
242
 
134
- onError: function (event) {
135
- // Error
136
- var message = event.message; // String
137
- var code = event.code; // String
138
- var pos = event.pos; // Integer
243
+ /**
244
+ * Called after a tag variable has been parsed.
245
+ *
246
+ * @example
247
+ * 1╭─ <div/el/>
248
+ * │ │╰─ tagVar.value "el"
249
+ * ╰─ ╰─ tagVar "/el"
250
+ */
251
+ onTagVar(range) {
252
+ range.value; // Another range that includes only the tag var itself and not the leading slash.
139
253
  },
140
- });
141
254
 
142
- parser.parse(str);
143
- ```
255
+ /**
256
+ * Called after tag arguments have been parsed.
257
+ *
258
+ * @example
259
+ * 1╭─ <if(x)>
260
+ * │ │╰─ tagArgs.value "x"
261
+ * ╰─ ╰─ tagArgs "(x)"
262
+ */
263
+ onTagArgs(range) {
264
+ range.value; // Another range that includes only the args themselves and not the outer parenthesis.
265
+ },
144
266
 
145
- ## Content Parsing Modes
267
+ /**
268
+ * Called after tag parameters have been parsed.
269
+ *
270
+ * @example
271
+ * 1╭─ <for|item| of=list>
272
+ * │ │╰─ tagParams.value "item"
273
+ * ╰─ ╰─ tagParams "|item|"
274
+ */
275
+ onTagParams(range) {
276
+ range.value; // Another range that includes only the params themselves and not the outer pipes.
277
+ },
146
278
 
147
- The parser, by default, will look for HTML tags within content. This behavior
148
- might not be desirable for certain tags, so the parser allows the parsing mode
149
- to be changed (usually in response to an `onOpenTag` event).
279
+ /**
280
+ * Called after an attribute name as been parsed.
281
+ * Note this may be followed by the related AttrArgs, AttrValue or AttrMethod. It can also be directly followed by another AttrName, AttrSpread or the OpenTagEnd if this is a boolean attribute.
282
+ *
283
+ * @example
284
+ * 1╭─ <div class="hi">
285
+ * ╰─ ╰─ attrName "class"
286
+ */
287
+ onAttrName(range) {},
288
+
289
+ /**
290
+ * Called after attr arguments have been parsed.
291
+ *
292
+ * @example
293
+ * 1╭─ <div if(x)>
294
+ * │ │╰─ attrArgs.value "x"
295
+ * ╰─ ╰─ attrArgs "(x)"
296
+ */
297
+ onAttrArgs(range) {
298
+ range.value; // Another range that includes only the args themselves and not the outer parenthesis.
299
+ },
150
300
 
151
- There are three content parsing modes:
301
+ /**
302
+ * Called after an attr value has been parsed.
303
+ *
304
+ * @example
305
+ * 1╭─ <input name="hi" value:=x>
306
+ * │ ││ │ ╰─ attrValue:bound.value
307
+ * │ ││ ╰─ attrValue:bound ":=x"
308
+ * │ │╰─ attrValue.value "\"hi\""
309
+ * ╰─ ╰─ attrValue "=\"hi\""
310
+ */
311
+ onAttrValue(range) {
312
+ range.bound; // true if the attribute value was preceded by :=.
313
+ range.value; // Another range that includes only the value itself without the leading = or :=.
314
+ },
152
315
 
153
- - **HTML Content (DEFAULT):**
154
- The parser will look for any HTML tag and content placeholders while in
155
- this mode and parse opening and closing tags accordingly.
316
+ /**
317
+ * Called after an attribute method shorthand has been parsed.
318
+ *
319
+ * @example
320
+ * 1╭─ <div onClick(ev) { foo(); }>
321
+ * │ ││ │╰─ attrMethod.body.value " foo(); "
322
+ * │ ││ ╰─ attrMethod.body "{ foo(); }"
323
+ * │ │╰─ attrMethod.params.value "ev"
324
+ * │ ├─ attrMethod.params "(ev)"
325
+ * ╰─ ╰─ attrMethod "(ev) { foo(); }"
326
+ */
327
+ onAttrMethod(range) {
328
+ range.params; // Another range which includes the params for the method.
329
+ range.params.value; // Another range which includes the method params without outer parenthesis.
330
+
331
+ range.body; // Another range which includes the entire body block.
332
+ range.body.value; // Another range which includes the body block without outer braces.
333
+ },
156
334
 
157
- - **Parsed Text Content**: The parser will look for the closing tag that matches
158
- the current open tag as well as content placeholders but all other content
159
- will be interpreted as text.
335
+ /**
336
+ * Called after we've parsed a spread attribute.
337
+ *
338
+ * @example
339
+ * 1╭─ <div ...attrs>
340
+ * │ │ ╰─ attrSpread.value "attrs"
341
+ * ╰─ ╰─ attrSpread "...attrs"
342
+ */
343
+ onAttrSpread(range) {
344
+ range.value; // Another range that includes only the value itself without the leading ...
345
+ },
160
346
 
161
- - **Static Text Content**: The parser will look for the closing tag that matches
162
- the current open tag but all other content will be interpreted as raw text.
347
+ /**
348
+ * Called once we've completed parsing the open tag.
349
+ *
350
+ * @example
351
+ * 1╭─ <div><span/></div>
352
+ * │ │ ╰─ openTagEnd:selfClosed "/>"
353
+ * ╰─ ╰─ openTagEnd ">"
354
+ */
355
+ onOpenTagEnd(range) {
356
+ range.selfClosed; // true if this tag was self closed (onCloseTag would not be called if so).
357
+ },
163
358
 
164
- ```javascript
165
- var htmljs = require('htmljs-parser');
166
- var parser = htmljs.createParser({
167
- onOpenTag: function(event) {
168
- // open tag
169
- switch(event.tagName) {
170
- case 'textarea':
171
- //fall through
172
- case 'script':
173
- //fall through
174
- case 'style':
175
- // parse the content within these tags but only
176
- // look for placeholders and the closing tag.
177
- parser.enterParsedTextContentState();
178
- break;
179
- case 'dummy'
180
- // treat content within <dummy>...</dummy> as raw
181
- // text and ignore other tags and placeholders
182
- parser.enterStaticTextContentState();
183
- break;
184
- default:
185
- // The parser will switch to HTML content parsing mode
186
- // if the parsing mode is not explicitly changed by
187
- // "onOpenTag" function.
188
- }
189
- }
359
+ /**
360
+ * Called once the closing tag (or in concise mode an outdent or eof) is parsed.
361
+ * Note this is not called for selfClosed, void or statement tags.
362
+ *
363
+ * @example
364
+ * 1╭─ <div><span/></div>
365
+ * │ │ ╰─ closeTag(div).value "div"
366
+ * ╰─ ╰─ closeTag(div) "</div>"
367
+ */
368
+ onCloseTag(range) {
369
+ range.value; // The raw content of the closing tag (undefined in concise mode).
370
+ },
190
371
  });
191
-
192
- parser.parse(str);
193
- ```
194
-
195
- ## Parsing Events
196
-
197
- The `htmljs-parser` is an event-based parser which means that it will emit
198
- events as it is parsing the document. Events are emitted via calls
199
- to `on<eventname>` function which are supplied as properties in the options
200
- via call to `require('htmljs-parser').createParser(options)`.
201
-
202
- ### onOpenTag
203
-
204
- The `onOpenTag` function will be called each time an opening tag is
205
- encountered.
206
-
207
- **EXAMPLE: Simple tag**
208
-
209
- INPUT:
210
-
211
- ```html
212
- <div></div>
213
- ```
214
-
215
- OUTPUT EVENT:
216
-
217
- ```javascript
218
- {
219
- type: 'openTag',
220
- tagName: 'div',
221
- attributes: []
222
- }
223
- ```
224
-
225
- **EXAMPLE: Tag with literal attribute values**
226
-
227
- INPUT:
228
-
229
- ```html
230
- <div class="demo" disabled="false" data-number="123"></div>
231
- ```
232
-
233
- OUTPUT EVENT:
234
-
235
- ```javascript
236
- {
237
- type: 'openTag',
238
- tagName: 'div',
239
- attributes: [
240
- {
241
- name: 'class',
242
- value: '"demo"'
243
- },
244
- {
245
- name: 'disabled',
246
- value: 'false'
247
- },
248
- {
249
- name: 'data-number',
250
- value: '123'
251
- }
252
- ]
253
- }
254
- ```
255
-
256
- **EXAMPLE: Tag with expression attribute**
257
-
258
- INPUT:
259
-
260
- ```html
261
- <say-something message=("Hello "+data.name)/>
262
- ```
263
-
264
- OUTPUT EVENT:
265
-
266
- ```javascript
267
- {
268
- type: 'openTag',
269
- tagName: 'div',
270
- attributes: [
271
- {
272
- name: 'message',
273
- value: '"Hello "+data.name'
274
- }
275
- ]
276
- }
277
- ```
278
-
279
- **EXAMPLE: Tag with an argument**
280
-
281
- INPUT:
282
-
283
- ```html
284
- <for(var i="0;" i < 10; i++)></for(var>
285
- ```
286
-
287
- OUTPUT EVENT:
288
-
289
- ```javascript
290
- {
291
- type: 'openTag',
292
- tagName: 'for',
293
- argument: {
294
- value: 'var i = 0; i < 10; i++',
295
- pos: ... // Integer
296
- },
297
- attributes: []
298
- }
299
- ```
300
-
301
- **EXAMPLE: Attribute with an argument**
302
-
303
- INPUT:
304
-
305
- ```html
306
- <div if(x>y)></div>
307
- ```
308
-
309
- OUTPUT EVENT:
310
-
311
- ```javascript
312
- {
313
- type: 'openTag',
314
- tagName: 'div',
315
- attributes: [
316
- {
317
- name: 'if',
318
- argument: {
319
- value: 'x > y',
320
- pos: ... // Integer
321
- }
322
- }
323
- ]
324
- }
325
- ```
326
-
327
- ### onCloseTag
328
-
329
- The `onCloseTag` function will be called each time a closing tag is
330
- encountered.
331
-
332
- **EXAMPLE: Simple close tag**
333
-
334
- INPUT:
335
-
336
- ```html
337
- </div>
338
- ```
339
-
340
- OUTPUT EVENT:
341
-
342
- ```javascript
343
- {
344
- type: 'closeTag',
345
- tagName: 'div'
346
- }
347
- ```
348
-
349
- ### onText
350
-
351
- The `onText` function will be called each time within an element
352
- when textual data is encountered.
353
-
354
- **NOTE:** Text within `<![CDATA[` `]]>` will be emitted via call
355
- to `onCDATA`.
356
-
357
- **EXAMPLE**
358
-
359
- In the following example code, the `TEXT` sequences will be emitted as
360
- text events.
361
-
362
- INPUT:
363
-
364
- ```html
365
- Simple text
366
372
  ```
367
373
 
368
- OUTPUT EVENT:
374
+ Finally after setting up the parser with it's handlers, it's time to pass in some source code to parse.
369
375
 
370
376
  ```javascript
371
- {
372
- type: 'text',
373
- value: 'Simple text'
374
- }
377
+ parser.parse("<div></div>");
375
378
  ```
376
379
 
377
- ### onCDATA
380
+ # Parser Helpers
378
381
 
379
- The `onCDATA` function will be called when text within `<![CDATA[` `]]>`
380
- is encountered.
381
-
382
- **EXAMPLE:**
383
-
384
- INPUT:
385
-
386
- ```html
387
- <![CDATA[This is text]]>
388
- ```
389
-
390
- OUTPUT EVENT:
382
+ The parser instance provides a few helpers to make it easier to work with the parsed content.
391
383
 
392
384
  ```javascript
393
- {
394
- type: 'cdata',
395
- value: 'This is text'
396
- }
397
- ```
398
-
399
- ### onPlaceholder
400
-
401
- The `onPlaceholder` function will be called each time a placeholder
402
- is encountered.
403
-
404
- If the placeholder starts with the `$!{` sequence then `event.escape`
405
- will be `false`.
406
-
407
- If the placeholder starts with the `${` sequence then `event.escape` will be
408
- `true`.
409
-
410
- Text within `<![CDATA[` `]]>` and `<!--` `-->` will not be parsed so you
411
- cannot use placeholders for these blocks of code.
412
-
413
- **EXAMPLE:**
414
-
415
- INPUT:
416
-
417
- ```html
418
- ${"This is an escaped placeholder"} $!{"This is a non-escaped placeholder"}
419
- ```
420
-
421
- OUTPUT EVENTS
422
-
423
- ```html
424
- ${name}
425
- ```
426
-
427
- ```javascript
428
- {
429
- type: 'placeholder',
430
- value: 'name',
431
- escape: true
432
- }
433
- ```
434
-
435
- ---
436
-
437
- ```html
438
- $!{name}
439
- ```
440
-
441
- ```javascript
442
- {
443
- type: 'placeholder',
444
- value: 'name',
445
- escape: true
446
- }
447
- ```
448
-
449
- **NOTE:**
450
- The `escape` flag is merely informational. The application code is responsible
451
- for interpreting this flag to properly escape the expression.
452
-
453
- Here's an example of modifying the expression based on the `event.escape` flag:
454
-
455
- ```javascript
456
- onPlaceholder: function(event) {
457
- if (event.escape) {
458
- event.value = 'escapeXml(' + event.value + ')';
459
- }
460
- }
461
- ```
462
-
463
- ### onDocumentType
464
-
465
- The `onDocumentType` function will be called when the document type declaration
466
- is encountered _anywhere_ in the content.
467
-
468
- **EXAMPLE:**
385
+ // Pass any range object into this method to get the raw string from the source for the range.
386
+ parser.read(range);
469
387
 
470
- INPUT:
388
+ // Given an zero based offset within the source code, returns a position object that contains line and column properties.
389
+ parser.positionAt(offset);
471
390
 
472
- ```html
473
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN">
391
+ // Given a range object returns a location object with start and end properties which are each position objects as returned from the "positionAt" api.
392
+ parser.locationAt(range);
474
393
  ```
475
394
 
476
- OUTPUT EVENT:
395
+ # Code of Conduct
477
396
 
478
- ```javascript
479
- {
480
- type: 'documentType',
481
- value: 'DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"'
482
- }
483
- ```
484
-
485
- ### onDeclaration
486
-
487
- The `onDeclaration` function will be called when an XML declaration
488
- is encountered _anywhere_ in the content.
489
-
490
- **EXAMPLE:**
491
-
492
- INPUT:
493
-
494
- ```html
495
- <?xml version="1.0" encoding="UTF-8"?>
496
- ```
497
-
498
- OUTPUT EVENT:
499
-
500
- ```javascript
501
- {
502
- type: 'declaration',
503
- value: 'xml version="1.0" encoding="UTF-8"'
504
- }
505
- ```
506
-
507
- ### onComment
508
-
509
- The `onComment` function will be called when text within `<!--` `-->`
510
- is encountered.
511
-
512
- **EXAMPLE:**
513
-
514
- INPUT:
515
-
516
- ```html
517
- <!--This is a comment-->
518
- ```
519
-
520
- OUTPUT EVENT:
521
-
522
- ```javascript
523
- {
524
- type: 'comment',
525
- value: 'This is a comment'
526
- }
527
- ```
528
-
529
- ### onScriptlet
530
-
531
- The `onScriptlet` function will be called when text within `<%` `%>`
532
- is encountered.
533
-
534
- **EXAMPLE:**
535
-
536
- INPUT:
537
-
538
- ```html
539
- <% console.log("Hello World!"); %>
540
- ```
541
-
542
- OUTPUT EVENT:
543
-
544
- ```javascript
545
- {
546
- type: 'scriptlet',
547
- value: ' console.log("Hello World!"); '
548
- }
549
- ```
550
-
551
- ### onError
552
-
553
- The `onError` function will be called when malformed content is detected.
554
- The most common cause for an error is due to reaching the end of the
555
- input while still parsing an open tag, close tag, XML comment, CDATA section,
556
- DTD, XML declaration, or placeholder.
557
-
558
- Possible error codes:
559
-
560
- - `MISSING_END_TAG`
561
- - `MISSING_END_DELIMITER`
562
- - `MALFORMED_OPEN_TAG`
563
- - `MALFORMED_CLOSE_TAG`
564
- - `MALFORMED_CDATA`
565
- - `MALFORMED_PLACEHOLDER`
566
- - `MALFORMED_DOCUMENT_TYPE`
567
- - `MALFORMED_DECLARATION`
568
- - `MALFORMED_COMMENT`
569
- - `EXTRA_CLOSING_TAG`
570
- - `MISMATCHED_CLOSING_TAG`
571
- - ...
572
-
573
- **EXAMPLE:**
574
-
575
- INPUT:
576
-
577
- ```html
578
- <a href="
579
- ```
580
-
581
- OUTPUT EVENT:
582
-
583
- ```javascript
584
- {
585
- type: 'error',
586
- code: 'MALFORMED_OPEN_TAG',
587
- message: 'EOF reached while parsing open tag.',
588
- pos: 0,
589
- endPos: 9
590
- }
591
- ```
397
+ This project adheres to the [eBay Code of Conduct](./.github/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.