Skip to content

Commit

Permalink
theme block theme check
Browse files Browse the repository at this point in the history
Finished tests

Added changeset
  • Loading branch information
AribaRajput committed Dec 4, 2024
1 parent 00fbe01 commit 0a7932b
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .changeset/chilled-bugs-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@shopify/theme-check-common': minor
'theme-check-vscode': minor
---

Add the `ValidBlockPresetSettings` check.
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 @@ -42,6 +42,7 @@ import { ValidSchemaName } from './valid-schema-name';
import { ValidStaticBlockType } from './valid-static-block-type';
import { VariableName } from './variable-name';
import { MissingSchema } from './missing-schema';
import { ValidBlockPresetSettings } from './valid-block-preset-settings';

export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [
AppBlockValidTags,
Expand Down Expand Up @@ -85,6 +86,7 @@ export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [
ValidStaticBlockType,
VariableName,
ValidSchemaName,
ValidBlockPresetSettings,
];

/**
Expand Down
Empty file.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { describe, expect, it } from 'vitest';
import { ValidBlockPresetSettings } from '.';
import { check } from '../../test/test-helper';
import { MockTheme } from '../../test/MockTheme';

describe('ValidBlockPresetSettings', () => {
it('should report invalid preset settings', async () => {
const theme: MockTheme = {
'blocks/price.liquid': `
{% schema %}
{
"name": "t:names.product_price",
"settings": [
{
"type": "product",
"id": "product",
"label": "t:settings.product"
},
],
"presets": [
{
"name": "t:names.product_price",
"settings": {
"product": "{{ context.product }}",
"undefined_setting": "some value",
}
}
]
}
{% endschema %}
`,
};
const offenses = await check(theme, [ValidBlockPresetSettings]);
expect(offenses).to.have.length(1);
});

it('should report invalid theme block preset settings', async () => {
const theme: MockTheme = {
'blocks/block_1.liquid': `
{% schema %}
{
"name": "t:names.block_1",
"settings": [
{
"type": "text",
"id": "block_1_setting_key",
"label": "t:settings.block_1"
},
]
}
{% endschema %}
`,
'blocks/price.liquid': `
{% schema %}
{
"name": "t:names.product_price",
"settings": [
{
"type": "product",
"id": "product",
"label": "t:settings.product"
}
],
"blocks": [
{
"type": "block_1",
"name": "t:names.block_1",
}
],
"presets": [
{
"name": "t:names.product_price",
"settings": {
"product": "{{ context.product }}",
},
"blocks": [
{
"block_1": {
"type": "block_1",
"settings": {
"block_1_setting_key": "correct setting key",
"undefined_setting": "incorrect setting key"
}
}
}
],
}
]
}
{% endschema %}
`,
};

const offenses = await check(theme, [ValidBlockPresetSettings]);
expect(offenses).to.have.length(1);
expect(offenses[0].message).to.include(
'Preset setting "undefined_setting" does not exist in the block type "block_1"\'s settings',
);
});

it('should not report when all section and block preset settings are valid', async () => {
const theme: MockTheme = {
'blocks/block_1.liquid': `
{% schema %}
{
"name": "t:names.block_1",
"settings": [
{
"type": "text",
"id": "block_1_setting_key",
"label": "t:settings.block_1"
}
]
}
{% endschema %}
`,
'blocks/price.liquid': `
{% schema %}
{
"name": "t:names.product_price",
"settings": [
{
"type": "product",
"id": "product",
"label": "t:settings.product"
},
{
"type": "text",
"id": "section_setting",
"label": "t:settings.section"
}
],
"blocks": [
{
"type": "block_1",
"name": "t:names.block_1"
}
],
"presets": [
{
"name": "t:names.product_price",
"settings": {
"product": "{{ context.product }}",
"section_setting": "some value"
},
"blocks": [
{
"block_1": {
"type": "block_1",
"settings": {
"block_1_setting_key": "correct setting key"
}
}
}
]
}
]
}
{% endschema %}
`,
};

const offenses = await check(theme, [ValidBlockPresetSettings]);
expect(offenses).to.have.length(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { isSection, isBlock } from '../../to-schema';
import { basename } from '../../path';
import { LiquidCheckDefinition, Severity, SourceCodeType, ThemeBlock } from '../../types';
import { Preset } from '../../types/schemas/preset';
import { Setting } from '../../types/schemas/setting';

export const ValidBlockPresetSettings: LiquidCheckDefinition = {
meta: {
code: 'ValidBlockPresetSettings',
name: 'Reports invalid preset settings for a theme block',
docs: {
description: 'Reports invalid preset settings for a theme block',
recommended: true,
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/valid-block-preset-settings',
},
severity: Severity.ERROR,
type: SourceCodeType.LiquidHtml,
schema: {},
targets: [],
},

create(context) {
function getSchema() {
const name = basename(context.file.uri, '.liquid');
switch (true) {
case isBlock(context.file.uri):
return context.getBlockSchema?.(name);
case isSection(context.file.uri):
return context.getSectionSchema?.(name);
default:
return undefined;
}
}

function getInlineSettingsTypesAndKeys(settings: Setting.Any[]) {
if (!settings) return [];
return settings.map((setting: { id: any; type: any }) => ({
id: setting.id,
type: setting.type,
}));
}

function getPresetSettingsKeys(presets: Preset.Preset[]) {
const allKeys: string[] = [];
for (const preset of presets) {
if (preset.settings) {
allKeys.push(...Object.keys(preset.settings));
}
}
return allKeys;
}

function getPresetBlockSettingsKeys(blocks: Preset.PresetBlocks) {
const allKeys: string[] = [];
for (const block of Object.values(blocks)) {
for (const [_, blockData] of Object.entries(block)) {
if (blockData && typeof blockData === 'object' && 'settings' in blockData) {
const settings = blockData.settings;
if (settings && typeof settings === 'object') {
allKeys.push(...Object.keys(settings));
}
}
}
}
return allKeys;
}

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

const schema = await getSchema();
if (!schema) return;
if (schema.validSchema instanceof Error) return;

const validSchema = schema.validSchema;
const settingsKeys = getInlineSettingsTypesAndKeys(validSchema.settings);
const presetSettingsKeys = getPresetSettingsKeys(validSchema.presets ?? []);

for (const key of presetSettingsKeys) {
if (!settingsKeys.some((setting) => setting.id === key)) {
context.report({
message: `Preset setting "${key}" does not exist in the block's settings`,
startIndex: 0,
endIndex: 0,
});
}
}

if (validSchema.blocks) {
for (const block of validSchema.blocks) {
const blockSchema = await context.getBlockSchema?.(block.type);
if (!blockSchema || blockSchema.validSchema instanceof Error) continue;

for (const preset of validSchema.presets ?? []) {
if (!preset.blocks) continue;
const presetBlockSettingKeys = getPresetBlockSettingsKeys(preset.blocks) ?? [];

for (const key of presetBlockSettingKeys) {
if (!blockSchema.validSchema.settings?.some((setting) => setting.id === key)) {
context.report({
message: `Preset setting "${key}" does not exist in the block type "${block.type}"'s settings`,
startIndex: 0,
endIndex: 0,
});
}
}
}
}
}
},
};
},
};
3 changes: 3 additions & 0 deletions packages/theme-check-node/configs/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ UnknownFilter:
UnusedAssign:
enabled: true
severity: 1
ValidBlockPresetSettings:
enabled: true
severity: 0
ValidBlockTarget:
enabled: true
severity: 0
Expand Down
3 changes: 3 additions & 0 deletions packages/theme-check-node/configs/recommended.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ UnusedAssign:
ValidBlockTarget:
enabled: true
severity: 0
ValidBlockPresetSettings:
enabled: true
severity: 0
ValidContentForArguments:
enabled: true
severity: 0
Expand Down

0 comments on commit 0a7932b

Please sign in to comment.