Skip to content

Commit

Permalink
Add liquidDoc tag to liquidHTMLParser
Browse files Browse the repository at this point in the history
Add stage-2 ast tests for doc tags
  • Loading branch information
jamesmengo committed Dec 3, 2024
1 parent 21bffc5 commit 2fa3af7
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 0 deletions.
14 changes: 14 additions & 0 deletions packages/liquid-html-parser/grammar/liquid-html.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Liquid <: Helpers {
endOfIdentifier = endOfTagName | endOfVarName

liquidNode =
| liquidDoc
| liquidBlockComment
| liquidRawTag
| liquidDrop
Expand Down Expand Up @@ -211,6 +212,15 @@ Liquid <: Helpers {
commentBlockStart = "{%" "-"? space* ("comment" endOfIdentifier) space* tagMarkup "-"? "%}"
commentBlockEnd = "{%" "-"? space* ("endcomment" endOfIdentifier) space* tagMarkup "-"? "%}"

liquidDoc =
liquidDocStart
liquidDocBody
liquidDocEnd

liquidDocStart = "{%" "-"? space* ("doc" endOfIdentifier) space* tagMarkup "-"? "%}"
liquidDocEnd = "{%" "-"? space* ("enddoc" endOfIdentifier) space* tagMarkup "-"? "%}"
liquidDocBody = anyExceptStar<(liquidDocStart | liquidDocEnd)>

// In order for the grammar to "fallback" to the base case, this
// rule must pass if and only if we support what we parse. This
// implies that—since we don't support filters yet—we have a
Expand Down Expand Up @@ -373,6 +383,10 @@ LiquidStatement <: Liquid {
delimTag := liquidStatementEnd
}

LiquidDoc <: Helpers {
Node := (TextNode)*
}

LiquidHTML <: Liquid {
Node := yamlFrontmatter? (HtmlNode | liquidNode | TextNode)*
openControl += "<"
Expand Down
1 change: 1 addition & 0 deletions packages/liquid-html-parser/src/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ohm from 'ohm-js';
export const liquidHtmlGrammars = ohm.grammars(require('../grammar/liquid-html.ohm.js'));

export const TextNodeGrammar = liquidHtmlGrammars['Helpers'];
export const LiquidDocGrammar = liquidHtmlGrammars['LiquidDoc'];

export interface LiquidGrammars {
Liquid: ohm.Grammar;
Expand Down
28 changes: 28 additions & 0 deletions packages/liquid-html-parser/src/stage-1-cst.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,34 @@ describe('Unit: Stage 1 (CST)', () => {
}
});

it('should parse doc tags', () => {
for (const { toCST, expectPath } of testCases) {
const testStr = `{% doc -%} Renders loading-spinner. {%- enddoc %}`;

cst = toCST(testStr);
expectPath(cst, '0.type').to.equal('LiquidRawTag');
expectPath(cst, '0.name').to.equal('doc');
expectPath(cst, '0.body').to.include('Renders loading-spinner');
expectPath(cst, '0.whitespaceStart').to.equal('');
expectPath(cst, '0.whitespaceEnd').to.equal('-');
expectPath(cst, '0.delimiterWhitespaceStart').to.equal('-');
expectPath(cst, '0.delimiterWhitespaceEnd').to.equal('');
expectPath(cst, '0.blockStartLocStart').to.equal(0);
expectPath(cst, '0.blockStartLocEnd').to.equal(0 + '{% doc -%}'.length);
expectPath(cst, '0.blockEndLocStart').to.equal(testStr.length - '{%- enddoc %}'.length);
expectPath(cst, '0.blockEndLocEnd').to.equal(testStr.length);
expectPath(cst, '0.children').to.deep.equal([
{
locEnd: 25,
locStart: 1,
source: '{% doc -%} Renders loading-spinner. {%- enddoc %}',
type: 'TextNode',
value: 'Renders loading-spinner.',
},
]);
}
});

it('should parse tag open / close', () => {
BLOCKS.forEach((block: string) => {
for (const { toCST, expectPath } of testCases) {
Expand Down
39 changes: 39 additions & 0 deletions packages/liquid-html-parser/src/stage-1-cst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Parser } from 'prettier';
import ohm, { Node } from 'ohm-js';
import { toAST } from 'ohm-js/extras';
import {
LiquidDocGrammar,
LiquidGrammars,
TextNodeGrammar,
placeholderGrammars,
Expand Down Expand Up @@ -625,6 +626,30 @@ function toCST<T>(
blockEndLocStart: (tokens: Node[]) => tokens[2].source.startIdx,
blockEndLocEnd: (tokens: Node[]) => tokens[2].source.endIdx,
},
liquidDoc: {
type: ConcreteNodeTypes.LiquidRawTag,
name: 'doc',
body: (tokens: Node[]) => tokens[1].sourceString,
children: (tokens: Node[]) => {
const contentNode = tokens[1];
return toLiquidDocAST(
source,
contentNode.sourceString,
offset + contentNode.source.startIdx,
);
},
whitespaceStart: (tokens: Node[]) => tokens[0].children[1].sourceString,
whitespaceEnd: (tokens: Node[]) => tokens[0].children[7].sourceString,
delimiterWhitespaceStart: (tokens: Node[]) => tokens[2].children[1].sourceString,
delimiterWhitespaceEnd: (tokens: Node[]) => tokens[2].children[7].sourceString,
locStart,
locEnd,
source,
blockStartLocStart: (tokens: Node[]) => tokens[0].source.startIdx,
blockStartLocEnd: (tokens: Node[]) => tokens[0].source.endIdx,
blockEndLocStart: (tokens: Node[]) => tokens[2].source.startIdx,
blockEndLocEnd: (tokens: Node[]) => tokens[2].source.endIdx,
},
liquidInlineComment: {
type: ConcreteNodeTypes.LiquidTag,
name: 3,
Expand Down Expand Up @@ -1084,6 +1109,20 @@ function toCST<T>(
},
};

function toLiquidDocAST(source: string, matchingSource: string, offset: number) {
const res = LiquidDocGrammar.match(matchingSource, 'Node');
if (res.failed()) {
throw new LiquidHTMLCSTParsingError(res);
}

const LiquidDocMappings: Mapping = {
Node: 0,
TextNode: textNode,
};

return toAST(res, LiquidDocMappings);
}

const LiquidHTMLMappings: Mapping = {
Node(frontmatter: Node, nodes: Node) {
const self = this as any;
Expand Down
26 changes: 26 additions & 0 deletions packages/liquid-html-parser/src/stage-2-ast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,32 @@ describe('Unit: Stage 2 (AST)', () => {
expectPath(ast, 'children.0.markup.1.children.0.children.1.markup.name').to.eql('var3');
});

it(`should parse doc tags`, () => {
ast = toLiquidAST(`{% doc %}{% enddoc %}`);
expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
expectPath(ast, 'children.0.name').to.eql('doc');
expectPath(ast, 'children.0.body.value').to.eql('');
expectPath(ast, 'children.0.body.type').toEqual('RawMarkup');
expectPath(ast, 'children.0.body.nodes').toEqual([]);

ast = toLiquidAST(`{% doc -%} single line doc {%- enddoc %}`);
expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
expectPath(ast, 'children.0.name').to.eql('doc');
expectPath(ast, 'children.0.body.value').to.eql(' single line doc ');
expectPath(ast, 'children.0.body.nodes.0.type').toEqual('TextNode');

ast = toLiquidAST(`{% doc -%}
multi line doc
multi line doc
{%- enddoc %}`);
expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
expectPath(ast, 'children.0.name').to.eql('doc');
expectPath(ast, 'children.0.body.nodes.0.value').to.eql(
`multi line doc\n multi line doc`,
);
expectPath(ast, 'children.0.body.nodes.0.type').toEqual('TextNode');
});

it('should parse unclosed tables with assignments', () => {
ast = toLiquidAST(`
{%- liquid
Expand Down

0 comments on commit 2fa3af7

Please sign in to comment.