diff --git a/.changeset/dirty-terms-refuse.md b/.changeset/dirty-terms-refuse.md
new file mode 100644
index 00000000..ce7b6757
--- /dev/null
+++ b/.changeset/dirty-terms-refuse.md
@@ -0,0 +1,6 @@
+---
+'@shopify/theme-check-common': minor
+'@shopify/theme-check-node': minor
+---
+
+Added MissingSchema theme check to identify missing schemas in theme app extensions.
diff --git a/packages/theme-check-common/src/checks/index.ts b/packages/theme-check-common/src/checks/index.ts
index a82f9d44..3a26c813 100644
--- a/packages/theme-check-common/src/checks/index.ts
+++ b/packages/theme-check-common/src/checks/index.ts
@@ -40,6 +40,7 @@ import { ValidSchema } from './valid-schema';
import { ValidSchemaName } from './valid-schema-name';
import { ValidStaticBlockType } from './valid-static-block-type';
import { VariableName } from './variable-name';
+import { MissingSchema } from './missing-schema';
export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [
AppBlockValidTags,
@@ -62,6 +63,7 @@ export const allChecks: (LiquidCheckDefinition | JSONCheckDefinition)[] = [
MatchingTranslations,
MissingAsset,
MissingTemplate,
+ MissingSchema,
PaginationSize,
ParserBlockingScript,
RemoteAsset,
diff --git a/packages/theme-check-common/src/checks/missing-schema/index.spec.ts b/packages/theme-check-common/src/checks/missing-schema/index.spec.ts
new file mode 100644
index 00000000..15c14531
--- /dev/null
+++ b/packages/theme-check-common/src/checks/missing-schema/index.spec.ts
@@ -0,0 +1,87 @@
+import { describe, expect, it } from 'vitest';
+import { runLiquidCheck } from '../../test';
+import { MissingSchema } from './index';
+import { Config } from '../../types';
+
+describe('MissingSchema', () => {
+ it('should report an error when schema tag is missing on a theme app extension', async () => {
+ const sourceCode = `
+
+ `;
+
+ const offenses = await runLiquidCheck(MissingSchema, sourceCode);
+ expect(offenses).to.have.lengthOf(1);
+ });
+
+ it('should not report when the schema is present on a theme app extension', async () => {
+ const sourceCode = `
+
+
+{% schema %}
+{
+ "name": "Footer",
+ "max_blocks": 8,
+ "blocks": [
+ {
+ "type": "link",
+ "name": "Link",
+ "settings": [
+ {
+ "id": "linkurl",
+ "type": "url",
+ "label": "URL link"
+ },
+ {
+ "id": "linktext",
+ "type": "text",
+ "label": "Link text"
+ }
+ ]
+ },
+ {
+ "type": "custom-text",
+ "name": "Custom Text",
+ "settings": [
+ {
+ "id": "custom-text-field",
+ "type": "textarea",
+ "label": "Text"
+ }
+ ]
+ }
+ ]
+}
+{% endschema %}`;
+
+ const offenses = await runLiquidCheck(MissingSchema, sourceCode);
+ expect(offenses).to.have.lengthOf(0);
+ });
+});
diff --git a/packages/theme-check-common/src/checks/missing-schema/index.ts b/packages/theme-check-common/src/checks/missing-schema/index.ts
new file mode 100644
index 00000000..a31c0fb1
--- /dev/null
+++ b/packages/theme-check-common/src/checks/missing-schema/index.ts
@@ -0,0 +1,37 @@
+import { ConfigTarget, LiquidCheckDefinition, Severity, SourceCodeType } from '../../types';
+
+export const MissingSchema: LiquidCheckDefinition = {
+ meta: {
+ code: 'MissingSchema',
+ name: 'Missing schema definitions in theme app extensions should be avoided',
+ docs: {
+ description: 'Report missing schema definitions in theme app extensions',
+ recommended: true,
+ url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/missing-schema',
+ },
+ severity: Severity.ERROR,
+ type: SourceCodeType.LiquidHtml,
+ schema: {},
+ targets: [ConfigTarget.ThemeAppExtension],
+ },
+
+ create(context) {
+ let foundSchema = false;
+
+ return {
+ async LiquidRawTag(node) {
+ if (node.name == 'schema') foundSchema = true;
+ },
+
+ async onCodePathEnd() {
+ if (!foundSchema) {
+ context.report({
+ message: `The schema does not exist`,
+ startIndex: 0,
+ endIndex: 0,
+ });
+ }
+ },
+ };
+ },
+};
diff --git a/packages/theme-check-node/configs/all.yml b/packages/theme-check-node/configs/all.yml
index 3d1ab2f9..c3291991 100644
--- a/packages/theme-check-node/configs/all.yml
+++ b/packages/theme-check-node/configs/all.yml
@@ -64,6 +64,9 @@ MatchingTranslations:
MissingAsset:
enabled: true
severity: 0
+MissingSchema:
+ enabled: false
+ severity: 0
MissingTemplate:
enabled: true
severity: 0
diff --git a/packages/theme-check-node/configs/theme-app-extension.yml b/packages/theme-check-node/configs/theme-app-extension.yml
index 9a3e99d5..bb0e66a9 100644
--- a/packages/theme-check-node/configs/theme-app-extension.yml
+++ b/packages/theme-check-node/configs/theme-app-extension.yml
@@ -16,6 +16,9 @@ AssetSizeAppBlockJavaScript:
enabled: true
severity: 0
thresholdInBytes: 10000
+MissingSchema:
+ enabled: true
+ severity: 0
RequiredLayoutThemeObject:
enabled: false
severity: 0