Skip to content

Commit 330f04b

Browse files
committed
Add CODEOWNERS file for changeset validation
1 parent a922f57 commit 330f04b

File tree

13 files changed

+391
-54
lines changed

13 files changed

+391
-54
lines changed

packages/liquid-html-parser/grammar/liquid-html.ohm

+10-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,16 @@ LiquidStatement <: Liquid {
389389
}
390390

391391
LiquidDoc <: Helpers {
392-
Node := (TextNode)*
392+
Node := (LiquidDocNode | TextNode)*
393+
LiquidDocNode =
394+
| paramNode
395+
| fallbackNode
396+
397+
fallbackNode = "@" anyExceptStar<newline>
398+
paramNode = "@param" space* (paramType)? space* (paramName)? space* "-"? paramDescription?
399+
paramType = "{" anyExceptStar<"}"> "}"
400+
paramName = identifierCharacter+
401+
paramDescription = (~newline identifierCharacter | space)+
393402
}
394403

395404
LiquidHTML <: Liquid {

packages/liquid-html-parser/src/stage-1-cst.spec.ts

+107-35
Original file line numberDiff line numberDiff line change
@@ -981,53 +981,125 @@ describe('Unit: Stage 1 (CST)', () => {
981981
expectPath(cst, '0.blockEndLocEnd').to.equal(testStr.length);
982982
}
983983
});
984+
});
984985

