@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.
- package/CHANGELOG.md +7 -0
- package/dist/cjs/index.js +0 -2
- package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +2 -2
- package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +3 -0
- package/dist/cjs/rules/feature-gating/no-alias/index.js +1 -1
- package/dist/cjs/rules/util/context-compat.js +4 -2
- package/dist/es2019/index.js +0 -2
- package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +2 -2
- package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +3 -0
- package/dist/es2019/rules/feature-gating/no-alias/index.js +1 -1
- package/dist/es2019/rules/util/context-compat.js +4 -2
- package/dist/esm/index.js +0 -2
- package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +2 -2
- package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +3 -0
- package/dist/esm/rules/feature-gating/no-alias/index.js +1 -1
- package/dist/esm/rules/util/context-compat.js +4 -2
- package/dist/types/index.d.ts +4 -6
- package/dist/types/rules/util/handle-ast-object.d.ts +1 -1
- package/dist/types-ts4.5/index.d.ts +4 -6
- package/dist/types-ts4.5/rules/util/handle-ast-object.d.ts +1 -1
- package/package.json +2 -2
- 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-valid-platform-yarn-protocol-usage/index.js +0 -79
- package/dist/es2019/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -62
- package/dist/esm/rules/ensure-valid-platform-yarn-protocol-usage/index.js +0 -75
- package/dist/types/rules/ensure-valid-platform-yarn-protocol-usage/index.d.ts +0 -3
- package/dist/types-ts4.5/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
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { tester } from '../../../../__tests__/utils/_tester';
|
|
2
|
-
import rule from '../../index';
|
|
3
|
-
|
|
4
|
-
describe('test no-pre-post-installs rule', () => {
|
|
5
|
-
tester.run('no-pre-post-installs', rule, {
|
|
6
|
-
valid: [
|
|
7
|
-
{
|
|
8
|
-
code: `const foo = {}`,
|
|
9
|
-
filename: 'package.json',
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
code: `const foo = { "atlassian": { "team": "bar" } }`,
|
|
13
|
-
filename: 'foo/package.json',
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
invalid: [
|
|
17
|
-
{
|
|
18
|
-
code: `const foo = { "atlassian": {} }`,
|
|
19
|
-
filename: 'foo/package.json',
|
|
20
|
-
errors: [{ messageId: 'atlassianTeamRequired' }],
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
import type { Rule } from 'eslint';
|
|
3
|
-
|
|
4
|
-
const rule: Rule.RuleModule = {
|
|
5
|
-
meta: {
|
|
6
|
-
type: 'problem',
|
|
7
|
-
docs: {
|
|
8
|
-
description: 'This rule ensures that the internal packages have a responsible team attached.',
|
|
9
|
-
recommended: true,
|
|
10
|
-
},
|
|
11
|
-
hasSuggestions: false,
|
|
12
|
-
messages: {
|
|
13
|
-
atlassianTeamRequired: 'The atlassian.team property is required',
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
create(context) {
|
|
17
|
-
return {
|
|
18
|
-
ObjectExpression: (node: Rule.Node) => {
|
|
19
|
-
if (!context.getFilename().endsWith('package.json') || node.type !== 'ObjectExpression') {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const atlassianProp = node.properties.find(
|
|
24
|
-
(p) => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === 'atlassian',
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
if (!atlassianProp) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (atlassianProp.type !== 'Property' || atlassianProp.value.type !== 'ObjectExpression') {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const teamProp = atlassianProp.value.properties.find(
|
|
36
|
-
(p) => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === 'team',
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
// this just checks for existence, we can potentially cross-reference it with teams.json to make sure its valid in the future.
|
|
40
|
-
if (!teamProp) {
|
|
41
|
-
return context.report({
|
|
42
|
-
node,
|
|
43
|
-
messageId: 'atlassianTeamRequired',
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export default rule;
|
|
@@ -1,200 +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-critical-dependency-resolutions rule', () => {
|
|
7
|
-
tester.run('ensure-critical-dependency-resolutions', rule, {
|
|
8
|
-
valid: [
|
|
9
|
-
// Root package.json, have all of the correct resolutions
|
|
10
|
-
{
|
|
11
|
-
code: `const foo = {
|
|
12
|
-
"resolutions": {
|
|
13
|
-
"@types/react": "16.14.15",
|
|
14
|
-
"typescript": "5.4.2",
|
|
15
|
-
"tslib": "2.6.3",
|
|
16
|
-
"react-relay": "npm:atl-react-relay@0.0.0-main-5980a913",
|
|
17
|
-
"relay-compiler": "npm:atl-relay-compiler@0.0.0-main-5980a913",
|
|
18
|
-
"relay-runtime": "npm:atl-relay-runtime@0.0.0-main-5980a913",
|
|
19
|
-
"relay-test-utils": "npm:atl-relay-test-utils@0.0.0-main-5980a913",
|
|
20
|
-
}
|
|
21
|
-
}`,
|
|
22
|
-
filename: `${cwd}/package.json`,
|
|
23
|
-
},
|
|
24
|
-
// Root package.json, have all of the correct resolutions
|
|
25
|
-
{
|
|
26
|
-
code: `const foo = {
|
|
27
|
-
"resolutions": {
|
|
28
|
-
"@types/react": "18.2.28",
|
|
29
|
-
"typescript": "5.4.2",
|
|
30
|
-
"tslib": "2.6.3",
|
|
31
|
-
"react-relay": "npm:atl-react-relay@0.0.0-main-5980a913",
|
|
32
|
-
"relay-compiler": "npm:atl-relay-compiler@0.0.0-main-5980a913",
|
|
33
|
-
"relay-runtime": "npm:atl-relay-runtime@0.0.0-main-5980a913",
|
|
34
|
-
"relay-test-utils": "npm:atl-relay-test-utils@0.0.0-main-5980a913",
|
|
35
|
-
}
|
|
36
|
-
}`,
|
|
37
|
-
filename: `${cwd}/package.json`,
|
|
38
|
-
},
|
|
39
|
-
// Root package.json, have all of the correct resolutions with ~
|
|
40
|
-
{
|
|
41
|
-
code: `const foo = {
|
|
42
|
-
"resolutions": {
|
|
43
|
-
"@types/react": "~16.14.25",
|
|
44
|
-
"typescript": "~5.4.2",
|
|
45
|
-
"tslib": "~2.6.3",
|
|
46
|
-
"react-relay": "npm:atl-react-relay@0.0.0-main-5980a913",
|
|
47
|
-
"relay-compiler": "npm:atl-relay-compiler@0.0.0-main-5980a913",
|
|
48
|
-
"relay-runtime": "npm:atl-relay-runtime@0.0.0-main-5980a913",
|
|
49
|
-
"relay-test-utils": "npm:atl-relay-test-utils@0.0.0-main-5980a913",
|
|
50
|
-
}
|
|
51
|
-
}`,
|
|
52
|
-
filename: `${cwd}/package.json`,
|
|
53
|
-
},
|
|
54
|
-
// Root package.json, have some of the correct resolutions
|
|
55
|
-
{
|
|
56
|
-
code: `const foo = {
|
|
57
|
-
"resolutions": {
|
|
58
|
-
"@types/react": "~16.14.25",
|
|
59
|
-
"typescript": "~5.4.2",
|
|
60
|
-
"tslib": "~2.6.3",
|
|
61
|
-
}
|
|
62
|
-
}`,
|
|
63
|
-
filename: `${cwd}/package.json`,
|
|
64
|
-
},
|
|
65
|
-
// Root package.json, have some of the correct resolutions
|
|
66
|
-
{
|
|
67
|
-
code: `const foo = {
|
|
68
|
-
"resolutions": {
|
|
69
|
-
"@types/react": "~16.14.25",
|
|
70
|
-
"typescript": "~5.4.2",
|
|
71
|
-
"react-relay": "npm:atl-react-relay@0.0.0-main-5980a913",
|
|
72
|
-
},
|
|
73
|
-
"dependencies": {
|
|
74
|
-
"@types/react": "~16.14.25",
|
|
75
|
-
"typescript": "~5.4.2",
|
|
76
|
-
"react-relay": "npm:atl-react-relay@0.0.0-main-5980a913",
|
|
77
|
-
}
|
|
78
|
-
}`,
|
|
79
|
-
filename: `${cwd}/package.json`,
|
|
80
|
-
},
|
|
81
|
-
// Individual package's package.json. Have part of correct resolutions
|
|
82
|
-
{
|
|
83
|
-
code: `const foo = {
|
|
84
|
-
"resolutions": {
|
|
85
|
-
"@types/react": "16.14.15",
|
|
86
|
-
}
|
|
87
|
-
}`,
|
|
88
|
-
filename: `${cwd}/package/name/package.json`,
|
|
89
|
-
},
|
|
90
|
-
// Individual package's package.json. Not have relevant package resolutions
|
|
91
|
-
{
|
|
92
|
-
code: `const foo = {
|
|
93
|
-
"resolutions": {
|
|
94
|
-
"@types/abcd": "1.2.3",
|
|
95
|
-
}
|
|
96
|
-
}`,
|
|
97
|
-
filename: `${cwd}/package/name/package.json`,
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
invalid: [
|
|
101
|
-
// Root package.json. One package is correct, the other is missing
|
|
102
|
-
{
|
|
103
|
-
code: `const foo = {
|
|
104
|
-
"resolutions": {
|
|
105
|
-
"typescript": "~5.4.2",
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
"dependencies": {
|
|
109
|
-
"@types/react": "~16.14.25",
|
|
110
|
-
"typescript": "~5.4.2",
|
|
111
|
-
}
|
|
112
|
-
}`,
|
|
113
|
-
filename: `${cwd}/package.json`,
|
|
114
|
-
errors: [
|
|
115
|
-
{
|
|
116
|
-
messageId: 'invalidPackageResolution',
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
},
|
|
120
|
-
// Root package.json. Resolutions are missing but package is present in dependencies
|
|
121
|
-
{
|
|
122
|
-
code: `const foo = {
|
|
123
|
-
"resolutions": {
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
"dependencies": {
|
|
127
|
-
"@types/react": "~16.14.25",
|
|
128
|
-
"typescript": "~5.4.2",
|
|
129
|
-
}
|
|
130
|
-
}`,
|
|
131
|
-
filename: `${cwd}/package.json`,
|
|
132
|
-
errors: [
|
|
133
|
-
{
|
|
134
|
-
messageId: 'invalidPackageResolution',
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
},
|
|
138
|
-
// Root package.json. Both packages have the wrong version ranges
|
|
139
|
-
{
|
|
140
|
-
code: `const foo = {
|
|
141
|
-
"resolutions": {
|
|
142
|
-
"@types/react": "16.8.25",
|
|
143
|
-
"typescript": "5.1.1",
|
|
144
|
-
}
|
|
145
|
-
}`,
|
|
146
|
-
filename: `${cwd}/package.json`,
|
|
147
|
-
errors: [
|
|
148
|
-
{
|
|
149
|
-
messageId: 'invalidPackageResolution',
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
},
|
|
153
|
-
// Root package.json. One package is correct, the other has wrong version range
|
|
154
|
-
{
|
|
155
|
-
code: `const foo = {
|
|
156
|
-
"resolutions": {
|
|
157
|
-
"@types/react": "~16.14.25",
|
|
158
|
-
"typescript": "~5.1.1",
|
|
159
|
-
}
|
|
160
|
-
}`,
|
|
161
|
-
filename: `${cwd}/package.json`,
|
|
162
|
-
errors: [
|
|
163
|
-
{
|
|
164
|
-
messageId: 'invalidPackageResolution',
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
},
|
|
168
|
-
// Root package.json. One package is correct, the other has ^ in its version
|
|
169
|
-
{
|
|
170
|
-
code: `const foo = {
|
|
171
|
-
"resolutions": {
|
|
172
|
-
"@types/react": "~16.14.25",
|
|
173
|
-
"typescript": "^5.4.2",
|
|
174
|
-
}
|
|
175
|
-
}`,
|
|
176
|
-
filename: `${cwd}/package.json`,
|
|
177
|
-
errors: [
|
|
178
|
-
{
|
|
179
|
-
messageId: 'invalidPackageResolution',
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
},
|
|
183
|
-
// Individual package's package.json. One package is correct, the other is wrong
|
|
184
|
-
{
|
|
185
|
-
code: `const foo = {
|
|
186
|
-
"resolutions": {
|
|
187
|
-
"@types/react": "16.14.15",
|
|
188
|
-
"typescript": "5.1.1",
|
|
189
|
-
}
|
|
190
|
-
}`,
|
|
191
|
-
filename: `${cwd}/packages/packge/directory/package.json`,
|
|
192
|
-
errors: [
|
|
193
|
-
{
|
|
194
|
-
messageId: 'invalidPackageResolution',
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
});
|
|
200
|
-
});
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { findRootSync } from '@manypkg/find-root';
|
|
2
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
3
|
-
import type { Rule } from 'eslint';
|
|
4
|
-
import type { ObjectExpression } from 'estree';
|
|
5
|
-
import { getObjectPropertyAsObject } from '../util/handle-ast-object';
|
|
6
|
-
|
|
7
|
-
// Here we only need to specify the major and minor versions
|
|
8
|
-
// In matchMinorVersion, we will check if the versions in resolutions fall in the right ranges.
|
|
9
|
-
//
|
|
10
|
-
const DESIRED_PKG_VERSIONS: Record<string, string[]> = {
|
|
11
|
-
typescript: ['5.4'],
|
|
12
|
-
tslib: ['2.6'],
|
|
13
|
-
'@types/react': ['16.14', '18.2'],
|
|
14
|
-
'react-relay': ['npm:atl-react-relay@0.0.0-main-5980a913'],
|
|
15
|
-
'relay-compiler': ['npm:atl-relay-compiler@0.0.0-main-5980a913'],
|
|
16
|
-
'relay-runtime': ['npm:atl-relay-runtime@0.0.0-main-5980a913'],
|
|
17
|
-
'relay-test-utils': ['npm:atl-relay-test-utils@0.0.0-main-5980a913'],
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const matchMinorVersion = (desiredVersion: string, versionInResolutions: string): boolean => {
|
|
21
|
-
const firstChar = versionInResolutions[0];
|
|
22
|
-
// The version is invalid if it doesn't start with a number or ~
|
|
23
|
-
if (!/^\d$/.test(firstChar) && firstChar !== '~' && !versionInResolutions.startsWith('npm:')) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
versionInResolutions.startsWith(desiredVersion) ||
|
|
29
|
-
versionInResolutions.startsWith('~' + desiredVersion)
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const verifyResolutionFromObject = ({
|
|
34
|
-
resolutions,
|
|
35
|
-
dependencies,
|
|
36
|
-
devDependencies,
|
|
37
|
-
pkg,
|
|
38
|
-
version,
|
|
39
|
-
optional,
|
|
40
|
-
}: {
|
|
41
|
-
resolutions: ObjectExpression;
|
|
42
|
-
dependencies: ObjectExpression | null;
|
|
43
|
-
devDependencies: ObjectExpression | null;
|
|
44
|
-
pkg: string;
|
|
45
|
-
version: string;
|
|
46
|
-
optional: boolean;
|
|
47
|
-
}): boolean => {
|
|
48
|
-
// For root package.json, we require the critical packages' resolutions exist and with matching version
|
|
49
|
-
// For individual package's package.json, it's ok if resolutions don't exist. But if they do, the version should match
|
|
50
|
-
const resolutionExist = resolutions.properties.some(
|
|
51
|
-
(p) => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === pkg,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
isDependencyPresent({
|
|
55
|
-
resolutions,
|
|
56
|
-
dependencies,
|
|
57
|
-
devDependencies,
|
|
58
|
-
pkg,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
if (!resolutionExist) {
|
|
62
|
-
// when package is not a part of dependencies/devDependencies
|
|
63
|
-
if (
|
|
64
|
-
optional === false &&
|
|
65
|
-
!isDependencyPresent({
|
|
66
|
-
resolutions,
|
|
67
|
-
dependencies,
|
|
68
|
-
devDependencies,
|
|
69
|
-
pkg,
|
|
70
|
-
})
|
|
71
|
-
) {
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return optional;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const resolutionExistAndMatch = resolutions.properties.some(
|
|
79
|
-
(p) =>
|
|
80
|
-
p.type === 'Property' &&
|
|
81
|
-
p.key.type === 'Literal' &&
|
|
82
|
-
p.key.value === pkg &&
|
|
83
|
-
p.value.type === 'Literal' &&
|
|
84
|
-
matchMinorVersion(version, p.value.value as string),
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
return resolutionExistAndMatch;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
type IsDependencyPresentProps = {
|
|
91
|
-
resolutions: ObjectExpression | null;
|
|
92
|
-
dependencies: ObjectExpression | null;
|
|
93
|
-
devDependencies: ObjectExpression | null;
|
|
94
|
-
pkg: string;
|
|
95
|
-
};
|
|
96
|
-
const isDependencyPresent = ({
|
|
97
|
-
resolutions,
|
|
98
|
-
dependencies,
|
|
99
|
-
devDependencies,
|
|
100
|
-
pkg,
|
|
101
|
-
}: IsDependencyPresentProps) => {
|
|
102
|
-
const dependencyExist =
|
|
103
|
-
dependencies !== null &&
|
|
104
|
-
dependencies.properties.some(
|
|
105
|
-
(p) => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === pkg,
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const devDependencyExist =
|
|
109
|
-
devDependencies !== null &&
|
|
110
|
-
devDependencies.properties.some(
|
|
111
|
-
(p) => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === pkg,
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
return dependencyExist || devDependencyExist;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const rule: Rule.RuleModule = {
|
|
118
|
-
meta: {
|
|
119
|
-
type: 'problem',
|
|
120
|
-
docs: {
|
|
121
|
-
description:
|
|
122
|
-
'Enforce the versions of critical packages are within desired ranges by checking resolutions section in package.json',
|
|
123
|
-
recommended: true,
|
|
124
|
-
},
|
|
125
|
-
hasSuggestions: false,
|
|
126
|
-
messages: {
|
|
127
|
-
invalidPackageResolution: `Make sure the resolutions for the following packages match major and minor version ranges ${JSON.stringify(
|
|
128
|
-
DESIRED_PKG_VERSIONS,
|
|
129
|
-
)}`,
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
create(context) {
|
|
133
|
-
const fileName = context.getFilename();
|
|
134
|
-
return {
|
|
135
|
-
ObjectExpression: (node: Rule.Node) => {
|
|
136
|
-
if (!fileName.endsWith('package.json') || node.type !== 'ObjectExpression') {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const packageResolutions = getObjectPropertyAsObject(node, 'resolutions');
|
|
141
|
-
const packageDependencies = getObjectPropertyAsObject(node, 'dependencies');
|
|
142
|
-
const packageDevDependencies = getObjectPropertyAsObject(node, 'devDependencies');
|
|
143
|
-
const rootDir = findRootSync(process.cwd());
|
|
144
|
-
const isRootPackageJson = fileName.endsWith(`${rootDir}/package.json`);
|
|
145
|
-
|
|
146
|
-
if (packageResolutions !== null) {
|
|
147
|
-
for (const [key, values] of Object.entries(DESIRED_PKG_VERSIONS)) {
|
|
148
|
-
if (
|
|
149
|
-
!values.some((value) => {
|
|
150
|
-
return verifyResolutionFromObject({
|
|
151
|
-
resolutions: packageResolutions as ObjectExpression,
|
|
152
|
-
dependencies: packageDependencies,
|
|
153
|
-
devDependencies: packageDevDependencies,
|
|
154
|
-
pkg: key,
|
|
155
|
-
version: value,
|
|
156
|
-
optional: !isRootPackageJson,
|
|
157
|
-
});
|
|
158
|
-
})
|
|
159
|
-
) {
|
|
160
|
-
return context.report({
|
|
161
|
-
node,
|
|
162
|
-
messageId: 'invalidPackageResolution',
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
export default rule;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { tester } from '../../../../__tests__/utils/_tester';
|
|
2
|
-
import rule from '../../index';
|
|
3
|
-
import { type PackageJson } from 'read-pkg-up';
|
|
4
|
-
|
|
5
|
-
let mockPath = 'test/package.json';
|
|
6
|
-
|
|
7
|
-
let mockPackageJson: PackageJson = {
|
|
8
|
-
'platform-feature-flags': {
|
|
9
|
-
'test-flag': {
|
|
10
|
-
type: 'boolean',
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
jest.mock('read-pkg-up', () => ({
|
|
15
|
-
sync: () => ({
|
|
16
|
-
path: mockPath,
|
|
17
|
-
packageJson: mockPackageJson,
|
|
18
|
-
}),
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
describe('test prefix rule', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
mockPath = 'test/package.json';
|
|
24
|
-
|
|
25
|
-
mockPackageJson = {
|
|
26
|
-
'platform-feature-flags': {
|
|
27
|
-
'prefix-flag': {
|
|
28
|
-
type: 'boolean',
|
|
29
|
-
},
|
|
30
|
-
'no-prefix-flag': {
|
|
31
|
-
type: 'boolean',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// this isolates the invalid case so we can test the suggestion properly
|
|
38
|
-
tester.run('ensure-feature-flag-registration', rule, {
|
|
39
|
-
valid: [
|
|
40
|
-
{
|
|
41
|
-
options: [{ allowedPrefixes: ['prefix'] }],
|
|
42
|
-
code: `ffTest('prefix-flag')`,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
options: [{ allowedPrefixes: ['prefix'] }],
|
|
46
|
-
code: `getBooleanFF('prefix-flag')`,
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
invalid: [
|
|
50
|
-
{
|
|
51
|
-
options: [{ allowedPrefixes: ['prefix'] }],
|
|
52
|
-
code: `ffTest('no-prefix-flag')`,
|
|
53
|
-
errors: [
|
|
54
|
-
{
|
|
55
|
-
messageId: 'featureFlagIncorrectPrefix',
|
|
56
|
-
data: {
|
|
57
|
-
featureFlag: 'no-prefix-flag',
|
|
58
|
-
allowedPrefixes: 'prefix',
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
});
|
|
65
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
import type { Rule } from 'eslint';
|
|
3
|
-
import { getMetadataForFilename, getterIdentifierToFlagTypeMap } from '../util/registration-utils';
|
|
4
|
-
|
|
5
|
-
type RuleOptions = {
|
|
6
|
-
allowedPrefixes: string[];
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const rule: Rule.RuleModule = {
|
|
10
|
-
meta: {
|
|
11
|
-
docs: {
|
|
12
|
-
url: 'http://go/pff-eslint',
|
|
13
|
-
recommended: false,
|
|
14
|
-
},
|
|
15
|
-
type: 'problem',
|
|
16
|
-
messages: {
|
|
17
|
-
featureFlagIncorrectPrefix: `Is this a LaunchDarkly feature flag? It needs a prefix so Confluence picks it up: [{{ allowedPrefixes }}]. Statsig feature gates don't need a prefix.`,
|
|
18
|
-
},
|
|
19
|
-
hasSuggestions: true,
|
|
20
|
-
schema: [
|
|
21
|
-
{
|
|
22
|
-
type: 'object',
|
|
23
|
-
properties: {
|
|
24
|
-
allowedPrefixes: {
|
|
25
|
-
type: 'array',
|
|
26
|
-
items: [
|
|
27
|
-
{
|
|
28
|
-
type: 'string',
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
required: ['allowedPrefixes'],
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
create(context) {
|
|
38
|
-
const { allowedPrefixes } = (context.options?.[0] as RuleOptions) ?? {};
|
|
39
|
-
|
|
40
|
-
return Object.fromEntries(
|
|
41
|
-
(
|
|
42
|
-
Object.keys(getterIdentifierToFlagTypeMap) as (keyof typeof getterIdentifierToFlagTypeMap)[]
|
|
43
|
-
).map((getterIdentifier) => [
|
|
44
|
-
`CallExpression[callee.name=/${getterIdentifier}/]`,
|
|
45
|
-
(node: Rule.Node) => {
|
|
46
|
-
// to make typescript happy
|
|
47
|
-
if (node.type === 'CallExpression') {
|
|
48
|
-
const args = node.arguments;
|
|
49
|
-
|
|
50
|
-
const filename = context.getFilename();
|
|
51
|
-
const { pkgJson: packageJson } = getMetadataForFilename(filename);
|
|
52
|
-
const platformFeatureFlags = packageJson['platform-feature-flags'];
|
|
53
|
-
|
|
54
|
-
// existence of registration section is done in 'ensure-feature-flag-registration'
|
|
55
|
-
if (!platformFeatureFlags) {
|
|
56
|
-
return {};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (args.length === 1 && args[0].type === 'Literal' && args[0].raw) {
|
|
60
|
-
const featureFlag = args[0].value as string;
|
|
61
|
-
if (!allowedPrefixes.some((prefix) => featureFlag.startsWith(prefix))) {
|
|
62
|
-
return context.report({
|
|
63
|
-
node: args[0],
|
|
64
|
-
messageId: 'featureFlagIncorrectPrefix',
|
|
65
|
-
data: {
|
|
66
|
-
allowedPrefixes: allowedPrefixes.map((p) => `${p}`).join(','),
|
|
67
|
-
featureFlag,
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return {};
|
|
75
|
-
},
|
|
76
|
-
]),
|
|
77
|
-
);
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export default rule;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { tester } from '../../../../__tests__/utils/_tester';
|
|
2
|
-
import rule from '../../index';
|
|
3
|
-
import { type PackageJson } from 'read-pkg-up';
|
|
4
|
-
|
|
5
|
-
let mockPath = 'test/package.json';
|
|
6
|
-
|
|
7
|
-
let mockPackageJson: PackageJson = {
|
|
8
|
-
'platform-feature-flags': {
|
|
9
|
-
'test-flag': {
|
|
10
|
-
type: 'boolean',
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
jest.mock('read-pkg-up', () => ({
|
|
15
|
-
sync: () => ({
|
|
16
|
-
path: mockPath,
|
|
17
|
-
packageJson: mockPackageJson,
|
|
18
|
-
}),
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
describe('with existing platform-feature-flags section', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
mockPath = 'test/package.json';
|
|
24
|
-
|
|
25
|
-
mockPackageJson = {
|
|
26
|
-
'platform-feature-flags': {
|
|
27
|
-
'test-flag': {
|
|
28
|
-
type: 'boolean',
|
|
29
|
-
},
|
|
30
|
-
'string-flag': {
|
|
31
|
-
type: 'string',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// this isolates the invalid case so we can test the suggestion properly
|
|
38
|
-
tester.run('ensure-feature-flag-registration', rule, {
|
|
39
|
-
valid: [
|
|
40
|
-
{
|
|
41
|
-
code: `getBooleanFF('test-flag')`,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
code: `ffTest('test-flag')`,
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
invalid: [
|
|
48
|
-
{
|
|
49
|
-
code: `ffTest('test-flag-invalid')`,
|
|
50
|
-
errors: [
|
|
51
|
-
{
|
|
52
|
-
messageId: 'featureFlagMissing',
|
|
53
|
-
suggestions: [
|
|
54
|
-
{
|
|
55
|
-
messageId: 'changeFeatureFlag',
|
|
56
|
-
data: {
|
|
57
|
-
closestFlag: 'test-flag',
|
|
58
|
-
},
|
|
59
|
-
output: `ffTest('test-flag')`,
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
code: `getBooleanFF('test-flag-invalid')`,
|
|
67
|
-
errors: [
|
|
68
|
-
{
|
|
69
|
-
messageId: 'featureFlagMissing',
|
|
70
|
-
suggestions: [
|
|
71
|
-
{
|
|
72
|
-
messageId: 'changeFeatureFlag',
|
|
73
|
-
data: {
|
|
74
|
-
closestFlag: 'test-flag',
|
|
75
|
-
},
|
|
76
|
-
output: `getBooleanFF('test-flag')`,
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
code: `getBooleanFF('string-flag')`,
|
|
84
|
-
errors: [
|
|
85
|
-
{
|
|
86
|
-
messageId: 'featureFlagIncorrectType',
|
|
87
|
-
data: {
|
|
88
|
-
featureFlag: 'string-flag',
|
|
89
|
-
expectedType: 'boolean',
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
describe('with missing platform-feature-flags section', () => {
|
|
99
|
-
beforeEach(() => {
|
|
100
|
-
// change path to bust cache
|
|
101
|
-
mockPath = 'invalid-pkg/package.json';
|
|
102
|
-
mockPackageJson = {};
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
tester.run('ensure-feature-flag-registration', rule, {
|
|
106
|
-
valid: [],
|
|
107
|
-
invalid: [
|
|
108
|
-
{
|
|
109
|
-
filename: 'other-directory/index.ts',
|
|
110
|
-
code: `if(getBooleanFF('test-flag')) { }`,
|
|
111
|
-
errors: [{ messageId: 'registrationSectionMissing' }],
|
|
112
|
-
},
|
|
113
|
-
],
|
|
114
|
-
});
|
|
115
|
-
});
|