@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,147 +0,0 @@
1
- import { tester } from '../../../../__tests__/utils/_tester';
2
- import rule from '../../index';
3
-
4
- const cwd = process.cwd();
5
-
6
- describe('test ensure-valid-platform-yarn-protocol-usage rule', () => {
7
- tester.run('workspace protocol', rule, {
8
- valid: [
9
- // Workspace protocol 'workspace:*' is allowed in packages as dependencies
10
- {
11
- code: `const foo = {
12
- "dependencies": {
13
- "@atlaskit/button": "workspace:*",
14
- "@atlaskit/primitives": "workspace:*",
15
- "@atlaskit/tokens": "workspace:*"
16
- }
17
- }`,
18
- filename: `${cwd}/packages/foo/package.json`,
19
- },
20
- // Workspace protocol 'workspace:*' is allowed in packages as devDependencies
21
- {
22
- code: `const foo = {
23
- "devDependencies": {
24
- "@atlaskit/button": "workspace:*",
25
- "@atlaskit/primitives": "workspace:*"
26
- }
27
- }`,
28
- filename: `${cwd}/packages/foo/package.json`,
29
- },
30
- // Workspace protocol is allowed in packages dependencies and devDependencies
31
- {
32
- code: `const foo = {
33
- "dependencies": {
34
- "@atlaskit/button": "workspace:*",
35
- "@atlaskit/primitives": "workspace:*"
36
- },
37
- "devDependencies": {
38
- "@atlaskit/button": "workspace:*",
39
- "@atlaskit/primitives": "workspace:*"
40
- }
41
- }`,
42
- filename: `${cwd}/packages/foo/package.json`,
43
- },
44
- ],
45
- invalid: [],
46
- });
47
-
48
- tester.run('root: protocol', rule, {
49
- // 'root:' protocol is not allowed in any platform package
50
- valid: [],
51
- invalid: [
52
- // 'root:' protocol is not allowed in packages as dependencies
53
- {
54
- code: `const foo = {
55
- "dependencies": {
56
- "react": "root:*",
57
- }
58
- }`,
59
- filename: `${cwd}/packages/foo/package.json`,
60
- errors: [
61
- {
62
- messageId: 'invalidRootProtocolUsage',
63
- },
64
- ],
65
- },
66
- // 'root:' protocol is not allowed in private packages as dependencies
67
- {
68
- code: `const foo = {
69
- "private": true,
70
- "dependencies": {
71
- "react": "root:*",
72
- }
73
- }`,
74
- filename: `${cwd}/packages/foo/package.json`,
75
- errors: [
76
- {
77
- messageId: 'invalidRootProtocolUsage',
78
- },
79
- ],
80
- },
81
- // 'root:' protocol is not allowed in public packages as devDependencies
82
- {
83
- code: `const foo = {
84
- "devDependencies": {
85
- "react": "root:*",
86
- }
87
- }`,
88
- filename: `${cwd}/packages/foo/package.json`,
89
- errors: [
90
- {
91
- messageId: 'invalidRootProtocolUsage',
92
- },
93
- ],
94
- },
95
- // 'root:' protocol is not allowed in private packages as devDependencies
96
- {
97
- code: `const foo = {
98
- "private": true,
99
- "devDependencies": {
100
- "react": "root:*",
101
- }
102
- }`,
103
- filename: `${cwd}/packages/foo/package.json`,
104
- errors: [
105
- {
106
- messageId: 'invalidRootProtocolUsage',
107
- },
108
- ],
109
- },
110
- // 'root:' protocol is not allowed in public packages as dependencies and devDependencies
111
- {
112
- code: `const foo = {
113
- "dependencies": {
114
- "lodash": "root:*"
115
- },
116
- "devDependencies": {
117
- "react": "root:*"
118
- }
119
- }`,
120
- filename: `${cwd}/packages/foo/package.json`,
121
- errors: [
122
- {
123
- messageId: 'invalidRootProtocolUsage',
124
- },
125
- ],
126
- },
127
- // 'root:' protocol is not allowed in private packages as dependencies and devDependencies
128
- {
129
- code: `const foo = {
130
- "private": true,
131
- "dependencies": {
132
- "lodash": "root:*"
133
- },
134
- "devDependencies": {
135
- "react": "root:*"
136
- }
137
- }`,
138
- filename: `${cwd}/packages/foo/package.json`,
139
- errors: [
140
- {
141
- messageId: 'invalidRootProtocolUsage',
142
- },
143
- ],
144
- },
145
- ],
146
- });
147
- });
@@ -1,67 +0,0 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
- import type { Rule } from 'eslint';
3
- import type { ObjectExpression } from 'estree';
4
- import { getObjectPropertyAsObject } from '../util/handle-ast-object';
5
-
6
- const rootProtocolRegex = /^root:[\^~\*]$/;
7
-
8
- /**
9
- * Checks if the 'workspace:' and 'root:' protocol are used as either dependencies or devDependencies
10
- */
11
- function getYarnProtocolsUsed(node: ObjectExpression) {
12
- const protocolsUsed = { workspace: false, root: false };
13
-
14
- const dependencies = getObjectPropertyAsObject(node, 'dependencies');
15
- const devDependencies = getObjectPropertyAsObject(node, 'devDependencies');
16
- const peerDependencies = getObjectPropertyAsObject(node, 'peerDependencies');
17
-
18
- for (const obj of [dependencies, devDependencies, peerDependencies]) {
19
- for (const p of obj?.properties || []) {
20
- if (p.type === 'Property' && p.value.type === 'Literal') {
21
- if (typeof p.value.value === 'string') {
22
- if (rootProtocolRegex.test(p.value.value)) {
23
- protocolsUsed.root = true;
24
- }
25
- }
26
- }
27
- }
28
- }
29
-
30
- return protocolsUsed;
31
- }
32
-
33
- const rule: Rule.RuleModule = {
34
- meta: {
35
- type: 'problem',
36
- docs: {
37
- description: `Ensures that yarn protocols 'workspace:' and 'root:' are used correctly.`,
38
- recommended: true,
39
- },
40
- hasSuggestions: false,
41
- messages: {
42
- invalidWorkspaceProtocolUsage: `The 'workspace:^'protocol is Used. To resolve this error, please use the 'workspace:*' protocol instead.`,
43
- invalidRootProtocolUsage: `The 'root:' protocol is not allowed in platform packages. To resolve this error, replace the 'root:' protocol with specific package versions (e.g. '^1.0.0').`,
44
- },
45
- },
46
- create(context) {
47
- return {
48
- ObjectExpression: (node: Rule.Node) => {
49
- if (!context.filename.endsWith('package.json') || node.type !== 'ObjectExpression') {
50
- return;
51
- }
52
-
53
- const yarnProtocolsUsed = getYarnProtocolsUsed(node);
54
-
55
- // The 'root:' protocol can not be used in any platform packages
56
- if (yarnProtocolsUsed.root) {
57
- context.report({
58
- node,
59
- messageId: 'invalidRootProtocolUsage',
60
- });
61
- }
62
- },
63
- };
64
- },
65
- };
66
-
67
- export default rule;
@@ -1,8 +0,0 @@
1
- ### Notes
2
-
3
- - feature-gating/\* rules are copied from
4
- [eslint-plugin-jira/rules/ff](https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/jira/dev-tooling/packages/eslint-plugin-jira/rules/ff)
5
- with small variations as mentioned in this
6
- [PR](https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/pull-requests/115546/overview)
7
- - these rules could be a WIP since they are still targeting JFE libraries- see
8
- [discussion](https://atlassian.slack.com/archives/C026LTWFZ47/p1730776455371799?thread_ts=1730715221.742919&cid=C026LTWFZ47)
@@ -1,53 +0,0 @@
1
- # Inline feature flags/gates and experiments usages (feature-flags/inline-usage)
2
-
3
- Ensure feature flags/gates and experiments are inlined so that they can be statically analyzable.
4
-
5
- ## Examples
6
-
7
- 👎 Examples of **incorrect** code for this rule: Feature flag call is assigned to a variable
8
-
9
- ```tsx
10
- import { ff } from '@atlassian/jira-feature-flagging';
11
-
12
- const myFF = () => ff('my_flag');
13
-
14
- export const doSomething = () => {
15
- if (myFF()) {
16
- doSomethingNew();
17
- } else {
18
- doSomethingOld();
19
- }
20
- };
21
- ```
22
-
23
- 👍 Examples of **correct** code for this rule: Usage is inlined
24
-
25
- ```tsx
26
- import { ff } from '@atlassian/jira-feature-flagging';
27
- import { fg } from '@atlassian/jira-feature-gating';
28
- import { expValEquals } from '@atlassian/jira-feature-experiments';
29
-
30
- export const doSomething = () => {
31
- if (ff('my_flag')) {
32
- doSomethingNew();
33
- } else {
34
- doSomethingOld();
35
- }
36
- };
37
-
38
- export const doSomething = () => {
39
- if (fg('my_gate')) {
40
- doSomethingNew();
41
- } else {
42
- doSomethingOld();
43
- }
44
- };
45
-
46
- export const doSomething = () => {
47
- if (expValEquals('my_exp', 'on', true)) {
48
- doSomethingNew();
49
- } else {
50
- doSomethingOld();
51
- }
52
- };
53
- ```
@@ -1,106 +0,0 @@
1
- import outdent from 'outdent';
2
- import { tester } from '../../../../__tests__/utils/_tester';
3
- import rule from '../index';
4
-
5
- const options = ['ssOnly'];
6
-
7
- tester.run('feature-flags/inline-usage', rule, {
8
- valid: [
9
- {
10
- name: 'Valid API usage',
11
- code: outdent`
12
- import { ff } from '@atlassian/jira-feature-flagging';
13
- import { fg } from '@atlassian/jira-feature-gating';
14
- import { expVal, expValEquals } from '@atlassian/jira-feature-experiments';
15
-
16
- export const FFComponent = () => ff('is.enabled') && <>Valid</>;
17
- export const FGComponent = () => fg('is.enabled') && <>Valid</>;
18
-
19
- export const ExpValComponent = function() {
20
- return expVal('is.enabled', 'on', false) && <>Valid</>;
21
- };
22
-
23
- export function ExpValEqualsComponent() {
24
- expValEquals('is.enabled', 'on', true) && <>Valid</>;
25
- }
26
-
27
- const valid1 = () => {
28
- console.log('valid');
29
- };
30
-
31
- const valid2 = () => 'valid';
32
- `,
33
- },
34
- {
35
- name: 'When using ssOnly option, ff() can avoid inline-usage',
36
- options,
37
- code: outdent`
38
- import { ff } from '@atlassian/jira-feature-flagging';
39
- import { fg } from '@atlassian/jira-feature-gating';
40
-
41
- const invalidFF = () => ff('is.enabled');
42
- export const FGComponent = () => fg('is.enabled') && <>Valid</>;
43
- `,
44
- },
45
- {
46
- name: 'Edge cases',
47
- code: outdent`
48
- import { ff } from '@atlassian/jira-feature-flagging';
49
-
50
- function edgeCase1() {
51
- console.log('workAround');
52
- return ff('is.enabled');
53
- }
54
-
55
- const edgeCase2 = function() {
56
- console.log('workAround');
57
- return ff('is.enabled');
58
- };
59
-
60
- const edgeCase3 = () => {
61
- console.log('workAround');
62
- return ff('is.enabled');
63
- };
64
-
65
- const edgeCase4 = () => {
66
- ff('is.enabled');
67
- };
68
- `,
69
- },
70
- ],
71
- invalid: [
72
- {
73
- name: 'Invalid API usage',
74
- code: outdent`
75
- import { ff } from '@atlassian/jira-feature-flagging';
76
- import { fg } from '@atlassian/jira-feature-gating';
77
- import { expVal, expValEquals, UNSAFE_noExposureExp } from '@atlassian/jira-feature-experiments';
78
-
79
- const invalidFF = () => ff('is.enabled');
80
- const invalidFG = () => fg('is.enabled');
81
- const invalidExpVal = () => expVal('is.enabled', 'on', false);
82
- const invalidExpValBinary = () => expVal('is.enabled', 'cohort', null) === 'variation';
83
- const invalidExpValBinary2 = () => expVal('is.enabled', 'count', 0) >= 0;
84
-
85
- const invalidExpValEquals = function() {
86
- return expValEquals('is.enabled', 'on', true);
87
- };
88
-
89
- function invalidUnsafe() {
90
- return UNSAFE_noExposureExp('is.enabled');
91
- }
92
- `,
93
- errors: Array.from(Array(7), () => ({ messageId: 'inlineUsage' })),
94
- },
95
- {
96
- name: 'When using ssOnly option, Statsig functions should be inlined',
97
- options,
98
- code: outdent`
99
- import { fg } from '@atlassian/jira-feature-gating';
100
-
101
- const invalidFG = () => fg('is.enabled');
102
- `,
103
- errors: [{ messageId: 'inlineUsage' }],
104
- },
105
- ],
106
- });
@@ -1,135 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- import { isAPIimport, type Node } from '../utils';
3
-
4
- const FUNCTION_NAMES = new Set(['ff', 'fg', 'expVal', 'expValEquals', 'UNSAFE_noExposureExp']);
5
- const STATSIG_ONLY_FUNCTION_NAMES = new Set([
6
- 'fg',
7
- 'expVal',
8
- 'expValEquals',
9
- 'UNSAFE_noExposureExp',
10
- ]);
11
-
12
- const findDefinitionDeclaration = (
13
- node: Rule.Node,
14
- ): (Node<'VariableDeclaration'> | Node<'FunctionDeclaration'>) & Rule.NodeParentExtension =>
15
- node.type === 'VariableDeclaration' || node.type === 'FunctionDeclaration'
16
- ? node
17
- : findDefinitionDeclaration(node.parent);
18
-
19
- const validateCallExpression = (
20
- node: Node<'CallExpression'> & Rule.NodeParentExtension,
21
- context: Rule.RuleContext,
22
- ) => {
23
- const targetedFunctionsSwitch =
24
- context.options[0] === 'ssOnly' ? STATSIG_ONLY_FUNCTION_NAMES : FUNCTION_NAMES;
25
-
26
- const { callee } = node;
27
- const shouldWarn =
28
- callee.type === 'Identifier' &&
29
- targetedFunctionsSwitch.has(callee.name) &&
30
- isAPIimport(callee.name, context, node);
31
-
32
- if (shouldWarn) {
33
- const defDeclaration = findDefinitionDeclaration(node.parent);
34
-
35
- context.report({
36
- messageId: 'inlineUsage',
37
- node:
38
- defDeclaration.parent.type === 'ExportNamedDeclaration'
39
- ? defDeclaration.parent
40
- : defDeclaration,
41
- });
42
-
43
- return true;
44
- }
45
-
46
- return false;
47
- };
48
-
49
- const validateBinaryExpression = (
50
- node: Node<'BinaryExpression'> & Rule.NodeParentExtension,
51
- context: Rule.RuleContext,
52
- ) => {
53
- // Match all comparator operators i.e ===, >=, <
54
- if (node.operator.match(/^[=|<|>]/)) {
55
- if (
56
- node.left.type === 'CallExpression' &&
57
- validateCallExpression(node.left as Node<'CallExpression'>, context)
58
- ) {
59
- return;
60
- }
61
-
62
- if (node.right.type === 'CallExpression') {
63
- validateCallExpression(node.right as Node<'CallExpression'>, context);
64
- }
65
- }
66
- };
67
-
68
- const validateReturnExpression = ({ body }: Node<'BlockStatement'>, context: Rule.RuleContext) => {
69
- if (body.length !== 1) {
70
- return;
71
- }
72
-
73
- const [statement] = body;
74
-
75
- if (statement.type === 'ReturnStatement') {
76
- const { argument } = statement;
77
-
78
- if (argument && argument.type === 'CallExpression') {
79
- validateCallExpression(argument as Node<'CallExpression'>, context);
80
- } else if (argument && argument.type === 'BinaryExpression') {
81
- validateBinaryExpression(argument as Node<'BinaryExpression'>, context);
82
- }
83
- }
84
- };
85
-
86
- const validateFunctionBody = (
87
- body: Node<'BinaryExpression'> | Node<'CallExpression'> | Node<'BlockStatement'>,
88
- context: Rule.RuleContext,
89
- ) => {
90
- switch (body.type) {
91
- case 'CallExpression':
92
- validateCallExpression(body, context);
93
- break;
94
- case 'BinaryExpression':
95
- validateBinaryExpression(body, context);
96
- break;
97
- case 'BlockStatement':
98
- validateReturnExpression(body, context);
99
- break;
100
- default:
101
- }
102
- };
103
-
104
- const rule: Rule.RuleModule = {
105
- meta: {
106
- type: 'problem',
107
- docs: {
108
- description:
109
- 'Ensure feature flags/gates and experiments are inlined so that they can be statically analyzable.',
110
- url: 'https://stash.atlassian.com/projects/ATLASSIAN/repos/atlassian-frontend-monorepo/browse/platform/packages/platform/eslint-plugin/src/rules/inline-usage/README.md',
111
- },
112
- schema: [
113
- {
114
- enum: ['ssOnly'],
115
- },
116
- ],
117
- messages: {
118
- inlineUsage:
119
- 'Do not export or wrap feature flags/gates and experiments usages. Inline calls at the callsite to ensure it is statically analyzable.',
120
- },
121
- },
122
- create(context) {
123
- return {
124
- 'VariableDeclaration[declarations.length=1] > VariableDeclarator[id.type="Identifier"]:matches([init.type="ArrowFunctionExpression"], [init.type="FunctionExpression"]) > *.init':
125
- ({ body }: Node<'FunctionDeclaration'>) => {
126
- validateFunctionBody(body as Node<'BlockStatement'> & Rule.NodeParentExtension, context);
127
- },
128
- FunctionDeclaration: ({ body }: Node<'FunctionDeclaration'>) => {
129
- validateFunctionBody(body as Node<'BlockStatement'> & Rule.NodeParentExtension, context);
130
- },
131
- };
132
- },
133
- };
134
-
135
- export default rule;
@@ -1,29 +0,0 @@
1
- # Avoid aliasing feature flag utils (feature-flags/no-alias)
2
-
3
- Ensure feature flag usage is statically analyzable. This applies to all methods, weather you are
4
- writing production code, unit testing or storybooks.
5
-
6
- ## Examples
7
-
8
- 👎 Examples of **incorrect** code for this rule:
9
-
10
- ```tsx
11
- // Do not alias utils
12
- import { ff as getBoolean } from '@atlassian/jira-feature-flagging';
13
-
14
- // Do not reassign utils
15
- import { fg } from '@atlassian/jira-feature-gating';
16
- const aliasedFG = fg;
17
- ```
18
-
19
- 👍 Examples of **correct** code for this rule: Do not alias utils
20
-
21
- ```tsx
22
- import { ff } from '@atlassian/jira-feature-flagging';
23
-
24
- export const doSomething = () => {
25
- if (ff('my.flag')) {
26
- console.log('hello');
27
- }
28
- };
29
- ```
@@ -1,76 +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-alias', rule, {
6
- valid: [
7
- {
8
- name: '`@atlassian/jira-feature-flagging` imports are not aliased',
9
- code: outdent`
10
- import { ff, ffVal } from '@atlassian/jira-feature-flagging';
11
- `,
12
- },
13
- {
14
- name: '`@atlassian/jira-feature-flagging-utils` imports are not aliased',
15
- code: outdent`
16
- import { componentWithFF } from '@atlassian/jira-feature-flagging-utils';
17
- `,
18
- },
19
- {
20
- name: '`@atlassian/jira-feature-gating` imports are not aliased',
21
- code: outdent`
22
- import { fg } from '@atlassian/jira-feature-gating';
23
- `,
24
- },
25
- {
26
- name: '`@atlassian/jira-feature-gates-test-mocks` imports are not aliased',
27
- code: outdent`
28
- import { passGate } from '@atlassian/jira-feature-gates-test-mocks';
29
- `,
30
- },
31
- {
32
- name: '`@atlassian/jira-feature-gates-storybook-mocks` imports are not aliased',
33
- code: outdent`
34
- import { withGate } from '@atlassian/jira-feature-gates-storybook-mocks';
35
- `,
36
- },
37
- {
38
- name: 'Aliases for non related imports are allowed',
39
- code: outdent`
40
- import { memo as m } from 'react';
41
- import * as r from 'redux';
42
- `,
43
- },
44
- ],
45
- invalid: [
46
- {
47
- name: 'Do not alias utils',
48
- code: outdent`
49
- import { ff as getBoolean } from '@atlassian/jira-feature-flagging';
50
- `,
51
- errors: [{ messageId: 'noSpecifierAlias' }],
52
- },
53
- {
54
- name: 'Do not alias multiple utils',
55
- code: outdent`
56
- import { ff as getBoolean, ffVal as getFeatureFlagValue } from '@atlassian/jira-feature-flagging';
57
- `,
58
- errors: [{ messageId: 'noSpecifierAlias' }, { messageId: 'noSpecifierAlias' }],
59
- },
60
- {
61
- name: 'Do not allow namespace imports',
62
- code: outdent`
63
- import * as ffUtils from '@atlassian/jira-feature-flagging';
64
- `,
65
- errors: [{ messageId: 'noNamespaceSpecifier' }],
66
- },
67
- {
68
- name: 'Do not reassign utils',
69
- code: outdent`
70
- import { fg } from '@atlassian/jira-feature-gating';
71
- const aliasedFG = fg;
72
- `,
73
- errors: [{ messageId: 'noReassignment' }],
74
- },
75
- ],
76
- });