985-
it('should parse doc tags', () => {
986-
for (const { toCST, expectPath } of testCases) {
987-
const testStr = `{% doc -%} Renders loading-spinner. {%- enddoc %}`;
988-
986+
describe('Case: LiquidDoc', () => {
987+
for (const { toCST, expectPath } of testCases) {
988+
it('should parse basic doc tag structure', () => {
989+
const testStr = `{% doc -%} content {%- enddoc %}`;
989990
cst = toCST(testStr);
991+
990992
expectPath(cst, '0.type').to.equal('LiquidRawTag');
991993
expectPath(cst, '0.name').to.equal('doc');
992-
expectPath(cst, '0.body').to.include('Renders loading-spinner');
993994
expectPath(cst, '0.whitespaceStart').to.equal('');
994995
expectPath(cst, '0.whitespaceEnd').to.equal('-');
995996
expectPath(cst, '0.delimiterWhitespaceStart').to.equal('-');
996997
expectPath(cst, '0.delimiterWhitespaceEnd').to.equal('');
997-
expectPath(cst, '0.blockStartLocStart').to.equal(0);
998-
expectPath(cst, '0.blockStartLocEnd').to.equal(0 + '{% doc -%}'.length);
998+
expectPath(cst, '0.blockStartLocStart').to.equal(testStr.indexOf('{% doc -%}'));
999+
expectPath(cst, '0.blockStartLocEnd').to.equal(
1000+
testStr.indexOf('{% doc -%}') + '{% doc -%}'.length,
1001+
);
9991002
expectPath(cst, '0.blockEndLocStart').to.equal(testStr.length - '{%- enddoc %}'.length);
10001003
expectPath(cst, '0.blockEndLocEnd').to.equal(testStr.length);
1001-
expectPath(cst, '0.children').to.deep.equal([
1002-
{
1003-
locEnd: 35,
1004-
locStart: 11,
1005-
source: '{% doc -%} Renders loading-spinner. {%- enddoc %}',
1006-
type: 'TextNode',
1007-
value: 'Renders loading-spinner.',
1008-
},
1009-
]);
1010-
}
1011-
});
1004+
});
10121005

1013-
it('should parse tag open / close', () => {
1014-
BLOCKS.forEach((block: string) => {
1015-
for (const { toCST, expectPath } of testCases) {
1016-
cst = toCST(`{% ${block} args -%}{%- end${block} %}`);
1017-
expectPath(cst, '0.type').to.equal('LiquidTagOpen', block);
1018-
expectPath(cst, '0.name').to.equal(block);
1019-
expectPath(cst, '0.whitespaceStart').to.equal(null);
1020-
expectPath(cst, '0.whitespaceEnd').to.equal('-');
1021-
if (!NamedTags.hasOwnProperty(block)) {
1022-
expectPath(cst, '0.markup').to.equal('args');
1023-
}
1024-
expectPath(cst, '1.type').to.equal('LiquidTagClose');
1025-
expectPath(cst, '1.name').to.equal(block);
1026-
expectPath(cst, '1.whitespaceStart').to.equal('-');
1027-
expectPath(cst, '1.whitespaceEnd').to.equal(null);
1028-
}
1006+
it('should parse @param with no name or description', () => {
1007+
const testStr = `{% doc %} @param {% enddoc %}`;
1008+
cst = toCST(testStr);
1009+
1010+
expectPath(cst, '0.children.0.type').to.equal('LiquidDocParamNode');
1011+
expectPath(cst, '0.children.0.value').to.equal('@param');
1012+
expectPath(cst, '0.children.0.paramName.type').to.equal('TextNode');
1013+
expectPath(cst, '0.children.0.paramName.value').to.equal('');
1014+
expectPath(cst, '0.children.0.paramDescription.type').to.equal('TextNode');
1015+
expectPath(cst, '0.children.0.paramDescription.value').to.equal('');
10291016
});
1030-
});
1017+
1018+
it('should parse @param with name but no description', () => {
1019+
const testStr = `{% doc %} @param paramWithNoDescription {% enddoc %}`;
1020+
cst = toCST(testStr);
1021+
1022+
expectPath(cst, '0.children.0.type').to.equal('LiquidDocParamNode');
1023+
expectPath(cst, '0.children.0.paramName.type').to.equal('TextNode');
1024+
expectPath(cst, '0.children.0.paramName.value').to.equal('paramWithNoDescription');
1025+
expectPath(cst, '0.children.0.paramName.locStart').to.equal(
1026+
testStr.indexOf('paramWithNoDescription'),
1027+
);
1028+
expectPath(cst, '0.children.0.paramName.locEnd').to.equal(
1029+
testStr.indexOf('paramWithNoDescription') + 'paramWithNoDescription'.length,
1030+
);
1031+
expectPath(cst, '0.children.0.paramDescription.type').to.equal('TextNode');
1032+
expectPath(cst, '0.children.0.paramDescription.value').to.equal('');
1033+
});
1034+
1035+
it('should parse @param with name and description', () => {
1036+
const testStr = `{% doc %} @param paramWithDescription param with description {% enddoc %}`;
1037+
cst = toCST(testStr);
1038+
1039+
expectPath(cst, '0.children.0.type').to.equal('LiquidDocParamNode');
1040+
expectPath(cst, '0.children.0.paramName.type').to.equal('TextNode');
1041+
expectPath(cst, '0.children.0.paramName.value').to.equal('paramWithDescription');
1042+
expectPath(cst, '0.children.0.paramDescription.type').to.equal('TextNode');
1043+
expectPath(cst, '0.children.0.paramDescription.value').to.equal('param with description');
1044+
});
1045+
1046+
it('should parse @param with type', () => {
1047+
const testStr = `{% doc %} @param {String} paramWithType {% enddoc %}`;
1048+
cst = toCST(testStr);
1049+
1050+
expectPath(cst, '0.children.0.type').to.equal('LiquidDocParamNode');
1051+
expectPath(cst, '0.children.0.paramName.value').to.equal('paramWithType');
1052+
1053+
expectPath(cst, '0.children.0.paramType.type').to.equal('TextNode');
1054+
expectPath(cst, '0.children.0.paramType.value').to.equal('String');
1055+
expectPath(cst, '0.children.0.paramType.locStart').to.equal(testStr.indexOf('{String}'));
1056+
expectPath(cst, '0.children.0.paramType.locEnd').to.equal(
1057+
testStr.indexOf('{String}') + '{String}'.length,
1058+
);
1059+
});
1060+
1061+
it('should parse @param with description seperated by a dash', () => {
1062+
const testStr = `{% doc %}
1063+
@param dashWithSpace - param with description
1064+
@param dashWithNoSpace -param with description
1065+
@param notDashSeparated param with description
1066+
{% enddoc %}`;
1067+
cst = toCST(testStr);
1068+
1069+
expectPath(cst, '0.children.0.paramDescription.dashSeparated').to.equal(true);
1070+
expectPath(cst, '0.children.1.paramDescription.dashSeparated').to.equal(true);
1071+
expectPath(cst, '0.children.2.paramDescription.dashSeparated').to.equal(false);
1072+
});
1073+
1074+
it('should parse unsupported doc tags as text nodes', () => {
1075+
const testStr = `{% doc %} @unsupported this tag is not supported {% enddoc %}`;
1076+
cst = toCST(testStr);
1077+
1078+
expectPath(cst, '0.children.0.type').to.equal('TextNode');
1079+
expectPath(cst, '0.children.0.value').to.equal('@unsupported this tag is not supported');
1080+
});
1081+
1082+
it('should parse multiple doc tags in sequence', () => {
1083+
const testStr = `{% doc %}
1084+
@param param1 first parameter
1085+
@param param2 second parameter
1086+
@unsupported
1087+
{% enddoc %}`;
1088+
1089+
cst = toCST(testStr);
1090+
1091+
expectPath(cst, '0.children.0.type').to.equal('LiquidDocParamNode');
1092+
expectPath(cst, '0.children.0.paramName.value').to.equal('param1');
1093+
expectPath(cst, '0.children.0.paramDescription.value').to.equal('first parameter');
1094+
1095+
expectPath(cst, '0.children.1.type').to.equal('LiquidDocParamNode');
1096+
expectPath(cst, '0.children.1.paramName.value').to.equal('param2');
1097+
expectPath(cst, '0.children.1.paramDescription.value').to.equal('second parameter');
1098+
1099+
expectPath(cst, '0.children.2.type').to.equal('TextNode');
1100+
expectPath(cst, '0.children.2.value').to.equal('@unsupported');
1101+
});
1102+
}
10311103
});
10321104
});
10331105

