@atlaskit/eslint-plugin-platform 2.7.0 → 2.7.2

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.
Files changed (158) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +4 -9
  3. package/dist/cjs/rules/constants.js +1 -1
  4. package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +7 -7
  5. package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  6. package/dist/cjs/rules/ensure-no-private-dependencies/index.js +48 -66
  7. package/dist/cjs/rules/feature-gating/inline-usage/index.js +14 -3
  8. package/dist/cjs/rules/feature-gating/no-alias/index.js +2 -2
  9. package/dist/cjs/rules/feature-gating/no-module-level-eval/index.js +1 -1
  10. package/dist/cjs/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
  11. package/dist/cjs/rules/feature-gating/no-preconditioning/index.js +4 -1
  12. package/dist/cjs/rules/feature-gating/prefer-fg/index.js +1 -1
  13. package/dist/cjs/rules/feature-gating/static-feature-flags/index.js +2 -2
  14. package/dist/cjs/rules/feature-gating/use-recommended-utils/index.js +1 -1
  15. package/dist/cjs/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
  16. package/dist/cjs/rules/no-sparse-checkout/index.js +1 -1
  17. package/dist/cjs/rules/prefer-crypto-random-uuid/index.js +87 -0
  18. package/dist/cjs/rules/use-entrypoints-in-examples/index.js +1 -1
  19. package/dist/cjs/rules/util/context-compat.js +4 -2
  20. package/dist/es2019/index.js +4 -9
  21. package/dist/es2019/rules/constants.js +1 -1
  22. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +7 -7
  23. package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  24. package/dist/es2019/rules/ensure-no-private-dependencies/index.js +10 -9
  25. package/dist/es2019/rules/feature-gating/inline-usage/index.js +14 -3
  26. package/dist/es2019/rules/feature-gating/no-alias/index.js +2 -2
  27. package/dist/es2019/rules/feature-gating/no-module-level-eval/index.js +1 -1
  28. package/dist/es2019/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
  29. package/dist/es2019/rules/feature-gating/no-preconditioning/index.js +4 -1
  30. package/dist/es2019/rules/feature-gating/prefer-fg/index.js +1 -1
  31. package/dist/es2019/rules/feature-gating/static-feature-flags/index.js +2 -2
  32. package/dist/es2019/rules/feature-gating/use-recommended-utils/index.js +1 -1
  33. package/dist/es2019/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
  34. package/dist/es2019/rules/no-sparse-checkout/index.js +1 -1
  35. package/dist/es2019/rules/prefer-crypto-random-uuid/index.js +81 -0
  36. package/dist/es2019/rules/use-entrypoints-in-examples/index.js +1 -1
  37. package/dist/es2019/rules/util/context-compat.js +4 -2
  38. package/dist/esm/index.js +4 -9
  39. package/dist/esm/rules/constants.js +1 -1
  40. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +7 -7
  41. package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  42. package/dist/esm/rules/ensure-no-private-dependencies/index.js +48 -65
  43. package/dist/esm/rules/feature-gating/inline-usage/index.js +14 -3
  44. package/dist/esm/rules/feature-gating/no-alias/index.js +2 -2
  45. package/dist/esm/rules/feature-gating/no-module-level-eval/index.js +1 -1
  46. package/dist/esm/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
  47. package/dist/esm/rules/feature-gating/no-preconditioning/index.js +4 -1
  48. package/dist/esm/rules/feature-gating/prefer-fg/index.js +1 -1
  49. package/dist/esm/rules/feature-gating/static-feature-flags/index.js +2 -2
  50. package/dist/esm/rules/feature-gating/use-recommended-utils/index.js +1 -1
  51. package/dist/esm/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
  52. package/dist/esm/rules/no-sparse-checkout/index.js +1 -1
  53. package/dist/esm/rules/prefer-crypto-random-uuid/index.js +81 -0
  54. package/dist/esm/rules/use-entrypoints-in-examples/index.js +1 -1
  55. package/dist/esm/rules/util/context-compat.js +4 -2
  56. package/dist/types/index.d.ts +6 -20
  57. package/dist/types/rules/util/handle-ast-object.d.ts +1 -1
  58. package/dist/types-ts4.5/index.d.ts +6 -32
  59. package/dist/types-ts4.5/rules/util/handle-ast-object.d.ts +1 -1
  60. package/package.json +2 -5
  61. package/afm-cc/tsconfig.json +0 -24
  62. package/afm-jira/tsconfig.json +0 -24
  63. package/build/tsconfig.json +0 -17
  64. package/dist/cjs/rules/ensure-feature-flag-prefix/index.js +0 -75
  65. package/dist/cjs/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -79
  66. package/dist/es2019/rules/ensure-feature-flag-prefix/index.js +0 -65
  67. package/dist/es2019/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -62
  68. package/dist/esm/rules/ensure-feature-flag-prefix/index.js +0 -69
  69. package/dist/esm/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -75
  70. package/dist/types/rules/ensure-feature-flag-prefix/index.d.ts +0 -3
  71. package/dist/types/rules/ensure-valid-platform-yarn-protocol-usage/index.d.ts +0 -3
  72. package/src/__tests__/utils/_tester.tsx +0 -26
  73. package/src/index.tsx +0 -254
  74. package/src/rules/compiled/README.md +0 -3
  75. package/src/rules/compiled/expand-background-shorthand/README.md +0 -23
  76. package/src/rules/compiled/expand-background-shorthand/__tests__/rule.test.ts +0 -160
  77. package/src/rules/compiled/expand-background-shorthand/index.tsx +0 -43
  78. package/src/rules/compiled/expand-border-shorthand/README.md +0 -51
  79. package/src/rules/compiled/expand-border-shorthand/__tests__/rule.test.ts +0 -211
  80. package/src/rules/compiled/expand-border-shorthand/index.ts +0 -103
  81. package/src/rules/compiled/expand-spacing-shorthand/README.md +0 -38
  82. package/src/rules/compiled/expand-spacing-shorthand/__tests__/rule.test.ts +0 -448
  83. package/src/rules/compiled/expand-spacing-shorthand/index.ts +0 -240
  84. package/src/rules/constants.tsx +0 -20
  85. package/src/rules/ensure-atlassian-team/__tests__/unit/rule.test.ts +0 -24
  86. package/src/rules/ensure-atlassian-team/index.ts +0 -51
  87. package/src/rules/ensure-critical-dependency-resolutions/__test__/unit/rule.test.tsx +0 -200
  88. package/src/rules/ensure-critical-dependency-resolutions/index.tsx +0 -172
  89. package/src/rules/ensure-feature-flag-prefix/__tests__/unit/rule.test.tsx +0 -65
  90. package/src/rules/ensure-feature-flag-prefix/index.tsx +0 -81
  91. package/src/rules/ensure-feature-flag-registration/__tests__/unit/rule.test.tsx +0 -115
  92. package/src/rules/ensure-feature-flag-registration/index.tsx +0 -106
  93. package/src/rules/ensure-native-and-af-exports-synced/__tests__/unit/rule.test.tsx +0 -199
  94. package/src/rules/ensure-native-and-af-exports-synced/index.tsx +0 -188
  95. package/src/rules/ensure-no-private-dependencies/__tests__/unit/rule.test.ts +0 -212
  96. package/src/rules/ensure-no-private-dependencies/index.ts +0 -64
  97. package/src/rules/ensure-publish-valid/__tests__/unit/rule.test.ts +0 -39
  98. package/src/rules/ensure-publish-valid/index.ts +0 -81
  99. package/src/rules/ensure-test-runner-arguments/__tests__/unit/rule.test.tsx +0 -298
  100. package/src/rules/ensure-test-runner-arguments/index.tsx +0 -121
  101. package/src/rules/ensure-test-runner-nested-count/__tests__/unit/rule.test.tsx +0 -308
  102. package/src/rules/ensure-test-runner-nested-count/index.tsx +0 -82
  103. package/src/rules/ensure-valid-bin-values/__tests__/unit/rule.test.ts +0 -159
  104. package/src/rules/ensure-valid-bin-values/index.ts +0 -70
  105. package/src/rules/ensure-valid-platform-yarn-protocol-usage/__tests__/unit/rule.test.ts +0 -147
  106. package/src/rules/ensure-valid-platform-yarn-protocol-usage/index.ts +0 -67
  107. package/src/rules/feature-gating/README.md +0 -8
  108. package/src/rules/feature-gating/inline-usage/README.md +0 -53
  109. package/src/rules/feature-gating/inline-usage/__tests__/rule.test.tsx +0 -106
  110. package/src/rules/feature-gating/inline-usage/index.tsx +0 -135
  111. package/src/rules/feature-gating/no-alias/README.md +0 -29
  112. package/src/rules/feature-gating/no-alias/__tests__/rule.test.tsx +0 -76
  113. package/src/rules/feature-gating/no-alias/index.tsx +0 -80
  114. package/src/rules/feature-gating/no-module-level-eval/README.md +0 -53
  115. package/src/rules/feature-gating/no-module-level-eval/__tests__/test.tsx +0 -133
  116. package/src/rules/feature-gating/no-module-level-eval/index.tsx +0 -54
  117. package/src/rules/feature-gating/no-module-level-eval-nav4/README.md +0 -8
  118. package/src/rules/feature-gating/no-module-level-eval-nav4/__tests__/test.tsx +0 -130
  119. package/src/rules/feature-gating/no-module-level-eval-nav4/index.tsx +0 -73
  120. package/src/rules/feature-gating/no-preconditioning/README.md +0 -69
  121. package/src/rules/feature-gating/no-preconditioning/__tests__/rule.test.tsx +0 -164
  122. package/src/rules/feature-gating/no-preconditioning/index.tsx +0 -138
  123. package/src/rules/feature-gating/prefer-fg/README.md +0 -3
  124. package/src/rules/feature-gating/prefer-fg/__tests__/rule.test.tsx +0 -83
  125. package/src/rules/feature-gating/prefer-fg/index.tsx +0 -110
  126. package/src/rules/feature-gating/static-feature-flags/README.md +0 -3
  127. package/src/rules/feature-gating/static-feature-flags/__tests__/test.tsx +0 -135
  128. package/src/rules/feature-gating/static-feature-flags/index.tsx +0 -103
  129. package/src/rules/feature-gating/use-recommended-utils/README.md +0 -67
  130. package/src/rules/feature-gating/use-recommended-utils/__tests__/rule.test.tsx +0 -78
  131. package/src/rules/feature-gating/use-recommended-utils/index.tsx +0 -57
  132. package/src/rules/feature-gating/utils.tsx +0 -48
  133. package/src/rules/no-direct-document-usage/index.tsx +0 -111
  134. package/src/rules/no-duplicate-dependencies/__tests__/unit/rule.test.ts +0 -116
  135. package/src/rules/no-duplicate-dependencies/index.ts +0 -79
  136. package/src/rules/no-invalid-feature-flag-usage/__tests__/unit/rule.test.tsx +0 -69
  137. package/src/rules/no-invalid-feature-flag-usage/index.tsx +0 -128
  138. package/src/rules/no-invalid-storybook-decorator-usage/__tests__/unit/rule.test.tsx +0 -18
  139. package/src/rules/no-invalid-storybook-decorator-usage/index.tsx +0 -39
  140. package/src/rules/no-pre-post-installs/__tests__/unit/rule.test.ts +0 -41
  141. package/src/rules/no-pre-post-installs/index.ts +0 -35
  142. package/src/rules/no-set-immediate/index.tsx +0 -43
  143. package/src/rules/no-sparse-checkout/__tests__/unit/rule.test.tsx +0 -48
  144. package/src/rules/no-sparse-checkout/index.tsx +0 -54
  145. package/src/rules/use-entrypoints-in-examples/README.md +0 -27
  146. package/src/rules/use-entrypoints-in-examples/__tests__/rule.test.tsx +0 -34
  147. package/src/rules/use-entrypoints-in-examples/index.tsx +0 -43
  148. package/src/rules/util/__tests__/context-compat.test.ts +0 -122
  149. package/src/rules/util/compiled-utils.ts +0 -27
  150. package/src/rules/util/context-compat.ts +0 -41
  151. package/src/rules/util/file-exclusions.ts +0 -39
  152. package/src/rules/util/handle-ast-object.ts +0 -33
  153. package/src/rules/util/registration-utils.ts +0 -59
  154. package/tsconfig.app.json +0 -43
  155. package/tsconfig.dev.json +0 -40
  156. package/tsconfig.json +0 -23
  157. /package/dist/{types-ts4.5/rules/ensure-valid-platform-yarn-protocol-usage → types/rules/prefer-crypto-random-uuid}/index.d.ts +0 -0
  158. /package/dist/types-ts4.5/rules/{ensure-feature-flag-prefix → prefer-crypto-random-uuid}/index.d.ts +0 -0
