@atlaskit/eslint-plugin-platform 2.6.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 (119) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/index.js +4 -3
  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/no-direct-document-usage/index.js +1 -1
  7. package/dist/cjs/rules/no-set-immediate/index.js +39 -0
  8. package/dist/cjs/rules/util/context-compat.js +4 -2
  9. package/dist/es2019/index.js +4 -3
  10. package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +2 -2
  11. package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  12. package/dist/es2019/rules/feature-gating/no-alias/index.js +1 -1
  13. package/dist/es2019/rules/no-direct-document-usage/index.js +1 -1
  14. package/dist/es2019/rules/no-set-immediate/index.js +33 -0
  15. package/dist/es2019/rules/util/context-compat.js +4 -2
  16. package/dist/esm/index.js +4 -3
  17. package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +2 -2
  18. package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +3 -0
  19. package/dist/esm/rules/feature-gating/no-alias/index.js +1 -1
  20. package/dist/esm/rules/no-direct-document-usage/index.js +1 -1
  21. package/dist/esm/rules/no-set-immediate/index.js +33 -0
  22. package/dist/esm/rules/util/context-compat.js +4 -2
  23. package/dist/types/index.d.ts +14 -6
  24. package/dist/types/rules/util/handle-ast-object.d.ts +1 -1
  25. package/dist/types-ts4.5/index.d.ts +14 -6
  26. package/dist/types-ts4.5/rules/util/handle-ast-object.d.ts +1 -1
  27. package/package.json +2 -2
  28. package/afm-cc/tsconfig.json +0 -24
  29. package/afm-jira/tsconfig.json +0 -24
  30. package/build/tsconfig.json +0 -17
  31. package/dist/cjs/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -79
  32. package/dist/es2019/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -62
  33. package/dist/esm/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -75
  34. package/src/__tests__/utils/_tester.tsx +0 -26
  35. package/src/index.tsx +0 -251
  36. package/src/rules/compiled/README.md +0 -3
  37. package/src/rules/compiled/expand-background-shorthand/README.md +0 -23
  38. package/src/rules/compiled/expand-background-shorthand/__tests__/rule.test.ts +0 -160
  39. package/src/rules/compiled/expand-background-shorthand/index.tsx +0 -43
  40. package/src/rules/compiled/expand-border-shorthand/README.md +0 -51
  41. package/src/rules/compiled/expand-border-shorthand/__tests__/rule.test.ts +0 -211
  42. package/src/rules/compiled/expand-border-shorthand/index.ts +0 -103
  43. package/src/rules/compiled/expand-spacing-shorthand/README.md +0 -38
  44. package/src/rules/compiled/expand-spacing-shorthand/__tests__/rule.test.ts +0 -448
  45. package/src/rules/compiled/expand-spacing-shorthand/index.ts +0 -240
  46. package/src/rules/constants.tsx +0 -20
  47. package/src/rules/ensure-atlassian-team/__tests__/unit/rule.test.ts +0 -24
  48. package/src/rules/ensure-atlassian-team/index.ts +0 -51
  49. package/src/rules/ensure-critical-dependency-resolutions/__test__/unit/rule.test.tsx +0 -200
  50. package/src/rules/ensure-critical-dependency-resolutions/index.tsx +0 -172
  51. package/src/rules/ensure-feature-flag-prefix/__tests__/unit/rule.test.tsx +0 -65
  52. package/src/rules/ensure-feature-flag-prefix/index.tsx +0 -81
  53. package/src/rules/ensure-feature-flag-registration/__tests__/unit/rule.test.tsx +0 -115
  54. package/src/rules/ensure-feature-flag-registration/index.tsx +0 -106
  55. package/src/rules/ensure-native-and-af-exports-synced/__tests__/unit/rule.test.tsx +0 -199
  56. package/src/rules/ensure-native-and-af-exports-synced/index.tsx +0 -188
  57. package/src/rules/ensure-no-private-dependencies/__tests__/unit/rule.test.ts +0 -212
  58. package/src/rules/ensure-no-private-dependencies/index.ts +0 -64
  59. package/src/rules/ensure-publish-valid/__tests__/unit/rule.test.ts +0 -39
  60. package/src/rules/ensure-publish-valid/index.ts +0 -81
  61. package/src/rules/ensure-test-runner-arguments/__tests__/unit/rule.test.tsx +0 -298
  62. package/src/rules/ensure-test-runner-arguments/index.tsx +0 -121
  63. package/src/rules/ensure-test-runner-nested-count/__tests__/unit/rule.test.tsx +0 -308
  64. package/src/rules/ensure-test-runner-nested-count/index.tsx +0 -82
  65. package/src/rules/ensure-valid-bin-values/__tests__/unit/rule.test.ts +0 -159
  66. package/src/rules/ensure-valid-bin-values/index.ts +0 -70
  67. package/src/rules/ensure-valid-platform-yarn-protocol-usage/__tests__/unit/rule.test.ts +0 -147
  68. package/src/rules/ensure-valid-platform-yarn-protocol-usage/index.ts +0 -67
  69. package/src/rules/feature-gating/README.md +0 -8
  70. package/src/rules/feature-gating/inline-usage/README.md +0 -53
  71. package/src/rules/feature-gating/inline-usage/__tests__/rule.test.tsx +0 -106
  72. package/src/rules/feature-gating/inline-usage/index.tsx +0 -135
  73. package/src/rules/feature-gating/no-alias/README.md +0 -29
  74. package/src/rules/feature-gating/no-alias/__tests__/rule.test.tsx +0 -76
  75. package/src/rules/feature-gating/no-alias/index.tsx +0 -80
  76. package/src/rules/feature-gating/no-module-level-eval/README.md +0 -53
  77. package/src/rules/feature-gating/no-module-level-eval/__tests__/test.tsx +0 -133
  78. package/src/rules/feature-gating/no-module-level-eval/index.tsx +0 -54
  79. package/src/rules/feature-gating/no-module-level-eval-nav4/README.md +0 -8
  80. package/src/rules/feature-gating/no-module-level-eval-nav4/__tests__/test.tsx +0 -130
  81. package/src/rules/feature-gating/no-module-level-eval-nav4/index.tsx +0 -73
  82. package/src/rules/feature-gating/no-preconditioning/README.md +0 -69
  83. package/src/rules/feature-gating/no-preconditioning/__tests__/rule.test.tsx +0 -164
  84. package/src/rules/feature-gating/no-preconditioning/index.tsx +0 -138
  85. package/src/rules/feature-gating/prefer-fg/README.md +0 -3
  86. package/src/rules/feature-gating/prefer-fg/__tests__/rule.test.tsx +0 -83
  87. package/src/rules/feature-gating/prefer-fg/index.tsx +0 -110
  88. package/src/rules/feature-gating/static-feature-flags/README.md +0 -3
  89. package/src/rules/feature-gating/static-feature-flags/__tests__/test.tsx +0 -135
  90. package/src/rules/feature-gating/static-feature-flags/index.tsx +0 -103
  91. package/src/rules/feature-gating/use-recommended-utils/README.md +0 -67
  92. package/src/rules/feature-gating/use-recommended-utils/__tests__/rule.test.tsx +0 -78
  93. package/src/rules/feature-gating/use-recommended-utils/index.tsx +0 -57
  94. package/src/rules/feature-gating/utils.tsx +0 -48
  95. package/src/rules/no-direct-document-usage/index.tsx +0 -109
  96. package/src/rules/no-duplicate-dependencies/__tests__/unit/rule.test.ts +0 -116
  97. package/src/rules/no-duplicate-dependencies/index.ts +0 -79
  98. package/src/rules/no-invalid-feature-flag-usage/__tests__/unit/rule.test.tsx +0 -69
  99. package/src/rules/no-invalid-feature-flag-usage/index.tsx +0 -128
  100. package/src/rules/no-invalid-storybook-decorator-usage/__tests__/unit/rule.test.tsx +0 -18
  101. package/src/rules/no-invalid-storybook-decorator-usage/index.tsx +0 -39
  102. package/src/rules/no-pre-post-installs/__tests__/unit/rule.test.ts +0 -41
  103. package/src/rules/no-pre-post-installs/index.ts +0 -35
  104. package/src/rules/no-sparse-checkout/__tests__/unit/rule.test.tsx +0 -48
  105. package/src/rules/no-sparse-checkout/index.tsx +0 -54
  106. package/src/rules/use-entrypoints-in-examples/README.md +0 -27
  107. package/src/rules/use-entrypoints-in-examples/__tests__/rule.test.tsx +0 -34
  108. package/src/rules/use-entrypoints-in-examples/index.tsx +0 -43
  109. package/src/rules/util/__tests__/context-compat.test.ts +0 -122
  110. package/src/rules/util/compiled-utils.ts +0 -27
  111. package/src/rules/util/context-compat.ts +0 -41
  112. package/src/rules/util/file-exclusions.ts +0 -39
  113. package/src/rules/util/handle-ast-object.ts +0 -33
  114. package/src/rules/util/registration-utils.ts +0 -59
  115. package/tsconfig.app.json +0 -43
  116. package/tsconfig.dev.json +0 -40
  117. package/tsconfig.json +0 -23
  118. /package/dist/types/rules/{ensure-valid-platform-yarn-protocol-usage → no-set-immediate}/index.d.ts +0 -0
  119. /package/dist/types-ts4.5/rules/{ensure-valid-platform-yarn-protocol-usage → no-set-immediate}/index.d.ts +0 -0
