From 7eea0d21136a3a552785477dc4900decdda715df Mon Sep 17 00:00:00 2001 From: Nathan Ferguson Date: Fri, 26 Jun 2026 12:01:52 -0400 Subject: [PATCH 1/3] Add NoFiltersInRenderArguments check --- .changeset/no-filters-in-render-arguments.md | 15 ++++ .../theme-check-common/src/checks/index.ts | 2 + .../index.spec.ts | 62 +++++++++++++ .../no-filters-in-render-arguments/index.ts | 86 +++++++++++++++++++ packages/theme-check-node/configs/all.yml | 3 + .../theme-check-node/configs/recommended.yml | 3 + 6 files changed, 171 insertions(+) create mode 100644 .changeset/no-filters-in-render-arguments.md create mode 100644 packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts create mode 100644 packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts diff --git a/.changeset/no-filters-in-render-arguments.md b/.changeset/no-filters-in-render-arguments.md new file mode 100644 index 000000000..d824ba86a --- /dev/null +++ b/.changeset/no-filters-in-render-arguments.md @@ -0,0 +1,15 @@ +--- +'@shopify/theme-check-common': minor +--- + +Add `NoFiltersInRenderArguments` check to error when a filter is used in `render` and `include` tags. + +Filters are not supported on values passed as arguments to `render`/`include` tags. In production, they are silently dropped, leading to unexpected outcomes when rendered. + +In the example below, the `bar` argument passes through unchanged when the snippet is rendered. + +```liquid +{% render 'foo', bar: 'hello' | append: ' world' %} +``` + +This check now reports an error pointing at the offending filter so the broken behavior is caught instead of failing silently. diff --git a/packages/theme-check-common/src/checks/index.ts b/packages/theme-check-common/src/checks/index.ts index be0370c41..a8fe22d2b 100644 --- a/packages/theme-check-common/src/checks/index.ts +++ b/packages/theme-check-common/src/checks/index.ts @@ -30,6 +30,7 @@ import { MissingAsset } from './missing-asset'; import { MissingContentForArguments } from './missing-content-for-arguments'; import { MissingRenderSnippetArguments } from './missing-render-snippet-arguments'; import { MissingTemplate } from './missing-template'; +import { NoFiltersInRenderArguments } from './no-filters-in-render-arguments'; import { OrphanedSnippet } from './orphaned-snippet'; import { PaginationSize } from './pagination-size'; import { ParserBlockingScript } from './parser-blocking-script'; @@ -100,6 +101,7 @@ export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [ MissingContentForArguments, MissingRenderSnippetArguments, MissingTemplate, + NoFiltersInRenderArguments, AppBlockMissingSchema, OrphanedSnippet, PaginationSize, diff --git a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts new file mode 100644 index 000000000..679f98176 --- /dev/null +++ b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts @@ -0,0 +1,62 @@ +import { expect, describe, it } from 'vitest'; +import { highlightedOffenses, runLiquidCheck } from '../../test'; +import { NoFiltersInRenderArguments } from './index'; + +describe('Module: NoFiltersInRenderArguments', () => { + it('reports an offense when a render argument uses a filter', async () => { + const sourceCode = `{% render 'foo', param1: 'bar', param2: 'hello' | append: ' world' %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(1); + expect(offenses[0].message).toContain( + "Filters cannot be used on arguments passed to the 'render' tag", + ); + + const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses); + expect(highlights).toEqual([`| append: ' world'`]); + }); + + it('reports an offense when an include argument uses a filter', async () => { + const sourceCode = `{% include 'foo', x: bar | upcase %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(1); + expect(offenses[0].message).toContain( + "Filters cannot be used on arguments passed to the 'include' tag", + ); + + const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses); + expect(highlights).toEqual([`| upcase`]); + }); + + it('does not report when no filter is used', async () => { + const sourceCode = `{% render 'foo', param1: 'bar', param2: 'hello' %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(0); + }); + + it('does not report a false positive for a pipe inside a string literal', async () => { + const sourceCode = `{% render 'foo', sep: 'a|b' %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(0); + }); + + it('does not report on a plain render with no arguments', async () => { + const sourceCode = `{% render 'foo' %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(0); + }); + + it('reports offenses on nested render tags', async () => { + const sourceCode = `{% if true %}{% render 'foo', x: bar | upcase %}{% endif %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(1); + expect(offenses[0].message).toContain( + "Filters cannot be used on arguments passed to the 'render' tag", + ); + }); +}); diff --git a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts new file mode 100644 index 000000000..b2e63bfc7 --- /dev/null +++ b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts @@ -0,0 +1,86 @@ +import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types'; + +/** + * Finds the index of the first filter pipe (`|`) in a raw render/include markup + * string that is *not* inside a quoted string literal. Returns -1 if none is + * found. + * + * This is a heuristic on the raw markup because, when a `render`/`include` tag + * contains a filter, the strict grammar refuses it and the parser falls back to + * a base-case `LiquidTag` whose `markup` is a raw string (there is no structured + * `RenderMarkup` node to inspect). + */ +function indexOfFilterPipe(markup: string): number { + let quote: "'" | '"' | null = null; + + for (let i = 0; i < markup.length; i++) { + const char = markup[i]; + + if (quote) { + if (char === quote) { + quote = null; + } + continue; + } + + if (char === "'" || char === '"') { + quote = char; + continue; + } + + if (char === '|') { + return i; + } + } + + return -1; +} + +export const NoFiltersInRenderArguments: LiquidCheckDefinition = { + meta: { + code: 'NoFiltersInRenderArguments', + name: 'No Filters in Render Arguments', + docs: { + description: + "This check warns against using filters on values passed as arguments to a 'render' or 'include' tag. " + + 'Filters are not applied in that position and the value is passed through unchanged, which silently ' + + 'produces incorrect output.', + recommended: true, + url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/no-filters-in-render-arguments', + }, + type: SourceCodeType.LiquidHtml, + severity: Severity.ERROR, + schema: {}, + targets: [], + }, + + create(context) { + return { + async LiquidTag(node) { + if (node.name !== 'render' && node.name !== 'include') return; + + // When the tag parses successfully, `markup` is a structured + // `RenderMarkup` object and filters are impossible. A raw string means + // the strict parse failed (e.g. because of a filter pipe). + if (typeof node.markup !== 'string') return; + + const relativePipeIndex = indexOfFilterPipe(node.markup); + if (relativePipeIndex === -1) return; + + const markupStart = node.source.indexOf(node.markup, node.position.start); + if (markupStart === -1) return; + + const startIndex = markupStart + relativePipeIndex; + const endIndex = markupStart + node.markup.length; + + context.report({ + message: + `Filters cannot be used on arguments passed to the '${node.name}' tag. ` + + `Apply the filter beforehand (e.g. with {% assign %}) and pass the result instead.`, + startIndex, + endIndex, + }); + }, + }; + }, +}; diff --git a/packages/theme-check-node/configs/all.yml b/packages/theme-check-node/configs/all.yml index 426115894..7f4a205cd 100644 --- a/packages/theme-check-node/configs/all.yml +++ b/packages/theme-check-node/configs/all.yml @@ -101,6 +101,9 @@ MissingTemplate: enabled: true severity: 0 ignoreMissing: [] +NoFiltersInRenderArguments: + enabled: true + severity: 0 OrphanedSnippet: enabled: true severity: 1 diff --git a/packages/theme-check-node/configs/recommended.yml b/packages/theme-check-node/configs/recommended.yml index adaa03fdf..6062e7d6c 100644 --- a/packages/theme-check-node/configs/recommended.yml +++ b/packages/theme-check-node/configs/recommended.yml @@ -79,6 +79,9 @@ MissingTemplate: enabled: true severity: 0 ignoreMissing: [] +NoFiltersInRenderArguments: + enabled: true + severity: 0 OrphanedSnippet: enabled: true severity: 1 From dc9447185f762f225aa3a7f35153ed3a15c7e95c Mon Sep 17 00:00:00 2001 From: Nathan Ferguson Date: Fri, 26 Jun 2026 21:58:06 -0400 Subject: [PATCH 2/3] Address review: cover content_for, use NamedTags enum, add regression specs Co-authored-by: Pi AI (anthropic/claude-opus-4-8) --- .changeset/no-filters-in-render-arguments.md | 4 +- .../index.spec.ts | 59 +++++++++++++++++++ .../no-filters-in-render-arguments/index.ts | 15 +++-- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/.changeset/no-filters-in-render-arguments.md b/.changeset/no-filters-in-render-arguments.md index d824ba86a..e4cc3cb26 100644 --- a/.changeset/no-filters-in-render-arguments.md +++ b/.changeset/no-filters-in-render-arguments.md @@ -2,9 +2,9 @@ '@shopify/theme-check-common': minor --- -Add `NoFiltersInRenderArguments` check to error when a filter is used in `render` and `include` tags. +Add `NoFiltersInRenderArguments` check to error when a filter is used in `render`, `include`, and `content_for` tags. -Filters are not supported on values passed as arguments to `render`/`include` tags. In production, they are silently dropped, leading to unexpected outcomes when rendered. +Filters are not supported on values passed as arguments to `render`/`include`/`content_for` tags. In production, they are silently dropped, leading to unexpected outcomes when rendered. In the example below, the `bar` argument passes through unchanged when the snippet is rendered. diff --git a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts index 679f98176..cb8ac2551 100644 --- a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts +++ b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts @@ -36,6 +36,19 @@ describe('Module: NoFiltersInRenderArguments', () => { expect(offenses).toHaveLength(0); }); + it('reports an offense when a content_for argument uses a filter', async () => { + const sourceCode = `{% content_for 'block', type: 'foo', id: 'bar' | append: 'x' %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(1); + expect(offenses[0].message).toContain( + "Filters cannot be used on arguments passed to the 'content_for' tag", + ); + + const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses); + expect(highlights).toEqual([`| append: 'x'`]); + }); + it('does not report a false positive for a pipe inside a string literal', async () => { const sourceCode = `{% render 'foo', sep: 'a|b' %}`; const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); @@ -43,6 +56,52 @@ describe('Module: NoFiltersInRenderArguments', () => { expect(offenses).toHaveLength(0); }); + it('does not report a false positive for a quoted pipe even when strict parse fails', async () => { + // `invalid_argument` forces the strict parse to fail, so the raw-markup + // scanner runs while the only pipe is still inside a quoted string literal. + const sourceCode = `{% render 'foo', sep: 'a|b', invalid_argument %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(0); + }); + + it('does not report a false positive for a double-quoted pipe even when strict parse fails', async () => { + const sourceCode = `{% render 'foo', sep: "a|b", invalid_argument %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(0); + }); + + it('highlights the real filter when a quoted pipe precedes it', async () => { + const sourceCode = `{% render 'foo', sep: 'a|b', x: bar | upcase %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(1); + + const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses); + expect(highlights).toEqual([`| upcase`]); + }); + + it('reports distinct highlights for two render tags on one line', async () => { + const sourceCode = `{% render 'foo', x: a | upcase %} {% render 'bar', y: b | downcase %}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(2); + + const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses); + expect(highlights).toEqual([`| upcase`, `| downcase`]); + }); + + it('reports an offense for a render statement inside a {% liquid %} tag', async () => { + const sourceCode = `{% liquid\n render 'foo', x: bar | upcase\n%}`; + const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + + expect(offenses).toHaveLength(1); + + const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses); + expect(highlights).toEqual([`| upcase`]); + }); + it('does not report on a plain render with no arguments', async () => { const sourceCode = `{% render 'foo' %}`; const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); diff --git a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts index b2e63bfc7..f361f5ea0 100644 --- a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts +++ b/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts @@ -1,12 +1,15 @@ +import { NamedTags } from '@shopify/liquid-html-parser'; import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types'; +const CHECKED_TAGS: string[] = [NamedTags.render, NamedTags.include, NamedTags.content_for]; + /** - * Finds the index of the first filter pipe (`|`) in a raw render/include markup - * string that is *not* inside a quoted string literal. Returns -1 if none is + * Finds the index of the first filter pipe (`|`) in a raw render/include/content_for + * markup string that is *not* inside a quoted string literal. Returns -1 if none is * found. * - * This is a heuristic on the raw markup because, when a `render`/`include` tag - * contains a filter, the strict grammar refuses it and the parser falls back to + * This is a heuristic on the raw markup because, when a `render`/`include`/`content_for` + * tag contains a filter, the strict grammar refuses it and the parser falls back to * a base-case `LiquidTag` whose `markup` is a raw string (there is no structured * `RenderMarkup` node to inspect). */ @@ -42,7 +45,7 @@ export const NoFiltersInRenderArguments: LiquidCheckDefinition = { name: 'No Filters in Render Arguments', docs: { description: - "This check warns against using filters on values passed as arguments to a 'render' or 'include' tag. " + + "This check warns against using filters on values passed as arguments to a 'render', 'include', or 'content_for' tag. " + 'Filters are not applied in that position and the value is passed through unchanged, which silently ' + 'produces incorrect output.', recommended: true, @@ -57,7 +60,7 @@ export const NoFiltersInRenderArguments: LiquidCheckDefinition = { create(context) { return { async LiquidTag(node) { - if (node.name !== 'render' && node.name !== 'include') return; + if (!CHECKED_TAGS.includes(node.name)) return; // When the tag parses successfully, `markup` is a structured // `RenderMarkup` object and filters are impossible. A raw string means From 180eacbee68eea0dc46ebee6e788c0d3d28ac053 Mon Sep 17 00:00:00 2001 From: Nathan Ferguson Date: Fri, 26 Jun 2026 22:17:02 -0400 Subject: [PATCH 3/3] Rename check to UnsupportedFilterArguments Co-authored-by: Pi AI (anthropic/claude-opus-4-8) --- ...nts.md => unsupported-filter-arguments.md} | 2 +- .../theme-check-common/src/checks/index.ts | 4 +-- .../index.spec.ts | 28 +++++++++---------- .../index.ts | 8 +++--- packages/theme-check-node/configs/all.yml | 6 ++-- .../theme-check-node/configs/recommended.yml | 6 ++-- 6 files changed, 27 insertions(+), 27 deletions(-) rename .changeset/{no-filters-in-render-arguments.md => unsupported-filter-arguments.md} (90%) rename packages/theme-check-common/src/checks/{no-filters-in-render-arguments => unsupported-filter-arguments}/index.spec.ts (82%) rename packages/theme-check-common/src/checks/{no-filters-in-render-arguments => unsupported-filter-arguments}/index.ts (93%) diff --git a/.changeset/no-filters-in-render-arguments.md b/.changeset/unsupported-filter-arguments.md similarity index 90% rename from .changeset/no-filters-in-render-arguments.md rename to .changeset/unsupported-filter-arguments.md index e4cc3cb26..0d40bce50 100644 --- a/.changeset/no-filters-in-render-arguments.md +++ b/.changeset/unsupported-filter-arguments.md @@ -2,7 +2,7 @@ '@shopify/theme-check-common': minor --- -Add `NoFiltersInRenderArguments` check to error when a filter is used in `render`, `include`, and `content_for` tags. +Add `UnsupportedFilterArguments` check to error when a filter is used in `render`, `include`, and `content_for` tags. Filters are not supported on values passed as arguments to `render`/`include`/`content_for` tags. In production, they are silently dropped, leading to unexpected outcomes when rendered. diff --git a/packages/theme-check-common/src/checks/index.ts b/packages/theme-check-common/src/checks/index.ts index a8fe22d2b..6396762ed 100644 --- a/packages/theme-check-common/src/checks/index.ts +++ b/packages/theme-check-common/src/checks/index.ts @@ -30,7 +30,6 @@ import { MissingAsset } from './missing-asset'; import { MissingContentForArguments } from './missing-content-for-arguments'; import { MissingRenderSnippetArguments } from './missing-render-snippet-arguments'; import { MissingTemplate } from './missing-template'; -import { NoFiltersInRenderArguments } from './no-filters-in-render-arguments'; import { OrphanedSnippet } from './orphaned-snippet'; import { PaginationSize } from './pagination-size'; import { ParserBlockingScript } from './parser-blocking-script'; @@ -50,6 +49,7 @@ import { UnrecognizedContentForArguments } from './unrecognized-content-for-argu import { UnrecognizedRenderSnippetArguments } from './unrecognized-render-snippet-arguments'; import { UnusedAssign } from './unused-assign'; import { UnsupportedDocTag } from './unsupported-doc-tag'; +import { UnsupportedFilterArguments } from './unsupported-filter-arguments'; import { UnusedDocParam } from './unused-doc-param'; import { ValidContentForArguments } from './valid-content-for-arguments'; import { ValidContentForArgumentTypes } from './valid-content-for-argument-types'; @@ -101,7 +101,7 @@ export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [ MissingContentForArguments, MissingRenderSnippetArguments, MissingTemplate, - NoFiltersInRenderArguments, + UnsupportedFilterArguments, AppBlockMissingSchema, OrphanedSnippet, PaginationSize, diff --git a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts b/packages/theme-check-common/src/checks/unsupported-filter-arguments/index.spec.ts similarity index 82% rename from packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts rename to packages/theme-check-common/src/checks/unsupported-filter-arguments/index.spec.ts index cb8ac2551..0ac72427d 100644 --- a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.spec.ts +++ b/packages/theme-check-common/src/checks/unsupported-filter-arguments/index.spec.ts @@ -1,11 +1,11 @@ import { expect, describe, it } from 'vitest'; import { highlightedOffenses, runLiquidCheck } from '../../test'; -import { NoFiltersInRenderArguments } from './index'; +import { UnsupportedFilterArguments } from './index'; -describe('Module: NoFiltersInRenderArguments', () => { +describe('Module: UnsupportedFilterArguments', () => { it('reports an offense when a render argument uses a filter', async () => { const sourceCode = `{% render 'foo', param1: 'bar', param2: 'hello' | append: ' world' %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(1); expect(offenses[0].message).toContain( @@ -18,7 +18,7 @@ describe('Module: NoFiltersInRenderArguments', () => { it('reports an offense when an include argument uses a filter', async () => { const sourceCode = `{% include 'foo', x: bar | upcase %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(1); expect(offenses[0].message).toContain( @@ -31,14 +31,14 @@ describe('Module: NoFiltersInRenderArguments', () => { it('does not report when no filter is used', async () => { const sourceCode = `{% render 'foo', param1: 'bar', param2: 'hello' %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(0); }); it('reports an offense when a content_for argument uses a filter', async () => { const sourceCode = `{% content_for 'block', type: 'foo', id: 'bar' | append: 'x' %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(1); expect(offenses[0].message).toContain( @@ -51,7 +51,7 @@ describe('Module: NoFiltersInRenderArguments', () => { it('does not report a false positive for a pipe inside a string literal', async () => { const sourceCode = `{% render 'foo', sep: 'a|b' %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(0); }); @@ -60,21 +60,21 @@ describe('Module: NoFiltersInRenderArguments', () => { // `invalid_argument` forces the strict parse to fail, so the raw-markup // scanner runs while the only pipe is still inside a quoted string literal. const sourceCode = `{% render 'foo', sep: 'a|b', invalid_argument %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(0); }); it('does not report a false positive for a double-quoted pipe even when strict parse fails', async () => { const sourceCode = `{% render 'foo', sep: "a|b", invalid_argument %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(0); }); it('highlights the real filter when a quoted pipe precedes it', async () => { const sourceCode = `{% render 'foo', sep: 'a|b', x: bar | upcase %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(1); @@ -84,7 +84,7 @@ describe('Module: NoFiltersInRenderArguments', () => { it('reports distinct highlights for two render tags on one line', async () => { const sourceCode = `{% render 'foo', x: a | upcase %} {% render 'bar', y: b | downcase %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(2); @@ -94,7 +94,7 @@ describe('Module: NoFiltersInRenderArguments', () => { it('reports an offense for a render statement inside a {% liquid %} tag', async () => { const sourceCode = `{% liquid\n render 'foo', x: bar | upcase\n%}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(1); @@ -104,14 +104,14 @@ describe('Module: NoFiltersInRenderArguments', () => { it('does not report on a plain render with no arguments', async () => { const sourceCode = `{% render 'foo' %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(0); }); it('reports offenses on nested render tags', async () => { const sourceCode = `{% if true %}{% render 'foo', x: bar | upcase %}{% endif %}`; - const offenses = await runLiquidCheck(NoFiltersInRenderArguments, sourceCode); + const offenses = await runLiquidCheck(UnsupportedFilterArguments, sourceCode); expect(offenses).toHaveLength(1); expect(offenses[0].message).toContain( diff --git a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts b/packages/theme-check-common/src/checks/unsupported-filter-arguments/index.ts similarity index 93% rename from packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts rename to packages/theme-check-common/src/checks/unsupported-filter-arguments/index.ts index f361f5ea0..429c42790 100644 --- a/packages/theme-check-common/src/checks/no-filters-in-render-arguments/index.ts +++ b/packages/theme-check-common/src/checks/unsupported-filter-arguments/index.ts @@ -39,17 +39,17 @@ function indexOfFilterPipe(markup: string): number { return -1; } -export const NoFiltersInRenderArguments: LiquidCheckDefinition = { +export const UnsupportedFilterArguments: LiquidCheckDefinition = { meta: { - code: 'NoFiltersInRenderArguments', - name: 'No Filters in Render Arguments', + code: 'UnsupportedFilterArguments', + name: 'Unsupported Filter Arguments', docs: { description: "This check warns against using filters on values passed as arguments to a 'render', 'include', or 'content_for' tag. " + 'Filters are not applied in that position and the value is passed through unchanged, which silently ' + 'produces incorrect output.', recommended: true, - url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/no-filters-in-render-arguments', + url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/unsupported-filter-arguments', }, type: SourceCodeType.LiquidHtml, severity: Severity.ERROR, diff --git a/packages/theme-check-node/configs/all.yml b/packages/theme-check-node/configs/all.yml index 7f4a205cd..c83e19ec4 100644 --- a/packages/theme-check-node/configs/all.yml +++ b/packages/theme-check-node/configs/all.yml @@ -101,9 +101,6 @@ MissingTemplate: enabled: true severity: 0 ignoreMissing: [] -NoFiltersInRenderArguments: - enabled: true - severity: 0 OrphanedSnippet: enabled: true severity: 1 @@ -163,6 +160,9 @@ UnrecognizedRenderSnippetArguments: UnsupportedDocTag: enabled: true severity: 0 +UnsupportedFilterArguments: + enabled: true + severity: 0 UnusedAssign: enabled: true severity: 1 diff --git a/packages/theme-check-node/configs/recommended.yml b/packages/theme-check-node/configs/recommended.yml index 6062e7d6c..79b8072e5 100644 --- a/packages/theme-check-node/configs/recommended.yml +++ b/packages/theme-check-node/configs/recommended.yml @@ -79,9 +79,6 @@ MissingTemplate: enabled: true severity: 0 ignoreMissing: [] -NoFiltersInRenderArguments: - enabled: true - severity: 0 OrphanedSnippet: enabled: true severity: 1 @@ -141,6 +138,9 @@ UnrecognizedRenderSnippetArguments: UnsupportedDocTag: enabled: true severity: 0 +UnsupportedFilterArguments: + enabled: true + severity: 0 UnusedAssign: enabled: true severity: 1