@gitlab/eslint-plugin 20.1.0 → 20.2.0

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [20.2.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v20.1.0...v20.2.0) (2024-09-03)
2
+
3
+
4
+ ### Features
5
+
6
+ * add rule for uninitialised apollo query properties ([f89eace](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/f89eace7498900d90e06b0be214c51a589474d53)), closes [#74](https://gitlab.com/gitlab-org/frontend/eslint-plugin/issues/74)
7
+
1
8
  # [20.1.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v20.0.0...v20.1.0) (2024-08-28)
2
9
 
3
10
 
@@ -0,0 +1,95 @@
1
+ # @gitlab/vue-no-undef-apollo-properties
2
+
3
+ Require Apollo query properties to be initialized in component data.
4
+
5
+ Sensible initial values for Apollo queries should be defined in component data.
6
+ This reduces the likelihood of unexpected behaviour, especially when running
7
+ under @vue/compat.
8
+
9
+ This rule does not detect whether the initial value is of the correct type.
10
+
11
+ ## Rule Details
12
+
13
+ ### Examples of **incorrect** code for this rule
14
+
15
+ ```html
16
+ <script>
17
+ export default {
18
+ data() {
19
+ return {
20
+ foo: '',
21
+ qux: undefined,
22
+ };
23
+ },
24
+ apollo: {
25
+ $subscribe: {
26
+ foo: {
27
+ // ...
28
+ },
29
+ bar: {
30
+ // This isn't defined in data.
31
+ // ...
32
+ },
33
+ },
34
+ qux: {
35
+ // ...
36
+ },
37
+ bbq: {
38
+ // This isn't defined in data.
39
+ // ...
40
+ },
41
+ },
42
+ render(h) {
43
+ return h();
44
+ },
45
+ };
46
+ </script>
47
+ ```
48
+
49
+ ### Examples of **correct** code for this rule
50
+
51
+ ```html
52
+ <script>
53
+ export default {
54
+ data() {
55
+ return {
56
+ foo: '',
57
+ qux: undefined,
58
+ bar: null,
59
+ bbq: [],
60
+ };
61
+ },
62
+ apollo: {
63
+ $subscribe: {
64
+ foo: {
65
+ // ...
66
+ },
67
+ bar: {
68
+ // ...
69
+ },
70
+ },
71
+ qux: {
72
+ // ...
73
+ },
74
+ bbq: {
75
+ // ...
76
+ },
77
+ },
78
+ render(h) {
79
+ return h();
80
+ },
81
+ };
82
+ </script>
83
+ ```
84
+
85
+ ## Options
86
+
87
+ Nothing
88
+
89
+ ## Related rules
90
+
91
+ Nothing
92
+
93
+ ## When Not To Use It
94
+
95
+ If you don't use VueApollo with the Options API, don't use this rule.
package/docs/rules.md CHANGED
@@ -8,15 +8,16 @@ Available rules:
8
8
  - [no-runtime-template-compiler](./rules/no-runtime-template-compiler.md): Disallow components which rely on a runtime template compiler.
9
9
  - [require-i18n-strings](./rules/require-i18n-strings.md): Detect a string which has been hard coded and requires externalization.
10
10
  - [require-valid-i18n-helpers](./rules/require-valid-i18n-helpers.md): Enforces valid usage of translation helpers in JavaScript.
11
- - [tailwind](./rules/tailwind.md): Prevents undesired Tailwind CSS features from being used.
11
+ - [tailwind](./rules/tailwind.md): Ensures Tailwind CSS is used according to our internal guidelines.
12
12
  - [vtu-no-explicit-wrapper-destroy](./rules/vtu-no-explicit-wrapper-destroy.md): Prevents redundant destroy calls and null asignment
13
13
  - [vtu-no-wrapper-vm](./rules/vtu-no-wrapper-vm.md): Prevent direct access to `vm` internals for `@vue/test-utils` wrappers.
14
14
  - [vue-no-data-toggle](./rules/vue-no-data-toggle.md): Restrict the use of `data-toggle` bootstrap behaviors within Vue templates
15
15
  - [vue-no-new-non-primitive-in-template](./rules/vue-no-new-non-primitive-in-template.md): Prevents non-primitive values from being declared in templates
16
+ - [vue-no-undef-apollo-properties](./rules/vue-no-undef-apollo-properties.md): Require Apollo query properties to be initialized in component data.
16
17
  - [vue-prefer-dollar-scopedslots](./rules/vue-prefer-dollar-scopedslots.md): Prefer $scopedSlots over $slots for Vue 2.x.
17
18
  - [vue-require-i18n-attribute-strings](./rules/vue-require-i18n-attribute-strings.md): Detect non externalized strings in vue `<template>` attributes
18
19
  - [vue-require-i18n-strings](./rules/vue-require-i18n-strings.md): enforce no bare strings in vue `<template>`
19
20
  - [vue-require-required-key](./rules/vue-require-required-key.md): Require the required key to be set
20
21
  - [vue-require-valid-i18n-helpers](./rules/vue-require-valid-i18n-helpers.md): Enforces valid usage of translation helpers in Vue templates.
21
22
  - [vue-slot-name-casing](./rules/vue-slot-name-casing.md): enforce specific casing for slot naming style in template
22
- - [vue-tailwind](./rules/vue-tailwind.md): Prevents undesired Tailwind CSS features from being used in Vue templates.
23
+ - [vue-tailwind](./rules/vue-tailwind.md): Ensures Tailwind CSS is used according to our internal guidelines in Vue templates.
package/lib/index.js CHANGED
@@ -12,6 +12,7 @@ module.exports = {
12
12
  'vtu-no-wrapper-vm': require('./rules/vtu-no-wrapper-vm.js'),
13
13
  'vue-no-data-toggle': require('./rules/vue-no-data-toggle.js'),
14
14
  'vue-no-new-non-primitive-in-template': require('./rules/vue-no-new-non-primitive-in-template.js'),
15
+ 'vue-no-undef-apollo-properties': require('./rules/vue-no-undef-apollo-properties.js'),
15
16
  'vue-prefer-dollar-scopedslots': require('./rules/vue-prefer-dollar-scopedslots.js'),
16
17
  'vue-require-i18n-attribute-strings': require('./rules/vue-require-i18n-attribute-strings.js'),
17
18
  'vue-require-i18n-strings': require('./rules/vue-require-i18n-strings.js'),
@@ -0,0 +1,102 @@
1
+ // ------------------------------------------------------------------------------
2
+ // Requirements
3
+ // ------------------------------------------------------------------------------
4
+ const { DOCS_BASE_URL } = require('../constants');
5
+ const utils = require('eslint-plugin-vue/lib/utils');
6
+
7
+ // ------------------------------------------------------------------------------
8
+ // Helpers
9
+ // ------------------------------------------------------------------------------
10
+
11
+ const DOLLAR_SUBSCRIBE = '$subscribe';
12
+
13
+ const VUE_APOLLO_SPECIAL_OPTIONS = new Set([
14
+ '$skipAll',
15
+ '$skipAllQueries',
16
+ '$skipAllSubscriptions',
17
+ '$deep',
18
+ '$error',
19
+ '$query',
20
+
21
+ // This one isn't special in the same way as the rest, but the normal code
22
+ // paths below need to be skipped for this case as well. It is handled in a
23
+ // separate code path.
24
+ DOLLAR_SUBSCRIBE,
25
+ ]);
26
+
27
+ function findProperty(objectExpression, name) {
28
+ return objectExpression.properties.find(
29
+ (p) =>
30
+ p.type === 'Property' &&
31
+ utils.getStaticPropertyName(p) === name &&
32
+ p.value.type === 'ObjectExpression',
33
+ );
34
+ }
35
+
36
+ function getDataKeys(componentObject) {
37
+ return new Set(
38
+ Array.from(utils.iterateProperties(componentObject, new Set(['data']))).map(({ name }) => name),
39
+ );
40
+ }
41
+
42
+ function* getApolloKeyNodeWrappers(componentObject) {
43
+ const apolloNode = findProperty(componentObject, 'apollo');
44
+
45
+ if (!apolloNode) return;
46
+
47
+ for (const nodeWrapper of utils.iterateObjectExpression(apolloNode.value)) {
48
+ if (VUE_APOLLO_SPECIAL_OPTIONS.has(nodeWrapper.name)) {
49
+ // Future-proofing against VueApollo 4's special options:
50
+ // https://apollo.vuejs.org/guide-option/special-options.html#special-options
51
+ continue;
52
+ }
53
+
54
+ yield nodeWrapper;
55
+ }
56
+
57
+ const subscribeNode = findProperty(apolloNode.value, DOLLAR_SUBSCRIBE);
58
+
59
+ if (subscribeNode) {
60
+ yield* utils.iterateObjectExpression(subscribeNode.value);
61
+ }
62
+ }
63
+
64
+ function* getApolloKeyNodes(componentObject) {
65
+ for (const nodeWrapper of getApolloKeyNodeWrappers(componentObject)) {
66
+ yield nodeWrapper.node;
67
+ }
68
+ }
69
+
70
+ // ------------------------------------------------------------------------------
71
+ // Rule Definition
72
+ // ------------------------------------------------------------------------------
73
+
74
+ module.exports = {
75
+ meta: {
76
+ type: 'error',
77
+ docs: {
78
+ description: 'Require Apollo query properties to be initialized in component data.',
79
+ url: DOCS_BASE_URL + '/vue-no-undef-apollo-properties.md',
80
+ },
81
+ messages: {
82
+ noInitialValue: 'Apollo query property {{name}} is not initialized in component data.',
83
+ },
84
+ },
85
+ create(context) {
86
+ return utils.executeOnVue(context, (node) => {
87
+ const dataKeys = getDataKeys(node);
88
+
89
+ for (const apolloKeyNode of getApolloKeyNodes(node)) {
90
+ if (!dataKeys.has(apolloKeyNode.name)) {
91
+ context.report({
92
+ node: apolloKeyNode,
93
+ messageId: 'noInitialValue',
94
+ data: {
95
+ name: apolloKeyNode.name,
96
+ },
97
+ });
98
+ }
99
+ }
100
+ });
101
+ },
102
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/eslint-plugin",
3
- "version": "20.1.0",
3
+ "version": "20.2.0",
4
4
  "description": "GitLab package for our custom eslint rules",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {