@@ -82,7 +82,7 @@ export interface ParserOptions<T extends TreeAdapterTypeMap> {
82
82
*
83
83
* @default `true`
84
84
*/
85
- scriptingEnabled ?: boolean | undefined ;
85
+ scriptingEnabled ?: boolean ;
86
86
87
87
/**
88
88
* Enables source code location information. When enabled, each node (except the root node)
@@ -94,14 +94,14 @@ export interface ParserOptions<T extends TreeAdapterTypeMap> {
94
94
*
95
95
* @default `false`
96
96
*/
97
- sourceCodeLocationInfo ?: boolean | undefined ;
97
+ sourceCodeLocationInfo ?: boolean ;
98
98
99
99
/**
100
100
* Specifies the resulting tree format.
101
101
*
102
102
* @default `treeAdapters.default`
103
103
*/
104
- treeAdapter ?: TreeAdapter < T > | undefined ;
104
+ treeAdapter ?: TreeAdapter < T > ;
105
105
106
106
/**
107
107
* Callback for parse errors.
@@ -111,86 +111,115 @@ export interface ParserOptions<T extends TreeAdapterTypeMap> {
111
111
onParseError ?: ParserErrorHandler | null ;
112
112
}
113
113
114
+ export const defaultParserOptions = {
115
+ scriptingEnabled : true ,
116
+ sourceCodeLocationInfo : false ,
117
+ treeAdapter : defaultTreeAdapter ,
118
+ onParseError : null ,
119
+ } ;
120
+
114
121
//Parser
115
122
export class Parser < T extends TreeAdapterTypeMap > {
116
- options : ParserOptions < T > ;
117
123
treeAdapter : TreeAdapter < T > ;
118
124
private onParseError : ParserErrorHandler | null ;
119
125
private currentToken : Token | null = null ;
120
126
121
- constructor ( options ?: ParserOptions < T > ) {
122
- this . options = {
123
- scriptingEnabled : true ,
124
- sourceCodeLocationInfo : false ,
125
- ...options ,
126
- } ;
127
-
127
+ public constructor (
128
+ public options : Required < ParserOptions < T > > ,
129
+ public document : T [ 'document' ] = options . treeAdapter . createDocument ( ) ,
130
+ public fragmentContext : T [ 'element' ] | null = null
131
+ ) {
128
132
this . treeAdapter = this . options . treeAdapter ??= defaultTreeAdapter as TreeAdapter < T > ;
129
133
this . onParseError = this . options . onParseError ??= null ;
130
134
131
135
// Always enable location info if we report parse errors.
132
136
if ( this . onParseError ) {
133
137
this . options . sourceCodeLocationInfo = true ;
134
138
}
139
+
140
+ this . tokenizer = new Tokenizer ( this . options ) ;
141
+ this . activeFormattingElements = new FormattingElementList ( this . treeAdapter ) ;
142
+
143
+ this . fragmentContextID = fragmentContext ? getTagID ( this . treeAdapter . getTagName ( fragmentContext ) ) : $ . UNKNOWN ;
144
+ this . _setContextModes ( fragmentContext ?? document , this . fragmentContextID ) ;
145
+
146
+ this . openElements = new OpenElementStack (
147
+ this . document ,
148
+ this . treeAdapter ,
149
+ this . onItemPush . bind ( this ) ,
150
+ this . onItemPop . bind ( this )
151
+ ) ;
135
152
}
136
153
137
154
// API
138
- public parse ( html : string ) : T [ 'document' ] {
139
- const document = this . treeAdapter . createDocument ( ) ;
155
+ public static parse < T extends TreeAdapterTypeMap > ( html : string , options ?: ParserOptions < T > ) : T [ 'document' ] {
156
+ const opts = {
157
+ ...defaultParserOptions ,
158
+ ...options ,
159
+ } ;
140
160
141
- this . _bootstrap ( document , null ) ;
142
- this . tokenizer . write ( html , true ) ;
143
- this . _runParsingLoop ( null ) ;
161
+ const parser = new this ( opts ) ;
144
162
145
- return document ;
163
+ parser . tokenizer . write ( html , true ) ;
164
+ parser . _runParsingLoop ( null ) ;
165
+
166
+ return parser . document ;
146
167
}
147
168
148
- public parseFragment ( html : string , fragmentContext ?: T [ 'parentNode' ] | null ) : T [ 'documentFragment' ] {
169
+ public static parseFragment < T extends TreeAdapterTypeMap > (
170
+ html : string ,
171
+ fragmentContext ?: T [ 'parentNode' ] | null ,
172
+ options ?: ParserOptions < T >
173
+ ) : T [ 'documentFragment' ] {
174
+ const opts : Required < ParserOptions < T > > = {
175
+ ...defaultParserOptions ,
176
+ ...options ,
177
+ } ;
178
+
149
179
//NOTE: use <template> element as a fragment context if context element was not provided,
150
180
//so we will parse in "forgiving" manner
151
- fragmentContext ??= this . treeAdapter . createElement ( TN . TEMPLATE , NS . HTML , [ ] ) ;
181
+ fragmentContext ??= opts . treeAdapter . createElement ( TN . TEMPLATE , NS . HTML , [ ] ) ;
152
182
153
183
//NOTE: create fake element which will be used as 'document' for fragment parsing.
154
184
//This is important for jsdom there 'document' can't be recreated, therefore
155
185
//fragment parsing causes messing of the main `document`.
156
- const documentMock = this . treeAdapter . createElement ( 'documentmock' , NS . HTML , [ ] ) ;
186
+ const documentMock = opts . treeAdapter . createElement ( 'documentmock' , NS . HTML , [ ] ) ;
157
187
158
- this . _bootstrap ( documentMock , fragmentContext ) ;
188
+ const parser = new this ( opts , documentMock , fragmentContext ) ;
159
189
160
- if ( this . fragmentContextID === $ . TEMPLATE ) {
161
- this . tmplInsertionModeStack . unshift ( InsertionMode . IN_TEMPLATE ) ;
190
+ if ( parser . fragmentContextID === $ . TEMPLATE ) {
191
+ parser . tmplInsertionModeStack . unshift ( InsertionMode . IN_TEMPLATE ) ;
162
192
}
163
193
164
- this . _initTokenizerForFragmentParsing ( ) ;
165
- this . _insertFakeRootElement ( ) ;
166
- this . _resetInsertionMode ( ) ;
167
- this . _findFormInFragmentContext ( ) ;
168
- this . tokenizer . write ( html , true ) ;
169
- this . _runParsingLoop ( null ) ;
194
+ parser . _initTokenizerForFragmentParsing ( ) ;
195
+ parser . _insertFakeRootElement ( ) ;
196
+ parser . _resetInsertionMode ( ) ;
197
+ parser . _findFormInFragmentContext ( ) ;
198
+ parser . tokenizer . write ( html , true ) ;
199
+ parser . _runParsingLoop ( null ) ;
170
200
171
- const rootElement = this . treeAdapter . getFirstChild ( documentMock ) as T [ 'parentNode' ] ;
172
- const fragment = this . treeAdapter . createDocumentFragment ( ) ;
201
+ const rootElement = opts . treeAdapter . getFirstChild ( documentMock ) as T [ 'parentNode' ] ;
202
+ const fragment = opts . treeAdapter . createDocumentFragment ( ) ;
173
203
174
- this . _adoptNodes ( rootElement , fragment ) ;
204
+ parser . _adoptNodes ( rootElement , fragment ) ;
175
205
176
206
return fragment ;
177
207
}
178
208
179
- tokenizer ! : Tokenizer ;
209
+ tokenizer : Tokenizer ;
210
+
180
211
stopped = false ;
181
212
insertionMode = InsertionMode . INITIAL ;
182
213
originalInsertionMode = InsertionMode . INITIAL ;
183
214
184
- document ! : T [ 'document' ] ;
185
- fragmentContext ! : T [ 'element' ] | null ;
186
- fragmentContextID = $ . UNKNOWN ;
215
+ fragmentContextID : $ ;
187
216
188
217
headElement : null | T [ 'element' ] = null ;
189
218
formElement : null | T [ 'element' ] = null ;
190
219
pendingScript : null | T [ 'element' ] = null ;
191
220
192
221
openElements ! : OpenElementStack < T > ;
193
- activeFormattingElements ! : FormattingElementList < T > ;
222
+ activeFormattingElements : FormattingElementList < T > ;
194
223
private _considerForeignContent = false ;
195
224
196
225
/**
@@ -206,44 +235,6 @@ export class Parser<T extends TreeAdapterTypeMap> {
206
235
skipNextNewLine = false ;
207
236
fosterParentingEnabled = false ;
208
237
209
- //Bootstrap parser
210
- _bootstrap ( document : T [ 'document' ] , fragmentContext : T [ 'element' ] | null ) : void {
211
- this . tokenizer = new Tokenizer ( this . options ) ;
212
-
213
- this . stopped = false ;
214
-
215
- this . insertionMode = InsertionMode . INITIAL ;
216
- this . originalInsertionMode = InsertionMode . INITIAL ;
217
-
218
- this . document = document ;
219
- this . fragmentContext = fragmentContext ;
220
- this . fragmentContextID = fragmentContext ? getTagID ( this . treeAdapter . getTagName ( fragmentContext ) ) : $ . UNKNOWN ;
221
- this . _setContextModes ( fragmentContext ?? document , this . fragmentContextID ) ;
222
-
223
- this . headElement = null ;
224
- this . formElement = null ;
225
- this . pendingScript = null ;
226
- this . currentToken = null ;
227
-
228
- this . openElements = new OpenElementStack (
229
- this . document ,
230
- this . treeAdapter ,
231
- this . onItemPush . bind ( this ) ,
232
- this . onItemPop . bind ( this )
233
- ) ;
234
-
235
- this . activeFormattingElements = new FormattingElementList ( this . treeAdapter ) ;
236
-
237
- this . tmplInsertionModeStack . length = 0 ;
238
-
239
- this . pendingCharacterTokens . length = 0 ;
240
- this . hasNonWhitespacePendingCharacterToken = false ;
241
-
242
- this . framesetOk = true ;
243
- this . skipNextNewLine = false ;
244
- this . fosterParentingEnabled = false ;
245
- }
246
-
247
238
//Errors
248
239
_err ( token : Token , code : ERR , beforeToken ?: boolean ) : void {
249
240
if ( ! this . onParseError ) return ;
0 commit comments