parser warnings

pull/3741/head
Tan Li Hau 6 years ago
parent 1f6e0eb316
commit faa55d7869

@ -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');

@ -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;

@ -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 <style> tags —
// one scoped, one global. for now, only allow one

@ -142,6 +142,11 @@ export default function tag(parser: Parser) {
parent.end = start;
parser.stack.pop();
parser.warn(parent, {
code: `missing-closing-tag`,
message: `Missing </${parent.name}> before </${name}>`
});
parent = parser.current();
}
@ -152,6 +157,11 @@ export default function tag(parser: Parser) {
} else if (closing_tag_omitted(parent.name, name)) {
parent.end = start;
parser.stack.pop();
parser.warn(parent, {
code: `missing-closing-tag`,
message: `Missing </${parent.name}> before <${name}>`
});
}
const unique_names: Set<string> = new Set();

@ -21,9 +21,10 @@ describe('parse', () => {
const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8').replace(/\s+$/, '');
const expectedOutput = tryToLoadJson(`${__dirname}/samples/${dir}/output.json`);
const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`);
const expectedWarnings = tryToLoadJson(`${__dirname}/samples/${dir}/warnings.json`);
try {
const { ast } = svelte.compile(input, Object.assign(options, {
const { ast, warnings } = svelte.compile(input, Object.assign(options, {
generate: false
}));
@ -33,6 +34,15 @@ describe('parse', () => {
assert.deepEqual(ast.css, expectedOutput.css);
assert.deepEqual(ast.instance, expectedOutput.instance);
assert.deepEqual(ast.module, expectedOutput.module);
const parserWarnings = warnings
.filter(warning => warning.type === 'parser')
.map(warning => ({ code: warning.code, fullMessage: warning.toString() }));
if (expectedWarnings) {
assert.deepEqual(parserWarnings, expectedWarnings);
} else if (parserWarnings.length) {
throw new Error(`Received unexpected warnings: ${JSON.stringify(parserWarnings)}`);
}
} catch (err) {
if (err.name !== 'ParseError') throw err;
if (!expectedError) throw err;

@ -0,0 +1,10 @@
[
{
"code": "missing-closing-tag",
"fullMessage": "Missing </li> before <li> (2:1)\n1: <ul>\n2: <li>a\n ^\n3: {#if true}\n4: <li>b"
},
{
"code": "missing-closing-tag",
"fullMessage": "Missing </li> before </ul> (6:1)\n4: <li>b\n5: {/if}\n6: <li>c\n ^\n7: </ul>"
}
]

@ -0,0 +1,14 @@
[
{
"code": "missing-closing-tag",
"fullMessage": "Missing </li> before <li> (2:1)\n1: <ul>\n2: <li>a\n ^\n3: <li>b\n4: <li>c"
},
{
"code": "missing-closing-tag",
"fullMessage": "Missing </li> before <li> (3:1)\n1: <ul>\n2: <li>a\n3: <li>b\n ^\n4: <li>c\n5: </ul>"
},
{
"code": "missing-closing-tag",
"fullMessage": "Missing </li> before </ul> (4:1)\n2: <li>a\n3: <li>b\n4: <li>c\n ^\n5: </ul>"
}
]
Loading…
Cancel
Save