Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tolerant parsing: continue parsing when a Glimmer syntax is invalid #1666

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/@glimmer/syntax/lib/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,18 @@ export abstract class Parser {
>
> = null;
public tokenizer: EventedTokenizer;
protected continueOnError: boolean;

constructor(
source: src.Source,
entityParser = new EntityParser(namedCharRefs),
mode: 'precompile' | 'codemod' = 'precompile'
mode: 'precompile' | 'codemod' = 'precompile',
continueOnError = false,
) {
this.source = source;
this.lines = source.source.split(/\r\n?|\n/u);
this.tokenizer = new EventedTokenizer(this, entityParser, mode);
this.continueOnError = continueOnError;
}

offset(): src.SourceOffset {
Expand Down
10 changes: 9 additions & 1 deletion packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,15 @@ export abstract class HandlebarsNodeVisitors extends Parser {
return comment;
}

PartialStatement(partial: HBS.PartialStatement): never {
PartialStatement(partial: HBS.PartialStatement): ASTv1.ErrorNode {
if(this.continueOnError) {
let error = b.error({
loc: this.source.spanFor(partial.loc),
message: `Handlebars partials are not supported`
});
appendChild(this.currentElement(), error);
return error;
}
throw generateSyntaxError(
`Handlebars partials are not supported`,
this.source.spanFor(partial.loc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@ export interface PreprocessOptions {
escaping/unescaping of HTML entity codes.
*/
mode?: 'codemod' | 'precompile' | undefined;
continueOnError?: boolean | undefined;
}

export interface Syntax {
Expand Down Expand Up @@ -786,7 +787,7 @@ export function preprocess(
end: offsets.endPosition,
};

let template = new TokenizerEventHandlers(source, entityParser, mode).parse(
let template = new TokenizerEventHandlers(source, entityParser, mode, options.continueOnError).parse(
ast,
options.locals ?? []
);
Expand Down
2 changes: 1 addition & 1 deletion packages/@glimmer/syntax/lib/v1/handlebars-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface NodeMap {
Decorator: { input: Decorator; output: never };
BlockStatement: { input: BlockStatement; output: ASTv1.BlockStatement | void };
DecoratorBlock: { input: DecoratorBlock; output: never };
PartialStatement: { input: PartialStatement; output: never };
PartialStatement: { input: PartialStatement; output: ASTv1.ErrorNode };
PartialBlockStatement: { input: PartialBlockStatement; output: never };
ContentStatement: { input: ContentStatement; output: void };
CommentStatement: { input: CommentStatement; output: ASTv1.MustacheCommentStatement | null };
Expand Down
8 changes: 8 additions & 0 deletions packages/@glimmer/syntax/lib/v1/nodes-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export type StatementName =
| 'BlockStatement'
| 'MustacheCommentStatement'
| 'TextNode'
| 'ErrorNode'
| 'ElementNode';

export interface AttrNode extends BaseNode {
Expand Down Expand Up @@ -288,6 +289,11 @@ export interface HashPair extends BaseNode {
value: Expression;
}

export interface ErrorNode extends BaseNode {
type: 'ErrorNode';
message: string;
}

export interface StripFlags {
open: boolean;
close: boolean;
Expand Down Expand Up @@ -318,6 +324,8 @@ export type Nodes = {

Hash: Hash;
HashPair: HashPair;

ErrorNode: ErrorNode;
};

export type NodeType = keyof Nodes;
Expand Down
8 changes: 8 additions & 0 deletions packages/@glimmer/syntax/lib/v1/parser-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,14 @@ class Builders {
}): T {
return buildLegacyLiteral({ type, value, loc });
}

error({ message, loc }: { message: string; loc: SourceSpan }): ASTv1.ErrorNode {
return {
type: 'ErrorNode',
message,
loc,
};
}
}

const b = new Builders();
Expand Down
8 changes: 8 additions & 0 deletions packages/@glimmer/syntax/lib/v1/public-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,13 @@ function buildLoc(
}
}

function buildError(message = '', loc?: SourceLocation): ASTv1.ErrorNode {
return b.error({
message,
loc: buildLoc(loc || null),
});
}

export default {
mustache: buildMustache,
block: buildBlock,
Expand All @@ -503,6 +510,7 @@ export default {
template: buildTemplate,
loc: buildLoc,
pos: buildPosition,
error: buildError,

path: buildPath,

Expand Down
9 changes: 9 additions & 0 deletions packages/@glimmer/syntax/test/parser-node-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ test('various html element paths', () => {
}
});

test('continue parsing after an error', () => {
let t = '<img id="one">{{> face}}<img id="two">';
astEqual(t, b.template([
element('img', ['attrs', ['id', 'one']]),
b.error('Handlebars partials are not supported'),
element('img', ['attrs', ['id', 'two']])
]), undefined, { continueOnError: true });
});

test('elements can have empty attributes', () => {
let t = '<img id="">';
astEqual(t, b.template([element('img', ['attrs', ['id', '']])]));
Expand Down