@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,174 +1,184 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
YAMLCheckDefinition,
|
|
3
3
|
JSONNode,
|
|
4
|
-
|
|
4
|
+
YAMLSourceCode,
|
|
5
5
|
Severity,
|
|
6
6
|
SourceCodeType,
|
|
7
7
|
PropertyNode,
|
|
8
|
+
ObjectNode,
|
|
8
9
|
} from '../../types';
|
|
9
10
|
|
|
10
11
|
const PLURALIZATION_KEYS = new Set(['zero', 'one', 'two', 'few', 'many', 'other']);
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Returns the locale declared in a YAML translation file by reading its first
|
|
15
|
+
* top-level key (e.g. `en`, `pt-BR`, `fr`). platformOS determines a file's
|
|
16
|
+
* locale from content, not from its path.
|
|
17
|
+
*/
|
|
18
|
+
function getLocaleFromAst(ast: JSONNode | Error): string | null {
|
|
19
|
+
if (ast instanceof Error) return null;
|
|
20
|
+
if (ast.type !== 'Object') return null;
|
|
21
|
+
const firstProp = (ast as ObjectNode).children[0];
|
|
22
|
+
if (!firstProp || firstProp.type !== 'Property') return null;
|
|
23
|
+
return firstProp.key.value || null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Extracts the translations base directory from a relative file path.
|
|
28
|
+
*
|
|
29
|
+
* e.g. `app/translations/pt-BR.yml` → `app/translations`
|
|
30
|
+
* `app/translations/pt-BR/validation.yml` → `app/translations`
|
|
31
|
+
* `modules/x/public/translations/en.yml` → `modules/x/public/translations`
|
|
32
|
+
*
|
|
33
|
+
* Returns `null` if the path doesn't contain a `/translations/` segment.
|
|
34
|
+
*/
|
|
35
|
+
function getTranslationRelativeBase(relativePath: string): string | null {
|
|
36
|
+
const idx = relativePath.lastIndexOf('/translations/');
|
|
37
|
+
if (idx === -1) return null;
|
|
38
|
+
return relativePath.substring(0, idx + '/translations'.length);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const MatchingTranslations: YAMLCheckDefinition = {
|
|
13
42
|
meta: {
|
|
14
43
|
code: 'MatchingTranslations',
|
|
15
44
|
name: 'Translation files should have the same keys',
|
|
16
45
|
docs: {
|
|
17
46
|
description: 'TODO',
|
|
18
47
|
recommended: true,
|
|
19
|
-
url: 'https://
|
|
48
|
+
url: 'https://documentation.platformos.com/developer-guide/platformos-check/checks/matching-translations',
|
|
20
49
|
},
|
|
21
|
-
type: SourceCodeType.
|
|
50
|
+
type: SourceCodeType.YAML,
|
|
22
51
|
severity: Severity.ERROR,
|
|
23
52
|
schema: {},
|
|
24
53
|
targets: [],
|
|
25
54
|
},
|
|
26
55
|
|
|
27
56
|
create(context) {
|
|
28
|
-
// State
|
|
29
|
-
const
|
|
30
|
-
const
|
|
57
|
+
// ── State ──────────────────────────────────────────────────────────────
|
|
58
|
+
const enTranslations = new Set<string>(); // keys present in the en scope
|
|
59
|
+
const missingFromLocale = new Set<string>(); // en keys absent from the entire locale scope
|
|
31
60
|
const nodesByPath = new Map<string, PropertyNode>();
|
|
32
|
-
|
|
61
|
+
|
|
62
|
+
const file = context.file as YAMLSourceCode;
|
|
33
63
|
const fileUri = file.uri;
|
|
34
64
|
const relativePath = context.toRelativePath(fileUri);
|
|
35
65
|
const ast = file.ast;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
66
|
+
|
|
67
|
+
// ── Guard: only lint translation files ────────────────────────────────
|
|
68
|
+
const isTranslationFile = relativePath.includes('/translations/');
|
|
69
|
+
|
|
70
|
+
// The locale is always the first top-level key in the YAML file (e.g. `en`,
|
|
71
|
+
// `pt-BR`). platformOS resolves locale from content, not from the file path.
|
|
72
|
+
const locale = getLocaleFromAst(ast);
|
|
73
|
+
|
|
74
|
+
if (!isTranslationFile || !locale || locale === 'en' || ast instanceof Error) {
|
|
44
75
|
return {};
|
|
45
76
|
}
|
|
46
77
|
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
78
|
+
// ── Derive scope (translation base URI) ──────────────────────────────
|
|
79
|
+
const relativeBase = getTranslationRelativeBase(relativePath);
|
|
80
|
+
if (!relativeBase) return {};
|
|
81
|
+
|
|
82
|
+
const translationBaseUri = context.toUri(relativeBase);
|
|
52
83
|
|
|
53
|
-
|
|
54
|
-
|
|
84
|
+
// A "primary" locale file is the top-level `{locale}.yml` (not inside a
|
|
85
|
+
// locale sub-directory like `pt-BR/`). Only the primary file reports
|
|
86
|
+
// missing translations to avoid duplicate offenses across split files.
|
|
87
|
+
const pathAfterBase = relativePath.substring(relativeBase.length + 1);
|
|
88
|
+
const isPrimaryLocaleFile = !pathAfterBase.includes('/');
|
|
55
89
|
|
|
90
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
91
|
+
const isTerminalNode = ({ type }: JSONNode) => type === 'Literal';
|
|
92
|
+
const isPluralizationNode = (node: PropertyNode) => PLURALIZATION_KEYS.has(node.key.value);
|
|
56
93
|
const isPluralizationPath = (path: string) =>
|
|
57
94
|
[...PLURALIZATION_KEYS].some((key) => path.endsWith(key));
|
|
58
95
|
|
|
59
|
-
const
|
|
60
|
-
const
|
|
96
|
+
const countCommonParts = (a: string[], b: string[]): number => {
|
|
97
|
+
const min = Math.min(a.length, b.length);
|
|
98
|
+
for (let i = 0; i < min; i++) if (a[i] !== b[i]) return i;
|
|
99
|
+
return min;
|
|
100
|
+
};
|
|
61
101
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
const closestTranslationKey = (key: string) => {
|
|
103
|
+
const keyParts = key.split('.');
|
|
104
|
+
let closest = '';
|
|
105
|
+
let max = 0;
|
|
106
|
+
for (const path of nodesByPath.keys()) {
|
|
107
|
+
const common = countCommonParts(path.split('.'), keyParts);
|
|
108
|
+
if (common > max) {
|
|
109
|
+
max = common;
|
|
110
|
+
closest = path;
|
|
65
111
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const childPaths = jsonPaths(childJson);
|
|
69
|
-
|
|
70
|
-
return acc.concat(childPaths.map((path) => `${key}.${path}`));
|
|
71
|
-
}, []);
|
|
112
|
+
}
|
|
113
|
+
return nodesByPath.get(closest) ?? ast;
|
|
72
114
|
};
|
|
73
115
|
|
|
116
|
+
// Strip the locale prefix (first Property in the ancestors chain).
|
|
117
|
+
// YAML files wrap content under a locale key: { en: { hello: 'Hello' } }
|
|
118
|
+
// We want paths like 'hello', not 'en.hello'.
|
|
74
119
|
const objectPath = (nodes: JSONNode[]) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
120
|
+
const props = nodes.filter((n): n is PropertyNode => n.type === 'Property');
|
|
121
|
+
if (props.length <= 1) return '';
|
|
122
|
+
return props
|
|
123
|
+
.slice(1)
|
|
124
|
+
.map((p) => p.key.value)
|
|
78
125
|
.join('.');
|
|
79
126
|
};
|
|
80
127
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return i;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return minLength;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const closestTranslationKey = (translationKey: string) => {
|
|
94
|
-
const translationKeyParts = translationKey.split('.');
|
|
95
|
-
let closestMatch = '';
|
|
96
|
-
let maxCommonParts = 0;
|
|
97
|
-
|
|
98
|
-
for (const path of nodesByPath.keys()) {
|
|
99
|
-
const pathParts = path.split('.');
|
|
100
|
-
const commonParts = countCommonParts(pathParts, translationKeyParts);
|
|
101
|
-
|
|
102
|
-
if (commonParts > maxCommonParts) {
|
|
103
|
-
maxCommonParts = commonParts;
|
|
104
|
-
closestMatch = path;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return nodesByPath.get(closestMatch) ?? ast;
|
|
109
|
-
};
|
|
128
|
+
const jsonPaths = (json: any): string[] =>
|
|
129
|
+
Object.keys(json).reduce((acc: string[], key: string) => {
|
|
130
|
+
if (typeof json[key] !== 'object') return acc.concat(key);
|
|
131
|
+
return acc.concat(jsonPaths(json[key]).map((p) => `${key}.${p}`));
|
|
132
|
+
}, []);
|
|
110
133
|
|
|
111
134
|
return {
|
|
112
135
|
async onCodePathStart() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
|
|
136
|
+
// Aggregate ALL en translations in this scope (en.yml + en/*.yml)
|
|
137
|
+
const en = await context.getTranslationsForBase(translationBaseUri, 'en');
|
|
138
|
+
jsonPaths(en).forEach(Set.prototype.add, enTranslations);
|
|
139
|
+
|
|
140
|
+
if (!isPrimaryLocaleFile) return;
|
|
141
|
+
|
|
142
|
+
// For the primary locale file: pre-compute which en keys are absent
|
|
143
|
+
// from the entire locale scope (locale.yml + locale/*.yml).
|
|
144
|
+
const localeAgg = await context.getTranslationsForBase(translationBaseUri, locale);
|
|
145
|
+
const localeKeys = new Set(jsonPaths(localeAgg));
|
|
146
|
+
for (const key of enTranslations) {
|
|
147
|
+
if (!localeKeys.has(key)) missingFromLocale.add(key);
|
|
148
|
+
}
|
|
122
149
|
},
|
|
123
150
|
|
|
124
151
|
async Property(node, ancestors) {
|
|
125
152
|
const path = objectPath(ancestors.concat(node));
|
|
153
|
+
if (!path) return;
|
|
126
154
|
|
|
127
155
|
nodesByPath.set(path, node);
|
|
128
156
|
|
|
129
|
-
if (!hasDefaultTranslations()) return;
|
|
130
157
|
if (isPluralizationNode(node)) return;
|
|
131
158
|
if (!isTerminalNode(node.value)) return;
|
|
132
|
-
if (
|
|
159
|
+
if (!enTranslations.size) return; // no en reference — skip
|
|
133
160
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
if (!enTranslations.has(path)) {
|
|
162
|
+
context.report({
|
|
163
|
+
message: `A translation for '${path}' does not exist in the en locale`,
|
|
164
|
+
startIndex: node.loc!.start.offset,
|
|
165
|
+
endIndex: node.loc!.end.offset,
|
|
166
|
+
});
|
|
139
167
|
}
|
|
140
|
-
|
|
141
|
-
context.report({
|
|
142
|
-
message: `A default translation for '${path}' does not exist`,
|
|
143
|
-
startIndex: node.loc!.start.offset,
|
|
144
|
-
endIndex: node.loc!.end.offset,
|
|
145
|
-
suggest: [
|
|
146
|
-
{
|
|
147
|
-
message: 'Delete unneeded translation key',
|
|
148
|
-
fix(corrector) {
|
|
149
|
-
corrector.remove(path);
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
});
|
|
154
168
|
},
|
|
155
169
|
|
|
156
170
|
async onCodePathEnd() {
|
|
157
|
-
|
|
158
|
-
const closest = closestTranslationKey(path);
|
|
159
|
-
|
|
160
|
-
if (isPluralizationPath(path)) return;
|
|
161
|
-
if (isShopifyPath(path)) return;
|
|
171
|
+
if (!isPrimaryLocaleFile) return;
|
|
162
172
|
|
|
173
|
+
for (const key of missingFromLocale) {
|
|
174
|
+
if (isPluralizationPath(key)) continue;
|
|
175
|
+
const closest = closestTranslationKey(key);
|
|
163
176
|
context.report({
|
|
164
|
-
message: `The translation for '${
|
|
177
|
+
message: `The translation for '${key}' is missing`,
|
|
165
178
|
startIndex: closest.loc!.start.offset,
|
|
166
179
|
endIndex: closest.loc!.end.offset,
|
|
167
|
-
fix(corrector) {
|
|
168
|
-
corrector.add(path, 'TODO');
|
|
169
|
-
},
|
|
170
180
|
});
|
|
171
|
-
}
|
|
181
|
+
}
|
|
172
182
|
},
|
|
173
183
|
};
|
|
174
184
|
},
|
|
@@ -3,7 +3,6 @@ import yaml from 'js-yaml';
|
|
|
3
3
|
import { DocumentsLocator, DocumentType } from '@platformos/platformos-common';
|
|
4
4
|
import { URI } from 'vscode-uri';
|
|
5
5
|
import { LiquidNamedArgument, Position } from '@platformos/liquid-html-parser';
|
|
6
|
-
import { getLiquidDocParams } from '../../liquid-doc/arguments';
|
|
7
6
|
import { relative } from '../../path';
|
|
8
7
|
|
|
9
8
|
type Metadata = {
|
|
@@ -64,12 +63,11 @@ export const MetadataParamsCheck: LiquidCheckDefinition = {
|
|
|
64
63
|
}
|
|
65
64
|
let params = extractMetadataParams(await context.fs.readFile(locatedFile));
|
|
66
65
|
if (!params) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (!liquidDocParameters) return;
|
|
66
|
+
if (!context.getDocDefinition) return;
|
|
67
|
+
const relativePath = relative(locatedFile, context.config.rootUri);
|
|
68
|
+
const docDef = await context.getDocDefinition(relativePath);
|
|
69
|
+
if (!docDef?.liquidDoc?.parameters) return;
|
|
70
|
+
const liquidDocParameters = new Map(docDef.liquidDoc.parameters.map((p) => [p.name, p]));
|
|
73
71
|
|
|
74
72
|
params = Array.from(liquidDocParameters.values())
|
|
75
73
|
.filter((p) => p.required)
|
|
@@ -99,7 +97,7 @@ export const MetadataParamsCheck: LiquidCheckDefinition = {
|
|
|
99
97
|
|
|
100
98
|
return {
|
|
101
99
|
async RenderMarkup(node) {
|
|
102
|
-
const targetFile = 'value' in node.
|
|
100
|
+
const targetFile = 'value' in node.partial ? node.partial.value : node.partial.name;
|
|
103
101
|
if (!targetFile) {
|
|
104
102
|
return;
|
|
105
103
|
}
|
|
@@ -11,7 +11,7 @@ export const MissingAsset: LiquidCheckDefinition = {
|
|
|
11
11
|
docs: {
|
|
12
12
|
description: 'Reports missing asset files',
|
|
13
13
|
recommended: true,
|
|
14
|
-
url: 'https://
|
|
14
|
+
url: 'https://documentation.platformos.com/developer-guide/platformos-check/checks/missing-asset',
|
|
15
15
|
},
|
|
16
16
|
type: SourceCodeType.LiquidHtml,
|
|
17
17
|
severity: Severity.ERROR,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { expect, describe, it } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { MissingPartial } from '.';
|
|
3
3
|
import { check } from '../../test';
|
|
4
4
|
|
|
5
|
-
describe('Module:
|
|
6
|
-
it('should report missing
|
|
5
|
+
describe('Module: MissingPartial', () => {
|
|
6
|
+
it('should report missing partial errors', async () => {
|
|
7
7
|
const testCases = [
|
|
8
8
|
{
|
|
9
9
|
testCase: 'should report the missing partial to be rendered with "render"',
|
|
@@ -12,7 +12,7 @@ describe('Module: MissingTemplate', () => {
|
|
|
12
12
|
{% render myvariable %}
|
|
13
13
|
`,
|
|
14
14
|
expected: {
|
|
15
|
-
check: '
|
|
15
|
+
check: 'MissingPartial',
|
|
16
16
|
end: {
|
|
17
17
|
character: 27,
|
|
18
18
|
index: 28,
|
|
@@ -49,11 +49,11 @@ describe('Module: MissingTemplate', () => {
|
|
|
49
49
|
},
|
|
50
50
|
];
|
|
51
51
|
for (const { testCase, file, expected, filesWith } of testCases) {
|
|
52
|
-
const offenses = await check(filesWith(file), [
|
|
52
|
+
const offenses = await check(filesWith(file), [MissingPartial]);
|
|
53
53
|
|
|
54
54
|
expect(offenses).to.have.length(1);
|
|
55
55
|
expect(offenses, testCase).to.containOffense({
|
|
56
|
-
check:
|
|
56
|
+
check: MissingPartial.meta.code,
|
|
57
57
|
...expected,
|
|
58
58
|
});
|
|
59
59
|
}
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
LiquidTagNamed,
|
|
4
|
-
NamedTags,
|
|
5
|
-
NodeTypes,
|
|
6
|
-
Position,
|
|
7
|
-
} from '@platformos/liquid-html-parser';
|
|
8
|
-
import { minimatch } from 'minimatch';
|
|
9
|
-
import {
|
|
10
|
-
LiquidCheckDefinition,
|
|
11
|
-
RelativePath,
|
|
12
|
-
SchemaProp,
|
|
13
|
-
Severity,
|
|
14
|
-
SourceCodeType,
|
|
15
|
-
} from '../../types';
|
|
16
|
-
import { doesFileExist } from '../../utils/file-utils';
|
|
1
|
+
import { NodeTypes } from '@platformos/liquid-html-parser';
|
|
2
|
+
import { LiquidCheckDefinition, SchemaProp, Severity, SourceCodeType } from '../../types';
|
|
17
3
|
import { DocumentsLocator } from '@platformos/platformos-common';
|
|
18
4
|
import { URI } from 'vscode-uri';
|
|
19
5
|
|
|
@@ -21,14 +7,14 @@ const schema = {
|
|
|
21
7
|
ignoreMissing: SchemaProp.array(SchemaProp.string(), []),
|
|
22
8
|
};
|
|
23
9
|
|
|
24
|
-
export const
|
|
10
|
+
export const MissingPartial: LiquidCheckDefinition<typeof schema> = {
|
|
25
11
|
meta: {
|
|
26
|
-
code: '
|
|
27
|
-
name: 'Avoid rendering missing
|
|
12
|
+
code: 'MissingPartial',
|
|
13
|
+
name: 'Avoid rendering missing partials',
|
|
28
14
|
docs: {
|
|
29
15
|
description: 'Reports missing partial liquid file',
|
|
30
16
|
recommended: true,
|
|
31
|
-
url: 'https://
|
|
17
|
+
url: 'https://documentation.platformos.com/developer-guide/platformos-check/checks/missing-partial',
|
|
32
18
|
},
|
|
33
19
|
type: SourceCodeType.LiquidHtml,
|
|
34
20
|
severity: Severity.ERROR,
|
|
@@ -41,20 +27,20 @@ export const MissingTemplate: LiquidCheckDefinition<typeof schema> = {
|
|
|
41
27
|
|
|
42
28
|
return {
|
|
43
29
|
async RenderMarkup(node) {
|
|
44
|
-
if (node.
|
|
30
|
+
if (node.partial.type === NodeTypes.VariableLookup) return;
|
|
45
31
|
|
|
46
|
-
const
|
|
32
|
+
const partial = node.partial;
|
|
47
33
|
const location = await locator.locate(
|
|
48
34
|
URI.parse(context.config.rootUri),
|
|
49
35
|
'render',
|
|
50
|
-
|
|
36
|
+
partial.value,
|
|
51
37
|
);
|
|
52
38
|
|
|
53
39
|
if (!location) {
|
|
54
40
|
context.report({
|
|
55
|
-
message: `'${
|
|
56
|
-
startIndex: node.
|
|
57
|
-
endIndex: node.
|
|
41
|
+
message: `'${partial.value}' does not exist`,
|
|
42
|
+
startIndex: node.partial.position.start,
|
|
43
|
+
endIndex: node.partial.position.end,
|
|
58
44
|
});
|
|
59
45
|
}
|
|
60
46
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isPartial } from '../../
|
|
1
|
+
import { isPartial } from '../../path';
|
|
2
2
|
import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types';
|
|
3
3
|
|
|
4
4
|
export const OrphanedPartial: LiquidCheckDefinition = {
|
|
@@ -6,9 +6,9 @@ export const OrphanedPartial: LiquidCheckDefinition = {
|
|
|
6
6
|
code: 'OrphanedPartial',
|
|
7
7
|
name: 'Prevent orphaned partials',
|
|
8
8
|
docs: {
|
|
9
|
-
description: 'This check exists to prevent orphaned partials in
|
|
9
|
+
description: 'This check exists to prevent orphaned partials in platformOS apps.',
|
|
10
10
|
recommended: true,
|
|
11
|
-
url: 'https://
|
|
11
|
+
url: 'https://documentation.platformos.com/developer-guide/platformos-check/checks/orphaned-partial',
|
|
12
12
|
},
|
|
13
13
|
type: SourceCodeType.LiquidHtml,
|
|
14
14
|
severity: Severity.WARNING,
|
|
@@ -3,26 +3,6 @@ import { ParserBlockingScript } from '.';
|
|
|
3
3
|
import { applySuggestions, check as reportOffenses } from '../../test';
|
|
4
4
|
|
|
5
5
|
describe('Module: ParserBlockingScript', () => {
|
|
6
|
-
it('should report the correct offense for the Liquid filter', async () => {
|
|
7
|
-
const file = "{{ 'asset' | script_tag }}";
|
|
8
|
-
const startIndex = file.indexOf('script_tag');
|
|
9
|
-
const endIndex = startIndex + 'script_tag'.length;
|
|
10
|
-
|
|
11
|
-
const offenses = await reportOffenses(
|
|
12
|
-
{
|
|
13
|
-
'code.liquid': file,
|
|
14
|
-
},
|
|
15
|
-
[ParserBlockingScript],
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
expect(offenses).to.have.length(1);
|
|
19
|
-
const { check, message, start, end } = offenses[0];
|
|
20
|
-
expect(check).to.equal(ParserBlockingScript.meta.code);
|
|
21
|
-
expect(message).to.contain('Use a <script> tag with async');
|
|
22
|
-
expect(start.index).to.equal(startIndex);
|
|
23
|
-
expect(end.index).to.equal(endIndex);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
6
|
it('should report the correct offense when using the script tag', async () => {
|
|
27
7
|
const file = `
|
|
28
8
|
<script src="https://foo.bar/baz.js"></script>
|
|
@@ -45,104 +25,6 @@ describe('Module: ParserBlockingScript', () => {
|
|
|
45
25
|
expect(end.index).to.equal(endIndex);
|
|
46
26
|
});
|
|
47
27
|
|
|
48
|
-
describe('Case: LiquidFilter corrections', () => {
|
|
49
|
-
it('should suggest to replace a LiquidVariableOutput with a script tag that has the expression as URL', async () => {
|
|
50
|
-
const file = "{{ 'asset' | script_tag }}";
|
|
51
|
-
const offenses = await reportOffenses(
|
|
52
|
-
{
|
|
53
|
-
'code.liquid': file,
|
|
54
|
-
},
|
|
55
|
-
[ParserBlockingScript],
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
expect(offenses).to.have.length(1);
|
|
59
|
-
const offense = offenses[0]!;
|
|
60
|
-
expect(offense).to.suggest(file, `Use an HTML script tag with the defer attribute instead`, {
|
|
61
|
-
startIndex: 0,
|
|
62
|
-
endIndex: file.length,
|
|
63
|
-
insert: `<script src="{{ 'asset' }}" defer></script>`,
|
|
64
|
-
});
|
|
65
|
-
expect(offense).to.suggest(file, `Use an HTML script tag with the async attribute instead`, {
|
|
66
|
-
startIndex: 0,
|
|
67
|
-
endIndex: file.length,
|
|
68
|
-
insert: `<script src="{{ 'asset' }}" async></script>`,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const suggestions = applySuggestions(file, offense);
|
|
72
|
-
expect(suggestions).to.include(`<script src="{{ 'asset' }}" defer></script>`);
|
|
73
|
-
expect(suggestions).to.include(`<script src="{{ 'asset' }}" async></script>`);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should suggest to replace a LiquidVariableOutput with a script tag that has the expression and previous filters as URL', async () => {
|
|
77
|
-
const file = "{{ 'asset' | asset_url | script_tag }}";
|
|
78
|
-
const offenses = await reportOffenses(
|
|
79
|
-
{
|
|
80
|
-
'code.liquid': file,
|
|
81
|
-
},
|
|
82
|
-
[ParserBlockingScript],
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
expect(offenses).to.have.length(1);
|
|
86
|
-
const offense = offenses[0];
|
|
87
|
-
expect(offense).to.suggest(file, `Use an HTML script tag with the defer attribute instead`, {
|
|
88
|
-
startIndex: 0,
|
|
89
|
-
endIndex: file.length,
|
|
90
|
-
insert: `<script src="{{ 'asset' | asset_url }}" defer></script>`,
|
|
91
|
-
});
|
|
92
|
-
expect(offense).to.suggest(file, `Use an HTML script tag with the async attribute instead`, {
|
|
93
|
-
startIndex: 0,
|
|
94
|
-
endIndex: file.length,
|
|
95
|
-
insert: `<script src="{{ 'asset' | asset_url }}" async></script>`,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
const suggestions = applySuggestions(file, offense);
|
|
99
|
-
expect(suggestions).to.include(`<script src="{{ 'asset' | asset_url }}" defer></script>`);
|
|
100
|
-
expect(suggestions).to.include(`<script src="{{ 'asset' | asset_url }}" async></script>`);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should not suggest anything if the script_tag appears in an echo tag', async () => {
|
|
104
|
-
const file = "{% echo 'asset' | asset_url | script_tag %}";
|
|
105
|
-
const offenses = await reportOffenses(
|
|
106
|
-
{
|
|
107
|
-
'code.liquid': file,
|
|
108
|
-
},
|
|
109
|
-
[ParserBlockingScript],
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
expect(offenses).to.have.length(1);
|
|
113
|
-
const { suggest } = offenses[0];
|
|
114
|
-
expect(suggest).not.to.exist;
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should not suggest anything if the script_tag appears in an assign tag', async () => {
|
|
118
|
-
const file = "{% assign script = 'asset' | asset_url | script_tag %}";
|
|
119
|
-
const offenses = await reportOffenses(
|
|
120
|
-
{
|
|
121
|
-
'code.liquid': file,
|
|
122
|
-
},
|
|
123
|
-
[ParserBlockingScript],
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
expect(offenses).to.have.length(1);
|
|
127
|
-
const { suggest } = offenses[0];
|
|
128
|
-
expect(suggest).not.to.exist;
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should not suggest anything if the script_tag appears in a liquid tag', async () => {
|
|
132
|
-
const file = "{% liquid\necho 'asset' | asset_url | script_tag %}";
|
|
133
|
-
const offenses = await reportOffenses(
|
|
134
|
-
{
|
|
135
|
-
'code.liquid': file,
|
|
136
|
-
},
|
|
137
|
-
[ParserBlockingScript],
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
expect(offenses).to.have.length(1);
|
|
141
|
-
const { suggest } = offenses[0];
|
|
142
|
-
expect(suggest).not.to.exist;
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
28
|
describe('Case: script tag suggestion', () => {
|
|
147
29
|
it('should suggest adding both attributes at the end', async () => {
|
|
148
30
|
const file = `<script src="a.js"></script>`;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { NodeTypes } from '@platformos/liquid-html-parser';
|
|
2
1
|
import { LiquidCheckDefinition, Severity, SourceCodeType } from '../../types';
|
|
3
|
-
import { last } from '../../utils';
|
|
4
2
|
import { hasAttributeValueOf, isAttr, isHtmlAttribute, isValuedHtmlAttribute } from '../utils';
|
|
5
|
-
import {
|
|
3
|
+
import { scriptTagSuggestion } from './suggestions';
|
|
6
4
|
|
|
7
5
|
export const ParserBlockingScript: LiquidCheckDefinition = {
|
|
8
6
|
meta: {
|
|
@@ -10,9 +8,9 @@ export const ParserBlockingScript: LiquidCheckDefinition = {
|
|
|
10
8
|
aliases: ['ParserBlockingScriptTag'],
|
|
11
9
|
name: 'Avoid parser blocking scripts',
|
|
12
10
|
docs: {
|
|
13
|
-
description: '
|
|
11
|
+
description: 'Parser-blocking scripts delay page rendering by blocking the HTML parser.',
|
|
14
12
|
recommended: true,
|
|
15
|
-
url: 'https://
|
|
13
|
+
url: 'https://documentation.platformos.com/developer-guide/platformos-check/checks/parser-blocking-script',
|
|
16
14
|
},
|
|
17
15
|
type: SourceCodeType.LiquidHtml,
|
|
18
16
|
severity: Severity.ERROR,
|
|
@@ -22,34 +20,6 @@ export const ParserBlockingScript: LiquidCheckDefinition = {
|
|
|
22
20
|
|
|
23
21
|
create(context) {
|
|
24
22
|
return {
|
|
25
|
-
// {{ 'asset' | asset_url | script_tag }}
|
|
26
|
-
LiquidFilter: async (node, ancestors) => {
|
|
27
|
-
if (node.name !== 'script_tag') return;
|
|
28
|
-
|
|
29
|
-
const filterString = node.source.slice(node.position.start, node.position.end);
|
|
30
|
-
const offset = filterString.indexOf('script_tag');
|
|
31
|
-
const parentNode = last(ancestors);
|
|
32
|
-
const grandParentNode = last(ancestors, -1);
|
|
33
|
-
|
|
34
|
-
context.report({
|
|
35
|
-
message:
|
|
36
|
-
'The script_tag filter is parser-blocking. Use a <script> tag with async or defer for better performance',
|
|
37
|
-
startIndex: node.position.start + offset,
|
|
38
|
-
endIndex: node.position.end,
|
|
39
|
-
suggest:
|
|
40
|
-
grandParentNode &&
|
|
41
|
-
grandParentNode.type === NodeTypes.LiquidVariableOutput &&
|
|
42
|
-
parentNode &&
|
|
43
|
-
parentNode.type === NodeTypes.LiquidVariable &&
|
|
44
|
-
last(parentNode.filters) === node
|
|
45
|
-
? [
|
|
46
|
-
liquidFilterSuggestion('defer', node, parentNode, grandParentNode),
|
|
47
|
-
liquidFilterSuggestion('async', node, parentNode, grandParentNode),
|
|
48
|
-
]
|
|
49
|
-
: undefined,
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
|
|
53
23
|
// <script src="...">
|
|
54
24
|
HtmlRawNode: async (node) => {
|
|
55
25
|
if (node.name !== 'script') {
|