@platformos/platformos-check-common 0.0.6 → 0.0.8
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/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/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/index.js +2 -2
- 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 +20 -31
- 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 +7 -30
- 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 +2 -7
- package/dist/context-utils.js +39 -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/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 +4 -5
- package/dist/index.js +34 -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 +20 -28
- 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 +1 -1
- package/dist/liquid-doc/utils.js +4 -3
- package/dist/liquid-doc/utils.js.map +1 -1
- package/dist/path.d.ts +1 -0
- package/dist/path.js +5 -1
- package/dist/path.js.map +1 -1
- package/dist/test/MockApp.d.ts +16 -0
- 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-source-code.d.ts +4 -3
- 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 +18 -67
- package/dist/types.js +3 -5
- package/dist/types.js.map +1 -1
- package/dist/utils/file-utils.js +1 -2
- package/dist/utils/file-utils.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 +9 -9
- 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.ts +1 -1
- package/src/checks/graphql/index.ts +1 -1
- package/src/checks/img-width-and-height/index.ts +1 -1
- package/src/checks/index.ts +11 -80
- package/src/checks/invalid-hash-assign-target/index.spec.ts +14 -14
- package/src/checks/json-syntax-error/index.ts +1 -1
- 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/index.spec.ts +1 -6
- package/src/checks/liquid-html-syntax-error/index.ts +2 -2
- package/src/checks/matching-translations/index.spec.ts +89 -346
- package/src/checks/matching-translations/index.ts +24 -35
- package/src/checks/metadata-params/index.ts +5 -7
- 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 +6 -20
- 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 +3 -109
- package/src/checks/undefined-object/index.ts +8 -33
- 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 +1 -1
- 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 +1 -1
- 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 +39 -128
- package/src/disabled-checks/index.spec.ts +35 -0
- package/src/disabled-checks/index.ts +4 -2
- 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 +0 -1
- package/src/index.ts +33 -21
- package/src/jsonc/parse.ts +1 -1
- package/src/liquid-doc/arguments.spec.ts +19 -45
- package/src/liquid-doc/arguments.ts +26 -39
- package/src/liquid-doc/liquidDoc.ts +1 -2
- package/src/liquid-doc/utils.ts +4 -3
- package/src/path.ts +1 -0
- package/src/test/{MockTheme.ts → MockApp.ts} +1 -1
- package/src/test/MockFileSystem.ts +9 -6
- package/src/test/index.ts +1 -1
- package/src/test/test-helper.ts +29 -127
- package/src/to-source-code.ts +20 -1
- package/src/types/{theme-liquid-docs.ts → platformos-liquid-docs.ts} +8 -13
- package/src/types/schemas/index.ts +0 -2
- package/src/types.ts +21 -92
- package/src/utils/file-utils.ts +0 -1
- package/src/utils/index.ts +0 -1
- 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/section.ts +0 -86
- 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,65 +0,0 @@
|
|
|
1
|
-
import { NodeTypes, TextNode } from '@platformos/liquid-html-parser';
|
|
2
|
-
import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types';
|
|
3
|
-
import { ValuedHtmlAttribute, isAttr, isNodeOfType, isValuedHtmlAttribute } from '../utils';
|
|
4
|
-
|
|
5
|
-
function isPreload(attr: ValuedHtmlAttribute): boolean {
|
|
6
|
-
return (
|
|
7
|
-
isAttr(attr, 'rel') &&
|
|
8
|
-
attr.value.some((node) => node.type === NodeTypes.TextNode && node.value === 'preload')
|
|
9
|
-
);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const AssetPreload: LiquidCheckDefinition = {
|
|
13
|
-
meta: {
|
|
14
|
-
code: 'AssetPreload',
|
|
15
|
-
name: 'Prevent Manual Preloading of Assets',
|
|
16
|
-
docs: {
|
|
17
|
-
description:
|
|
18
|
-
'This check is aimed at discouraging the manual preloading of assets and encourages the use of appropriate Shopify filters.',
|
|
19
|
-
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/asset-preload',
|
|
20
|
-
recommended: true,
|
|
21
|
-
},
|
|
22
|
-
type: SourceCodeType.LiquidHtml,
|
|
23
|
-
severity: Severity.WARNING,
|
|
24
|
-
schema: {},
|
|
25
|
-
targets: [],
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
create(context) {
|
|
29
|
-
return {
|
|
30
|
-
async HtmlVoidElement(node) {
|
|
31
|
-
const preloadLinkAttr = node.attributes.find(
|
|
32
|
-
(attr) => isValuedHtmlAttribute(attr) && isPreload(attr),
|
|
33
|
-
) as ValuedHtmlAttribute | undefined;
|
|
34
|
-
|
|
35
|
-
if (node.name === 'link' && preloadLinkAttr) {
|
|
36
|
-
const asAttr: ValuedHtmlAttribute | undefined = node.attributes
|
|
37
|
-
.filter(isValuedHtmlAttribute)
|
|
38
|
-
.find((attr) => isAttr(attr, 'as'));
|
|
39
|
-
|
|
40
|
-
const assetType = asAttr?.value.find((node): node is TextNode =>
|
|
41
|
-
isNodeOfType(NodeTypes.TextNode, node),
|
|
42
|
-
)?.value;
|
|
43
|
-
|
|
44
|
-
let message = '';
|
|
45
|
-
|
|
46
|
-
if (assetType === 'style') {
|
|
47
|
-
message =
|
|
48
|
-
'For better performance, prefer using the preload argument of the stylesheet_tag filter';
|
|
49
|
-
} else if (assetType === 'image') {
|
|
50
|
-
message =
|
|
51
|
-
'For better performance, prefer using the preload argument of the image_tag filter';
|
|
52
|
-
} else {
|
|
53
|
-
message = 'For better performance, prefer using the preload_tag filter';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
context.report({
|
|
57
|
-
message,
|
|
58
|
-
startIndex: node.position.start,
|
|
59
|
-
endIndex: node.position.end,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { AssetSizeAppBlockCSS } from '.';
|
|
3
|
-
import { check, MockTheme } from '../../test';
|
|
4
|
-
|
|
5
|
-
describe('Module: AssetSizeAppBlockCSS', () => {
|
|
6
|
-
const extensionFiles: MockTheme = {
|
|
7
|
-
'assets/app.css': '* { color: green } ',
|
|
8
|
-
'blocks/app.liquid': `
|
|
9
|
-
{% schema %}
|
|
10
|
-
{
|
|
11
|
-
"stylesheet": "app.css"
|
|
12
|
-
}
|
|
13
|
-
{% endschema %}
|
|
14
|
-
`,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
it('should not report any offenses if CSS is smaller than threshold', async () => {
|
|
18
|
-
const offenses = await check(extensionFiles, [AssetSizeAppBlockCSS]);
|
|
19
|
-
|
|
20
|
-
expect(offenses).toHaveLength(0);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should report an offense if CSS is larger than threshold', async () => {
|
|
24
|
-
const offenses = await check(
|
|
25
|
-
extensionFiles,
|
|
26
|
-
[AssetSizeAppBlockCSS],
|
|
27
|
-
{},
|
|
28
|
-
{
|
|
29
|
-
AssetSizeAppBlockCSS: {
|
|
30
|
-
enabled: true,
|
|
31
|
-
thresholdInBytes: 1,
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
expect(offenses).toHaveLength(1);
|
|
37
|
-
expect(offenses[0]).toMatchObject({
|
|
38
|
-
message: `The file size for 'app.css' (19 B) exceeds the configured threshold (1 B)`,
|
|
39
|
-
uri: 'file:///blocks/app.liquid',
|
|
40
|
-
start: { index: 51 },
|
|
41
|
-
end: { index: 58 },
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should report an offense if the CSS file does not exist', async () => {
|
|
46
|
-
const extensionFiles: MockTheme = {
|
|
47
|
-
'blocks/app.liquid': `
|
|
48
|
-
{% schema %}
|
|
49
|
-
{
|
|
50
|
-
"stylesheet": "nonexistent.css"
|
|
51
|
-
}
|
|
52
|
-
{% endschema %}
|
|
53
|
-
`,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const offenses = await check(extensionFiles, [AssetSizeAppBlockCSS]);
|
|
57
|
-
|
|
58
|
-
expect(offenses).toHaveLength(1);
|
|
59
|
-
expect(offenses[0]).toMatchObject({
|
|
60
|
-
message: `'nonexistent.css' does not exist.`,
|
|
61
|
-
uri: 'file:///blocks/app.liquid',
|
|
62
|
-
start: { index: 57 },
|
|
63
|
-
end: { index: 72 },
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should reports offense if the CSS file does not exist and the asset has a trailing comma', async () => {
|
|
68
|
-
const extensionFiles: MockTheme = {
|
|
69
|
-
'blocks/app.liquid': `
|
|
70
|
-
{% schema %}
|
|
71
|
-
{
|
|
72
|
-
"stylesheet": "nonexistent.css",
|
|
73
|
-
}
|
|
74
|
-
{% endschema %}
|
|
75
|
-
`,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const offenses = await check(extensionFiles, [AssetSizeAppBlockCSS]);
|
|
79
|
-
|
|
80
|
-
expect(offenses).toHaveLength(1);
|
|
81
|
-
expect(offenses[0]).toMatchObject({
|
|
82
|
-
message: `'nonexistent.css' does not exist.`,
|
|
83
|
-
uri: 'file:///blocks/app.liquid',
|
|
84
|
-
start: { index: 57 },
|
|
85
|
-
end: { index: 72 },
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { parseJSON } from '../../json';
|
|
2
|
-
import {
|
|
3
|
-
ConfigTarget,
|
|
4
|
-
LiquidCheckDefinition,
|
|
5
|
-
SchemaProp,
|
|
6
|
-
Severity,
|
|
7
|
-
SourceCodeType,
|
|
8
|
-
} from '../../types';
|
|
9
|
-
import { isError } from '../../utils';
|
|
10
|
-
import { doesFileExceedThreshold, doesFileExist } from '../../utils/file-utils';
|
|
11
|
-
|
|
12
|
-
const schema = {
|
|
13
|
-
thresholdInBytes: SchemaProp.number(100000),
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const AssetSizeAppBlockCSS: LiquidCheckDefinition<typeof schema> = {
|
|
17
|
-
meta: {
|
|
18
|
-
code: 'AssetSizeAppBlockCSS',
|
|
19
|
-
name: 'Asset Size App Block CSS',
|
|
20
|
-
docs: {
|
|
21
|
-
description:
|
|
22
|
-
'This check is aimed at preventing large CSS bundles from being included via Theme App Extensions.',
|
|
23
|
-
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/asset-size-app-block-css',
|
|
24
|
-
recommended: true,
|
|
25
|
-
},
|
|
26
|
-
type: SourceCodeType.LiquidHtml,
|
|
27
|
-
severity: Severity.ERROR,
|
|
28
|
-
schema,
|
|
29
|
-
targets: [ConfigTarget.ThemeAppExtension],
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
create(context) {
|
|
33
|
-
if (!context.fileSize) {
|
|
34
|
-
return {};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
async LiquidRawTag(node) {
|
|
39
|
-
if (node.name !== 'schema') return;
|
|
40
|
-
const schema = parseJSON(node.body.value);
|
|
41
|
-
if (isError(schema)) return;
|
|
42
|
-
const stylesheet = schema.stylesheet;
|
|
43
|
-
if (!stylesheet) return;
|
|
44
|
-
|
|
45
|
-
const relativePath = `assets/${stylesheet}`;
|
|
46
|
-
const thresholdInBytes = context.settings.thresholdInBytes;
|
|
47
|
-
|
|
48
|
-
const startIndex = node.body.position.start + node.body.value.indexOf(stylesheet);
|
|
49
|
-
const endIndex = startIndex + stylesheet.length;
|
|
50
|
-
|
|
51
|
-
const fileExists = await doesFileExist(context, relativePath);
|
|
52
|
-
|
|
53
|
-
if (!fileExists) {
|
|
54
|
-
context.report({
|
|
55
|
-
message: `'${stylesheet}' does not exist.`,
|
|
56
|
-
startIndex: startIndex,
|
|
57
|
-
endIndex: endIndex,
|
|
58
|
-
});
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const [fileExceedsThreshold, fileSize] = await doesFileExceedThreshold(
|
|
63
|
-
context,
|
|
64
|
-
relativePath,
|
|
65
|
-
thresholdInBytes,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (fileExceedsThreshold) {
|
|
69
|
-
context.report({
|
|
70
|
-
message: `The file size for '${stylesheet}' (${fileSize} B) exceeds the configured threshold (${thresholdInBytes} B)`,
|
|
71
|
-
startIndex: startIndex,
|
|
72
|
-
endIndex: endIndex,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { AssetSizeAppBlockJavaScript } from '.';
|
|
3
|
-
import { check, MockTheme } from '../../test';
|
|
4
|
-
|
|
5
|
-
describe('Module: AssetSizeAppBlockJavaScript', () => {
|
|
6
|
-
const extensionFiles: MockTheme = {
|
|
7
|
-
'assets/app.js': 'console.log("Hello, world!");',
|
|
8
|
-
'blocks/app.liquid': `
|
|
9
|
-
{% schema %}
|
|
10
|
-
{
|
|
11
|
-
"javascript": "app.js"
|
|
12
|
-
}
|
|
13
|
-
{% endschema %}
|
|
14
|
-
`,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
it('should not report any offenses if JavaScript is smaller than threshold', async () => {
|
|
18
|
-
const offenses = await check(extensionFiles, [AssetSizeAppBlockJavaScript]);
|
|
19
|
-
|
|
20
|
-
expect(offenses).toHaveLength(0);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should report an offense if JavaScript is larger than threshold', async () => {
|
|
24
|
-
const offenses = await check(
|
|
25
|
-
extensionFiles,
|
|
26
|
-
[AssetSizeAppBlockJavaScript],
|
|
27
|
-
{},
|
|
28
|
-
{
|
|
29
|
-
AssetSizeAppBlockJavaScript: {
|
|
30
|
-
enabled: true,
|
|
31
|
-
thresholdInBytes: 1,
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
expect(offenses).toHaveLength(1);
|
|
37
|
-
expect(offenses[0]).toMatchObject({
|
|
38
|
-
message: `The file size for 'app.js' (29 B) exceeds the configured threshold (1 B)`,
|
|
39
|
-
uri: 'file:///blocks/app.liquid',
|
|
40
|
-
start: { index: 51 },
|
|
41
|
-
end: { index: 57 },
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should report an offense if the JavaScript file does not exist', async () => {
|
|
46
|
-
const extensionFiles: MockTheme = {
|
|
47
|
-
'blocks/app.liquid': `
|
|
48
|
-
{% schema %}
|
|
49
|
-
{
|
|
50
|
-
"javascript": "nonexistent.js"
|
|
51
|
-
}
|
|
52
|
-
{% endschema %}
|
|
53
|
-
`,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const offenses = await check(extensionFiles, [AssetSizeAppBlockJavaScript]);
|
|
57
|
-
|
|
58
|
-
expect(offenses).toHaveLength(1);
|
|
59
|
-
expect(offenses[0]).toMatchObject({
|
|
60
|
-
message: `'nonexistent.js' does not exist.`,
|
|
61
|
-
uri: 'file:///blocks/app.liquid',
|
|
62
|
-
start: { index: 57 },
|
|
63
|
-
end: { index: 71 },
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { parseJSON } from '../../json';
|
|
2
|
-
import {
|
|
3
|
-
ConfigTarget,
|
|
4
|
-
LiquidCheckDefinition,
|
|
5
|
-
SchemaProp,
|
|
6
|
-
Severity,
|
|
7
|
-
SourceCodeType,
|
|
8
|
-
} from '../../types';
|
|
9
|
-
import { isError } from '../../utils';
|
|
10
|
-
import { doesFileExist, doesFileExceedThreshold } from '../../utils/file-utils';
|
|
11
|
-
|
|
12
|
-
const schema = {
|
|
13
|
-
thresholdInBytes: SchemaProp.number(10000),
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const AssetSizeAppBlockJavaScript: LiquidCheckDefinition<typeof schema> = {
|
|
17
|
-
meta: {
|
|
18
|
-
code: 'AssetSizeAppBlockJavaScript',
|
|
19
|
-
name: 'Asset Size App Block JavaScript',
|
|
20
|
-
docs: {
|
|
21
|
-
description:
|
|
22
|
-
'This check is aimed at preventing large JavaScript bundles from being included via Theme App Extensions.',
|
|
23
|
-
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/asset-size-app-block-javascript',
|
|
24
|
-
recommended: true,
|
|
25
|
-
},
|
|
26
|
-
type: SourceCodeType.LiquidHtml,
|
|
27
|
-
severity: Severity.ERROR,
|
|
28
|
-
schema,
|
|
29
|
-
targets: [ConfigTarget.ThemeAppExtension],
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
create(context) {
|
|
33
|
-
if (!context.fileSize) {
|
|
34
|
-
return {};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
async LiquidRawTag(node) {
|
|
39
|
-
if (node.name !== 'schema') return;
|
|
40
|
-
const schema = parseJSON(node.body.value);
|
|
41
|
-
if (isError(schema)) return;
|
|
42
|
-
const javascript = schema.javascript;
|
|
43
|
-
if (!javascript) return;
|
|
44
|
-
|
|
45
|
-
const relativePath = `assets/${javascript}`;
|
|
46
|
-
const thresholdInBytes = context.settings.thresholdInBytes;
|
|
47
|
-
|
|
48
|
-
const startIndex = node.body.position.start + node.body.value.indexOf(javascript);
|
|
49
|
-
const endIndex = startIndex + javascript.length;
|
|
50
|
-
|
|
51
|
-
const fileExists = await doesFileExist(context, relativePath);
|
|
52
|
-
|
|
53
|
-
if (!fileExists) {
|
|
54
|
-
context.report({
|
|
55
|
-
message: `'${javascript}' does not exist.`,
|
|
56
|
-
startIndex: startIndex,
|
|
57
|
-
endIndex: endIndex,
|
|
58
|
-
});
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const [fileExceedsThreshold, fileSize] = await doesFileExceedThreshold(
|
|
63
|
-
context,
|
|
64
|
-
relativePath,
|
|
65
|
-
thresholdInBytes,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (fileExceedsThreshold) {
|
|
69
|
-
context.report({
|
|
70
|
-
message: `The file size for '${javascript}' (${fileSize} B) exceeds the configured threshold (${thresholdInBytes} B)`,
|
|
71
|
-
startIndex: startIndex,
|
|
72
|
-
endIndex: endIndex,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
};
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { vi, expect, describe, it, afterEach } from 'vitest';
|
|
2
|
-
import { AssetSizeCSS } from '.';
|
|
3
|
-
import { check, MockTheme } from '../../test';
|
|
4
|
-
import { SchemaProp } from '../../types';
|
|
5
|
-
import { hasRemoteAssetSizeExceededThreshold } from '../../utils/file-utils';
|
|
6
|
-
|
|
7
|
-
vi.mock('../../utils/file-utils', async (importOriginal) => {
|
|
8
|
-
const actual: any = await importOriginal();
|
|
9
|
-
return {
|
|
10
|
-
...actual,
|
|
11
|
-
hasRemoteAssetSizeExceededThreshold: vi.fn(),
|
|
12
|
-
};
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('Module: AssetSizeCSS', () => {
|
|
16
|
-
const extensionFiles: MockTheme = {
|
|
17
|
-
'assets/theme.css': '* { color: green !important; }',
|
|
18
|
-
'templates/index.liquid': `
|
|
19
|
-
<html>
|
|
20
|
-
<head>
|
|
21
|
-
<link href="{{ 'theme.css' | asset_url }}" rel="stylesheet">
|
|
22
|
-
</head>
|
|
23
|
-
</html>
|
|
24
|
-
`,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const httpTest: MockTheme = {
|
|
28
|
-
'templates/index.liquid': `
|
|
29
|
-
<html>
|
|
30
|
-
<head>
|
|
31
|
-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
32
|
-
</head>
|
|
33
|
-
</html>
|
|
34
|
-
`,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
afterEach(() => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should not report any offenses if CSS is smaller than threshold', async () => {
|
|
42
|
-
const offenses = await check(extensionFiles, [AssetSizeCSS]);
|
|
43
|
-
|
|
44
|
-
expect(offenses).toHaveLength(0);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should report an offense if CSS is larger than threshold', async () => {
|
|
48
|
-
const offenses = await check(
|
|
49
|
-
extensionFiles,
|
|
50
|
-
[AssetSizeCSS],
|
|
51
|
-
{},
|
|
52
|
-
{
|
|
53
|
-
AssetSizeCSS: {
|
|
54
|
-
enabled: true,
|
|
55
|
-
thresholdInBytes: 1,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
expect(offenses).toHaveLength(1);
|
|
61
|
-
expect(offenses[0]).toMatchObject({
|
|
62
|
-
message: 'The CSS file size exceeds the threshold of 1 bytes',
|
|
63
|
-
uri: 'file:///templates/index.liquid',
|
|
64
|
-
start: { index: 51 },
|
|
65
|
-
end: { index: 80 },
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should report a warning when the CSS file size exceeds the threshold', async () => {
|
|
70
|
-
vi.mocked(hasRemoteAssetSizeExceededThreshold).mockReturnValue(Promise.resolve(true));
|
|
71
|
-
const offenses = await check(
|
|
72
|
-
httpTest,
|
|
73
|
-
[AssetSizeCSS],
|
|
74
|
-
{},
|
|
75
|
-
{
|
|
76
|
-
AssetSizeCSS: {
|
|
77
|
-
enabled: true,
|
|
78
|
-
thresholdInBytes: 1,
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
expect(offenses).toHaveLength(1);
|
|
84
|
-
expect(offenses[0]).toMatchObject({
|
|
85
|
-
message: 'The CSS file size exceeds the threshold of 1 bytes',
|
|
86
|
-
uri: 'file:///templates/index.liquid',
|
|
87
|
-
start: { index: 51 },
|
|
88
|
-
end: { index: 122 },
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should not report any offenses if CSS is smaller than threshold 2', async () => {
|
|
93
|
-
vi.mocked(hasRemoteAssetSizeExceededThreshold).mockResolvedValue(false);
|
|
94
|
-
const extensionFiles: MockTheme = {
|
|
95
|
-
'assets/theme.css': 'console.log("hello world");',
|
|
96
|
-
'templates/index.liquid': `
|
|
97
|
-
<html>
|
|
98
|
-
<head>
|
|
99
|
-
{{ 'theme.css' | asset_url | stylesheet_tag }}
|
|
100
|
-
{{ "https://example.com" | stylesheet_tag }}
|
|
101
|
-
</head>
|
|
102
|
-
</html>
|
|
103
|
-
`,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const offenses = await check(extensionFiles, [AssetSizeCSS]);
|
|
107
|
-
|
|
108
|
-
expect(offenses).toHaveLength(0);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should report an offense if CSS is larger than threshold 2', async () => {
|
|
112
|
-
vi.mocked(hasRemoteAssetSizeExceededThreshold).mockResolvedValue(true);
|
|
113
|
-
const extensionFiles: MockTheme = {
|
|
114
|
-
'assets/theme.css': 'console.log("hello world");',
|
|
115
|
-
'templates/index.liquid': `
|
|
116
|
-
<html>
|
|
117
|
-
<head>
|
|
118
|
-
{{ 'theme.css' | asset_url | stylesheet_tag }}
|
|
119
|
-
{{ "https://example.com" | stylesheet_tag }}
|
|
120
|
-
</head>
|
|
121
|
-
</html>
|
|
122
|
-
`,
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const offenses = await check(
|
|
126
|
-
extensionFiles,
|
|
127
|
-
[AssetSizeCSS],
|
|
128
|
-
{},
|
|
129
|
-
{
|
|
130
|
-
AssetSizeCSS: {
|
|
131
|
-
enabled: true,
|
|
132
|
-
thresholdInBytes: 2,
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
expect(offenses).toHaveLength(2);
|
|
138
|
-
expect(offenses[0]).toMatchObject({
|
|
139
|
-
message: 'The CSS file size exceeds the threshold of 2 bytes',
|
|
140
|
-
uri: 'file:///templates/index.liquid',
|
|
141
|
-
start: { index: 48 },
|
|
142
|
-
end: { index: 89 },
|
|
143
|
-
});
|
|
144
|
-
expect(offenses[1]).toMatchObject({
|
|
145
|
-
message: 'The CSS file size exceeds the threshold of 2 bytes',
|
|
146
|
-
uri: 'file:///templates/index.liquid',
|
|
147
|
-
start: { index: 107 },
|
|
148
|
-
end: { index: 128 },
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should not report any offenses if there is no stylesheet', async () => {
|
|
153
|
-
const extensionFiles: MockTheme = {
|
|
154
|
-
'templates/index.liquid': `
|
|
155
|
-
<html>
|
|
156
|
-
<head>
|
|
157
|
-
</head>
|
|
158
|
-
</html>
|
|
159
|
-
`,
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const offenses = await check(extensionFiles, [AssetSizeCSS]);
|
|
163
|
-
|
|
164
|
-
expect(offenses).toHaveLength(0);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LiquidHtmlNode,
|
|
3
|
-
LiquidString,
|
|
4
|
-
LiquidVariable,
|
|
5
|
-
LiquidVariableOutput,
|
|
6
|
-
NodeTypes,
|
|
7
|
-
TextNode,
|
|
8
|
-
} from '@platformos/liquid-html-parser';
|
|
9
|
-
import { LiquidCheckDefinition, SchemaProp, Severity, SourceCodeType } from '../../types';
|
|
10
|
-
import { last } from '../../utils';
|
|
11
|
-
import {
|
|
12
|
-
hasRemoteAssetSizeExceededThreshold,
|
|
13
|
-
hasLocalAssetSizeExceededThreshold,
|
|
14
|
-
} from '../../utils/file-utils';
|
|
15
|
-
import {
|
|
16
|
-
ValuedHtmlAttribute,
|
|
17
|
-
isAttr,
|
|
18
|
-
isNodeOfType,
|
|
19
|
-
isValuedHtmlAttribute,
|
|
20
|
-
valueIncludes,
|
|
21
|
-
} from '../utils';
|
|
22
|
-
|
|
23
|
-
const schema = {
|
|
24
|
-
thresholdInBytes: SchemaProp.number(100000),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function isTextNode(node: LiquidHtmlNode): node is TextNode {
|
|
28
|
-
return node.type === NodeTypes.TextNode;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isLiquidVariableOutput(node: LiquidHtmlNode): node is LiquidVariableOutput {
|
|
32
|
-
return node.type === NodeTypes.LiquidVariableOutput;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isLiquidVariable(node: LiquidHtmlNode | string): node is LiquidVariable {
|
|
36
|
-
return typeof node !== 'string' && node.type === NodeTypes.LiquidVariable;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isString(node: LiquidHtmlNode): node is LiquidString {
|
|
40
|
-
return node.type === NodeTypes.String;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export const AssetSizeCSS: LiquidCheckDefinition<typeof schema> = {
|
|
44
|
-
meta: {
|
|
45
|
-
code: 'AssetSizeCSS',
|
|
46
|
-
aliases: ['AssetSizeCSSStylesheetTag'],
|
|
47
|
-
name: 'Prevent Large CSS bundles',
|
|
48
|
-
docs: {
|
|
49
|
-
description: 'This check is aimed at preventing large CSS bundles for speed.',
|
|
50
|
-
url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/asset-size-css',
|
|
51
|
-
recommended: false,
|
|
52
|
-
},
|
|
53
|
-
type: SourceCodeType.LiquidHtml,
|
|
54
|
-
severity: Severity.ERROR,
|
|
55
|
-
schema,
|
|
56
|
-
targets: [],
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
create(context) {
|
|
60
|
-
if (!context.fileSize) {
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const thresholdInBytes = context.settings.thresholdInBytes;
|
|
65
|
-
|
|
66
|
-
async function checkRemoteAssetSize(url: string, position: { start: number; end: number }) {
|
|
67
|
-
if (await hasRemoteAssetSizeExceededThreshold(url, thresholdInBytes)) {
|
|
68
|
-
context.report({
|
|
69
|
-
message: `The CSS file size exceeds the threshold of ${thresholdInBytes} bytes`,
|
|
70
|
-
startIndex: position.start,
|
|
71
|
-
endIndex: position.end,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function checkThemeAssetSize(srcValue: string, position: { start: number; end: number }) {
|
|
77
|
-
if (
|
|
78
|
-
await hasLocalAssetSizeExceededThreshold(context, `assets/${srcValue}`, thresholdInBytes)
|
|
79
|
-
) {
|
|
80
|
-
context.report({
|
|
81
|
-
message: `The CSS file size exceeds the threshold of ${thresholdInBytes} bytes`,
|
|
82
|
-
startIndex: position.start,
|
|
83
|
-
endIndex: position.end,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
async HtmlVoidElement(node) {
|
|
90
|
-
if (node.name !== 'link') return;
|
|
91
|
-
|
|
92
|
-
const relIsStylesheet = node.attributes
|
|
93
|
-
.filter(isValuedHtmlAttribute)
|
|
94
|
-
.find((attr) => isAttr(attr, 'rel') && valueIncludes(attr, 'stylesheet'));
|
|
95
|
-
if (!relIsStylesheet) return;
|
|
96
|
-
|
|
97
|
-
const href: ValuedHtmlAttribute | undefined = node.attributes
|
|
98
|
-
.filter(isValuedHtmlAttribute)
|
|
99
|
-
.find((attr) => isAttr(attr, 'href'));
|
|
100
|
-
if (!href) return;
|
|
101
|
-
if (href.value.length !== 1) return;
|
|
102
|
-
|
|
103
|
-
/* This ensures that the link entered is a text and not anything else like http//..{}
|
|
104
|
-
This also checks if the value starts with 'http://', 'https://' or '//' to ensure its a valid link. */
|
|
105
|
-
if (isTextNode(href.value[0]) && /(https?:)?\/\//.test(href.value[0].value)) {
|
|
106
|
-
const url = href.value[0].value;
|
|
107
|
-
await checkRemoteAssetSize(url, href.attributePosition);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/* This code checks if we have a link with a liquid variable
|
|
111
|
-
and that its a string with one filter, `asset_url`. This is done to ensure our .css link is
|
|
112
|
-
entered with a 'asset_url' to produce valid output. */
|
|
113
|
-
if (
|
|
114
|
-
isLiquidVariableOutput(href.value[0]) &&
|
|
115
|
-
isLiquidVariable(href.value[0].markup) &&
|
|
116
|
-
isString(href.value[0].markup.expression) &&
|
|
117
|
-
href.value[0].markup.filters.length === 1 &&
|
|
118
|
-
href.value[0].markup.filters[0].name === 'asset_url'
|
|
119
|
-
) {
|
|
120
|
-
const assetName = href.value[0].markup.expression.value;
|
|
121
|
-
await checkThemeAssetSize(assetName, href.attributePosition);
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
async LiquidFilter(node, ancestors) {
|
|
126
|
-
if (node.name !== 'stylesheet_tag') return;
|
|
127
|
-
|
|
128
|
-
const liquidVariableParent = last(ancestors);
|
|
129
|
-
|
|
130
|
-
if (!liquidVariableParent || !isNodeOfType(NodeTypes.LiquidVariable, liquidVariableParent))
|
|
131
|
-
return;
|
|
132
|
-
|
|
133
|
-
if (liquidVariableParent.expression.type !== NodeTypes.String) return;
|
|
134
|
-
|
|
135
|
-
/* This code ensures we have a liquid variable with 1 expression, 1 filter, and that it is a valid http link.
|
|
136
|
-
This is done to ensure a valid http link is entered with 1 filter being the `stylesheet_tag` for valid output. */
|
|
137
|
-
if (
|
|
138
|
-
liquidVariableParent.expression.value[0].length == 1 &&
|
|
139
|
-
liquidVariableParent.filters.length == 1 &&
|
|
140
|
-
/(https?:)?\/\//.test(liquidVariableParent.expression.value)
|
|
141
|
-
) {
|
|
142
|
-
const url = liquidVariableParent.expression.value;
|
|
143
|
-
await checkRemoteAssetSize(url, liquidVariableParent.expression.position);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/* This code ensures we have a liquid variable with 1 expression, 2 filters being asset_url and stylesheet_tag
|
|
147
|
-
This is done to ensure a .css file has the 'asset_url' and 'stylesheet_tag' to produce the appropriate output. */
|
|
148
|
-
if (
|
|
149
|
-
liquidVariableParent.expression.value[0].length == 1 &&
|
|
150
|
-
liquidVariableParent.filters.length == 2 &&
|
|
151
|
-
liquidVariableParent.filters[0].name === 'asset_url' &&
|
|
152
|
-
liquidVariableParent.filters[1].name === 'stylesheet_tag'
|
|
153
|
-
) {
|
|
154
|
-
const css = liquidVariableParent.expression.value;
|
|
155
|
-
await checkThemeAssetSize(css, liquidVariableParent.position);
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
},
|
|
160
|
-
};
|