Skip to content

Commit 155c81c

Browse files
authored
Merge pull request #1038 from Shopify/find-and-fix-no-echo-value
Find+fix echo tags & variable output with no value
2 parents 615d64a + a7d0e2d commit 155c81c

File tree

7 files changed

+87
-15
lines changed

7 files changed

+87
-15
lines changed

.changeset/hip-scissors-accept.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/theme-check-common': patch
3+
---
4+
5+
Find+fix echo tags & variable output with no value

packages/theme-check-common/src/checks/liquid-html-syntax-error/checks/MultipleEchoValues.spec.ts renamed to packages/theme-check-common/src/checks/liquid-html-syntax-error/checks/InvalidEchoValue.spec.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ vi.mock('@shopify/liquid-html-parser', async (importOriginal) => {
1616
};
1717
});
1818

19-
describe('detectMultipleEchoValues', async () => {
20-
it('should not report when there are no trailing values', async () => {
19+
describe('detectInvalidEchoValue', async () => {
20+
it('should not report when echo value is valid', async () => {
2121
const testCases = [
2222
`{% echo '123' %}`,
2323
`{% echo '123' | upcase %}`,
2424
`{{ '123' }}`,
2525
`{{ '123' | upcase }}`,
26+
`{{ }}`,
27+
`{{ echo }}`,
28+
`{% liquid echo %}`,
2629
];
2730

2831
for (const sourceCode of testCases) {
@@ -75,6 +78,23 @@ describe('detectMultipleEchoValues', async () => {
7578
}
7679
});
7780

81+
it('should report when there is no value', async () => {
82+
const testCases = [
83+
[`{% echo | upcase %}`, '{% echo blank %}'],
84+
[`{{ | upcase }}`, `{{ blank }}`],
85+
[`{% liquid echo | upcase %}`, `{% liquid echo blank %}`],
86+
];
87+
88+
for (const [sourceCode, expected] of testCases) {
89+
const offenses = await runLiquidCheck(LiquidHTMLSyntaxError, sourceCode);
90+
expect(offenses).to.have.length(1);
91+
expect(offenses[0].message).to.equal('Syntax is not supported');
92+
93+
const fixed = applyFix(sourceCode, offenses[0]);
94+
expect(fixed).to.equal(expected);
95+
}
96+
});
97+
7898
it('should not report when the fixed code produces an invalid AST', async () => {
7999
const sourceCode = `{% echo '123' 555 text %}`;
80100

packages/theme-check-common/src/checks/liquid-html-syntax-error/checks/MultipleEchoValues.ts renamed to packages/theme-check-common/src/checks/liquid-html-syntax-error/checks/InvalidEchoValue.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { LiquidTag, LiquidVariableOutput, NodeTypes } from '@shopify/liquid-html-parser';
22
import { Problem, SourceCodeType } from '../../..';
3-
import { ensureValidAst, getFirstValueInMarkup, INVALID_SYNTAX_MESSAGE } from './utils';
3+
import { ensureValidAst, getValuesInMarkup, INVALID_SYNTAX_MESSAGE } from './utils';
44

5-
export function detectMultipleEchoValues(
5+
export function detectInvalidEchoValue(
66
node: LiquidTag | LiquidVariableOutput,
77
): Problem<SourceCodeType.LiquidHtml> | undefined {
88
// We've broken it up into two groups:
@@ -16,7 +16,12 @@ export function detectMultipleEchoValues(
1616

1717
const markup = node.markup;
1818

19-
if (typeof markup !== 'string') {
19+
if (
20+
typeof markup !== 'string' ||
21+
// echo tags and variable outputs without markup are strict-valid:
22+
// e.g. {{ }}, {% echo %}, and {% liquid echo %}
23+
!markup
24+
) {
2025
return;
2126
}
2227

@@ -27,10 +32,20 @@ export function detectMultipleEchoValues(
2732

2833
const [, echoValue] = match;
2934

30-
const firstEchoValue = getFirstValueInMarkup(echoValue);
35+
const firstEchoValue = getValuesInMarkup(echoValue).at(0)?.value;
3136

3237
if (!firstEchoValue) {
33-
return;
38+
const startIndex = node.source.indexOf(markup, node.position.start);
39+
const endIndex = startIndex + markup.length;
40+
41+
return {
42+
message: INVALID_SYNTAX_MESSAGE,
43+
startIndex,
44+
endIndex,
45+
fix: (corrector) => {
46+
corrector.replace(startIndex, endIndex, 'blank');
47+
},
48+
};
3449
}
3550

3651
const removalIndices = (source: string) => {

packages/theme-check-common/src/checks/liquid-html-syntax-error/checks/MultipleAssignValues.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { LiquidTag } from '@shopify/liquid-html-parser';
22
import { Problem, SourceCodeType } from '../../..';
3-
import { ensureValidAst, getFirstValueInMarkup, INVALID_SYNTAX_MESSAGE } from './utils';
3+
import { ensureValidAst, getValuesInMarkup, INVALID_SYNTAX_MESSAGE } from './utils';
44

55
export function detectMultipleAssignValues(
66
node: LiquidTag,
@@ -42,7 +42,7 @@ export function detectMultipleAssignValues(
4242
assignmentValue,
4343
] = match;
4444

45-
const firstAssignmentValue = getFirstValueInMarkup(assignmentValue);
45+
const firstAssignmentValue = getValuesInMarkup(assignmentValue).at(0)?.value;
4646

4747
if (!firstAssignmentValue) {
4848
return;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { expect, describe, it } from 'vitest';
2+
import { getValuesInMarkup } from './utils';
3+
4+
describe('getValuesInMarkup', () => {
5+
it('should return the values in the markup (one value)', () => {
6+
const markup = '"123"';
7+
const values = getValuesInMarkup(markup);
8+
expect(values).toContainEqual({ value: '"123"', index: 0 });
9+
});
10+
11+
it('should return the values in the markup (multiple values)', () => {
12+
const markup = '"123" 555 text';
13+
const values = getValuesInMarkup(markup);
14+
expect(values).toContainEqual({ value: '"123"', index: 0 });
15+
expect(values).toContainEqual({ value: '555', index: 6 });
16+
expect(values).toContainEqual({ value: 'text', index: 10 });
17+
});
18+
19+
it('should return nothing when no values in the markup', () => {
20+
const markup = '';
21+
const values = getValuesInMarkup(markup);
22+
expect(values).to.have.length(0);
23+
});
24+
25+
it('should return nothing when only spaces in the markup', () => {
26+
const markup = ' ';
27+
const values = getValuesInMarkup(markup);
28+
expect(values).to.have.length(0);
29+
});
30+
});

packages/theme-check-common/src/checks/liquid-html-syntax-error/checks/utils.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ export function ensureValidAst(source: string) {
1212
}
1313
}
1414

15-
export function getFirstValueInMarkup(markup: string) {
16-
const match = markup.match(/"[^"]*"|'[^']*'|\S+/);
17-
return match?.at(0);
15+
export function getValuesInMarkup(markup: string) {
16+
return [...markup.matchAll(/"[^"]*"|'[^']*'|\S+/g)].map((match) => ({
17+
value: match[0],
18+
index: match.index,
19+
}));
1820
}

packages/theme-check-common/src/checks/liquid-html-syntax-error/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Severity, SourceCodeType, LiquidCheckDefinition } from '../../types';
22
import { getOffset, isError } from '../../utils';
33
import { detectMultipleAssignValues } from './checks/MultipleAssignValues';
44
import { detectInvalidBooleanExpressions } from './checks/InvalidBooleanExpressions';
5-
import { detectMultipleEchoValues as detectMultipleEchoValues } from './checks/MultipleEchoValues';
5+
import { detectInvalidEchoValue } from './checks/InvalidEchoValue';
66

77
type LineColPosition = {
88
line: number;
@@ -50,7 +50,7 @@ export const LiquidHTMLSyntaxError: LiquidCheckDefinition = {
5050
context.report(problem);
5151
},
5252
async LiquidTag(node) {
53-
const problem = detectMultipleAssignValues(node) || detectMultipleEchoValues(node);
53+
const problem = detectMultipleAssignValues(node) || detectInvalidEchoValue(node);
5454

5555
if (!problem) {
5656
return;
@@ -59,7 +59,7 @@ export const LiquidHTMLSyntaxError: LiquidCheckDefinition = {
5959
context.report(problem);
6060
},
6161
async LiquidVariableOutput(node) {
62-
const problem = detectMultipleEchoValues(node);
62+
const problem = detectInvalidEchoValue(node);
6363

6464
if (!problem) {
6565
return;

0 commit comments

Comments
 (0)