From faa55d786967e243535f5790d93a09c23f7fec4e Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 19 Oct 2019 06:57:33 +0800 Subject: [PATCH] parser warnings --- src/compiler/compile/index.ts | 2 +- src/compiler/interfaces.ts | 1 + src/compiler/parse/index.ts | 48 +++++++++++++++++-- src/compiler/parse/state/tag.ts | 10 ++++ test/parser/index.js | 12 ++++- .../implicitly-closed-li-block/warnings.json | 10 ++++ .../implicitly-closed-li/warnings.json | 14 ++++++ 7 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 test/parser/samples/implicitly-closed-li-block/warnings.json create mode 100644 test/parser/samples/implicitly-closed-li/warnings.json diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts index 98004ba5da..ced22b03ab 100644 --- a/src/compiler/compile/index.ts +++ b/src/compiler/compile/index.ts @@ -65,7 +65,7 @@ export default function compile(source: string, options: CompileOptions = {}) { validate_options(options, warnings); stats.start('parse'); - const ast = parse(source, options); + const ast = parse(source, options, warnings); stats.stop('parse'); stats.start('create component'); diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 1e0028e8aa..c48d823d70 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -91,6 +91,7 @@ export interface Ast { } export interface Warning { + type?: string, start?: { line: number; column: number; pos?: number }; end?: { line: number; column: number }; pos?: number; diff --git a/src/compiler/parse/index.ts b/src/compiler/parse/index.ts index 5416038a53..fb7d7475bd 100644 --- a/src/compiler/parse/index.ts +++ b/src/compiler/parse/index.ts @@ -1,15 +1,18 @@ import { isIdentifierStart, isIdentifierChar } from 'acorn'; +import { getLocator } from 'locate-character'; import fragment from './state/fragment'; import { whitespace } from '../utils/patterns'; import { reserved } from '../utils/names'; import full_char_code_at from '../utils/full_char_code_at'; -import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script } from '../interfaces'; +import get_code_frame from '../utils/get_code_frame'; +import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script, Warning } from '../interfaces'; import error from '../utils/error'; type ParserState = (parser: Parser) => (ParserState | void); export class Parser { readonly template: string; + readonly originalTemplate: string; readonly filename?: string; readonly customElement: boolean; @@ -21,14 +24,21 @@ export class Parser { js: Script[] = []; meta_tags = {}; - constructor(template: string, options: ParserOptions) { + warnings: Warning[]; + locate: (c: number) => { line: number; column: number }; + + constructor(template: string, options: ParserOptions, warnings: Warning[]) { if (typeof template !== 'string') { throw new TypeError('Template must be a string'); } + this.originalTemplate = template; + this.locate = getLocator(this.originalTemplate, { offsetLine: 1 }); + this.template = template.replace(/\s+$/, ''); this.filename = options.filename; this.customElement = options.customElement; + this.warnings = warnings; this.html = { start: null, @@ -89,6 +99,35 @@ export class Parser { }, err.pos); } + warn( + pos: { + start: number; + end: number; + }, + warning: { + code: string; + message: string; + } + ) { + const start = this.locate(pos.start); + const end = this.locate(pos.end); + + const frame = get_code_frame(this.originalTemplate, start.line - 1, start.column); + + this.warnings.push({ + type: 'parser', + code: warning.code, + message: warning.message, + frame, + start, + end, + pos: pos.start, + filename: this.filename, + toString: () => + `${warning.message} (${start.line}:${start.column})\n${frame}`, + }); + } + error({ code, message }: { code: string; message: string }, index = this.index) { error(message, { name: 'ParseError', @@ -203,9 +242,10 @@ export class Parser { export default function parse( template: string, - options: ParserOptions = {} + options: ParserOptions = {}, + warnings: Warning[] ): Ast { - const parser = new Parser(template, options); + const parser = new Parser(template, options, warnings); // TODO we may want to allow multiple