diff --git a/packages/parse5-parser-stream/lib/index.ts b/packages/parse5-parser-stream/lib/index.ts index 35ab6f472..d01f46b59 100644 --- a/packages/parse5-parser-stream/lib/index.ts +++ b/packages/parse5-parser-stream/lib/index.ts @@ -43,9 +43,7 @@ export class ParserStream super({ decodeStrings: false }); this.parser = new Parser(options); - - this.document = this.parser.treeAdapter.createDocument(); - this.parser._bootstrap(this.document, null); + this.document = this.parser.document; } //WritableStream implementation diff --git a/packages/parse5/lib/index.ts b/packages/parse5/lib/index.ts index dd3744b2c..333759412 100644 --- a/packages/parse5/lib/index.ts +++ b/packages/parse5/lib/index.ts @@ -29,9 +29,7 @@ export function parse( html: string, options?: ParserOptions ): T['document'] { - const parser = new Parser(options); - - return parser.parse(html); + return Parser.parse(html, options); } /** @@ -77,7 +75,5 @@ export function parseFragment ({ node: test.fragmentContext @@ -27,21 +27,25 @@ describe('parser', () => { describe('Regression - Incorrect arguments fallback for the parser.parseFragment (GH-82, GH-83)', () => { beforeEach(() => { - Parser.prototype.parseFragment = function ( - this: Parser, + Parser.parseFragment = function ( html: string, - fragmentContext?: T['element'] - ): { html: string; fragmentContext: T['element'] | null | undefined; options: ParserOptions } { + fragmentContext?: T['element'], + options?: ParserOptions + ): { + html: string; + fragmentContext: T['element'] | null | undefined; + options: ParserOptions | undefined; + } { return { html, fragmentContext, - options: this.options, + options, }; }; }); afterEach(() => { - Parser.prototype.parseFragment = origParseFragment; + Parser.parseFragment = origParseFragment; }); it('parses correctly', () => { @@ -65,7 +69,7 @@ describe('parser', () => { assert.ok(!args.fragmentContext); expect(args).toHaveProperty('html', html); - assert.ok(!args.options.sourceCodeLocationInfo); + assert.ok(!args.options); }); }); diff --git a/packages/parse5/lib/parser/index.ts b/packages/parse5/lib/parser/index.ts index 840bd4e34..b3045620a 100644 --- a/packages/parse5/lib/parser/index.ts +++ b/packages/parse5/lib/parser/index.ts @@ -82,7 +82,7 @@ export interface ParserOptions { * * @default `true` */ - scriptingEnabled?: boolean | undefined; + scriptingEnabled?: boolean; /** * Enables source code location information. When enabled, each node (except the root node) @@ -94,14 +94,14 @@ export interface ParserOptions { * * @default `false` */ - sourceCodeLocationInfo?: boolean | undefined; + sourceCodeLocationInfo?: boolean; /** * Specifies the resulting tree format. * * @default `treeAdapters.default` */ - treeAdapter?: TreeAdapter | undefined; + treeAdapter?: TreeAdapter; /** * Callback for parse errors. @@ -111,86 +111,119 @@ export interface ParserOptions { onParseError?: ParserErrorHandler | null; } +const defaultParserOptions = { + scriptingEnabled: true, + sourceCodeLocationInfo: false, + treeAdapter: defaultTreeAdapter, + onParseError: null, +}; + //Parser export class Parser { - options: ParserOptions; treeAdapter: TreeAdapter; private onParseError: ParserErrorHandler | null; private currentToken: Token | null = null; + public options: Required>; + public document: T['document']; - constructor(options?: ParserOptions) { + public constructor( + options?: ParserOptions, + document?: T['document'], + public fragmentContext: T['element'] | null = null + ) { this.options = { - scriptingEnabled: true, - sourceCodeLocationInfo: false, + ...defaultParserOptions, ...options, }; - this.treeAdapter = this.options.treeAdapter ??= defaultTreeAdapter as TreeAdapter; - this.onParseError = this.options.onParseError ??= null; + this.treeAdapter = this.options.treeAdapter; + this.onParseError = this.options.onParseError; // Always enable location info if we report parse errors. if (this.onParseError) { this.options.sourceCodeLocationInfo = true; } + + this.document = document ?? this.treeAdapter.createDocument(); + + this.tokenizer = new Tokenizer(this.options); + this.activeFormattingElements = new FormattingElementList(this.treeAdapter); + + this.fragmentContextID = fragmentContext ? getTagID(this.treeAdapter.getTagName(fragmentContext)) : $.UNKNOWN; + this._setContextModes(fragmentContext ?? this.document, this.fragmentContextID); + + this.openElements = new OpenElementStack( + this.document, + this.treeAdapter, + this.onItemPush.bind(this), + this.onItemPop.bind(this) + ); } // API - public parse(html: string): T['document'] { - const document = this.treeAdapter.createDocument(); + public static parse(html: string, options?: ParserOptions): T['document'] { + const parser = new this(options); - this._bootstrap(document, null); - this.tokenizer.write(html, true); - this._runParsingLoop(null); + parser.tokenizer.write(html, true); + parser._runParsingLoop(null); - return document; + return parser.document; } - public parseFragment(html: string, fragmentContext?: T['parentNode'] | null): T['documentFragment'] { + public static parseFragment( + html: string, + fragmentContext?: T['parentNode'] | null, + options?: ParserOptions + ): T['documentFragment'] { + const opts: Required> = { + ...defaultParserOptions, + ...options, + }; + //NOTE: use