@@ -1,164 +0,0 @@
1
- import outdent from 'outdent';
2
- import { tester } from '../../../../__tests__/utils/_tester';
3
- import rule from '../index';
4
-
5
- tester.run('feature-flags/no-preconditioning', rule, {
6
- valid: [
7
- {
8
- code: outdent`
9
- import { fg } from '@atlassian/jira-feature-gating';
10
-
11
- if (preCheck && fg('gate')) {}
12
- `,
13
- },
14
- {
15
- code: outdent`
16
- import { fg } from '@atlassian/jira-feature-gating';
17
-
18
- if (preCheck1 && preCheck2 && fg('gate')) {}
19
- `,
20
- },
21
- {
22
- code: outdent`
23
- import { fg } from '@atlassian/jira-feature-gating';
24
-
25
- const value = fg('gate') && 'value';
26
- `,
27
- },
28
- {
29
- code: outdent`
30
- import { fg } from '@atlassian/jira-feature-gating';
31
-
32
- const value = preCheck && fg('gate') ? 'value' : '';
33
- `,
34
- },
35
- {
36
- code: outdent`
37
- import { expVal } from '@atlassian/jira-feature-experiments';
38
-
39
- if (expVal('one') && expVal('two') ) {}
40
- `,
41
- },
42
- {
43
- code: outdent`
44
- import { expVal, expValEquals } from '@atlassian/jira-feature-experiments';
45
-
46
- if (expVal('one') && expValEquals('two', 'value') ) {}
47
- `,
48
- },
49
- {
50
- code: outdent`
51
- import { expVal } from '@atlassian/jira-feature-experiments';
52
-
53
- if (expVal('one') || expVal('two') ) {}
54
- `,
55
- },
56
- {
57
- code: outdent`
58
- import { fg } from '@atlassian/jira-feature-gating';
59
-
60
- if (preGate && fg('one') || preGate && fg('two') ) {}
61
- `,
62
- },
63
- {
64
- code: outdent`
65
- import { fg } from '@atlassian/jira-feature-gating';
66
-
67
- if (count > 0 && fg('my_gate') ) {}
68
- `,
69
- },
70
- ],
71
- invalid: [
72
- {
73
- code: outdent`
74
- import { fg } from '@atlassian/jira-feature-gating';
75
-
76
- if (fg('one') && fg('two')) {}
77
- `,
78
- errors: [{ messageId: 'useConfig' }],
79
- },
80
- {
81
- code: outdent`
82
- import { fg } from '@atlassian/jira-feature-gating';
83
- import { expVal } from '@atlassian/jira-feature-experiments';
84
-
85
- if (expVal('one') && fg('two')) {}
86
- `,
87
- errors: [{ messageId: 'useConfig' }],
88
- },
89
- {
90
- code: outdent`
91
- import { fg } from '@atlassian/jira-feature-gating';
92
- import { expVal } from '@atlassian/jira-feature-experiments';
93
-
94
- if (fg('one') && fg('two') && expVal('three')) {}
95
- `,
96
- errors: [{ messageId: 'useConfig' }],
97
- },
98
- {
99
- code: outdent`
100
- import { fg } from '@atlassian/jira-feature-gating';
101
-
102
- if (preCheck && fg('one') && fg('two')) {}
103
- `,
104
- errors: [{ messageId: 'useConfig' }],
105
- },
106
- {
107
- code: outdent`
108
- import { fg } from '@atlassian/jira-feature-gating';
109
- import { expVal } from '@atlassian/jira-feature-experiments';
110
-
111
- if (fg('my_gate') && expVal('my_exp', 'cohort', 'not-enrolled') === 'variation') {}
112
- `,
113
- errors: [{ messageId: 'useConfig' }],
114
- },
115
- {
116
- code: outdent`
117
- import { fg } from '@atlassian/jira-feature-gating';
118
-
119
- const value = fg('one') && preCheck && fg('two') ? 'value' : '';
120
- `,
121
- errors: [{ messageId: 'incorrectExposure' }],
122
- },
123
- {
124
- code: outdent`
125
- import { fg } from '@atlassian/jira-feature-gating';
126
-
127
- if (fg('gate') && isAdmin) {}
128
- `,
129
- errors: [{ messageId: 'incorrectExposure' }],
130
- },
131
- {
132
- code: outdent`
133
- import { expVal } from '@atlassian/jira-feature-experiments';
134
-
135
- if (expVal('exp') && isAdmin) {}
136
- `,
137
- errors: [{ messageId: 'incorrectExposure' }],
138
- },
139
- {
140
- code: outdent`
141
- import { expValEquals } from '@atlassian/jira-feature-experiments';
142
-
143
- if (expValEquals('exp', 'value') && isAdmin) {}
144
- `,
145
- errors: [{ messageId: 'incorrectExposure' }],
146
- },
147
- {
148
- code: outdent`
149
- import { fg } from '@atlassian/jira-feature-gating';
150
-
151
- if (fg('one') && fg('two') && isAdmin) {}
152
- `,
153
- errors: [{ messageId: 'incorrectExposure' }, { messageId: 'useConfig' }],
154
- },
155
- {
156
- code: outdent`
157
- import { fg } from '@atlassian/jira-feature-gating';
158
-
159
- if (preGate && fg('one') || fg('two') && preGate ) {}
160
- `,
161
- errors: [{ messageId: 'incorrectExposure' }],
162
- },
163
- ],
164
- });
@@ -1,138 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- import { isAPIimport, type Node } from '../utils';
3
-
4
- const isAndExpression = (node: Rule.Node): node is Node<'LogicalExpression'> =>
5
- node.type === 'LogicalExpression' && node.operator === '&&';
6
-
7
- const isExpUsage = (calleeName: string) => calleeName === 'expVal' || calleeName === 'expValEquals';
8
-
9
- const getGateType = (node: Rule.Node, context: Rule.RuleContext): string => {
10
- const { type } = node;
11
-
12
- if (type === 'BinaryExpression') {
13
- return (
14
- getGateType(node.left as Node<'BinaryExpression'>, context) ||
15
- getGateType(node.right as Node<'BinaryExpression'>, context)
16
- );
17
- }
18
-
19
- if (node.type === 'CallExpression') {
20
- const { callee } = node;
21
-
22
- const isFeatureGate =
23
- type === 'CallExpression' &&
24
- callee.type === 'Identifier' &&
25
- // Experiments cannot have other experiments as preconditions, only gates
26
- (callee.name === 'fg' || isExpUsage(callee.name)) &&
27
- isAPIimport(callee.name, context, node);
28
-
29
- return isFeatureGate ? callee.name : '';
30
- }
31
-
32
- return '';
33
- };
34
-
35
- const getPreconditionStatus = (
36
- logicalExpression: Node<'LogicalExpression'>,
37
- context: Rule.RuleContext,
38
- ) => {
39
- const { left } = logicalExpression as any;
40
- // If left side is a nested AND expression then the left side node is on the nested's right
41
- const leftGateType = getGateType(isAndExpression(left) ? left.right : left, context);
42
-
43
- if (leftGateType) {
44
- const rightGateType = getGateType(logicalExpression.right as any, context);
45
- // Check this scenario: fg('gate') && isAdmin
46
- if (!rightGateType) {
47
- return 'early-exposure';
48
- }
49
-
50
- // Using experiment values in logical expressions in valid
51
- // i.e. expVal() && expVal()
52
- if (isExpUsage(leftGateType) && isExpUsage(rightGateType)) {
53
- return '';
54
- }
55
-
56
- // Then is scenario: fg('gate1') && fg('gate2')
57
- return 'unnecessary-gate';
58
- }
59
-
60
- return '';
61
- };
62
-
63
- const rule: Rule.RuleModule = {
64
- meta: {
65
- docs: {
66
- description: 'Inform on how to use gates and experiments in logical expressions',
67
- url: 'https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/platform/packages/platform/eslint-plugin/src/rules/no-preconditioning/README.md',
68
- },
69
- messages: {
70
- useConfig:
71
- 'Do not precondition gates or experiments with another gate. Configure this in Statsig instead to reduce unnecessary code, simplify cleanup and to ensure accurate exposures in Statsig.',
72
- incorrectExposure:
73
- 'Evaluate gates or experiments at the end of your logical expression to ensure exposure is tracked correctly.',
74
- },
75
- },
76
- create(context) {
77
- return {
78
- 'LogicalExpression[operator="&&"]': (node: Node<'LogicalExpression'>) => {
79
- const { parent } = node;
80
- // Don't analyze nested AND logical expressions
81
- if (isAndExpression(parent)) {
82
- return;
83
- }
84
-
85
- const isAssignmentStatement =
86
- parent.type !== 'IfStatement' &&
87
- parent.type !== 'ConditionalExpression' &&
88
- // @ts-expect-error — this isn't a valid statement but does fail tests when removed.
89
- // When updating this rule please resolve this supression.
90
- !(parent.type === 'LogicalExpression' && parent.operator === '||');
91
-
92
- let nextLogicalExpression: Node<'LogicalExpression'> | undefined = node;
93
- let exposureReported = false;
94
- let configReported = false;
95
-
96
- while (nextLogicalExpression) {
97
- const preconditionStatus = getPreconditionStatus(nextLogicalExpression, context);
98
- // Allow us to check for: fg('') && <Component />
99
- const isReturningValue =
100
- // Check if we are on the root logical expression
101
- // as this is where the returning value is
102
- // `node` is root logical expression
103
- isAssignmentStatement && nextLogicalExpression === node;
104
-
105
- if (!exposureReported && !isReturningValue && preconditionStatus === 'early-exposure') {
106
- context.report({
107
- messageId: 'incorrectExposure',
108
- node,
109
- });
110
-
111
- exposureReported = true;
112
- }
113
-
114
- if (!configReported && preconditionStatus === 'unnecessary-gate') {
115
- context.report({
116
- messageId: 'useConfig',
117
- node,
118
- });
119
-
120
- configReported = true;
121
- }
122
-
123
- if (exposureReported && configReported) {
124
- return;
125
- }
126
-
127
- nextLogicalExpression = isAndExpression(
128
- nextLogicalExpression.left as Node<'LogicalExpression'>,
129
- )
130
- ? (nextLogicalExpression.left as Node<'LogicalExpression'>)
131
- : undefined;
132
- }
133
- },
134
- };
135
- },
136
- };
137
-
138
- export default rule;
@@ -1,3 +0,0 @@
1
- # Keep usages of boolean feature flags consistent (feature-flags/prefer-fg)
2
-
3
- Use `fg` with boolean feature flags
@@ -1,83 +0,0 @@
1
- import outdent from 'outdent';
2
- import { tester } from '../../../../__tests__/utils/_tester';
3
- import rule from '../index';
4
-
5
- const errors = [{ messageId: 'autoFixImports' }, { messageId: 'preferFG' }];
6
-
7
- tester.run('feature-flags/prefer-fg', rule, {
8
- valid: [
9
- {
10
- code: outdent`
11
- import { getFeatureFlagValue } from '@atlassian/jira-feature-flagging';
12
-
13
- const ffVal = getFeatureFlagValue('get.value', '');
14
- `,
15
- },
16
- {
17
- code: outdent`
18
- import { getFeatureFlagValue } from '@atlassian/jira-feature-flagging';
19
-
20
- const ffVal = getFeatureFlagValue('get.value', {});
21
- `,
22
- },
23
- {
24
- code: outdent`
25
- import { getFeatureFlagValue } from '@atlassian/jira-feature-flagging-using-meta';
26
-
27
- const ffVal = getFeatureFlagValue('get.value', 0);
28
- `,
29
- },
30
- {
31
- code: outdent`
32
- import { getFeatureFlagValue } from '@atlassian/jira-feature-flagging-using-meta';
33
-
34
- const isEnabled = getFeatureFlagValue('is.enabled', true);
35
- `,
36
- },
37
- {
38
- code: outdent`
39
- import { getFeatureFlagValue, getFeatureFlagClient } from '@atlassian/jira-feature-flagging-using-meta';
40
-
41
- const ffVal = getFeatureFlagValue('get.value', {});
42
- const newVal = getFeatureFlagClient().getFlagEvaluation('get.value', {});
43
- `,
44
- },
45
- {
46
- code: outdent`
47
- import { getFeatureFlagValue } from 'thirdparty';
48
-
49
- const isEnabled = getFeatureFlagValue('is.enabled', false);
50
- `,
51
- },
52
- ],
53
- invalid: [
54
- {
55
- code: outdent`
56
- import { getBooleanFF, fg } from '@atlaskit/platform-feature-flags';
57
-
58
- const isCorrect = fg('is.correct');
59
- const isAlsoEnabled = getBooleanFF('is.also.enabled', false);
60
- `,
61
- output: outdent`
62
- import { fg } from '@atlaskit/platform-feature-flags';
63
-
64
- const isCorrect = fg('is.correct');
65
- const isAlsoEnabled = fg('is.also.enabled');
66
- `,
67
- errors,
68
- },
69
- {
70
- code: outdent`
71
- import { getBooleanFF } from '@atlaskit/platform-feature-flags';
72
-
73
- const isEnabled = getBooleanFF('is.enabled', false);
74
- `,
75
- output: outdent`
76
- import { fg } from '@atlaskit/platform-feature-flags';
77
-
78
- const isEnabled = fg('is.enabled');
79
- `,
80
- errors: [{ messageId: 'autoFixImports' }, { messageId: 'preferFG' }],
81
- },
82
- ],
83
- });
@@ -1,110 +0,0 @@
1
- import type { Rule } from 'eslint';
2
-
3
- import { FEATURE_API_IMPORT_SOURCES } from '../../constants';
4
- import type { Node } from '../utils';
5
- import type { Program } from 'estree';
6
- import { getScope } from '../../util/context-compat';
7
-
8
- const validateUsage = (
9
- node: Node<'CallExpression'>,
10
- utilName: string,
11
- context: Rule.RuleContext,
12
- changeMap: Map<any, any>,
13
- ) => {
14
- const resolved = getScope(context, node).references.find(
15
- (ref) => ref.identifier.name === utilName,
16
- )?.resolved;
17
-
18
- const importSpecifierDefinition = resolved?.defs.find(
19
- (def: any) =>
20
- def.node?.type === 'ImportSpecifier' &&
21
- FEATURE_API_IMPORT_SOURCES.has(def.parent?.source.value),
22
- );
23
-
24
- if (importSpecifierDefinition) {
25
- const [flagNameArg] = node.arguments;
26
-
27
- context.report({
28
- messageId: 'preferFG',
29
- node,
30
- *fix(fixer) {
31
- yield fixer.replaceText(node, `fg(${context.sourceCode.getText(flagNameArg)})`);
32
-
33
- const importDeclaration = importSpecifierDefinition.parent;
34
-
35
- if (changeMap.has(importDeclaration)) {
36
- const changeCounts = changeMap.get(importDeclaration);
37
-
38
- changeMap.set(importDeclaration, {
39
- ...changeCounts,
40
- [utilName]: changeCounts[utilName] + 1 || 1,
41
- });
42
- } else {
43
- changeMap.set(importDeclaration, { [utilName]: 1 });
44
- }
45
- },
46
- });
47
- }
48
- };
49
-
50
- const rule: Rule.RuleModule = {
51
- meta: {
52
- docs: {
53
- url: 'https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/platform/packages/platform/eslint-plugin/src/rules/prefer-fg/README.md',
54
- description: 'Keep usages of boolean feature flags consistent',
55
- },
56
- fixable: 'code',
57
- messages: {
58
- preferFG: 'Use `fg` instead for boolean feature flags',
59
- autoFixImports: 'Use `fg` instead for boolean feature flags',
60
- },
61
- },
62
- create(context) {
63
- let changeMap: Map<any, any>;
64
-
65
- return {
66
- 'CallExpression[callee.name="getBooleanFF"]': (node: Node<any>) => {
67
- if (node.type !== 'CallExpression') {
68
- return;
69
- }
70
-
71
- changeMap = changeMap || new Map();
72
- validateUsage(node, 'getBooleanFF', context, changeMap);
73
- },
74
- 'Program:exit': (node: Program) => {
75
- if (changeMap?.size) {
76
- changeMap.forEach((changeCounts, importDeclaration) => {
77
- const [moduleScope] = getScope(context, node).childScopes;
78
- const importSpecifiers = new Set(
79
- importDeclaration.specifiers.map(({ imported }: any) => imported.name),
80
- );
81
-
82
- importSpecifiers.add('fg');
83
-
84
- Object.keys(changeCounts).forEach((utilName) => {
85
- if (changeCounts[utilName] === moduleScope.set.get(utilName)?.references.length) {
86
- importSpecifiers.delete(utilName);
87
- }
88
- });
89
-
90
- context.report({
91
- messageId: 'autoFixImports',
92
- node: importDeclaration,
93
- fix: (fixer) =>
94
- fixer.replaceText(
95
- importDeclaration,
96
- `import { ${Array.from(importSpecifiers).join(', ')} } from '${
97
- importDeclaration.source.value
98
- }';`,
99
- ),
100
- });
101
- });
102
-
103
- changeMap.clear();
104
- }
105
- },
106
- };
107
- },
108
- };
109
-
110
- export default rule;
@@ -1,3 +0,0 @@
1
- # Ensure `featureFlagName` is a string literal (feature-flags/static-feature-flags) 🍊
2
-
3
- Ensure feature flags are static
@@ -1,135 +0,0 @@
1
- import outdent from 'outdent';
2
- import { tester } from '../../../../__tests__/utils/_tester';
3
- import rule from '../index';
4
-
5
- const errors = [{ messageId: 'FFLiteral' }];
6
- const options = ['ssOnly'];
7
-
8
- tester.run('feature-flags/static-feature-flags', rule, {
9
- valid: [
10
- {
11
- code: outdent`
12
- import { ff } from '@atlassian/jira-feature-flagging';
13
-
14
- const isEnabled = ff('is.enabled');
15
- `,
16
- },
17
- {
18
- code: outdent`
19
- import { getFeatureFlagClient } from '@atlassian/jira-feature-flagging';
20
-
21
- const FLAG_NAME = 'flag';
22
- const isEnabled = getFeatureFlagClient().getBooleanValue(FLAG_NAME);
23
- `,
24
- },
25
- {
26
- code: outdent`
27
- import { fg } from '@atlassian/jira-feature-gating';
28
-
29
- const isEnabled = fg('fg_enabled');
30
- `,
31
- },
32
- {
33
- code: outdent`
34
- import { componentWithFF } from '@atlassian/jira-feature-flagging-utils';
35
-
36
- export const Component = componentWithFF('is_redesign', NewComponent, OldComponent);
37
- `,
38
- },
39
- {
40
- name: 'When using ssOnly option, Launch Darkly functions can avoid static usage',
41
- options,
42
- code: outdent`
43
- import { ff, getFeatureFlagValue, getMultivariateFeatureFlag } from '@atlassian/jira-feature-flagging';
44
- import { fg } from '@atlassian/jira-feature-gating';
45
- import { componentWithFF } from '@atlassian/jira-feature-flagging-utils';
46
-
47
- const FLAG_NAME = 'flag';
48
-
49
- const isEnabled = ff(FLAG_NAME);
50
- const FFValue = getFeatureFlagValue(FLAG_NAME, 10);
51
- const FFMultiValue = multiFFValuegetMultivariateFeatureFlag(FLAG_NAME, 10, [10, 20])
52
- const Component = componentWithFF(FLAG_NAME, NewComponent, OldComponent);
53
- `,
54
- },
55
- ],
56
- invalid: [
57
- {
58
- code: outdent`
59
- import { ff } from '@atlassian/jira-feature-flagging';
60
-
61
- const FLAG_NAME = 'flag';
62
- const isEnabled = ff(FLAG_NAME);
63
- `,
64
- output: outdent`
65
- import { ff } from '@atlassian/jira-feature-flagging';
66
-
67
- const FLAG_NAME = 'flag';
68
- const isEnabled = ff('flag');
69
- `,
70
- errors,
71
- },
72
- {
73
- code: outdent`
74
- import { FLAG_NAME } from 'names';
75
- import { ff } from '@atlassian/jira-feature-flagging';
76
-
77
- const isEnabled = ff(FLAG_NAME);
78
- `,
79
- errors,
80
- },
81
- {
82
- code: outdent`
83
- import { FLAG_NAME } from 'names';
84
- import { fg } from '@atlassian/jira-feature-gating';
85
-
86
- const isEnabled = fg(FLAG_NAME);
87
- `,
88
- errors,
89
- },
90
- {
91
- code: outdent`
92
- import { FLAG_NAME } from 'names';
93
- import { expVal } from '@atlassian/jira-feature-experiments';
94
-
95
- const isEnabled = expVal(FLAG_NAME, 'my_param', 'my_default_value');
96
- `,
97
- errors,
98
- },
99
- {
100
- code: outdent`
101
- import { FLAG_NAME } from 'names';
102
- import { UNSAFE_noExposureExp } from '@atlassian/jira-feature-experiments';
103
-
104
- const isEnabled = UNSAFE_noExposureExp(FLAG_NAME, 'my_param', 'my_default_value');
105
- `,
106
- errors,
107
- },
108
- {
109
- code: outdent`
110
- import { FLAG_NAME } from 'names';
111
- import { componentWithFF } from '@atlassian/jira-feature-flagging-utils';
112
-
113
- export const Component = componentWithFF(FLAG_NAME, NewComponent, OldComponent);
114
- `,
115
- errors,
116
- },
117
- {
118
- name: 'When using ssOnly option, Statsig functions should be static',
119
- options,
120
- code: outdent`
121
- import { fg } from '@atlassian/jira-feature-gating';
122
-
123
- const FLAG_NAME = 'flag';
124
- const isEnabled2 = fg(FLAG_NAME);
125
- `,
126
- output: outdent`
127
- import { fg } from '@atlassian/jira-feature-gating';
128
-
129
- const FLAG_NAME = 'flag';
130
- const isEnabled2 = fg('flag');
131
- `,
132
- errors,
133
- },
134
- ],
135
- });