diff --git a/packages/@glimmer/syntax/lib/parser.ts b/packages/@glimmer/syntax/lib/parser.ts
index efbc7c7ee2..8f4a2e4821 100644
--- a/packages/@glimmer/syntax/lib/parser.ts
+++ b/packages/@glimmer/syntax/lib/parser.ts
@@ -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 {
diff --git a/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts b/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts
index 8b3831761d..92b0e8e7e5 100644
--- a/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts
+++ b/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts
@@ -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)
diff --git a/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts b/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts
index a0432acb8d..ee516256d2 100644
--- a/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts
+++ b/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts
@@ -716,6 +716,7 @@ export interface PreprocessOptions {
escaping/unescaping of HTML entity codes.
*/
mode?: 'codemod' | 'precompile' | undefined;
+ continueOnError?: boolean | undefined;
}
export interface Syntax {
@@ -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 ?? []
);
diff --git a/packages/@glimmer/syntax/lib/v1/handlebars-ast.ts b/packages/@glimmer/syntax/lib/v1/handlebars-ast.ts
index 371503f7b9..ef5f923c41 100644
--- a/packages/@glimmer/syntax/lib/v1/handlebars-ast.ts
+++ b/packages/@glimmer/syntax/lib/v1/handlebars-ast.ts
@@ -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 };
diff --git a/packages/@glimmer/syntax/lib/v1/nodes-v1.ts b/packages/@glimmer/syntax/lib/v1/nodes-v1.ts
index baf49864b9..e0ad70329f 100644
--- a/packages/@glimmer/syntax/lib/v1/nodes-v1.ts
+++ b/packages/@glimmer/syntax/lib/v1/nodes-v1.ts
@@ -135,6 +135,7 @@ export type StatementName =
| 'BlockStatement'
| 'MustacheCommentStatement'
| 'TextNode'
+ | 'ErrorNode'
| 'ElementNode';
export interface AttrNode extends BaseNode {
@@ -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;
@@ -318,6 +324,8 @@ export type Nodes = {
Hash: Hash;
HashPair: HashPair;
+
+ ErrorNode: ErrorNode;
};
export type NodeType = keyof Nodes;
diff --git a/packages/@glimmer/syntax/lib/v1/parser-builders.ts b/packages/@glimmer/syntax/lib/v1/parser-builders.ts
index be683407c3..4ea2ecbe45 100644
--- a/packages/@glimmer/syntax/lib/v1/parser-builders.ts
+++ b/packages/@glimmer/syntax/lib/v1/parser-builders.ts
@@ -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();
diff --git a/packages/@glimmer/syntax/lib/v1/public-builders.ts b/packages/@glimmer/syntax/lib/v1/public-builders.ts
index fee841de63..35507426c3 100644
--- a/packages/@glimmer/syntax/lib/v1/public-builders.ts
+++ b/packages/@glimmer/syntax/lib/v1/public-builders.ts
@@ -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,
@@ -503,6 +510,7 @@ export default {
template: buildTemplate,
loc: buildLoc,
pos: buildPosition,
+ error: buildError,
path: buildPath,
diff --git a/packages/@glimmer/syntax/test/parser-node-test.ts b/packages/@glimmer/syntax/test/parser-node-test.ts
index db63f42616..dcc918d190 100644
--- a/packages/@glimmer/syntax/test/parser-node-test.ts
+++ b/packages/@glimmer/syntax/test/parser-node-test.ts
@@ -35,6 +35,15 @@ test('various html element paths', () => {
}
});
+test('continue parsing after an error', () => {
+ let t = '{{> face}}';
+ 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 = '';
astEqual(t, b.template([element('img', ['attrs', ['id', '']])]));