packages/liquid-html-parser/src/stage-1-cst.ts

+72-2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ export enum ConcreteNodeTypes {
8383
PaginateMarkup = 'PaginateMarkup',
8484
RenderVariableExpression = 'RenderVariableExpression',
8585
ContentForNamedArgument = 'ContentForNamedArgument',
86+
87+
LiquidDocParamNode = 'LiquidDocParamNode',
8688
}
8789

8890
export const LiquidLiteralValues = {
@@ -105,6 +107,22 @@ export interface ConcreteBasicNode<T> {
105107
locEnd: number;
106108
}
107109

110+
// todo: change param and description to concrete nodes
111+
export interface ConcreteLiquidDocParamNode
112+
extends ConcreteBasicNode<ConcreteNodeTypes.LiquidDocParamNode> {
113+
name: string;
114+
value: string;
115+
paramName: ConcreteTextNode;
116+
paramDescription: ConcreteLiquidDocParamDescription;
117+
paramType: ConcreteTextNode;
118+
}
119+
120+
export interface ConcreteLiquidDocParamDescription
121+
extends ConcreteBasicNode<ConcreteNodeTypes.TextNode> {
122+
dashSeparated: boolean;
123+
value: string;
124+
}
125+
108126
export interface ConcreteHtmlNodeBase<T> extends ConcreteBasicNode<T> {
109127
attrList?: ConcreteAttributeNode[];
110128
}
@@ -440,10 +458,13 @@ export type LiquidConcreteNode =
440458
| ConcreteTextNode
441459
| ConcreteYamlFrontmatterNode;
442460

443-
export type LiquidHtmlCST = LiquidHtmlConcreteNode[];
461+
export type LiquidHtmlCST = LiquidHtmlConcreteNode[] | LiquidDocCST;
444462

445463
export type LiquidCST = LiquidConcreteNode[];
446464

465+
type LiquidDocCST = LiquidDocConcreteNode[];
466+
export type LiquidDocConcreteNode = ConcreteLiquidDocParamNode;
467+
447468
interface Mapping {
448469
[k: string]: number | TemplateMapping | TopLevelFunctionMapping;
449470
}
@@ -1306,7 +1327,7 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number)
13061327

