@atlaskit/eslint-plugin-platform 2.7.0 → 2.7.1

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 (114) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/index.js +0 -2
  3. package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +2 -2
  4. package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  5. package/dist/cjs/rules/feature-gating/no-alias/index.js +1 -1
  6. package/dist/cjs/rules/util/context-compat.js +4 -2
  7. package/dist/es2019/index.js +0 -2
  8. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +2 -2
  9. package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  10. package/dist/es2019/rules/feature-gating/no-alias/index.js +1 -1
  11. package/dist/es2019/rules/util/context-compat.js +4 -2
  12. package/dist/esm/index.js +0 -2
  13. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +2 -2
  14. package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  15. package/dist/esm/rules/feature-gating/no-alias/index.js +1 -1
  16. package/dist/esm/rules/util/context-compat.js +4 -2
  17. package/dist/types/index.d.ts +4 -6
  18. package/dist/types/rules/util/handle-ast-object.d.ts +1 -1
  19. package/dist/types-ts4.5/index.d.ts +4 -6
  20. package/dist/types-ts4.5/rules/util/handle-ast-object.d.ts +1 -1
  21. package/package.json +2 -2
  22. package/afm-cc/tsconfig.json +0 -24
  23. package/afm-jira/tsconfig.json +0 -24
  24. package/build/tsconfig.json +0 -17
  25. package/dist/cjs/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -79
  26. package/dist/es2019/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -62
  27. package/dist/esm/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -75
  28. package/dist/types/rules/ensure-valid-platform-yarn-protocol-usage/index.d.ts +0 -3
  29. package/dist/types-ts4.5/rules/ensure-valid-platform-yarn-protocol-usage/index.d.ts +0 -3
  30. package/src/__tests__/utils/_tester.tsx +0 -26
  31. package/src/index.tsx +0 -254
  32. package/src/rules/compiled/README.md +0 -3
  33. package/src/rules/compiled/expand-background-shorthand/README.md +0 -23
  34. package/src/rules/compiled/expand-background-shorthand/__tests__/rule.test.ts +0 -160
  35. package/src/rules/compiled/expand-background-shorthand/index.tsx +0 -43
  36. package/src/rules/compiled/expand-border-shorthand/README.md +0 -51
  37. package/src/rules/compiled/expand-border-shorthand/__tests__/rule.test.ts +0 -211
  38. package/src/rules/compiled/expand-border-shorthand/index.ts +0 -103
  39. package/src/rules/compiled/expand-spacing-shorthand/README.md +0 -38
  40. package/src/rules/compiled/expand-spacing-shorthand/__tests__/rule.test.ts +0 -448
  41. package/src/rules/compiled/expand-spacing-shorthand/index.ts +0 -240
  42. package/src/rules/constants.tsx +0 -20
  43. package/src/rules/ensure-atlassian-team/__tests__/unit/rule.test.ts +0 -24
  44. package/src/rules/ensure-atlassian-team/index.ts +0 -51
  45. package/src/rules/ensure-critical-dependency-resolutions/__test__/unit/rule.test.tsx +0 -200
  46. package/src/rules/ensure-critical-dependency-resolutions/index.tsx +0 -172
  47. package/src/rules/ensure-feature-flag-prefix/__tests__/unit/rule.test.tsx +0 -65
  48. package/src/rules/ensure-feature-flag-prefix/index.tsx +0 -81
  49. package/src/rules/ensure-feature-flag-registration/__tests__/unit/rule.test.tsx +0 -115
  50. package/src/rules/ensure-feature-flag-registration/index.tsx +0 -106
  51. package/src/rules/ensure-native-and-af-exports-synced/__tests__/unit/rule.test.tsx +0 -199
  52. package/src/rules/ensure-native-and-af-exports-synced/index.tsx +0 -188
  53. package/src/rules/ensure-no-private-dependencies/__tests__/unit/rule.test.ts +0 -212
  54. package/src/rules/ensure-no-private-dependencies/index.ts +0 -64
  55. package/src/rules/ensure-publish-valid/__tests__/unit/rule.test.ts +0 -39
  56. package/src/rules/ensure-publish-valid/index.ts +0 -81
  57. package/src/rules/ensure-test-runner-arguments/__tests__/unit/rule.test.tsx +0 -298
  58. package/src/rules/ensure-test-runner-arguments/index.tsx +0 -121
  59. package/src/rules/ensure-test-runner-nested-count/__tests__/unit/rule.test.tsx +0 -308
  60. package/src/rules/ensure-test-runner-nested-count/index.tsx +0 -82
  61. package/src/rules/ensure-valid-bin-values/__tests__/unit/rule.test.ts +0 -159
  62. package/src/rules/ensure-valid-bin-values/index.ts +0 -70
  63. package/src/rules/ensure-valid-platform-yarn-protocol-usage/__tests__/unit/rule.test.ts +0 -147
  64. package/src/rules/ensure-valid-platform-yarn-protocol-usage/index.ts +0 -67
  65. package/src/rules/feature-gating/README.md +0 -8
  66. package/src/rules/feature-gating/inline-usage/README.md +0 -53
  67. package/src/rules/feature-gating/inline-usage/__tests__/rule.test.tsx +0 -106
  68. package/src/rules/feature-gating/inline-usage/index.tsx +0 -135
  69. package/src/rules/feature-gating/no-alias/README.md +0 -29
  70. package/src/rules/feature-gating/no-alias/__tests__/rule.test.tsx +0 -76
  71. package/src/rules/feature-gating/no-alias/index.tsx +0 -80
  72. package/src/rules/feature-gating/no-module-level-eval/README.md +0 -53
  73. package/src/rules/feature-gating/no-module-level-eval/__tests__/test.tsx +0 -133
  74. package/src/rules/feature-gating/no-module-level-eval/index.tsx +0 -54
  75. package/src/rules/feature-gating/no-module-level-eval-nav4/README.md +0 -8
  76. package/src/rules/feature-gating/no-module-level-eval-nav4/__tests__/test.tsx +0 -130
  77. package/src/rules/feature-gating/no-module-level-eval-nav4/index.tsx +0 -73
  78. package/src/rules/feature-gating/no-preconditioning/README.md +0 -69
  79. package/src/rules/feature-gating/no-preconditioning/__tests__/rule.test.tsx +0 -164
  80. package/src/rules/feature-gating/no-preconditioning/index.tsx +0 -138
  81. package/src/rules/feature-gating/prefer-fg/README.md +0 -3
  82. package/src/rules/feature-gating/prefer-fg/__tests__/rule.test.tsx +0 -83
  83. package/src/rules/feature-gating/prefer-fg/index.tsx +0 -110
  84. package/src/rules/feature-gating/static-feature-flags/README.md +0 -3
  85. package/src/rules/feature-gating/static-feature-flags/__tests__/test.tsx +0 -135
  86. package/src/rules/feature-gating/static-feature-flags/index.tsx +0 -103
  87. package/src/rules/feature-gating/use-recommended-utils/README.md +0 -67
  88. package/src/rules/feature-gating/use-recommended-utils/__tests__/rule.test.tsx +0 -78
  89. package/src/rules/feature-gating/use-recommended-utils/index.tsx +0 -57
  90. package/src/rules/feature-gating/utils.tsx +0 -48
  91. package/src/rules/no-direct-document-usage/index.tsx +0 -111
  92. package/src/rules/no-duplicate-dependencies/__tests__/unit/rule.test.ts +0 -116
  93. package/src/rules/no-duplicate-dependencies/index.ts +0 -79
  94. package/src/rules/no-invalid-feature-flag-usage/__tests__/unit/rule.test.tsx +0 -69
  95. package/src/rules/no-invalid-feature-flag-usage/index.tsx +0 -128
  96. package/src/rules/no-invalid-storybook-decorator-usage/__tests__/unit/rule.test.tsx +0 -18
  97. package/src/rules/no-invalid-storybook-decorator-usage/index.tsx +0 -39
  98. package/src/rules/no-pre-post-installs/__tests__/unit/rule.test.ts +0 -41
  99. package/src/rules/no-pre-post-installs/index.ts +0 -35
  100. package/src/rules/no-set-immediate/index.tsx +0 -43
  101. package/src/rules/no-sparse-checkout/__tests__/unit/rule.test.tsx +0 -48
  102. package/src/rules/no-sparse-checkout/index.tsx +0 -54
  103. package/src/rules/use-entrypoints-in-examples/README.md +0 -27
  104. package/src/rules/use-entrypoints-in-examples/__tests__/rule.test.tsx +0 -34
  105. package/src/rules/use-entrypoints-in-examples/index.tsx +0 -43
  106. package/src/rules/util/__tests__/context-compat.test.ts +0 -122
  107. package/src/rules/util/compiled-utils.ts +0 -27
  108. package/src/rules/util/context-compat.ts +0 -41
  109. package/src/rules/util/file-exclusions.ts +0 -39
  110. package/src/rules/util/handle-ast-object.ts +0 -33
  111. package/src/rules/util/registration-utils.ts +0 -59
  112. package/tsconfig.app.json +0 -43
  113. package/tsconfig.dev.json +0 -40
  114. package/tsconfig.json +0 -23
@@ -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
- });