@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.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/index.js +4 -9
- package/dist/cjs/rules/constants.js +1 -1
- package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +7 -7
- package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +3 -0
- package/dist/cjs/rules/ensure-no-private-dependencies/index.js +48 -66
- package/dist/cjs/rules/feature-gating/inline-usage/index.js +14 -3
- package/dist/cjs/rules/feature-gating/no-alias/index.js +2 -2
- package/dist/cjs/rules/feature-gating/no-module-level-eval/index.js +1 -1
- package/dist/cjs/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
- package/dist/cjs/rules/feature-gating/no-preconditioning/index.js +4 -1
- package/dist/cjs/rules/feature-gating/prefer-fg/index.js +1 -1
- package/dist/cjs/rules/feature-gating/static-feature-flags/index.js +2 -2
- package/dist/cjs/rules/feature-gating/use-recommended-utils/index.js +1 -1
- package/dist/cjs/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
- package/dist/cjs/rules/no-sparse-checkout/index.js +1 -1
- package/dist/cjs/rules/prefer-crypto-random-uuid/index.js +87 -0
- package/dist/cjs/rules/use-entrypoints-in-examples/index.js +1 -1
- package/dist/cjs/rules/util/context-compat.js +4 -2
- package/dist/es2019/index.js +4 -9
- package/dist/es2019/rules/constants.js +1 -1
- package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +7 -7
- package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +3 -0
- package/dist/es2019/rules/ensure-no-private-dependencies/index.js +10 -9
- package/dist/es2019/rules/feature-gating/inline-usage/index.js +14 -3
- package/dist/es2019/rules/feature-gating/no-alias/index.js +2 -2
- package/dist/es2019/rules/feature-gating/no-module-level-eval/index.js +1 -1
- package/dist/es2019/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
- package/dist/es2019/rules/feature-gating/no-preconditioning/index.js +4 -1
- package/dist/es2019/rules/feature-gating/prefer-fg/index.js +1 -1
- package/dist/es2019/rules/feature-gating/static-feature-flags/index.js +2 -2
- package/dist/es2019/rules/feature-gating/use-recommended-utils/index.js +1 -1
- package/dist/es2019/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
- package/dist/es2019/rules/no-sparse-checkout/index.js +1 -1
- package/dist/es2019/rules/prefer-crypto-random-uuid/index.js +81 -0
- package/dist/es2019/rules/use-entrypoints-in-examples/index.js +1 -1
- package/dist/es2019/rules/util/context-compat.js +4 -2
- package/dist/esm/index.js +4 -9
- package/dist/esm/rules/constants.js +1 -1
- package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +7 -7
- package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +3 -0
- package/dist/esm/rules/ensure-no-private-dependencies/index.js +48 -65
- package/dist/esm/rules/feature-gating/inline-usage/index.js +14 -3
- package/dist/esm/rules/feature-gating/no-alias/index.js +2 -2
- package/dist/esm/rules/feature-gating/no-module-level-eval/index.js +1 -1
- package/dist/esm/rules/feature-gating/no-module-level-eval-nav4/index.js +1 -1
- package/dist/esm/rules/feature-gating/no-preconditioning/index.js +4 -1
- package/dist/esm/rules/feature-gating/prefer-fg/index.js +1 -1
- package/dist/esm/rules/feature-gating/static-feature-flags/index.js +2 -2
- package/dist/esm/rules/feature-gating/use-recommended-utils/index.js +1 -1
- package/dist/esm/rules/no-invalid-storybook-decorator-usage/index.js +1 -1
- package/dist/esm/rules/no-sparse-checkout/index.js +1 -1
- package/dist/esm/rules/prefer-crypto-random-uuid/index.js +81 -0
- package/dist/esm/rules/use-entrypoints-in-examples/index.js +1 -1
- package/dist/esm/rules/util/context-compat.js +4 -2
- package/dist/types/index.d.ts +6 -20
- package/dist/types/rules/util/handle-ast-object.d.ts +1 -1
- package/dist/types-ts4.5/index.d.ts +6 -32
- package/dist/types-ts4.5/rules/util/handle-ast-object.d.ts +1 -1
- package/package.json +2 -5
- package/afm-cc/tsconfig.json +0 -24
- package/afm-jira/tsconfig.json +0 -24
- package/build/tsconfig.json +0 -17
- package/dist/cjs/rules/ensure-feature-flag-prefix/index.js +0 -75
- package/dist/cjs/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -79
- package/dist/es2019/rules/ensure-feature-flag-prefix/index.js +0 -65
- package/dist/es2019/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -62
- package/dist/esm/rules/ensure-feature-flag-prefix/index.js +0 -69
- package/dist/esm/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -75
- package/dist/types/rules/ensure-feature-flag-prefix/index.d.ts +0 -3
- package/dist/types/rules/ensure-valid-platform-yarn-protocol-usage/index.d.ts +0 -3
- package/src/__tests__/utils/_tester.tsx +0 -26
- package/src/index.tsx +0 -254
- package/src/rules/compiled/README.md +0 -3
- package/src/rules/compiled/expand-background-shorthand/README.md +0 -23
- package/src/rules/compiled/expand-background-shorthand/__tests__/rule.test.ts +0 -160
- package/src/rules/compiled/expand-background-shorthand/index.tsx +0 -43
- package/src/rules/compiled/expand-border-shorthand/README.md +0 -51
- package/src/rules/compiled/expand-border-shorthand/__tests__/rule.test.ts +0 -211
- package/src/rules/compiled/expand-border-shorthand/index.ts +0 -103
- package/src/rules/compiled/expand-spacing-shorthand/README.md +0 -38
- package/src/rules/compiled/expand-spacing-shorthand/__tests__/rule.test.ts +0 -448
- package/src/rules/compiled/expand-spacing-shorthand/index.ts +0 -240
- package/src/rules/constants.tsx +0 -20
- package/src/rules/ensure-atlassian-team/__tests__/unit/rule.test.ts +0 -24
- package/src/rules/ensure-atlassian-team/index.ts +0 -51
- package/src/rules/ensure-critical-dependency-resolutions/__test__/unit/rule.test.tsx +0 -200
- package/src/rules/ensure-critical-dependency-resolutions/index.tsx +0 -172
- package/src/rules/ensure-feature-flag-prefix/__tests__/unit/rule.test.tsx +0 -65
- package/src/rules/ensure-feature-flag-prefix/index.tsx +0 -81
- package/src/rules/ensure-feature-flag-registration/__tests__/unit/rule.test.tsx +0 -115
- package/src/rules/ensure-feature-flag-registration/index.tsx +0 -106
- package/src/rules/ensure-native-and-af-exports-synced/__tests__/unit/rule.test.tsx +0 -199
- package/src/rules/ensure-native-and-af-exports-synced/index.tsx +0 -188
- package/src/rules/ensure-no-private-dependencies/__tests__/unit/rule.test.ts +0 -212
- package/src/rules/ensure-no-private-dependencies/index.ts +0 -64
- package/src/rules/ensure-publish-valid/__tests__/unit/rule.test.ts +0 -39
- package/src/rules/ensure-publish-valid/index.ts +0 -81
- package/src/rules/ensure-test-runner-arguments/__tests__/unit/rule.test.tsx +0 -298
- package/src/rules/ensure-test-runner-arguments/index.tsx +0 -121
- package/src/rules/ensure-test-runner-nested-count/__tests__/unit/rule.test.tsx +0 -308
- package/src/rules/ensure-test-runner-nested-count/index.tsx +0 -82
- package/src/rules/ensure-valid-bin-values/__tests__/unit/rule.test.ts +0 -159
- package/src/rules/ensure-valid-bin-values/index.ts +0 -70
- package/src/rules/ensure-valid-platform-yarn-protocol-usage/__tests__/unit/rule.test.ts +0 -147
- package/src/rules/ensure-valid-platform-yarn-protocol-usage/index.ts +0 -67
- package/src/rules/feature-gating/README.md +0 -8
- package/src/rules/feature-gating/inline-usage/README.md +0 -53
- package/src/rules/feature-gating/inline-usage/__tests__/rule.test.tsx +0 -106
- package/src/rules/feature-gating/inline-usage/index.tsx +0 -135
- package/src/rules/feature-gating/no-alias/README.md +0 -29
- package/src/rules/feature-gating/no-alias/__tests__/rule.test.tsx +0 -76
- package/src/rules/feature-gating/no-alias/index.tsx +0 -80
- package/src/rules/feature-gating/no-module-level-eval/README.md +0 -53
- package/src/rules/feature-gating/no-module-level-eval/__tests__/test.tsx +0 -133
- package/src/rules/feature-gating/no-module-level-eval/index.tsx +0 -54
- package/src/rules/feature-gating/no-module-level-eval-nav4/README.md +0 -8
- package/src/rules/feature-gating/no-module-level-eval-nav4/__tests__/test.tsx +0 -130
- package/src/rules/feature-gating/no-module-level-eval-nav4/index.tsx +0 -73
- package/src/rules/feature-gating/no-preconditioning/README.md +0 -69
- package/src/rules/feature-gating/no-preconditioning/__tests__/rule.test.tsx +0 -164
- package/src/rules/feature-gating/no-preconditioning/index.tsx +0 -138
- package/src/rules/feature-gating/prefer-fg/README.md +0 -3
- package/src/rules/feature-gating/prefer-fg/__tests__/rule.test.tsx +0 -83
- package/src/rules/feature-gating/prefer-fg/index.tsx +0 -110
- package/src/rules/feature-gating/static-feature-flags/README.md +0 -3
- package/src/rules/feature-gating/static-feature-flags/__tests__/test.tsx +0 -135
- package/src/rules/feature-gating/static-feature-flags/index.tsx +0 -103
- package/src/rules/feature-gating/use-recommended-utils/README.md +0 -67
- package/src/rules/feature-gating/use-recommended-utils/__tests__/rule.test.tsx +0 -78
- package/src/rules/feature-gating/use-recommended-utils/index.tsx +0 -57
- package/src/rules/feature-gating/utils.tsx +0 -48
- package/src/rules/no-direct-document-usage/index.tsx +0 -111
- package/src/rules/no-duplicate-dependencies/__tests__/unit/rule.test.ts +0 -116
- package/src/rules/no-duplicate-dependencies/index.ts +0 -79
- package/src/rules/no-invalid-feature-flag-usage/__tests__/unit/rule.test.tsx +0 -69
- package/src/rules/no-invalid-feature-flag-usage/index.tsx +0 -128
- package/src/rules/no-invalid-storybook-decorator-usage/__tests__/unit/rule.test.tsx +0 -18
- package/src/rules/no-invalid-storybook-decorator-usage/index.tsx +0 -39
- package/src/rules/no-pre-post-installs/__tests__/unit/rule.test.ts +0 -41
- package/src/rules/no-pre-post-installs/index.ts +0 -35
- package/src/rules/no-set-immediate/index.tsx +0 -43
- package/src/rules/no-sparse-checkout/__tests__/unit/rule.test.tsx +0 -48
- package/src/rules/no-sparse-checkout/index.tsx +0 -54
- package/src/rules/use-entrypoints-in-examples/README.md +0 -27
- package/src/rules/use-entrypoints-in-examples/__tests__/rule.test.tsx +0 -34
- package/src/rules/use-entrypoints-in-examples/index.tsx +0 -43
- package/src/rules/util/__tests__/context-compat.test.ts +0 -122
- package/src/rules/util/compiled-utils.ts +0 -27
- package/src/rules/util/context-compat.ts +0 -41
- package/src/rules/util/file-exclusions.ts +0 -39
- package/src/rules/util/handle-ast-object.ts +0 -33
- package/src/rules/util/registration-utils.ts +0 -59
- package/tsconfig.app.json +0 -43
- package/tsconfig.dev.json +0 -40
- package/tsconfig.json +0 -23
- /package/dist/{types-ts4.5/rules/ensure-valid-platform-yarn-protocol-usage → types/rules/prefer-crypto-random-uuid}/index.d.ts +0 -0
- /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
|
-
});
|