13071328
const LiquidDocMappings: Mapping = {
13081329
Node: 0,
1309-
TextNode: {
1330+
textNode: {
13101331
type: ConcreteNodeTypes.TextNode,
13111332
value: function () {
13121333
return (this as any).sourceString;
@@ -1315,6 +1336,55 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number)
13151336
locEnd,
13161337
source,
13171338
},
1339+
paramNode: {
1340+
type: ConcreteNodeTypes.LiquidDocParamNode,
1341+
name: 0,
1342+
value: 0,
1343+
locStart,
1344+
locEnd,
1345+
source,
1346+
paramType: function (nodes: Node[]) {
1347+
const typeNode = nodes[2];
1348+
return {
1349+
type: ConcreteNodeTypes.TextNode,
1350+
value: typeNode.sourceString.slice(1, -1).trim(),
1351+
source,
1352+
locStart: offset + typeNode.source.startIdx,
1353+
locEnd: offset + typeNode.source.endIdx,
1354+
};
1355+
},
1356+
paramName: function (nodes: Node[]) {
1357+
const nameNode = nodes[4];
1358+
return {
1359+
type: ConcreteNodeTypes.TextNode,
1360+
value: nameNode.sourceString.trim(),
1361+
source,
1362+
locStart: offset + nameNode.source.startIdx,
1363+
locEnd: offset + nameNode.source.endIdx,
1364+
};
1365+
},
1366+
paramDescription: function (nodes: Node[]) {
1367+
const dashNode = nodes[6];
1368+
const descriptionNode = nodes[7];
1369+
return {
1370+
type: ConcreteNodeTypes.TextNode,
1371+
value: descriptionNode.sourceString.trim(),
1372+
source,
1373+
locStart: offset + descriptionNode.source.startIdx,
1374+
locEnd: offset + descriptionNode.source.endIdx,
1375+
dashSeparated: dashNode.sourceString.trim() === '-',
1376+
};
1377+
},
1378+
},
1379+
fallbackNode: {
1380+
type: ConcreteNodeTypes.TextNode,
1381+
value: function () {
1382+
return (this as any).sourceString.trim();
1383+
},
1384+
locStart,
1385+
locEnd,
1386+
source,
1387+
},
13181388
};
13191389

13201390
return toAST(res, LiquidDocMappings);

packages/liquid-html-parser/src/stage-2-ast.spec.ts

+28-13
Original file line numberDiff line numberDiff line change
@@ -1229,22 +1229,37 @@ describe('Unit: Stage 2 (AST)', () => {
12291229
expectPath(ast, 'children.0.body.type').toEqual('RawMarkup');
12301230
expectPath(ast, 'children.0.body.nodes').toEqual([]);
12311231

1232-
ast = toLiquidAST(`{% doc -%} single line doc {%- enddoc %}`);
1233-
expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
1234-
expectPath(ast, 'children.0.name').to.eql('doc');
1235-
expectPath(ast, 'children.0.body.value').to.eql(' single line doc ');
1236-
expectPath(ast, 'children.0.body.nodes.0.type').toEqual('TextNode');
1237-
1238-
ast = toLiquidAST(`{% doc -%}
1239-
multi line doc
1240-
multi line doc
1241-
{%- enddoc %}`);
1232+
ast = toLiquidAST(`
1233+
{% doc -%}
1234+
@param asdf
1235+
@param {String} paramWithDescription - param with description
1236+
@unsupported this node falls back to a text node
1237+
{%- enddoc %}
1238+
`);
12421239
expectPath(ast, 'children.0.type').to.eql('LiquidRawTag');
12431240
expectPath(ast, 'children.0.name').to.eql('doc');
1244-
expectPath(ast, 'children.0.body.nodes.0.value').to.eql(
1245-
`multi line doc\n multi line doc`,
1241+
expectPath(ast, 'children.0.body.nodes.0.type').to.eql('LiquidDocParamNode');
1242+
expectPath(ast, 'children.0.body.nodes.0.name').to.eql('@param');
1243+
expectPath(ast, 'children.0.body.nodes.0.paramName.type').to.eql('TextNode');
1244+
expectPath(ast, 'children.0.body.nodes.0.paramName.value').to.eql('asdf');
1245+
expectPath(ast, 'children.0.body.nodes.0.paramDescription.type').to.eql('TextNode');
1246+
expectPath(ast, 'children.0.body.nodes.0.paramDescription.value').to.eql('');
1247+
expectPath(ast, 'children.0.body.nodes.0.paramDescription.dashSeparated').to.eql(false);
1248+
expectPath(ast, 'children.0.body.nodes.1.type').to.eql('LiquidDocParamNode');
1249+
expectPath(ast, 'children.0.body.nodes.1.name').to.eql('@param');
1250+
expectPath(ast, 'children.0.body.nodes.1.paramName.type').to.eql('TextNode');
1251+
expectPath(ast, 'children.0.body.nodes.1.paramName.value').to.eql('paramWithDescription');
1252+
expectPath(ast, 'children.0.body.nodes.1.paramDescription.type').to.eql('TextNode');
1253+
expectPath(ast, 'children.0.body.nodes.1.paramDescription.dashSeparated').to.eql(true);
1254+
expectPath(ast, 'children.0.body.nodes.1.paramDescription.value').to.eql(
1255+
'param with description',
1256+
);
1257+
expectPath(ast, 'children.0.body.nodes.1.paramType.type').to.eql('TextNode');
1258+
expectPath(ast, 'children.0.body.nodes.1.paramType.value').to.eql('String');
1259+
expectPath(ast, 'children.0.body.nodes.2.type').to.eql('TextNode');
1260+
expectPath(ast, 'children.0.body.nodes.2.value').to.eql(
1261+
'@unsupported this node falls back to a text node',
12461262
);
1247-
expectPath(ast, 'children.0.body.nodes.0.type').toEqual('TextNode');
12481263
});
12491264

12501265
it('should parse unclosed tables with assignments', () => {

0 commit comments

Comments
 (0)