@atlaskit/eslint-plugin-platform 0.2.5 → 0.3.0
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 +33 -21
- package/dist/cjs/index.js +28 -11
- package/dist/cjs/rules/ensure-atlassian-team/index.js +1 -2
- package/dist/cjs/rules/ensure-critical-dependency-resolutions/index.js +86 -0
- package/dist/cjs/rules/ensure-feature-flag-prefix/index.js +1 -2
- package/dist/cjs/rules/ensure-feature-flag-registration/index.js +3 -4
- package/dist/cjs/rules/ensure-publish-valid/index.js +5 -29
- package/dist/cjs/rules/ensure-test-runner-arguments/index.js +1 -2
- package/dist/cjs/rules/ensure-test-runner-nested-count/index.js +3 -4
- package/dist/cjs/rules/no-duplicate-dependencies/index.js +90 -0
- package/dist/cjs/rules/no-invalid-feature-flag-usage/index.js +1 -2
- package/dist/cjs/rules/no-invalid-storybook-decorator-usage/index.js +1 -2
- package/dist/cjs/rules/no-pre-post-installs/index.js +1 -2
- package/dist/cjs/rules/util/handle-ast-object.js +30 -0
- package/dist/cjs/rules/util/registration-utils.js +3 -5
- package/dist/es2019/index.js +25 -2
- package/dist/es2019/rules/ensure-critical-dependency-resolutions/index.js +64 -0
- package/dist/es2019/rules/ensure-publish-valid/index.js +1 -20
- package/dist/es2019/rules/no-duplicate-dependencies/index.js +65 -0
- package/dist/es2019/rules/util/handle-ast-object.js +20 -0
- package/dist/esm/index.js +24 -4
- package/dist/esm/rules/ensure-critical-dependency-resolutions/index.js +80 -0
- package/dist/esm/rules/ensure-feature-flag-registration/index.js +2 -2
- package/dist/esm/rules/ensure-publish-valid/index.js +1 -24
- package/dist/esm/rules/ensure-test-runner-nested-count/index.js +2 -2
- package/dist/esm/rules/no-duplicate-dependencies/index.js +83 -0
- package/dist/esm/rules/util/handle-ast-object.js +24 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/rules/ensure-critical-dependency-resolutions/index.d.ts +3 -0
- package/dist/types/rules/no-duplicate-dependencies/index.d.ts +3 -0
- package/dist/types/rules/util/handle-ast-object.d.ts +3 -0
- package/dist/types-ts4.5/index.d.ts +2 -0
- package/dist/types-ts4.5/rules/ensure-critical-dependency-resolutions/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/no-duplicate-dependencies/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/util/handle-ast-object.d.ts +3 -0
- package/package.json +3 -2
- package/report.api.md +2 -0
- package/src/index.tsx +26 -4
- package/src/rules/ensure-critical-dependency-resolutions/__test__/unit/rule.test.tsx +125 -0
- package/src/rules/ensure-critical-dependency-resolutions/index.tsx +113 -0
- package/src/rules/ensure-publish-valid/index.ts +4 -43
- package/src/rules/no-duplicate-dependencies/__tests__/unit/rule.test.ts +116 -0
- package/src/rules/no-duplicate-dependencies/index.ts +83 -0
- package/src/rules/util/handle-ast-object.ts +44 -0
- package/tmp/api-report-tmp.d.ts +2 -0
- package/tsconfig.app.json +36 -0
- package/tsconfig.dev.json +40 -0
package/src/index.tsx
CHANGED
|
@@ -4,8 +4,10 @@ import noPreAndPostInstallScripts from './rules/no-pre-post-installs';
|
|
|
4
4
|
import ensureTestRunnerArguments from './rules/ensure-test-runner-arguments';
|
|
5
5
|
import ensureTestRunnerNestedCount from './rules/ensure-test-runner-nested-count';
|
|
6
6
|
import ensureAtlassianTeam from './rules/ensure-atlassian-team';
|
|
7
|
+
import noDuplicateDependencies from './rules/no-duplicate-dependencies';
|
|
7
8
|
import noInvalidFeatureFlagUsage from './rules/no-invalid-feature-flag-usage';
|
|
8
9
|
import ensureFeatureFlagPrefix from './rules/ensure-feature-flag-prefix';
|
|
10
|
+
import ensureCriticalDependencyResolutions from './rules/ensure-critical-dependency-resolutions';
|
|
9
11
|
import noInvalidStorybookDecoratorUsage from './rules/no-invalid-storybook-decorator-usage';
|
|
10
12
|
import ensurePublishValid from './rules/ensure-publish-valid';
|
|
11
13
|
|
|
@@ -15,6 +17,8 @@ export const rules = {
|
|
|
15
17
|
'ensure-test-runner-arguments': ensureTestRunnerArguments,
|
|
16
18
|
'ensure-test-runner-nested-count': ensureTestRunnerNestedCount,
|
|
17
19
|
'ensure-atlassian-team': ensureAtlassianTeam,
|
|
20
|
+
'ensure-critical-dependency-resolutions': ensureCriticalDependencyResolutions,
|
|
21
|
+
'no-duplicate-dependencies': noDuplicateDependencies,
|
|
18
22
|
'no-invalid-feature-flag-usage': noInvalidFeatureFlagUsage,
|
|
19
23
|
'no-pre-post-install-scripts': noPreAndPostInstallScripts,
|
|
20
24
|
'no-invalid-storybook-decorator-usage': noInvalidStorybookDecoratorUsage,
|
|
@@ -39,14 +43,32 @@ export const configs = {
|
|
|
39
43
|
},
|
|
40
44
|
};
|
|
41
45
|
|
|
46
|
+
const jsonPrefix =
|
|
47
|
+
'/* eslint-disable quote-props, comma-dangle, quotes, semi, eol-last, @typescript-eslint/semi, no-template-curly-in-string */ module.exports = ';
|
|
48
|
+
|
|
42
49
|
export const processors = {
|
|
43
50
|
'package-json-processor': {
|
|
44
51
|
preprocess: (source: string) => {
|
|
45
52
|
// augment the json into a js file
|
|
46
|
-
return [
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
return [jsonPrefix + source.trim()];
|
|
54
|
+
},
|
|
55
|
+
postprocess: (messages) => {
|
|
56
|
+
return messages[0].map((message) => {
|
|
57
|
+
const { fix } = message;
|
|
58
|
+
if (!fix) {
|
|
59
|
+
return message;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const offset = jsonPrefix.length;
|
|
63
|
+
return {
|
|
64
|
+
...message,
|
|
65
|
+
fix: {
|
|
66
|
+
...fix,
|
|
67
|
+
range: [fix.range[0] - offset, fix.range[1] - offset],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
});
|
|
49
71
|
},
|
|
50
|
-
|
|
72
|
+
supportsAutofix: true,
|
|
51
73
|
} as Linter.Processor,
|
|
52
74
|
};
|
|
@@ -0,0 +1,125 @@
|
|
|
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": "4.9.5",
|
|
15
|
+
}
|
|
16
|
+
}`,
|
|
17
|
+
filename: `${cwd}/package.json`,
|
|
18
|
+
},
|
|
19
|
+
// Root package.json, have all of the correct resolutions with ~
|
|
20
|
+
{
|
|
21
|
+
code: `const foo = {
|
|
22
|
+
"resolutions": {
|
|
23
|
+
"@types/react": "~16.14.25",
|
|
24
|
+
"typescript": "~4.9.8",
|
|
25
|
+
}
|
|
26
|
+
}`,
|
|
27
|
+
filename: `${cwd}/package.json`,
|
|
28
|
+
},
|
|
29
|
+
// Individual package's package.json. Have part of correct resolutions
|
|
30
|
+
{
|
|
31
|
+
code: `const foo = {
|
|
32
|
+
"resolutions": {
|
|
33
|
+
"@types/react": "16.14.15",
|
|
34
|
+
}
|
|
35
|
+
}`,
|
|
36
|
+
filename: `${cwd}/package/name/package.json`,
|
|
37
|
+
},
|
|
38
|
+
// Individual package's package.json. Not have relevant package resolutions
|
|
39
|
+
{
|
|
40
|
+
code: `const foo = {
|
|
41
|
+
"resolutions": {
|
|
42
|
+
"@types/abcd": "1.2.3",
|
|
43
|
+
}
|
|
44
|
+
}`,
|
|
45
|
+
filename: `${cwd}/package/name/package.json`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
invalid: [
|
|
49
|
+
// Root package.json. One package is correct, the other is missing
|
|
50
|
+
{
|
|
51
|
+
code: `const foo = {
|
|
52
|
+
"resolutions": {
|
|
53
|
+
"typescript": "~4.9.8",
|
|
54
|
+
}
|
|
55
|
+
}`,
|
|
56
|
+
filename: `${cwd}/package.json`,
|
|
57
|
+
errors: [
|
|
58
|
+
{
|
|
59
|
+
messageId: 'invalidPackageResolution',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
// Root package.json. Both packages have the wrong version ranges
|
|
64
|
+
{
|
|
65
|
+
code: `const foo = {
|
|
66
|
+
"resolutions": {
|
|
67
|
+
"@types/react": "16.8.25",
|
|
68
|
+
"typescript": "4.5.8",
|
|
69
|
+
}
|
|
70
|
+
}`,
|
|
71
|
+
filename: `${cwd}/package.json`,
|
|
72
|
+
errors: [
|
|
73
|
+
{
|
|
74
|
+
messageId: 'invalidPackageResolution',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
// Root package.json. One package is correct, the other has wrong version range
|
|
79
|
+
{
|
|
80
|
+
code: `const foo = {
|
|
81
|
+
"resolutions": {
|
|
82
|
+
"@types/react": "~16.14.25",
|
|
83
|
+
"typescript": "~4.5.8",
|
|
84
|
+
}
|
|
85
|
+
}`,
|
|
86
|
+
filename: `${cwd}/package.json`,
|
|
87
|
+
errors: [
|
|
88
|
+
{
|
|
89
|
+
messageId: 'invalidPackageResolution',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
// Root package.json. One package is correct, the other has ^ in its version
|
|
94
|
+
{
|
|
95
|
+
code: `const foo = {
|
|
96
|
+
"resolutions": {
|
|
97
|
+
"@types/react": "~16.14.25",
|
|
98
|
+
"typescript": "^4.9.5",
|
|
99
|
+
}
|
|
100
|
+
}`,
|
|
101
|
+
filename: `${cwd}/package.json`,
|
|
102
|
+
errors: [
|
|
103
|
+
{
|
|
104
|
+
messageId: 'invalidPackageResolution',
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
// Individual package's package.json. One package is correct, the other is wrong
|
|
109
|
+
{
|
|
110
|
+
code: `const foo = {
|
|
111
|
+
"resolutions": {
|
|
112
|
+
"@types/react": "16.14.15",
|
|
113
|
+
"typescript": "4.5.8",
|
|
114
|
+
}
|
|
115
|
+
}`,
|
|
116
|
+
filename: `${cwd}/packages/packge/directory/package.json`,
|
|
117
|
+
errors: [
|
|
118
|
+
{
|
|
119
|
+
messageId: 'invalidPackageResolution',
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { findRootSync } from '@manypkg/find-root';
|
|
2
|
+
import type { Rule } from 'eslint';
|
|
3
|
+
import type { ObjectExpression } from 'estree';
|
|
4
|
+
import { getObjectPropertyAsObject } from '../util/handle-ast-object';
|
|
5
|
+
|
|
6
|
+
// Here we only need to specify the major and minor versions
|
|
7
|
+
// In matchMinorVersion, we will check if the versions in resolutions fall in the right ranges.
|
|
8
|
+
const DESIRED_PKG_VERSIONS: Record<string, string> = {
|
|
9
|
+
typescript: '4.9',
|
|
10
|
+
'@types/react': '16.14',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const matchMinorVersion = (
|
|
14
|
+
desiredVersion: string,
|
|
15
|
+
versionInResolutions: string,
|
|
16
|
+
): boolean => {
|
|
17
|
+
const firstChar = versionInResolutions[0];
|
|
18
|
+
// The version is invalid if it doesn't start with a number or ~
|
|
19
|
+
if (!/^\d$/.test(firstChar) && firstChar !== '~') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
versionInResolutions.startsWith(desiredVersion) ||
|
|
25
|
+
versionInResolutions.startsWith('~' + desiredVersion)
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const verifyResolutionFromObject = (
|
|
30
|
+
node: ObjectExpression,
|
|
31
|
+
pkg: string,
|
|
32
|
+
version: string,
|
|
33
|
+
optional: boolean,
|
|
34
|
+
): boolean => {
|
|
35
|
+
// For root package.json, we require the critical packages' resolutions exist and with matching version
|
|
36
|
+
// For individual package's package.json, it's ok if resolutions don't exist. But if they do, the version should match
|
|
37
|
+
const resolutionExist = node.properties.some(
|
|
38
|
+
(p) =>
|
|
39
|
+
p.type === 'Property' && p.key.type === 'Literal' && p.key.value === pkg,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (!resolutionExist) {
|
|
43
|
+
return optional;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const resolutionExistAndMatch = node.properties.some(
|
|
47
|
+
(p) =>
|
|
48
|
+
p.type === 'Property' &&
|
|
49
|
+
p.key.type === 'Literal' &&
|
|
50
|
+
p.key.value === pkg &&
|
|
51
|
+
p.value.type === 'Literal' &&
|
|
52
|
+
matchMinorVersion(version, p.value.value as string),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return resolutionExistAndMatch;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const rule: Rule.RuleModule = {
|
|
59
|
+
meta: {
|
|
60
|
+
type: 'problem',
|
|
61
|
+
docs: {
|
|
62
|
+
description:
|
|
63
|
+
'Enforce the versions of critical packages are within desired ranges by checking resolutions section in package.json',
|
|
64
|
+
recommended: true,
|
|
65
|
+
},
|
|
66
|
+
hasSuggestions: false,
|
|
67
|
+
messages: {
|
|
68
|
+
invalidPackageResolution: `Make sure the resolutions for the following packages match major and minor version ranges ${JSON.stringify(
|
|
69
|
+
DESIRED_PKG_VERSIONS,
|
|
70
|
+
)}`,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
create(context) {
|
|
74
|
+
const fileName = context.getFilename();
|
|
75
|
+
return {
|
|
76
|
+
ObjectExpression: (node: Rule.Node) => {
|
|
77
|
+
if (
|
|
78
|
+
!fileName.endsWith('package.json') ||
|
|
79
|
+
node.type !== 'ObjectExpression'
|
|
80
|
+
) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const packageResolutions = getObjectPropertyAsObject(
|
|
85
|
+
node,
|
|
86
|
+
'resolutions',
|
|
87
|
+
);
|
|
88
|
+
const rootDir = findRootSync(process.cwd());
|
|
89
|
+
const isRootPackageJson = fileName.endsWith(`${rootDir}/package.json`);
|
|
90
|
+
|
|
91
|
+
if (packageResolutions !== null) {
|
|
92
|
+
for (const [key, value] of Object.entries(DESIRED_PKG_VERSIONS)) {
|
|
93
|
+
if (
|
|
94
|
+
!verifyResolutionFromObject(
|
|
95
|
+
packageResolutions as ObjectExpression,
|
|
96
|
+
key,
|
|
97
|
+
value,
|
|
98
|
+
!isRootPackageJson,
|
|
99
|
+
)
|
|
100
|
+
) {
|
|
101
|
+
return context.report({
|
|
102
|
+
node,
|
|
103
|
+
messageId: 'invalidPackageResolution',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default rule;
|
|
@@ -1,47 +1,8 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
BigIntLiteral,
|
|
7
|
-
} from 'estree';
|
|
8
|
-
const getObjectPropertyAsLiteral = (
|
|
9
|
-
node: ObjectExpression,
|
|
10
|
-
property: string,
|
|
11
|
-
): SimpleLiteral['value'] | RegExpLiteral['value'] | BigIntLiteral['value'] => {
|
|
12
|
-
const prop = node.properties.find(
|
|
13
|
-
(p) =>
|
|
14
|
-
p.type === 'Property' &&
|
|
15
|
-
p.key.type === 'Literal' &&
|
|
16
|
-
p.key.value === property,
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
// double check for property is to make typescript happy
|
|
20
|
-
if (prop?.type === 'Property' && prop?.value.type === 'Literal') {
|
|
21
|
-
return prop.value.value ?? null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return null;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const getObjectPropertyAsObject = (
|
|
28
|
-
node: ObjectExpression,
|
|
29
|
-
property: string,
|
|
30
|
-
): ObjectExpression | null => {
|
|
31
|
-
const prop = node.properties.find(
|
|
32
|
-
(p) =>
|
|
33
|
-
p.type === 'Property' &&
|
|
34
|
-
p.key.type === 'Literal' &&
|
|
35
|
-
p.key.value === property,
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// double check for property is to make typescript happy
|
|
39
|
-
if (prop?.type === 'Property' && prop?.value.type === 'ObjectExpression') {
|
|
40
|
-
return prop.value ?? null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return null;
|
|
44
|
-
};
|
|
2
|
+
import {
|
|
3
|
+
getObjectPropertyAsLiteral,
|
|
4
|
+
getObjectPropertyAsObject,
|
|
5
|
+
} from '../util/handle-ast-object';
|
|
45
6
|
|
|
46
7
|
type RuleOptions = {
|
|
47
8
|
// exceptions to this rule, will be ignored
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { tester } from '../../../../__tests__/utils/_tester';
|
|
2
|
+
import rule from '../../index';
|
|
3
|
+
|
|
4
|
+
describe('test no-duplicate-dependencies rule', () => {
|
|
5
|
+
tester.run('no-duplicate-dependencies-rule', rule, {
|
|
6
|
+
valid: [
|
|
7
|
+
{
|
|
8
|
+
code: `
|
|
9
|
+
module.exports = {
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"foo": "^1.0.0",
|
|
12
|
+
"bar": "^2.0.0"
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
`,
|
|
16
|
+
filename: 'dependencies.json',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
code: `
|
|
20
|
+
module.exports = {
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"foo": "^1.0.0",
|
|
23
|
+
"bar": "^2.0.0"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
`,
|
|
27
|
+
filename: 'devDependencies.json',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
code: `
|
|
31
|
+
module.exports = {
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"foo": "^1.0.0",
|
|
34
|
+
"bar": "^2.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"baz": "^3.0.0",
|
|
38
|
+
"qux": "^4.0.0"
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
`,
|
|
42
|
+
filename: 'devAndDependencies.json',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
invalid: [
|
|
46
|
+
{
|
|
47
|
+
code: `
|
|
48
|
+
module.exports = {
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"foo": "^1.0.0",
|
|
51
|
+
"bar": "^2.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"foo": "^1.0.0",
|
|
55
|
+
"baz": "^3.0.0",
|
|
56
|
+
"qux": "^4.0.0"
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
`,
|
|
60
|
+
output: `
|
|
61
|
+
module.exports = {
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"foo": "^1.0.0",
|
|
64
|
+
"bar": "^2.0.0"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"baz": "^3.0.0",
|
|
68
|
+
"qux": "^4.0.0"
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
`,
|
|
72
|
+
errors: [
|
|
73
|
+
{
|
|
74
|
+
data: {
|
|
75
|
+
name: 'foo',
|
|
76
|
+
},
|
|
77
|
+
messageId: 'unexpectedDuplicateDependency',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
filename: 'duplicateDependenciesFirst.json',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
code: `
|
|
84
|
+
module.exports = {
|
|
85
|
+
"dependencies": {
|
|
86
|
+
"bar": "^1.0.0"
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"foo": "^2.0.0",
|
|
90
|
+
"bar": "^1.0.0"
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
`,
|
|
94
|
+
output: `
|
|
95
|
+
module.exports = {
|
|
96
|
+
"dependencies": {
|
|
97
|
+
"bar": "^1.0.0"
|
|
98
|
+
},
|
|
99
|
+
"devDependencies": {
|
|
100
|
+
"foo": "^2.0.0"
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
`,
|
|
104
|
+
errors: [
|
|
105
|
+
{
|
|
106
|
+
data: {
|
|
107
|
+
name: 'bar',
|
|
108
|
+
},
|
|
109
|
+
messageId: 'unexpectedDuplicateDependency',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
filename: 'duplicateDependenciesLast.json',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
|
|
3
|
+
const rule: Rule.RuleModule = {
|
|
4
|
+
meta: {
|
|
5
|
+
type: 'problem',
|
|
6
|
+
docs: {
|
|
7
|
+
description:
|
|
8
|
+
'This rule disallows a dependency to be defined in both dependencies and devDependencies',
|
|
9
|
+
recommended: false,
|
|
10
|
+
},
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
messages: {
|
|
13
|
+
unexpectedDuplicateDependency: 'Unexpected duplicate dependency {{name}}',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
create(context) {
|
|
17
|
+
const dependencies = new Map();
|
|
18
|
+
const devDependencies = new Map();
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
'ObjectExpression Property[key.value=dependencies] Property': (
|
|
22
|
+
node: Rule.Node,
|
|
23
|
+
) => {
|
|
24
|
+
// @ts-expect-error
|
|
25
|
+
dependencies.set(node.key.value, node.key);
|
|
26
|
+
},
|
|
27
|
+
'ObjectExpression Property[key.value=devDependencies] Property': (
|
|
28
|
+
node: Rule.Node,
|
|
29
|
+
) => {
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
devDependencies.set(node.key.value, node.key);
|
|
32
|
+
},
|
|
33
|
+
'Program:exit': () => {
|
|
34
|
+
for (const [dependency, node] of devDependencies) {
|
|
35
|
+
if (dependencies.has(dependency)) {
|
|
36
|
+
context.report({
|
|
37
|
+
data: {
|
|
38
|
+
name: dependency,
|
|
39
|
+
},
|
|
40
|
+
fix(fixer) {
|
|
41
|
+
const sourceCode = context.getSourceCode();
|
|
42
|
+
const property = node.parent;
|
|
43
|
+
const isLastLine =
|
|
44
|
+
sourceCode.getTokenAfter(property)?.value === '}';
|
|
45
|
+
const end = property.loc.end;
|
|
46
|
+
|
|
47
|
+
if (!isLastLine) {
|
|
48
|
+
return fixer.removeRange([
|
|
49
|
+
sourceCode.getIndexFromLoc({
|
|
50
|
+
line: property.loc.start.line,
|
|
51
|
+
column: 0,
|
|
52
|
+
}),
|
|
53
|
+
sourceCode.getIndexFromLoc({
|
|
54
|
+
line: end.line + 1,
|
|
55
|
+
column: 0,
|
|
56
|
+
}),
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const previousToken = sourceCode.getTokenBefore(property)!;
|
|
61
|
+
|
|
62
|
+
return fixer.removeRange([
|
|
63
|
+
sourceCode.getIndexFromLoc({
|
|
64
|
+
line: previousToken.loc.end.line,
|
|
65
|
+
column: previousToken.loc.end.column - 1,
|
|
66
|
+
}),
|
|
67
|
+
sourceCode.getIndexFromLoc({
|
|
68
|
+
line: end.line,
|
|
69
|
+
column: end.column,
|
|
70
|
+
}),
|
|
71
|
+
]);
|
|
72
|
+
},
|
|
73
|
+
messageId: 'unexpectedDuplicateDependency',
|
|
74
|
+
node,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default rule;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ObjectExpression,
|
|
3
|
+
SimpleLiteral,
|
|
4
|
+
RegExpLiteral,
|
|
5
|
+
BigIntLiteral,
|
|
6
|
+
} from 'estree';
|
|
7
|
+
|
|
8
|
+
export const getObjectPropertyAsLiteral = (
|
|
9
|
+
node: ObjectExpression,
|
|
10
|
+
property: string,
|
|
11
|
+
): SimpleLiteral['value'] | RegExpLiteral['value'] | BigIntLiteral['value'] => {
|
|
12
|
+
const prop = node.properties.find(
|
|
13
|
+
(p) =>
|
|
14
|
+
p.type === 'Property' &&
|
|
15
|
+
p.key.type === 'Literal' &&
|
|
16
|
+
p.key.value === property,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// double check for property is to make typescript happy
|
|
20
|
+
if (prop?.type === 'Property' && prop?.value.type === 'Literal') {
|
|
21
|
+
return prop.value.value ?? null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const getObjectPropertyAsObject = (
|
|
28
|
+
node: ObjectExpression,
|
|
29
|
+
property: string,
|
|
30
|
+
): ObjectExpression | null => {
|
|
31
|
+
const prop = node.properties.find(
|
|
32
|
+
(p) =>
|
|
33
|
+
p.type === 'Property' &&
|
|
34
|
+
p.key.type === 'Literal' &&
|
|
35
|
+
p.key.value === property,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// double check for property is to make typescript happy
|
|
39
|
+
if (prop?.type === 'Property' && prop?.value.type === 'ObjectExpression') {
|
|
40
|
+
return prop.value ?? null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return null;
|
|
44
|
+
};
|
package/tmp/api-report-tmp.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ export const rules: {
|
|
|
37
37
|
'ensure-test-runner-arguments': Rule.RuleModule;
|
|
38
38
|
'ensure-test-runner-nested-count': Rule.RuleModule;
|
|
39
39
|
'ensure-atlassian-team': Rule.RuleModule;
|
|
40
|
+
'ensure-critical-dependency-resolutions': Rule.RuleModule;
|
|
41
|
+
'no-duplicate-dependencies': Rule.RuleModule;
|
|
40
42
|
'no-invalid-feature-flag-usage': Rule.RuleModule;
|
|
41
43
|
'no-pre-post-install-scripts': Rule.RuleModule;
|
|
42
44
|
'no-invalid-storybook-decorator-usage': Rule.RuleModule;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.node.json",
|
|
3
|
+
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
|
4
|
+
"exclude": [
|
|
5
|
+
"**/docs/**/*",
|
|
6
|
+
"**/__tests__/**/*",
|
|
7
|
+
"**/vr-tests/**/*",
|
|
8
|
+
"**/__perf__/**/*",
|
|
9
|
+
"**/*.test.*",
|
|
10
|
+
"**/test.*",
|
|
11
|
+
"**/test-*",
|
|
12
|
+
"**/examples.ts",
|
|
13
|
+
"**/examples.tsx",
|
|
14
|
+
"**/examples/*.ts",
|
|
15
|
+
"**/examples/*.tsx",
|
|
16
|
+
"**/examples/**/*.ts",
|
|
17
|
+
"**/examples/**/*.tsx",
|
|
18
|
+
"**/storybook/**/*",
|
|
19
|
+
"**/constellation/**/*",
|
|
20
|
+
".storybook/*",
|
|
21
|
+
"./__fixtures__/**/*",
|
|
22
|
+
"./__generated__/**/*",
|
|
23
|
+
"./mocks/**/*",
|
|
24
|
+
"./__mocks__/**/*",
|
|
25
|
+
"**/mock.*",
|
|
26
|
+
"**/codemods/**/*.ts",
|
|
27
|
+
"**/codemods/**/*.tsx"
|
|
28
|
+
],
|
|
29
|
+
"compilerOptions": {
|
|
30
|
+
"baseUrl": "./",
|
|
31
|
+
"lib": ["ES2021.String"],
|
|
32
|
+
"composite": true,
|
|
33
|
+
"outDir": "../../../tsDist/@atlaskit__eslint-plugin-platform/app"
|
|
34
|
+
},
|
|
35
|
+
"references": []
|
|
36
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.node.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"**/docs/**/*",
|
|
5
|
+
"**/__tests__/**/*",
|
|
6
|
+
"**/vr-tests/**/*",
|
|
7
|
+
"**/__perf__/**/*",
|
|
8
|
+
"**/*.test.*",
|
|
9
|
+
"**/test.*",
|
|
10
|
+
"**/test-*",
|
|
11
|
+
"**/examples.ts",
|
|
12
|
+
"**/examples.tsx",
|
|
13
|
+
"**/examples/*.ts",
|
|
14
|
+
"**/examples/*.tsx",
|
|
15
|
+
"**/examples/**/*.ts",
|
|
16
|
+
"**/examples/**/*.tsx",
|
|
17
|
+
"**/storybook/**/*",
|
|
18
|
+
"**/constellation/**/*",
|
|
19
|
+
".storybook/*",
|
|
20
|
+
"./__fixtures__/**/*",
|
|
21
|
+
"./__generated__/**/*",
|
|
22
|
+
"./mocks/**/*",
|
|
23
|
+
"./__mocks__/**/*",
|
|
24
|
+
"**/mock.*",
|
|
25
|
+
"**/codemods/**/*.ts",
|
|
26
|
+
"**/codemods/**/*.tsx"
|
|
27
|
+
],
|
|
28
|
+
"exclude": ["./dist/**/*", "./build/**/*", "./node_modules/**/*"],
|
|
29
|
+
"compilerOptions": {
|
|
30
|
+
"baseUrl": "./",
|
|
31
|
+
"lib": ["ES2021.String"],
|
|
32
|
+
"composite": true,
|
|
33
|
+
"outDir": "../../../tsDist/@atlaskit__eslint-plugin-platform/dev"
|
|
34
|
+
},
|
|
35
|
+
"references": [
|
|
36
|
+
{
|
|
37
|
+
"path": "tsconfig.app.json"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|