Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/discourage-section-blocks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/theme-check-common': minor
---

Add `DiscourageSectionBlocks` check that warns when section-defined blocks (blocks with `settings`) are used inside a section schema, suggesting the use of theme blocks instead.
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { expect, describe, it } from 'vitest';
import { runLiquidCheck } from '../../test';
import { DiscourageSectionBlocks } from './index';

const sectionFile = 'sections/my-section.liquid';

describe('Module: DiscourageSectionBlocks', () => {
it('reports a section block', async () => {
const source = `
{% schema %}
{
"name": "My Section",
"blocks": [
{
"type": "text",
"name": "Text",
"settings": [
{ "type": "text", "id": "content", "label": "Content" }
]
}
]
}
{% endschema %}
`;

const offenses = await runLiquidCheck(DiscourageSectionBlocks, source, sectionFile);

expect(offenses).toHaveLength(1);
});

it('reports each section block independently', async () => {
const source = `
{% schema %}
{
"name": "My Section",
"blocks": [
{
"type": "text",
"name": "Text",
"settings": []
},
{
"type": "image",
"name": "Image",
"settings": []
}
]
}
{% endschema %}
`;

const offenses = await runLiquidCheck(DiscourageSectionBlocks, source, sectionFile);

expect(offenses).toHaveLength(2);
});

it('does not report theme blocks', async () => {
const source = `
{% schema %}
{
"name": "My Section",
"blocks": [
{ "type": "@app" }
]
}
{% endschema %}
`;

const offenses = await runLiquidCheck(DiscourageSectionBlocks, source, sectionFile);

expect(offenses).toHaveLength(0);
});

it('does not report when there are no blocks', async () => {
const source = `
{% schema %}
{
"name": "My Section",
"settings": []
}
{% endschema %}
`;

const offenses = await runLiquidCheck(DiscourageSectionBlocks, source, sectionFile);

expect(offenses).toHaveLength(0);
});

it('does not report when the schema tag is missing', async () => {
const source = `<div>No schema here</div>`;

const offenses = await runLiquidCheck(DiscourageSectionBlocks, source, sectionFile);

expect(offenses).toHaveLength(0);
});

it('reports section blocks but not theme blocks when mixed', async () => {
const source = `
{% schema %}
{
"name": "My Section",
"blocks": [
{ "type": "@app" },
{
"type": "text",
"name": "Text",
"settings": []
}
]
}
{% endschema %}
`;

const offenses = await runLiquidCheck(DiscourageSectionBlocks, source, sectionFile);

expect(offenses).toHaveLength(1);
});

it('does not report for non-section files', async () => {
const source = `
{% schema %}
{
"name": "My Block",
"settings": [
{
"type": "text",
"id": "content",
"label": "Content"
}
]
}
{% endschema %}
`;

const offenses = await runLiquidCheck(
DiscourageSectionBlocks,
source,
'blocks/my-block.liquid',
);

expect(offenses).toHaveLength(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { getLocEnd, getLocStart, nodeAtPath } from '../../json';
import { getSchema, isSection } from '../../to-schema';
import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types';

export const DiscourageSectionBlocks: LiquidCheckDefinition = {
meta: {
code: 'DiscourageSectionBlocks',
name: 'Discourage Section Blocks',
docs: {
description: 'Discourages the use of section blocks in favor of theme blocks.',
recommended: false,
},
type: SourceCodeType.LiquidHtml,
severity: Severity.WARNING,
schema: {},
targets: [],
},

create(context) {
if (!isSection(context.file.uri)) {
return {};
}

return {
async LiquidRawTag(node) {
if (node.name !== 'schema' || node.body.kind !== 'json') {
return;
}

const schema = await getSchema(context);
const { ast, validSchema } = schema ?? {};

if (!ast || ast instanceof Error || !validSchema || validSchema instanceof Error) {
return;
}

const offset = node.blockStartPosition.end;
validSchema.blocks?.forEach((block, index) => {
if (!('settings' in block)) {
return;
}

const astNode = nodeAtPath(ast, ['blocks', String(index), 'type']);
if (!astNode) {
return;
}

context.report({
message: 'Consider using a ThemeBlock instead of a SectionBlock.',
startIndex: offset + getLocStart(astNode),
endIndex: offset + getLocEnd(astNode),
});
});
},
};
},
};
2 changes: 2 additions & 0 deletions packages/theme-check-common/src/checks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BlockIdUsage } from './block-id-usage';
import { CdnPreconnect } from './cdn-preconnect';
import { ContentForHeaderModification } from './content-for-header-modification';
import { DeprecateBgsizes } from './deprecate-bgsizes';
import { DiscourageSectionBlocks } from './discourage-section-blocks';
import { DeprecateLazysizes } from './deprecate-lazysizes';
import { DeprecatedFilter } from './deprecated-filter';
import { DeprecatedFontsOnSectionsAndBlocks } from './deprecated-fonts-on-sections-and-blocks';
Expand Down Expand Up @@ -81,6 +82,7 @@ export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [
ContentForHeaderModification,
DeprecateBgsizes,
DeprecateLazysizes,
DiscourageSectionBlocks,
DeprecatedFilter,
DeprecatedFontsOnSectionsAndBlocks,
DeprecatedFontsOnSettingsData,
Expand Down
Loading