@@ -1,80 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- import {
3
- FEATURE_API_IMPORT_SOURCES,
4
- FEATURE_MOCKS_IMPORT_SOURCES,
5
- FEATURE_UTILS_IMPORT_SOURCES,
6
- } from '../../constants';
7
- import { isIdentifierImportedFrom, type Node } from '../utils';
8
-
9
- const IMPORT_SOURCES = new Set([
10
- ...FEATURE_API_IMPORT_SOURCES,
11
- ...FEATURE_MOCKS_IMPORT_SOURCES,
12
- ...FEATURE_UTILS_IMPORT_SOURCES,
13
- ]);
14
-
15
- const rule: Rule.RuleModule = {
16
- meta: {
17
- docs: {
18
- url: 'https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/platform/packages/platform/eslint-plugin/src/rules/no-alias/README.md',
19
- description:
20
- 'Disallow aliasing of feature flag utils to ensure feature flag usage is statically analyzable',
21
- },
22
- messages: {
23
- noSpecifierAlias:
24
- 'Do not alias feature flag utils. Feature flag usage should be statically analyzable',
25
- noNamespaceSpecifier:
26
- 'Destructure feature flag utils from import. Feature flag usage should be statically analyzable',
27
- noReassignment:
28
- 'Do not reassign feature flag utils. Feature flag usage should be statically analyzable',
29
- },
30
- },
31
- create(context) {
32
- return {
33
- ImportDeclaration: (node) => {
34
- if (typeof node.source.value === 'string' && !IMPORT_SOURCES.has(node.source.value)) {
35
- return;
36
- }
37
-
38
- node.specifiers?.forEach((specifier) => {
39
- if (specifier.type === 'ImportSpecifier') {
40
- const { imported, local } = specifier;
41
-
42
- if (imported.name !== local.name) {
43
- context.report({
44
- messageId: 'noSpecifierAlias',
45
- node: specifier,
46
- });
47
- }
48
- } else if (specifier.type === 'ImportNamespaceSpecifier') {
49
- context.report({
50
- messageId: 'noNamespaceSpecifier',
51
- node: specifier,
52
- });
53
- }
54
- });
55
- },
56
- 'VariableDeclaration[kind="const"] > VariableDeclarator[id.type="Identifier"][init.type="Identifier"]':
57
- (node: Node<'VariableDeclarator'>) => {
58
- if (!node.init || node.init.type !== 'Identifier') {
59
- return;
60
- }
61
-
62
- const isReassignment = isIdentifierImportedFrom(
63
- node.init.name,
64
- IMPORT_SOURCES,
65
- context,
66
- node,
67
- );
68
-
69
- if (isReassignment) {
70
- context.report({
71
- messageId: 'noReassignment',
72
- node,
73
- });
74
- }
75
- },
76
- };
77
- },
78
- };
79
-
80
- export default rule;
@@ -1,53 +0,0 @@
1
- # Avoid using feature flags at module level (feature-flags/no-module-level-eval)
2
-
3
- Disallow usage of feature flags in global/module scope. Due to JFE using SSR, feature flags should
4
- not be called/initialised before components have mounted as it could cause errors.
5
-
6
- ## Examples
7
-
8
- 👎 Examples of **incorrect** code for this rule: ff is called in module scope
9
-
10
- ```tsx
11
- import { ff } from '@atlassian/jira-feature-flagging';
12
- import { OldComponent } from './old';
13
- import { NewComponent } from './new';
14
-
15
- const isEnabled = ff('is_enabled');
16
-
17
- export const Component = ff('is_redesign') ? NewComponent : OldComponent;
18
- ```
19
-
20
- 👍 Examples of **correct** code to feature flag components: use ComponentWithFF
21
-
22
- ```tsx
23
- import { ff } from '@atlassian/jira-feature-flagging';
24
- import { ComponentWithFF } from '@atlassian/jira-feature-flagging-utils';
25
-
26
- export const Container = () => {
27
- const isEnabled = ff('is_enabled');
28
- };
29
-
30
- export const Component = ComponentWithFF('is_redesign', NewComponent, OldComponent);
31
- ```
32
-
33
- 👍 Examples of **correct** code to feature flag functions: use functionWithCondition
34
-
35
- ```tsx
36
- import { ff } from '@atlassian/jira-feature-flagging';
37
- import { functionWithCondition } from '@atlassian/jira-feature-flagging-utils';
38
-
39
- const selectorOld = createSelector(
40
- permissionsSelector,
41
- (permissions: Permissions): boolean => permissions.canCreateChildren,
42
- );
43
-
44
- // Change the signature of the selector by adding new arguments
45
- const selectorNew = createSelector(
46
- permissionsSelector,
47
- supportedChildIssueTypesSelector,
48
- (permissions: Permissions, supportedChildIssueTypes: IssueTypeForHierarchyLevel[]): boolean =>
49
- permissions.canCreateChildren && !!supportedChildIssueTypes.length,
50
- );
51
-
52
- export const selector = functionWithCondition(() => ff('is_enabled'), selectorNew, selectorOld);
53
- ```
@@ -1,133 +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-module-level-eval', rule, {
6
- valid: [
7
- {
8
- name: 'Variable declaration within arrow function',
9
- code: outdent`
10
- import { ff } from '@atlassian/jira-feature-flagging';
11
-
12
- const Component = () => {
13
- const ff1 = ff('flag');
14
- }
15
- `,
16
- },
17
- {
18
- name: 'Boolean within arrow function',
19
- code: outdent`
20
- import { ff } from '@atlassian/jira-feature-flagging';
21
-
22
- const someFunction = () => {
23
- if(ff('flag')) {
24
- return true;
25
- }
26
- }
27
- `,
28
- },
29
- {
30
- name: 'Immediate call within a function',
31
- code: outdent`
32
- import { ff } from '@atlassian/jira-feature-flagging';
33
-
34
- function someFunction() {
35
- return ff('flag');
36
- }
37
- `,
38
- },
39
-
40
- {
41
- name: 'Does not affect other function calls in global scope',
42
- code: outdent`
43
- import { foo } from 'foo-lib'
44
- import { ff } from '@atlassian/jira-feature-flagging';
45
-
46
- foo();
47
- `,
48
- },
49
- {
50
- name: 'Variable declaration within an arrow function with different import source',
51
- code: outdent`
52
- import { ff } from '@atlassian/jira-feature-flagging-using-meta';
53
-
54
- const Component = () => {
55
- const ff1 = ff('flag');
56
- }
57
- `,
58
- },
59
- {
60
- name: 'Class field',
61
- code: outdent`
62
- import { ff } from '@atlassian/jira-feature-flagging';
63
-
64
- export class Component {
65
- value = ff('flag') ? 'newValue' : 'oldValue';
66
- }
67
- `,
68
- },
69
- ],
70
- invalid: [
71
- {
72
- name: 'Immediate call',
73
- code: outdent`
74
- import { ff } from '@atlassian/jira-feature-flagging';
75
-
76
- ff('flag');
77
- `,
78
- errors: [{ messageId: 'noModuleLevelEval' }],
79
- },
80
- {
81
- name: 'Module level storage into a variable',
82
- code: outdent`
83
- import { ff } from '@atlassian/jira-feature-flagging';
84
-
85
- const flag = ff('flag');
86
- `,
87
- errors: [{ messageId: 'noModuleLevelEval' }],
88
- },
89
- {
90
- name: 'Module level collection',
91
- code: outdent`
92
- import { ff } from '@atlassian/jira-feature-flagging';
93
-
94
- const collection = [ff('flag') ? 0 : 1];
95
- `,
96
- errors: [{ messageId: 'noModuleLevelEval' }],
97
- },
98
- {
99
- name: 'Module level object',
100
- code: outdent`
101
- import { ff } from '@atlassian/jira-feature-flagging';
102
-
103
- const object = {
104
- isEnabled: ff('flag')
105
- }
106
- `,
107
- errors: [{ messageId: 'noModuleLevelEval' }],
108
- },
109
- {
110
- name: 'Module level with boolean',
111
- code: outdent`
112
- import { ff } from '@atlassian/jira-feature-flagging';
113
- import { NewComp } from 'new';
114
- import { OldComp } from 'old';
115
-
116
- export const Component = ff('flag') ? NewComp : OldComp;
117
- `,
118
- errors: [{ messageId: 'noModuleLevelEval' }],
119
- },
120
- // This test fails unsure what the environment difference is between Jira + Platform.
121
- // {
122
- // name: 'Static class field',
123
- // code: outdent`
124
- // import { ff } from '@atlassian/jira-feature-flagging';
125
-
126
- // export class Component {
127
- // static value = ff('flag') ? 'newValue' : 'oldValue';
128
- // }
129
- // `,
130
- // errors: [{ messageId: 'noModuleLevelEval' }],
131
- // },
132
- ],
133
- });
@@ -1,54 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- import type { Node } from 'estree';
3
- import { isAPIimport } from '../utils';
4
- import { getScope } from '../../util/context-compat';
5
-
6
- const isInFunctionLevel = (context: Rule.RuleContext, node: Node) => {
7
- let scope: any = getScope(context, node);
8
-
9
- while (scope?.type !== 'module' && scope?.type !== 'global') {
10
- if (scope.type === 'function') {
11
- return true;
12
- }
13
-
14
- if (scope.type === 'class-field-initializer') {
15
- return !scope.block.parent.static;
16
- }
17
-
18
- scope = scope.upper;
19
- }
20
-
21
- return false;
22
- };
23
-
24
- const rule: Rule.RuleModule = {
25
- meta: {
26
- docs: {
27
- description: 'Disallow feature flag usage at module level',
28
- url: 'https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/platform/packages/platform/eslint-plugin/src/rules/no-module-level-eval/README.md',
29
- },
30
- messages: {
31
- noModuleLevelEval:
32
- 'Do not evaluate feature flags in the module level, it will always resolve to false when server side rendered or when flags are loaded async.',
33
- },
34
- },
35
- create(context) {
36
- return {
37
- 'CallExpression[callee.type="Identifier"]': (node: Node) => {
38
- if (
39
- node.type === 'CallExpression' &&
40
- node.callee.type === 'Identifier' &&
41
- isAPIimport(node.callee.name, context, node) &&
42
- !isInFunctionLevel(context, node)
43
- ) {
44
- context.report({
45
- messageId: 'noModuleLevelEval',
46
- node,
47
- });
48
- }
49
- },
50
- };
51
- },
52
- };
53
-
54
- export default rule;
@@ -1,8 +0,0 @@
1
- # Avoid using navigation/visual refresh feature flag functions at module level (feature-flags/no-module-level-eval-nav4)
2
-
3
- Disallow usage of navigation/visual refresh feature flag functions in global/module scope. Due to
4
- JFE using SSR, feature flags should not be called/initialised before components have mounted as it
5
- could cause errors.
6
-
7
- This is a temporary lint rule cloned from no-module-level-nav4 while we explore pulling these
8
- evaluations inline https://atlassian.slack.com/archives/CFGLH1ZS8/p1726449739284819
@@ -1,130 +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-module-level-eval', rule, {
6
- valid: [
7
- {
8
- name: 'Variable declaration within arrow function',
9
- code: outdent`
10
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
11
-
12
- const Component = () => {
13
- const ff1 = getWillShowNav4();
14
- }
15
- `,
16
- },
17
- {
18
- name: 'Boolean within arrow function',
19
- code: outdent`
20
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
21
-
22
- const someFunction = () => {
23
- if(getWillShowNav4()) {
24
- return true;
25
- }
26
- }
27
- `,
28
- },
29
- {
30
- name: 'Immediate call within a function',
31
- code: outdent`
32
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
33
-
34
- function someFunction() {
35
- return getWillShowNav4();
36
- }
37
- `,
38
- },
39
-
40
- {
41
- name: 'Does not affect other function calls in global scope',
42
- code: outdent`
43
- import { foo } from 'foo-lib'
44
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
45
-
46
- foo();
47
- `,
48
- },
49
- {
50
- name: 'Variable declaration within an arrow function with different import source',
51
- code: outdent`
52
- import { ff } from '@atlassian/jira-feature-flagging-using-meta';
53
-
54
- const Component = () => {
55
- const ff1 = getWillShowNav4();
56
- }
57
- `,
58
- },
59
- {
60
- name: 'Class field',
61
- code: outdent`
62
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
63
-
64
- export class Component {
65
- value = getWillShowNav4() ? 'newValue' : 'oldValue';
66
- }
67
- `,
68
- },
69
- ],
70
- invalid: [
71
- {
72
- name: 'Immediate call',
73
- code: outdent`
74
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
75
-
76
- getWillShowNav4();
77
- `,
78
- errors: [{ messageId: 'noModuleLevelEval' }],
79
- },
80
- {
81
- name: 'Immediate call isVisualRefreshEnabled',
82
- code: outdent`
83
- import { isVisualRefreshEnabled } from '@atlassian/jira-visual-refresh-rollout/src/feature-switch';
84
-
85
- isVisualRefreshEnabled();
86
- `,
87
- errors: [{ messageId: 'noModuleLevelEval' }],
88
- },
89
- {
90
- name: 'Module level storage into a variable',
91
- code: outdent`
92
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
93
-
94
- const flag = getWillShowNav4();
95
- `,
96
- errors: [{ messageId: 'noModuleLevelEval' }],
97
- },
98
- {
99
- name: 'Module level collection',
100
- code: outdent`
101
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
102
-
103
- const collection = [getWillShowNav4() ? 0 : 1];
104
- `,
105
- errors: [{ messageId: 'noModuleLevelEval' }],
106
- },
107
- {
108
- name: 'Module level object',
109
- code: outdent`
110
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
111
-
112
- const object = {
113
- isEnabled: getWillShowNav4()
114
- }
115
- `,
116
- errors: [{ messageId: 'noModuleLevelEval' }],
117
- },
118
- {
119
- name: 'Module level with boolean',
120
- code: outdent`
121
- import { getWillShowNav4 } from '@atlassian/jira-navigation-apps-sidebar-nav4-rollout';
122
- import { NewComp } from 'new';
123
- import { OldComp } from 'old';
124
-
125
- export const Component = getWillShowNav4() ? NewComp : OldComp;
126
- `,
127
- errors: [{ messageId: 'noModuleLevelEval' }],
128
- },
129
- ],
130
- });
@@ -1,73 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- import type { Node } from 'estree';
3
- import { getScope } from '../../util/context-compat';
4
-
5
- const featureLibraryFunctions = new Set([
6
- /*
7
- * STOP!
8
- *
9
- * Your code should call the API functions directly!
10
- * But, we are temporarily adding these methods to prevent SSR builds from breaking
11
- * while we work out a solution for the features to be evaluated inline.
12
- * Do not add anything here without the permission of #help-jfp-squads
13
- *
14
- * Slack thread: https://atlassian.slack.com/archives/CFGLH1ZS8/p1726449739284819
15
- */
16
- 'isVisualRefreshEnabled',
17
- 'getMetaBoolean',
18
- 'getNav4Rollout',
19
- 'getWillShowNav3',
20
- 'getWillShowNav4',
21
- 'getWillShowNav4UserOptIn',
22
- 'getWillShowNav4UserOptOut',
23
- ]);
24
-
25
- const isInFunctionLevel = (context: Rule.RuleContext, node: Node) => {
26
- let scope: any = getScope(context, node);
27
-
28
- while (scope?.type !== 'module' && scope?.type !== 'global') {
29
- if (scope.type === 'function') {
30
- return true;
31
- }
32
-
33
- if (scope.type === 'class-field-initializer') {
34
- return !scope.block.parent.static;
35
- }
36
-
37
- scope = scope.upper;
38
- }
39
-
40
- return false;
41
- };
42
-
43
- const rule: Rule.RuleModule = {
44
- meta: {
45
- docs: {
46
- description: 'Disallow getWillShowNav4 or isVisualRefreshEnabled usage at module level',
47
- url: 'https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/platform/packages/platform/eslint-plugin/src/rules/no-module-level-eval-nav4/README.md',
48
- },
49
- messages: {
50
- noModuleLevelEval:
51
- 'Do not evaluate getWillShowNav4 or isVisualRefreshEnabled at module level. This causes complications with SSR. If feature flagging components in `jira` use `componentWithCondition` from `@atlassian/jira-feature-flagging-utils`.',
52
- },
53
- },
54
- create(context) {
55
- return {
56
- 'CallExpression[callee.type="Identifier"]': (node: Node) => {
57
- if (
58
- node.type === 'CallExpression' &&
59
- node.callee.type === 'Identifier' &&
60
- featureLibraryFunctions.has(node.callee.name) &&
61
- !isInFunctionLevel(context, node)
62
- ) {
63
- context.report({
64
- messageId: 'noModuleLevelEval',
65
- node,
66
- });
67
- }
68
- },
69
- };
70
- },
71
- };
72
-
73
- export default rule;
@@ -1,69 +0,0 @@
1
- # Enforce feature gate not used as a precondition in logical expressions (feature-flags/no-preconditioning)
2
-
3
- Using a feature gate as a precondition in logical expressions can cause:
4
-
5
- - Exposure to be tracked incorrectly
6
- - Unnecessary code
7
-
8
- ## Examples
9
-
10
- Instead of adding prerequisite gating in code, configure this in Statsig UI by adding rules to gates
11
- and targeting for experiments. This will reduce unnecessary complexity in code and simplify cleanup.
12
-
13
- ```tsx
14
- import { fg } from '@atlassian/jira-feature-gating';
15
- import { expVal } from '@atlassian/jira-feature-experiments';
16
-
17
- // Setup rule in `feature_milestone2` to fail if `feature_milestone1` fails
18
- if (fg('feature_milestone1') && fg('feature_milestone2')) {
19
- doSomething();
20
- }
21
-
22
- // Setup targeting in `my_exp` to only enroll participants passing `my_gate`
23
- if (fg('my_gate') && expVal('my_exp')) {
24
- doSomething();
25
- }
26
- ```
27
-
28
- Gating experiment values from same or different experiments is valid.
29
-
30
- ```tsx
31
- import { expVal } from '@atlassian/jira-feature-experiments';
32
-
33
- if (expVal('my_exp', 'delayRetries', false) && expVal('my_exp', 'numRetries', 0) > 3) {
34
- doSomething();
35
- }
36
- ```
37
-
38
- 👎 Examples of **incorrect** exposure tracking
39
-
40
- If `isAdmin` is false exposure will be fired even though feature was not intended for audience.
41
-
42
- ```tsx
43
- import { fg } from '@atlassian/jira-feature-gating';
44
-
45
- if (fg('my_gate') && isAdmin) {
46
- doSomething();
47
- }
48
- ```
49
-
50
- If `hasSelectedProject` is false and experiment returns true. Expsoure will be tracked as true even
51
- though feature was not exposed to the user.
52
-
53
- ```tsx
54
- import { fg } from '@atlassian/jira-feature-gating';
55
-
56
- if (expVal('my_experiment', 'is_enabled', false) && hasSelectedProject) {
57
- doSomething();
58
- }
59
- ```
60
-
61
- 👍 Examples of **correct** exposure tracking
62
-
63
- ```tsx
64
- import { fg } from '@atlassian/jira-feature-gating';
65
-
66
- if (isAdmin && fg('my_gate')) {
67
- doSomething();
68
- }
69
- ```