@platformos/platformos-check-common 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +4 -4
- package/dist/AugmentedPlatformOSDocset.d.ts +11 -0
- package/dist/AugmentedPlatformOSDocset.js +81 -0
- package/dist/AugmentedPlatformOSDocset.js.map +1 -0
- package/dist/JSONValidator.js +1 -1
- package/dist/JSONValidator.js.map +1 -1
- package/dist/checks/deprecated-filter/index.js +4 -41
- package/dist/checks/deprecated-filter/index.js.map +1 -1
- package/dist/checks/deprecated-tag/index.js +21 -22
- package/dist/checks/deprecated-tag/index.js.map +1 -1
- package/dist/checks/duplicate-function-arguments/index.js +1 -1
- package/dist/checks/duplicate-function-arguments/index.js.map +1 -1
- package/dist/checks/duplicate-render-partial-arguments/index.js +1 -1
- package/dist/checks/duplicate-render-partial-arguments/index.js.map +1 -1
- package/dist/checks/graphql/index.js +1 -1
- package/dist/checks/graphql/index.js.map +1 -1
- package/dist/checks/graphql-variables/index.js +4 -0
- package/dist/checks/graphql-variables/index.js.map +1 -1
- package/dist/checks/img-width-and-height/index.js +1 -1
- package/dist/checks/img-width-and-height/index.js.map +1 -1
- package/dist/checks/index.d.ts +3 -3
- package/dist/checks/index.js +4 -79
- package/dist/checks/index.js.map +1 -1
- package/dist/checks/json-syntax-error/index.js +1 -1
- package/dist/checks/json-syntax-error/index.js.map +1 -1
- package/dist/checks/liquid-html-syntax-error/checks/InvalidLoopArguments.js +1 -1
- package/dist/checks/liquid-html-syntax-error/checks/InvalidLoopArguments.js.map +1 -1
- package/dist/checks/liquid-html-syntax-error/checks/InvalidTagSyntax.d.ts +19 -0
- package/dist/checks/liquid-html-syntax-error/checks/InvalidTagSyntax.js +79 -0
- package/dist/checks/liquid-html-syntax-error/checks/InvalidTagSyntax.js.map +1 -0
- package/dist/checks/liquid-html-syntax-error/checks/UnknownTag.d.ts +3 -0
- package/dist/checks/liquid-html-syntax-error/checks/UnknownTag.js +32 -0
- package/dist/checks/liquid-html-syntax-error/checks/UnknownTag.js.map +1 -0
- package/dist/checks/liquid-html-syntax-error/index.js +23 -5
- package/dist/checks/liquid-html-syntax-error/index.js.map +1 -1
- package/dist/checks/matching-translations/index.d.ts +2 -2
- package/dist/checks/matching-translations/index.js +114 -90
- package/dist/checks/matching-translations/index.js.map +1 -1
- package/dist/checks/metadata-params/index.js +6 -3
- package/dist/checks/metadata-params/index.js.map +1 -1
- package/dist/checks/missing-asset/index.js +1 -1
- package/dist/checks/missing-asset/index.js.map +1 -1
- package/dist/checks/missing-partial/index.d.ts +6 -0
- package/dist/checks/missing-partial/index.js +70 -0
- package/dist/checks/missing-partial/index.js.map +1 -0
- package/dist/checks/orphaned-partial/index.js +4 -4
- package/dist/checks/orphaned-partial/index.js.map +1 -1
- package/dist/checks/parser-blocking-script/index.js +10 -36
- package/dist/checks/parser-blocking-script/index.js.map +1 -1
- package/dist/checks/parser-blocking-script/suggestions.d.ts +1 -2
- package/dist/checks/parser-blocking-script/suggestions.js +1 -11
- package/dist/checks/parser-blocking-script/suggestions.js.map +1 -1
- package/dist/checks/reserved-doc-param-names/index.js +6 -5
- package/dist/checks/reserved-doc-param-names/index.js.map +1 -1
- package/dist/checks/translation-key-exists/index.js +1 -1
- package/dist/checks/translation-key-exists/index.js.map +1 -1
- package/dist/checks/unclosed-html-element/index.js +5 -1
- package/dist/checks/unclosed-html-element/index.js.map +1 -1
- package/dist/checks/undefined-object/index.js +13 -31
- package/dist/checks/undefined-object/index.js.map +1 -1
- package/dist/checks/unique-doc-param-names/index.js +1 -1
- package/dist/checks/unique-doc-param-names/index.js.map +1 -1
- package/dist/checks/unknown-filter/index.js +3 -3
- package/dist/checks/unknown-filter/index.js.map +1 -1
- package/dist/checks/unknown-property/index.js +1 -1
- package/dist/checks/unknown-property/index.js.map +1 -1
- package/dist/checks/unrecognized-render-partial-arguments/index.js +2 -2
- package/dist/checks/unrecognized-render-partial-arguments/index.js.map +1 -1
- package/dist/checks/unused-assign/index.js +1 -1
- package/dist/checks/unused-assign/index.js.map +1 -1
- package/dist/checks/unused-doc-param/index.js +1 -1
- package/dist/checks/unused-doc-param/index.js.map +1 -1
- package/dist/checks/utils.js +1 -1
- package/dist/checks/utils.js.map +1 -1
- package/dist/checks/valid-content-for-arguments/index.js +1 -1
- package/dist/checks/valid-content-for-arguments/index.js.map +1 -1
- package/dist/checks/valid-doc-param-types/index.js +4 -4
- package/dist/checks/valid-doc-param-types/index.js.map +1 -1
- package/dist/checks/valid-html-translation/index.d.ts +2 -2
- package/dist/checks/valid-html-translation/index.js +4 -4
- package/dist/checks/valid-html-translation/index.js.map +1 -1
- package/dist/checks/valid-json/index.js +1 -1
- package/dist/checks/valid-json/index.js.map +1 -1
- package/dist/checks/valid-render-partial-argument-types/index.js +2 -2
- package/dist/checks/valid-render-partial-argument-types/index.js.map +1 -1
- package/dist/checks/variable-name/index.js +1 -1
- package/dist/checks/variable-name/index.js.map +1 -1
- package/dist/context-utils.d.ts +18 -7
- package/dist/context-utils.js +68 -109
- package/dist/context-utils.js.map +1 -1
- package/dist/disabled-checks/index.js +4 -2
- package/dist/disabled-checks/index.js.map +1 -1
- package/dist/doc-generator/DocBlockGenerator.d.ts +16 -0
- package/dist/doc-generator/DocBlockGenerator.js +464 -0
- package/dist/doc-generator/DocBlockGenerator.js.map +1 -0
- package/dist/doc-generator/index.d.ts +1 -0
- package/dist/doc-generator/index.js +6 -0
- package/dist/doc-generator/index.js.map +1 -0
- package/dist/find-root.d.ts +7 -10
- package/dist/find-root.js +10 -17
- package/dist/find-root.js.map +1 -1
- package/dist/fixes/autofix.d.ts +4 -4
- package/dist/fixes/autofix.js +2 -2
- package/dist/fixes/autofix.js.map +1 -1
- package/dist/fixes/correctors/index.js +4 -0
- package/dist/fixes/correctors/index.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +50 -17
- package/dist/index.js.map +1 -1
- package/dist/jsonc/parse.d.ts +1 -1
- package/dist/jsonc/parse.js +1 -1
- package/dist/liquid-doc/arguments.d.ts +7 -8
- package/dist/liquid-doc/arguments.js +28 -29
- package/dist/liquid-doc/arguments.js.map +1 -1
- package/dist/liquid-doc/liquidDoc.d.ts +1 -1
- package/dist/liquid-doc/liquidDoc.js.map +1 -1
- package/dist/liquid-doc/utils.d.ts +3 -3
- package/dist/liquid-doc/utils.js +14 -3
- package/dist/liquid-doc/utils.js.map +1 -1
- package/dist/path.d.ts +1 -0
- package/dist/path.js +16 -1
- package/dist/path.js.map +1 -1
- package/{src/test/MockTheme.ts → dist/test/MockApp.d.ts} +2 -3
- package/dist/test/MockApp.js +16 -0
- package/dist/test/MockApp.js.map +1 -0
- package/dist/test/MockFileSystem.d.ts +3 -3
- package/dist/test/MockFileSystem.js +6 -6
- package/dist/test/MockFileSystem.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +1 -1
- package/dist/test/index.js.map +1 -1
- package/dist/test/test-helper.d.ts +10 -9
- package/dist/test/test-helper.js +15 -106
- package/dist/test/test-helper.js.map +1 -1
- package/dist/to-schema.d.ts +1 -1
- package/dist/to-source-code.d.ts +3 -2
- package/dist/to-source-code.js +20 -0
- package/dist/to-source-code.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/platformos-liquid-docs.d.ts +128 -0
- package/dist/types/platformos-liquid-docs.js +3 -0
- package/dist/types/platformos-liquid-docs.js.map +1 -0
- package/dist/types/schemas/index.d.ts +0 -2
- package/dist/types/schemas/index.js.map +1 -1
- package/dist/types.d.ts +26 -67
- package/dist/types.js +3 -5
- package/dist/types.js.map +1 -1
- package/dist/utils/block.js.map +1 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/yaml/parse.d.ts +5 -0
- package/dist/yaml/parse.js +94 -0
- package/dist/yaml/parse.js.map +1 -0
- package/package.json +4 -3
- package/src/{AugmentedThemeDocset.spec.ts → AugmentedPlatformOSDocset.spec.ts} +47 -34
- package/src/AugmentedPlatformOSDocset.ts +89 -0
- package/src/JSONValidator.ts +1 -1
- package/src/checks/deprecated-filter/index.spec.ts +76 -248
- package/src/checks/deprecated-filter/index.ts +5 -53
- package/src/checks/deprecated-tag/index.spec.ts +85 -34
- package/src/checks/deprecated-tag/index.ts +27 -22
- package/src/checks/duplicate-function-arguments/index.ts +1 -1
- package/src/checks/duplicate-render-partial-arguments/index.spec.ts +12 -12
- package/src/checks/duplicate-render-partial-arguments/index.ts +1 -1
- package/src/checks/graphql/index.ts +1 -1
- package/src/checks/graphql-variables/index.spec.ts +95 -0
- package/src/checks/graphql-variables/index.ts +4 -0
- package/src/checks/img-width-and-height/index.ts +2 -2
- package/src/checks/index.ts +11 -80
- package/src/checks/invalid-hash-assign-target/index.spec.ts +27 -27
- package/src/checks/json-syntax-error/index.ts +2 -2
- package/src/checks/liquid-html-syntax-error/checks/InvalidBooleanExpression.spec.ts +0 -11
- package/src/checks/liquid-html-syntax-error/checks/InvalidLoopArguments.spec.ts +1 -2
- package/src/checks/liquid-html-syntax-error/checks/InvalidLoopArguments.ts +2 -2
- package/src/checks/liquid-html-syntax-error/checks/InvalidTagSyntax.spec.ts +259 -0
- package/src/checks/liquid-html-syntax-error/checks/InvalidTagSyntax.ts +89 -0
- package/src/checks/liquid-html-syntax-error/checks/UnknownTag.spec.ts +293 -0
- package/src/checks/liquid-html-syntax-error/checks/UnknownTag.ts +43 -0
- package/src/checks/liquid-html-syntax-error/index.spec.ts +1 -6
- package/src/checks/liquid-html-syntax-error/index.ts +26 -5
- package/src/checks/matching-translations/index.spec.ts +187 -354
- package/src/checks/matching-translations/index.ts +117 -107
- package/src/checks/metadata-params/index.ts +6 -8
- package/src/checks/missing-asset/index.ts +1 -1
- package/src/checks/{missing-template → missing-partial}/index.spec.ts +6 -6
- package/src/checks/{missing-template → missing-partial}/index.ts +12 -26
- package/src/checks/orphaned-partial/index.ts +3 -3
- package/src/checks/parser-blocking-script/index.spec.ts +0 -118
- package/src/checks/parser-blocking-script/index.ts +3 -33
- package/src/checks/parser-blocking-script/suggestions.ts +1 -28
- package/src/checks/translation-key-exists/index.ts +1 -1
- package/src/checks/unclosed-html-element/index.ts +5 -1
- package/src/checks/undefined-object/index.spec.ts +32 -111
- package/src/checks/undefined-object/index.ts +15 -34
- package/src/checks/unique-doc-param-names/index.ts +1 -1
- package/src/checks/unknown-filter/index.spec.ts +2 -2
- package/src/checks/unknown-filter/index.ts +3 -3
- package/src/checks/unknown-property/index.ts +1 -1
- package/src/checks/unrecognized-render-partial-arguments/index.spec.ts +5 -5
- package/src/checks/unrecognized-render-partial-arguments/index.ts +2 -5
- package/src/checks/unused-assign/index.spec.ts +0 -30
- package/src/checks/unused-assign/index.ts +2 -2
- package/src/checks/unused-doc-param/index.ts +1 -1
- package/src/checks/utils.ts +1 -1
- package/src/checks/valid-doc-param-types/index.ts +4 -4
- package/src/checks/valid-html-translation/index.spec.ts +42 -32
- package/src/checks/valid-html-translation/index.ts +7 -7
- package/src/checks/valid-json/index.ts +2 -2
- package/src/checks/valid-render-partial-argument-types/index.spec.ts +13 -13
- package/src/checks/valid-render-partial-argument-types/index.ts +2 -5
- package/src/checks/variable-name/index.ts +1 -1
- package/src/context-utils.spec.ts +49 -77
- package/src/context-utils.ts +81 -129
- package/src/disabled-checks/index.spec.ts +26 -26
- package/src/disabled-checks/index.ts +2 -2
- package/src/disabled-checks/test-checks.ts +4 -4
- package/src/find-root.ts +12 -22
- package/src/fixes/autofix.spec.ts +2 -2
- package/src/fixes/autofix.ts +4 -4
- package/src/fixes/correctors/index.ts +4 -0
- package/src/ignore.spec.ts +4 -5
- package/src/index.ts +51 -21
- package/src/jsonc/parse.ts +1 -1
- package/src/liquid-doc/arguments.spec.ts +19 -45
- package/src/liquid-doc/arguments.ts +35 -42
- package/src/liquid-doc/liquidDoc.spec.ts +1 -1
- package/src/liquid-doc/liquidDoc.ts +1 -2
- package/src/liquid-doc/utils.ts +17 -8
- package/src/path.ts +16 -0
- package/src/test/MockApp.ts +17 -0
- package/src/test/MockFileSystem.spec.ts +10 -11
- package/src/test/MockFileSystem.ts +6 -6
- package/src/test/contain-offense.spec.ts +11 -3
- package/src/test/index.ts +1 -1
- package/src/test/test-helper.ts +43 -145
- package/src/to-source-code.ts +20 -1
- package/src/types/{theme-liquid-docs.ts → platformos-liquid-docs.ts} +8 -13
- package/src/types.ts +29 -92
- package/src/utils/index.ts +0 -1
- package/src/visitor.spec.ts +2 -2
- package/src/yaml/parse.ts +111 -0
- package/src/AugmentedThemeDocset.ts +0 -137
- package/src/checks/app-block-missing-schema/index.spec.ts +0 -121
- package/src/checks/app-block-missing-schema/index.ts +0 -46
- package/src/checks/app-block-valid-tags/index.spec.ts +0 -96
- package/src/checks/app-block-valid-tags/index.ts +0 -54
- package/src/checks/asset-preload/index.spec.ts +0 -78
- package/src/checks/asset-preload/index.ts +0 -65
- package/src/checks/asset-size-app-block-css/index.spec.ts +0 -88
- package/src/checks/asset-size-app-block-css/index.ts +0 -78
- package/src/checks/asset-size-app-block-javascript/index.spec.ts +0 -66
- package/src/checks/asset-size-app-block-javascript/index.ts +0 -78
- package/src/checks/asset-size-css/index.spec.ts +0 -166
- package/src/checks/asset-size-css/index.ts +0 -160
- package/src/checks/asset-size-javascript/index.spec.ts +0 -184
- package/src/checks/asset-size-javascript/index.ts +0 -144
- package/src/checks/block-id-usage/index.spec.ts +0 -76
- package/src/checks/block-id-usage/index.ts +0 -72
- package/src/checks/cdn-preconnect/index.spec.ts +0 -40
- package/src/checks/cdn-preconnect/index.ts +0 -43
- package/src/checks/content-for-header-modification/index.spec.ts +0 -65
- package/src/checks/content-for-header-modification/index.ts +0 -72
- package/src/checks/deprecate-bgsizes/index.spec.ts +0 -41
- package/src/checks/deprecate-bgsizes/index.ts +0 -49
- package/src/checks/deprecate-lazysizes/index.spec.ts +0 -26
- package/src/checks/deprecate-lazysizes/index.ts +0 -58
- package/src/checks/deprecated-filter/fixes.ts +0 -264
- package/src/checks/deprecated-fonts-on-sections-and-blocks/deprecated-fonts-data.ts +0 -1343
- package/src/checks/deprecated-fonts-on-sections-and-blocks/index.spec.ts +0 -613
- package/src/checks/deprecated-fonts-on-sections-and-blocks/index.ts +0 -284
- package/src/checks/deprecated-fonts-on-settings-schema/index.spec.ts +0 -102
- package/src/checks/deprecated-fonts-on-settings-schema/index.ts +0 -66
- package/src/checks/duplicate-content-for-arguments/index.spec.ts +0 -98
- package/src/checks/duplicate-content-for-arguments/index.ts +0 -43
- package/src/checks/empty-block-content/index.spec.ts +0 -117
- package/src/checks/empty-block-content/index.ts +0 -60
- package/src/checks/hardcoded-routes/index.spec.ts +0 -58
- package/src/checks/hardcoded-routes/index.ts +0 -100
- package/src/checks/json-missing-block/index.spec.ts +0 -435
- package/src/checks/json-missing-block/index.ts +0 -56
- package/src/checks/json-missing-block/missing-block-utils.ts +0 -147
- package/src/checks/liquid-free-settings/index.spec.ts +0 -180
- package/src/checks/liquid-free-settings/index.ts +0 -79
- package/src/checks/missing-content-for-arguments/index.spec.ts +0 -144
- package/src/checks/missing-content-for-arguments/index.ts +0 -46
- package/src/checks/pagination-size/index.spec.ts +0 -158
- package/src/checks/pagination-size/index.ts +0 -104
- package/src/checks/remote-asset/index.spec.ts +0 -280
- package/src/checks/remote-asset/index.ts +0 -238
- package/src/checks/reserved-doc-param-names/index.spec.ts +0 -62
- package/src/checks/reserved-doc-param-names/index.ts +0 -57
- package/src/checks/schema-presets-block-order/index.spec.ts +0 -344
- package/src/checks/schema-presets-block-order/index.ts +0 -154
- package/src/checks/schema-presets-static-blocks/index.spec.ts +0 -145
- package/src/checks/schema-presets-static-blocks/index.ts +0 -126
- package/src/checks/static-stylesheet-and-javascript-tags/index.spec.ts +0 -257
- package/src/checks/static-stylesheet-and-javascript-tags/index.ts +0 -48
- package/src/checks/unique-settings-id/index.spec.ts +0 -24
- package/src/checks/unique-settings-id/index.ts +0 -84
- package/src/checks/unique-settings-id/test-data.ts +0 -1191
- package/src/checks/unique-static-block-id/index.spec.ts +0 -55
- package/src/checks/unique-static-block-id/index.ts +0 -60
- package/src/checks/unrecognized-content-for-arguments/index.spec.ts +0 -145
- package/src/checks/unrecognized-content-for-arguments/index.ts +0 -55
- package/src/checks/valid-block-target/index.spec.ts +0 -1396
- package/src/checks/valid-block-target/index.ts +0 -142
- package/src/checks/valid-content-for-argument-types/index.spec.ts +0 -382
- package/src/checks/valid-content-for-argument-types/index.ts +0 -42
- package/src/checks/valid-content-for-arguments/index.spec.ts +0 -107
- package/src/checks/valid-content-for-arguments/index.ts +0 -98
- package/src/checks/valid-local-blocks/index.spec.ts +0 -286
- package/src/checks/valid-local-blocks/index.ts +0 -100
- package/src/checks/valid-local-blocks/valid-block-utils.ts +0 -97
- package/src/checks/valid-schema/index.spec.ts +0 -174
- package/src/checks/valid-schema/index.ts +0 -41
- package/src/checks/valid-schema-name/index.spec.ts +0 -112
- package/src/checks/valid-schema-name/index.ts +0 -75
- package/src/checks/valid-settings-key/index.spec.ts +0 -321
- package/src/checks/valid-settings-key/index.ts +0 -144
- package/src/checks/valid-static-block-type/index.spec.ts +0 -38
- package/src/checks/valid-static-block-type/index.ts +0 -58
- package/src/checks/valid-visible-if/index.spec.ts +0 -619
- package/src/checks/valid-visible-if/index.ts +0 -184
- package/src/checks/valid-visible-if/visible-if-utils.ts +0 -158
- package/src/tags/content-for.ts +0 -25
- package/src/to-schema.ts +0 -231
- package/src/types/schemas/index.ts +0 -5
- package/src/types/schemas/preset.ts +0 -52
- package/src/types/schemas/section.ts +0 -86
- package/src/types/schemas/setting.ts +0 -320
- package/src/types/schemas/template.ts +0 -34
- package/src/types/schemas/theme-block.ts +0 -34
- package/src/types/theme-schemas.ts +0 -80
- package/src/utils/block.ts +0 -300
- package/src/utils/markup.ts +0 -10
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LiquidRawTag,
|
|
3
|
-
LiquidTag,
|
|
4
|
-
LiquidVariableLookup,
|
|
5
|
-
NodeTypes,
|
|
6
|
-
} from '@platformos/liquid-html-parser';
|
|
7
|
-
import { LiquidCheckDefinition, SchemaProp, Severity, SourceCodeType } from '../../types';
|
|
8
|
-
import { isError, last } from '../../utils';
|
|
9
|
-
import { isNodeOfType } from '../utils';
|
|
10
|
-
import { parseJSON } from '../../json';
|
|
11
|
-
|
|
12
|
-
const schema = {
|
|
13
|
-
minSize: SchemaProp.number(1),
|
|
14
|
-
maxSize: SchemaProp.number(250),
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const PaginationSize: LiquidCheckDefinition<typeof schema> = {
|
|
18
|
-
meta: {
|
|
19
|
-
code: 'PaginationSize',
|
|
20
|
-
name: 'Ensure paginate tags are used with performant sizes',
|
|
21
|
-
docs: {
|
|
22
|
-
description: 'This check is aimed at keeping response times low.',
|
|
23
|
-
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/pagination-size',
|
|
24
|
-
recommended: true,
|
|
25
|
-
},
|
|
26
|
-
type: SourceCodeType.LiquidHtml,
|
|
27
|
-
severity: Severity.WARNING,
|
|
28
|
-
schema,
|
|
29
|
-
targets: [],
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
create(context) {
|
|
33
|
-
const minSize = context.settings.minSize;
|
|
34
|
-
const maxSize = context.settings.maxSize;
|
|
35
|
-
let schemaSettings: any[] = [];
|
|
36
|
-
const pageSizeLookups: LiquidVariableLookup[] = [];
|
|
37
|
-
|
|
38
|
-
function checkPageSize(
|
|
39
|
-
pageSizeNode: any,
|
|
40
|
-
value: number,
|
|
41
|
-
message: string = `Pagination size must be a positive integer between ${minSize} and ${maxSize}.`,
|
|
42
|
-
) {
|
|
43
|
-
if (minSize <= value && value <= maxSize) return;
|
|
44
|
-
context.report({
|
|
45
|
-
message,
|
|
46
|
-
startIndex: pageSizeNode.position.start,
|
|
47
|
-
endIndex: pageSizeNode.position.end,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
async LiquidTag(node: LiquidTag) {
|
|
53
|
-
if (typeof node.markup === 'string' || node.name !== 'paginate') return;
|
|
54
|
-
|
|
55
|
-
const pageSizeNode = node.markup.pageSize;
|
|
56
|
-
|
|
57
|
-
if (isNodeOfType(NodeTypes.VariableLookup, pageSizeNode)) {
|
|
58
|
-
pageSizeLookups.push(pageSizeNode);
|
|
59
|
-
} else if (isNodeOfType(NodeTypes.Number, pageSizeNode)) {
|
|
60
|
-
checkPageSize(pageSizeNode, Number(pageSizeNode.value));
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
async LiquidRawTag(node: LiquidRawTag) {
|
|
65
|
-
if (node.name === 'schema') {
|
|
66
|
-
const schema = parseJSON(node.body.value);
|
|
67
|
-
if (isError(schema)) return;
|
|
68
|
-
if (schema.settings && Array.isArray(schema.settings)) {
|
|
69
|
-
schemaSettings = schema.settings;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
async onCodePathEnd() {
|
|
75
|
-
pageSizeLookups.forEach((pageSizeVariableLookup) => {
|
|
76
|
-
// Kind of assumes that you're using settings of some sort.
|
|
77
|
-
const lastLookup = last(pageSizeVariableLookup.lookups);
|
|
78
|
-
if (lastLookup === undefined) return;
|
|
79
|
-
if (lastLookup.type !== NodeTypes.String) return;
|
|
80
|
-
|
|
81
|
-
const settingId = lastLookup.value;
|
|
82
|
-
const setting = schemaSettings.find((setting) => setting.id === settingId);
|
|
83
|
-
|
|
84
|
-
if (setting === undefined) return;
|
|
85
|
-
|
|
86
|
-
if (setting.default === undefined) {
|
|
87
|
-
context.report({
|
|
88
|
-
message: `Default pagination size should be defined in the section settings.`,
|
|
89
|
-
startIndex: pageSizeVariableLookup.position.start,
|
|
90
|
-
endIndex: pageSizeVariableLookup.position.end,
|
|
91
|
-
});
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
checkPageSize(
|
|
96
|
-
pageSizeVariableLookup,
|
|
97
|
-
setting.default,
|
|
98
|
-
`This setting's default value should be between ${minSize} and ${maxSize} but is currently ${setting.default}.`,
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
},
|
|
104
|
-
};
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { runLiquidCheck, highlightedOffenses, check, MockTheme } from '../../test';
|
|
3
|
-
import { RemoteAsset } from './index';
|
|
4
|
-
|
|
5
|
-
describe('Module: RemoteAsset', () => {
|
|
6
|
-
it('should report an offense when asset_url or img_url filters are not used', async () => {
|
|
7
|
-
const sourceCode = `
|
|
8
|
-
<img src="{{ 'image.png' }}" />
|
|
9
|
-
<link href="{{ 'style.css' }}" />
|
|
10
|
-
<script src="{{ 'script.js' }}" defer="defer"></script>
|
|
11
|
-
{{ url | img_tag }}
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
15
|
-
expect(offenses).to.have.length(4);
|
|
16
|
-
|
|
17
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
18
|
-
expect(highlights).to.eql([
|
|
19
|
-
'src="{{ \'image.png\' }}"',
|
|
20
|
-
'href="{{ \'style.css\' }}"',
|
|
21
|
-
'src="{{ \'script.js\' }}"',
|
|
22
|
-
'url | img_tag',
|
|
23
|
-
]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should not report an offense when asset_url or img_url filters are used', async () => {
|
|
27
|
-
const sourceCode = `
|
|
28
|
-
<img src="{{ 'image.png' | asset_url }}" />
|
|
29
|
-
<link href="{{ 'style.css' | img_url }}" />
|
|
30
|
-
<script src="{{ 'script.js' | img_url }}" defer="defer"></script>
|
|
31
|
-
{{ url | img_tag | asset_url }},
|
|
32
|
-
{{ 'bootstrap.min.css' | asset_url | stylesheet_tag }}
|
|
33
|
-
{{ product | image_url: width: 450 | img_tag }}
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
37
|
-
expect(offenses).to.be.empty;
|
|
38
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
39
|
-
expect(highlights).to.be.empty;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should not report an offense for links, videos, iframes', async () => {
|
|
43
|
-
const sourceCode = `
|
|
44
|
-
<iframe
|
|
45
|
-
id="inlineFrameExample"
|
|
46
|
-
title="Inline Frame Example"
|
|
47
|
-
width="300"
|
|
48
|
-
height="200"
|
|
49
|
-
src="https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik"
|
|
50
|
-
>
|
|
51
|
-
</iframe>
|
|
52
|
-
<a href="https://google.com"></a>
|
|
53
|
-
<embed type="video/webm" src="https://google.com/..." width="250" height="200">
|
|
54
|
-
`;
|
|
55
|
-
|
|
56
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
57
|
-
expect(offenses).to.be.empty;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should not report an offense when asset_url or img_url filters are used with other filters', async () => {
|
|
61
|
-
const sourceCode = `
|
|
62
|
-
<img src="{{ 'image.png' | asset_url | img_url }}" />
|
|
63
|
-
<link href="{{ 'style.css' | img_url | asset_url }}" />
|
|
64
|
-
<script src="{{ 'script.js' | img_url | asset_url}}" defer="defer"></script>
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
68
|
-
expect(offenses).to.be.empty;
|
|
69
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
70
|
-
expect(highlights).to.be.empty;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should not report an offense for non-asset elements', async () => {
|
|
74
|
-
const sourceCode = `
|
|
75
|
-
<div class="{{ 'my-class' }}"></div>
|
|
76
|
-
<a href="{{ 'page.html' }}">Link</a>
|
|
77
|
-
`;
|
|
78
|
-
|
|
79
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
80
|
-
expect(offenses).to.be.empty;
|
|
81
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
82
|
-
expect(highlights).to.be.empty;
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should not report an offense when asset_url filters are used with deprecated assets', async () => {
|
|
86
|
-
const sourceCode = `
|
|
87
|
-
{{ product.featured_image | product_img_url }}
|
|
88
|
-
{{ article.image | article_img_url }}
|
|
89
|
-
{{ collection.image | collection_img_url }}
|
|
90
|
-
{{ 'image.png' | img_url }}
|
|
91
|
-
`;
|
|
92
|
-
|
|
93
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
94
|
-
expect(offenses).to.be.empty;
|
|
95
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
96
|
-
expect(highlights).to.be.empty;
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should not report an offense when asset_url filters are used with non deprecated assets', async () => {
|
|
100
|
-
const sourceCode = `
|
|
101
|
-
{{ 'option_selection.js' | shopify_asset_url }}
|
|
102
|
-
{{ 'lightbox.js' | global_asset_url | script_tag }}
|
|
103
|
-
{{ 'disclaimer.pdf' | file_url }}
|
|
104
|
-
{{ 'potions-header.png' | file_img_url: 'large' }}
|
|
105
|
-
{{ 'cart.js' | asset_url }}
|
|
106
|
-
{{ 'red-and-black-bramble-berries.jpg' | asset_img_url }}
|
|
107
|
-
`;
|
|
108
|
-
|
|
109
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
110
|
-
expect(offenses).to.be.empty;
|
|
111
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
112
|
-
expect(highlights).to.be.empty;
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should report the correct message and index for a single offense', async () => {
|
|
116
|
-
const sourceCode = `<img src="{{ 'image.png' }}" />`;
|
|
117
|
-
|
|
118
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
119
|
-
expect(offenses).to.have.length(1);
|
|
120
|
-
|
|
121
|
-
const offense = offenses[0];
|
|
122
|
-
expect(offense.message).to.equal(
|
|
123
|
-
'Use one of the asset_url filters to serve assets for better performance.',
|
|
124
|
-
);
|
|
125
|
-
expect(offense.start.index).to.equal(5);
|
|
126
|
-
expect(offense.end.index).to.equal(28);
|
|
127
|
-
|
|
128
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
129
|
-
expect(highlights).to.eql(['src="{{ \'image.png\' }}"']);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should not report an offense when asset_url filters are used with known assets', async () => {
|
|
133
|
-
const sourceCode = `<link rel="canonical" href="{{ canonical_url }}">
|
|
134
|
-
<link href={{ canonical_url }}
|
|
135
|
-
{{ 'example.js' | canonical_url }}`;
|
|
136
|
-
|
|
137
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
138
|
-
expect(offenses).to.be.empty;
|
|
139
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
140
|
-
expect(highlights).to.be.empty;
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should report an offense for scripts from remote domains', async () => {
|
|
144
|
-
const sourceCode = `<script src="https://example.com/jquery.js" defer></script>`;
|
|
145
|
-
|
|
146
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
147
|
-
expect(offenses).to.have.length(1);
|
|
148
|
-
expect(offenses[0].message).to.equal(
|
|
149
|
-
'Asset should be served by the Shopify CDN for better performance.',
|
|
150
|
-
);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should report an offense for remote stylesheets', async () => {
|
|
154
|
-
const sourceCode = `
|
|
155
|
-
<link href="https://example.com/bootstrap.css" rel="stylesheet">
|
|
156
|
-
<link href="{{ "https://example.com/bootstrap.css" | replace: 'bootstrap', 'tailwind' }}" rel="stylesheet">
|
|
157
|
-
<link href=“https://thisisbad.com/styles.css“>
|
|
158
|
-
`;
|
|
159
|
-
|
|
160
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
161
|
-
|
|
162
|
-
expect(offenses).to.have.length(3);
|
|
163
|
-
offenses.forEach((offense) => {
|
|
164
|
-
expect(offense.message).to.equal(
|
|
165
|
-
'Asset should be served by the Shopify CDN for better performance.',
|
|
166
|
-
);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should report an offense when a non shopify cdn is used as a liquid filter and when a asset_url filter is not used.', async () => {
|
|
171
|
-
const sourceCode = `
|
|
172
|
-
{{ "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" | stylesheet_tag }}
|
|
173
|
-
`;
|
|
174
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
175
|
-
expect(offenses).to.have.length(2);
|
|
176
|
-
|
|
177
|
-
expect(offenses[0].message).to.equal(
|
|
178
|
-
'Asset should be served by the Shopify CDN for better performance.',
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
expect(offenses[1].message).to.equal(
|
|
182
|
-
'Use one of the asset_url filters to serve assets for better performance.',
|
|
183
|
-
);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('should report an offense for image drops without img_url filter', async () => {
|
|
187
|
-
const sourceCode = `
|
|
188
|
-
<img src="{{ image }}">
|
|
189
|
-
<img src="{{ image.src }}">
|
|
190
|
-
`;
|
|
191
|
-
|
|
192
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
193
|
-
expect(offenses).to.have.length(2);
|
|
194
|
-
offenses.forEach((offense) => {
|
|
195
|
-
expect(offense.message).to.equal(
|
|
196
|
-
'Use one of the asset_url filters to serve assets for better performance.',
|
|
197
|
-
);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should not report an offence if url is a shopify CDN', async () => {
|
|
202
|
-
const sourceCode = `
|
|
203
|
-
<link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin>
|
|
204
|
-
<link id="ModelViewerStyle" rel="stylesheet" href="https://cdn.shopify.com/shopifycloud/model-viewer-ui/assets/v1.0/model-viewer-ui.css" media="print" onload="this.media='all'">
|
|
205
|
-
<script id="hot-reload-client" src="/cdn/shopifycloud/theme-hot-reload/theme-hot-reload.js" defer></script>
|
|
206
|
-
`;
|
|
207
|
-
|
|
208
|
-
const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
|
|
209
|
-
expect(offenses).to.be.empty;
|
|
210
|
-
const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
|
|
211
|
-
expect(highlights).to.be.empty;
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('should report an offense if url is not listed in allowedDomains', async () => {
|
|
215
|
-
const themeFiles: MockTheme = {
|
|
216
|
-
'layout/theme.liquid': `
|
|
217
|
-
<script src="https://domain.com" defer></script>
|
|
218
|
-
`,
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const offenses = await check(
|
|
222
|
-
themeFiles,
|
|
223
|
-
[RemoteAsset],
|
|
224
|
-
{},
|
|
225
|
-
{
|
|
226
|
-
RemoteAsset: {
|
|
227
|
-
enabled: true,
|
|
228
|
-
allowedDomains: ['someotherdomain.com'],
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
expect(offenses).to.have.length(1);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should report an offense if the url in the config is malformed/missing protocol', async () => {
|
|
237
|
-
const themeFiles: MockTheme = {
|
|
238
|
-
'layout/theme.liquid': `
|
|
239
|
-
<script src="https://domain.com" defer></script>
|
|
240
|
-
<script src="https://www.domain.com" defer></script>
|
|
241
|
-
`,
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const offenses = await check(
|
|
245
|
-
themeFiles,
|
|
246
|
-
[RemoteAsset],
|
|
247
|
-
{},
|
|
248
|
-
{
|
|
249
|
-
RemoteAsset: {
|
|
250
|
-
enabled: true,
|
|
251
|
-
allowedDomains: ['www.domain.com', 'domain.com'],
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
expect(offenses).to.have.length(2);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('should not report an offense if url is listed in allowedDomains', async () => {
|
|
260
|
-
const themeFiles: MockTheme = {
|
|
261
|
-
'layout/theme.liquid': `
|
|
262
|
-
<script src="https://domain.com" defer></script>
|
|
263
|
-
`,
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const offenses = await check(
|
|
267
|
-
themeFiles,
|
|
268
|
-
[RemoteAsset],
|
|
269
|
-
{},
|
|
270
|
-
{
|
|
271
|
-
RemoteAsset: {
|
|
272
|
-
enabled: true,
|
|
273
|
-
allowedDomains: ['https://domain.com', 'http://domain.com', 'https://www.domain.com'],
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
expect(offenses).to.be.empty;
|
|
279
|
-
});
|
|
280
|
-
});
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HtmlRawNode,
|
|
3
|
-
HtmlVoidElement,
|
|
4
|
-
TextNode,
|
|
5
|
-
LiquidVariable,
|
|
6
|
-
LiquidVariableOutput,
|
|
7
|
-
} from '@platformos/liquid-html-parser';
|
|
8
|
-
import {
|
|
9
|
-
LiquidHtmlNodeTypes as NodeTypes,
|
|
10
|
-
LiquidHtmlNodeOfType as NodeOfType,
|
|
11
|
-
Severity,
|
|
12
|
-
SourceCodeType,
|
|
13
|
-
LiquidCheckDefinition,
|
|
14
|
-
LiquidHtmlNode,
|
|
15
|
-
SchemaProp,
|
|
16
|
-
} from '../../types';
|
|
17
|
-
import { isAttr, isValuedHtmlAttribute, isNodeOfType, ValuedHtmlAttribute } from '../utils';
|
|
18
|
-
import { last } from '../../utils';
|
|
19
|
-
|
|
20
|
-
const RESOURCE_TAGS = ['img', 'link', 'source', 'script'];
|
|
21
|
-
const SHOPIFY_CDN_DOMAINS = ['fonts.shopifycdn.com', 'cdn.shopify.com'];
|
|
22
|
-
const TAGNAMES = ['stylesheet_tag', 'script_tag', 'image_tag', 'img_tag'];
|
|
23
|
-
const DEPRECATED = ['product_img_url', 'article_img_url', 'collection_img_url', 'img_url'];
|
|
24
|
-
|
|
25
|
-
const NON_DEPRECATED = [
|
|
26
|
-
'asset_url',
|
|
27
|
-
'image_url',
|
|
28
|
-
'asset_img_url',
|
|
29
|
-
'file_img_url',
|
|
30
|
-
'file_url',
|
|
31
|
-
'global_asset_url',
|
|
32
|
-
'shopify_asset_url',
|
|
33
|
-
'external_video_url',
|
|
34
|
-
'font_url',
|
|
35
|
-
];
|
|
36
|
-
const LIQUID_OBJECT = 'canonical_url';
|
|
37
|
-
|
|
38
|
-
function isLiquidVariableOutput(node: LiquidHtmlNode): node is LiquidVariableOutput {
|
|
39
|
-
return node.type === NodeTypes.LiquidVariableOutput;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isLiquidVariable(node: LiquidHtmlNode | string): node is LiquidVariable {
|
|
43
|
-
return typeof node !== 'string' && node.type === NodeTypes.LiquidVariable;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function isUrlHostedbyShopify(url: string, allowedDomains: string[] = []): boolean {
|
|
47
|
-
if (/^\/cdn\//.test(url)) {
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
const urlObj = new URL(url);
|
|
52
|
-
return [...SHOPIFY_CDN_DOMAINS, ...allowedDomains].includes(urlObj.hostname);
|
|
53
|
-
} catch (_error) {
|
|
54
|
-
// Return false for any invalid URLs (missing protocol, malformed URLs, invalid characters etc.)
|
|
55
|
-
// Since we're validating if URLs are Shopify-hosted, any invalid URL should return false
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function valueIsDefinitelyNotShopifyHosted(
|
|
61
|
-
attr: ValuedHtmlAttribute,
|
|
62
|
-
allowedDomains: string[] = [],
|
|
63
|
-
): boolean {
|
|
64
|
-
return attr.value.some((node) => {
|
|
65
|
-
if (node.type === NodeTypes.TextNode && /^(https?:)?\/\//.test(node.value)) {
|
|
66
|
-
if (!isUrlHostedbyShopify(node.value, allowedDomains)) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (isLiquidVariableOutput(node)) {
|
|
72
|
-
const variable = node.markup;
|
|
73
|
-
if (isLiquidVariable(variable)) {
|
|
74
|
-
const expression = variable.expression;
|
|
75
|
-
if (expression.type === NodeTypes.String && /^https?:\/\//.test(expression.value)) {
|
|
76
|
-
if (!isUrlHostedbyShopify(expression.value, allowedDomains)) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return false;
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function valueIsShopifyHosted(attr: ValuedHtmlAttribute): boolean {
|
|
87
|
-
const ASSET_URL_FILTER_NAMES = [...DEPRECATED, ...NON_DEPRECATED];
|
|
88
|
-
const ASSET_URL_OBJECT_NAMES = [LIQUID_OBJECT];
|
|
89
|
-
|
|
90
|
-
return attr.value.some((node) => {
|
|
91
|
-
if (!isLiquidVariableOutput(node)) return false;
|
|
92
|
-
if (!isLiquidVariable(node.markup)) return false;
|
|
93
|
-
|
|
94
|
-
const includesFilter = node.markup.filters.some((filter) =>
|
|
95
|
-
ASSET_URL_FILTER_NAMES.includes(filter.name),
|
|
96
|
-
);
|
|
97
|
-
if (includesFilter) return true;
|
|
98
|
-
|
|
99
|
-
if (isNodeOfType(NodeTypes.VariableLookup, node.markup.expression)) {
|
|
100
|
-
if (
|
|
101
|
-
node.markup.expression.name
|
|
102
|
-
? ASSET_URL_OBJECT_NAMES.includes(node.markup.expression.name)
|
|
103
|
-
: false
|
|
104
|
-
)
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return false;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Takes a list of allowed domains, and normalises them into an expected domain: www.domain.com -> domain.com for equality checks.
|
|
113
|
-
function normaliseAllowedDomains(allowedDomains: string[]): string[] {
|
|
114
|
-
return allowedDomains
|
|
115
|
-
.map((domain) => {
|
|
116
|
-
try {
|
|
117
|
-
const url = new URL(domain);
|
|
118
|
-
// Hostname can still return www. from https://www.domain.com we want it to be https://www.domain.com -> domain.com
|
|
119
|
-
return url.hostname.replace(/^www\./, '');
|
|
120
|
-
} catch (_error) {
|
|
121
|
-
// we shouldn't return the malformed domain - should be strict and stick to web standards (new URL validation).
|
|
122
|
-
return undefined;
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
|
-
.filter((domain): domain is string => domain !== undefined);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const schema = {
|
|
129
|
-
allowedDomains: SchemaProp.array(SchemaProp.string()).optional(),
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export const RemoteAsset: LiquidCheckDefinition<typeof schema> = {
|
|
133
|
-
meta: {
|
|
134
|
-
code: 'RemoteAsset',
|
|
135
|
-
aliases: ['AssetUrlFilters'],
|
|
136
|
-
name: 'Remote Asset',
|
|
137
|
-
docs: {
|
|
138
|
-
description: 'This check is aimed at eliminating unnecessary HTTP connections.',
|
|
139
|
-
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/remote-asset',
|
|
140
|
-
recommended: true,
|
|
141
|
-
},
|
|
142
|
-
type: SourceCodeType.LiquidHtml,
|
|
143
|
-
severity: Severity.WARNING,
|
|
144
|
-
schema,
|
|
145
|
-
targets: [],
|
|
146
|
-
},
|
|
147
|
-
|
|
148
|
-
create(context) {
|
|
149
|
-
const allowedDomains = normaliseAllowedDomains(context.settings.allowedDomains || []);
|
|
150
|
-
|
|
151
|
-
function checkHtmlNode(node: HtmlVoidElement | HtmlRawNode) {
|
|
152
|
-
if (!RESOURCE_TAGS.includes(node.name)) return;
|
|
153
|
-
|
|
154
|
-
const urlAttribute: ValuedHtmlAttribute | undefined = node.attributes
|
|
155
|
-
.filter(isValuedHtmlAttribute)
|
|
156
|
-
.find((attr: ValuedHtmlAttribute) => isAttr(attr, 'src') || isAttr(attr, 'href'));
|
|
157
|
-
|
|
158
|
-
if (!urlAttribute) return;
|
|
159
|
-
|
|
160
|
-
const isShopifyUrl = urlAttribute.value
|
|
161
|
-
.filter((node): node is TextNode => node.type === NodeTypes.TextNode)
|
|
162
|
-
.some((textNode) => isUrlHostedbyShopify(textNode.value, allowedDomains));
|
|
163
|
-
|
|
164
|
-
if (isShopifyUrl) return;
|
|
165
|
-
|
|
166
|
-
const hasDefinitelyARemoteAssetUrl = valueIsDefinitelyNotShopifyHosted(
|
|
167
|
-
urlAttribute,
|
|
168
|
-
allowedDomains,
|
|
169
|
-
);
|
|
170
|
-
if (hasDefinitelyARemoteAssetUrl) {
|
|
171
|
-
context.report({
|
|
172
|
-
message: 'Asset should be served by the Shopify CDN for better performance.',
|
|
173
|
-
startIndex: urlAttribute.position.start,
|
|
174
|
-
endIndex: urlAttribute.position.end,
|
|
175
|
-
});
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const hasShopifyHostedValue = valueIsShopifyHosted(urlAttribute);
|
|
180
|
-
if (hasShopifyHostedValue) return;
|
|
181
|
-
|
|
182
|
-
context.report({
|
|
183
|
-
message: 'Use one of the asset_url filters to serve assets for better performance.',
|
|
184
|
-
startIndex: urlAttribute.position.start,
|
|
185
|
-
endIndex: urlAttribute.position.end,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function checkLiquidFilter(
|
|
190
|
-
node: NodeOfType<NodeTypes.LiquidFilter>,
|
|
191
|
-
ancestors: LiquidHtmlNode[],
|
|
192
|
-
) {
|
|
193
|
-
const tagName = node.name;
|
|
194
|
-
|
|
195
|
-
if (!TAGNAMES.includes(tagName)) return;
|
|
196
|
-
|
|
197
|
-
const parentNode = last(ancestors);
|
|
198
|
-
if (!parentNode || !isNodeOfType(NodeTypes.LiquidVariable, parentNode)) return;
|
|
199
|
-
|
|
200
|
-
const hasAsset = parentNode.filters.some(
|
|
201
|
-
(filter: { name: string }) =>
|
|
202
|
-
DEPRECATED.includes(filter.name) || NON_DEPRECATED.includes(filter.name),
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
if (hasAsset) return;
|
|
206
|
-
|
|
207
|
-
const urlNode = parentNode.expression;
|
|
208
|
-
if (
|
|
209
|
-
urlNode.type === NodeTypes.String &&
|
|
210
|
-
!isUrlHostedbyShopify(urlNode.value, allowedDomains)
|
|
211
|
-
) {
|
|
212
|
-
context.report({
|
|
213
|
-
message: 'Asset should be served by the Shopify CDN for better performance.',
|
|
214
|
-
startIndex: urlNode.position.start,
|
|
215
|
-
endIndex: urlNode.position.end,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
context.report({
|
|
220
|
-
message: `Use one of the asset_url filters to serve assets for better performance.`,
|
|
221
|
-
startIndex: parentNode.expression.position.start,
|
|
222
|
-
endIndex: node.position.end,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
async HtmlVoidElement(node) {
|
|
228
|
-
checkHtmlNode(node);
|
|
229
|
-
},
|
|
230
|
-
async HtmlRawNode(node) {
|
|
231
|
-
checkHtmlNode(node);
|
|
232
|
-
},
|
|
233
|
-
async LiquidFilter(node, ancestors) {
|
|
234
|
-
checkLiquidFilter(node, ancestors);
|
|
235
|
-
},
|
|
236
|
-
};
|
|
237
|
-
},
|
|
238
|
-
};
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { expect, describe, it } from 'vitest';
|
|
2
|
-
import { ReservedDocParamNames } from './index';
|
|
3
|
-
import { runLiquidCheck } from '../../test';
|
|
4
|
-
|
|
5
|
-
describe('Module: ReservedDocParamNames', () => {
|
|
6
|
-
describe('block file', () => {
|
|
7
|
-
it(`should not report an error when no doc params share names with reserved content_for tag params`, async () => {
|
|
8
|
-
const sourceCode = `
|
|
9
|
-
{% doc %}
|
|
10
|
-
@param param1 - Example param
|
|
11
|
-
{% enddoc %}
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
const offenses = await runLiquidCheck(
|
|
15
|
-
ReservedDocParamNames,
|
|
16
|
-
sourceCode,
|
|
17
|
-
'blocks/file.liquid',
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
expect(offenses).to.be.empty;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should report an error when a doc param shares names with reserved content_for tag params', async () => {
|
|
24
|
-
const sourceCode = `
|
|
25
|
-
{% doc %}
|
|
26
|
-
@param param1 - Example param
|
|
27
|
-
@param id - Example param
|
|
28
|
-
{% enddoc %}
|
|
29
|
-
`;
|
|
30
|
-
|
|
31
|
-
const offenses = await runLiquidCheck(
|
|
32
|
-
ReservedDocParamNames,
|
|
33
|
-
sourceCode,
|
|
34
|
-
'blocks/file.liquid',
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
expect(offenses).to.have.length(1);
|
|
38
|
-
expect(offenses[0].message).to.contain(
|
|
39
|
-
`The parameter name is not supported because it's a reserved argument for 'content_for' tags.`,
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('partial file', () => {
|
|
45
|
-
it('should not report an error when a doc param shares names with reserved content_for tag params', async () => {
|
|
46
|
-
const sourceCode = `
|
|
47
|
-
{% doc %}
|
|
48
|
-
@param param1 - Example param
|
|
49
|
-
@param id - Example param
|
|
50
|
-
{% enddoc %}
|
|
51
|
-
`;
|
|
52
|
-
|
|
53
|
-
const offenses = await runLiquidCheck(
|
|
54
|
-
ReservedDocParamNames,
|
|
55
|
-
sourceCode,
|
|
56
|
-
'app/views/partials/file.liquid',
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
expect(offenses).to.have.length(0);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|