@atlaskit/eslint-plugin-design-system 4.15.6 → 4.16.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 +6 -0
- package/README.md +56 -2
- package/configs/deprecated.json +33 -0
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/rules/no-deprecated-apis/helpers/filter-actionable-deprecations.js +59 -0
- package/dist/cjs/rules/no-deprecated-apis/helpers/validate-deprecated-apis-config.js +60 -0
- package/dist/cjs/rules/no-deprecated-apis/index.js +108 -32
- package/dist/cjs/version.json +1 -1
- package/dist/configs/deprecated.json +33 -0
- package/dist/es2019/index.js +2 -1
- package/dist/es2019/rules/no-deprecated-apis/helpers/filter-actionable-deprecations.js +50 -0
- package/dist/es2019/rules/no-deprecated-apis/helpers/validate-deprecated-apis-config.js +54 -0
- package/dist/es2019/rules/no-deprecated-apis/index.js +98 -32
- package/dist/es2019/version.json +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/rules/no-deprecated-apis/helpers/filter-actionable-deprecations.js +51 -0
- package/dist/esm/rules/no-deprecated-apis/helpers/validate-deprecated-apis-config.js +52 -0
- package/dist/esm/rules/no-deprecated-apis/index.js +104 -31
- package/dist/esm/version.json +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/rules/no-deprecated-apis/helpers/filter-actionable-deprecations.d.ts +1 -0
- package/dist/types/rules/no-deprecated-apis/helpers/validate-deprecated-apis-config.d.ts +2 -0
- package/dist/types/rules/no-deprecated-apis/index.d.ts +14 -2
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 4.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`efadee8e999`](https://bitbucket.org/atlassian/atlassian-frontend/commits/efadee8e999) - Update no-deprecated-apis ESlint rule to accept configurations
|
|
8
|
+
|
|
3
9
|
## 4.15.6
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -40,13 +40,67 @@ module.exports = {
|
|
|
40
40
|
+ '@atlaskit/design-system/no-unsafe-design-token-usage': ['error', { 'shouldEnsureFallbackUsage': true }],
|
|
41
41
|
+ '@atlaskit/design-system/use-visually-hidden': 'error',
|
|
42
42
|
+ '@atlaskit/design-system/no-deprecated-imports': 'error',
|
|
43
|
-
+ '@atlaskit/design-system/no-deprecated-api
|
|
43
|
+
+ '@atlaskit/design-system/no-deprecated-api': 'error',
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
## Usage of '@atlaskit/design-system/no-deprecated-api'
|
|
50
|
+
|
|
51
|
+
You can use the `@atlaskit/design-system/no-deprecated-api` rule to check for deprecated API usage in your codebase. The rule can take one option - `deprecatedConfig`, if not provided, the rule will use the default config file. If provided, the rule will override the default config file and use the config file provided.
|
|
52
|
+
See the examples below:
|
|
53
|
+
[Usage with default deprecated APIs config](###Usage-with-default-deprecated-APIs-config)
|
|
54
|
+
[Overwrite default deprecated APIs config](###Overwrite-default-deprecated-APIs-config)
|
|
55
|
+
|
|
56
|
+
### Default deprecated APIs config
|
|
57
|
+
|
|
58
|
+
The default config containing the deprecated APIs config for this rule. You can import the default config file from `@atlaskit/eslint-plugin-design-system`.
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import { configs } from '@atlaskit/eslint-plugin-design-system';
|
|
62
|
+
const { deprecatedConfig } = configs;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
In the default config file you can find the following fields:
|
|
66
|
+
|
|
67
|
+
- `deprecatedAPI`, which is the deprecated props. Each prop has the following fields:
|
|
68
|
+
|
|
69
|
+
- `moduleSpecifier`, which is the module specifier of the package in which the prop was deprecated. For example: `@atlaskit/button`.
|
|
70
|
+
- `namedSpecifier`**(optional)**, which is an array of named specifiers of the package in which the prop was deprecated. For example: `Button`.
|
|
71
|
+
- `actionableVersion`**(optional)**, which is the version of the package in which the prop can be actioned on. For example: `1.0.0`.
|
|
72
|
+
|
|
73
|
+
### Usage with default deprecated APIs config
|
|
74
|
+
|
|
75
|
+
Enable the rule as other rules if you want to use the default deprecated APIs config. The rule will automatically load the default config file and use it.
|
|
76
|
+
|
|
77
|
+
Enable the rule in your `.eslintrc.js` file:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
rules: {
|
|
81
|
+
'@atlaskit/design-system/no-deprecated-api': 'error'
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Overwrite default deprecated APIs config
|
|
86
|
+
|
|
87
|
+
You can overwrite the default deprecated APIs to suits your needs. You can do this by providing the `deprecatedConfig` option to the rule. The option can be partial of the default config file, or a new config that contatins the required fields described in the [Default deprecated APIs config](###Default-deprecated-APIs-config) section.
|
|
88
|
+
|
|
89
|
+
The plugin also provides a `filterActionableDeprecations` util function that accepts the `deprecated APIs config` and `your root package.json` as params, and will filter the default deprecated APIs config based on the package versions listed in the package.json file, and return a list of actionable entries.
|
|
90
|
+
Example:
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
import { configs } from '@atlaskit/eslint-plugin-design-system';
|
|
94
|
+
import packageJson from '.path-to/package.json';
|
|
95
|
+
|
|
96
|
+
rules: {
|
|
97
|
+
'@atlaskit/design-system/no-deprecated-api': ['error', {
|
|
98
|
+
'deprecatedConfig': filterActionableDeprecations(configs.deprecatedConfig, JSON.parse(packageJson)),
|
|
99
|
+
}]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Rules may come with fixers to assist.
|
|
50
104
|
For individual rules see the [`rules`](./src/rules) folder,
|
|
51
105
|
however its strongly recommended to use the rules as above.
|
|
52
106
|
You can read more about configuring eslint in their [documentation](https://eslint.org/docs/user-guide/configuring).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cssFn": [{
|
|
3
|
+
"moduleSpecifier": "@atlaskit/menu"
|
|
4
|
+
},
|
|
5
|
+
{
|
|
6
|
+
"moduleSpecifier": "@atlaskit/side-navigation"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"overrides": [{
|
|
10
|
+
"moduleSpecifier": "@atlaskit/menu"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"moduleSpecifier": "@atlaskit/side-navigation"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"type": [{
|
|
17
|
+
"moduleSpecifier": "@atlaskit/inline-message"
|
|
18
|
+
}],
|
|
19
|
+
"textColor": [{
|
|
20
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
21
|
+
|
|
22
|
+
}],
|
|
23
|
+
"iconColor": [{
|
|
24
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
25
|
+
}],
|
|
26
|
+
"iconGradientStart": [{
|
|
27
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
28
|
+
}],
|
|
29
|
+
"iconGradientStop": [{
|
|
30
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
31
|
+
}]
|
|
32
|
+
|
|
33
|
+
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -4,7 +4,14 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.
|
|
7
|
+
exports.configs = void 0;
|
|
8
|
+
Object.defineProperty(exports, "filterActionableDeprecations", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function get() {
|
|
11
|
+
return _filterActionableDeprecations.filterActionableDeprecations;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
exports.rules = void 0;
|
|
8
15
|
var _ensureDesignTokenUsage = _interopRequireDefault(require("./rules/ensure-design-token-usage"));
|
|
9
16
|
var _ensureDesignTokenUsageSpacing = _interopRequireDefault(require("./rules/ensure-design-token-usage-spacing"));
|
|
10
17
|
var _iconLabel = _interopRequireDefault(require("./rules/icon-label"));
|
|
@@ -14,6 +21,7 @@ var _noDeprecatedDesignTokenUsage = _interopRequireDefault(require("./rules/no-d
|
|
|
14
21
|
var _noDeprecatedImports = _interopRequireDefault(require("./rules/no-deprecated-imports"));
|
|
15
22
|
var _noUnsafeDesignTokenUsage = _interopRequireDefault(require("./rules/no-unsafe-design-token-usage"));
|
|
16
23
|
var _useVisuallyHidden = _interopRequireDefault(require("./rules/use-visually-hidden"));
|
|
24
|
+
var _filterActionableDeprecations = require("./rules/no-deprecated-apis/helpers/filter-actionable-deprecations");
|
|
17
25
|
var rules = {
|
|
18
26
|
'ensure-design-token-usage': _ensureDesignTokenUsage.default,
|
|
19
27
|
'icon-label': _iconLabel.default,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.filterActionableDeprecations = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
|
+
var _semver = _interopRequireDefault(require("semver"));
|
|
11
|
+
var _validateDeprecatedApisConfig = require("./validate-deprecated-apis-config");
|
|
12
|
+
var filterActionableDeprecations = function filterActionableDeprecations(originalDeprecatedConfig, rootPackageJson) {
|
|
13
|
+
// verify the config is valid and parse it to an object
|
|
14
|
+
var validatedDeprecatedConfig = (0, _validateDeprecatedApisConfig.getValidatedConfig)(originalDeprecatedConfig);
|
|
15
|
+
|
|
16
|
+
// verify the root package.json is valid and parse it to an object
|
|
17
|
+
var rootPackageDependencies;
|
|
18
|
+
try {
|
|
19
|
+
rootPackageDependencies = JSON.parse(rootPackageJson).dependencies;
|
|
20
|
+
if (!rootPackageDependencies) {
|
|
21
|
+
throw new Error('No dependencies found in the provided root package.json');
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
var error = e;
|
|
25
|
+
throw new Error("Failed to parse root package.json: ".concat(error.message));
|
|
26
|
+
}
|
|
27
|
+
var filteredConfig = {};
|
|
28
|
+
// filter out the deprecated APIs that are not actionable
|
|
29
|
+
for (var _i = 0, _Object$entries = Object.entries(validatedDeprecatedConfig); _i < _Object$entries.length; _i++) {
|
|
30
|
+
var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2),
|
|
31
|
+
apiKey = _Object$entries$_i[0],
|
|
32
|
+
apiValues = _Object$entries$_i[1];
|
|
33
|
+
var filterApiValues = apiValues.filter(function (apiValue) {
|
|
34
|
+
var _rootPackageDependenc;
|
|
35
|
+
var moduleSpecifier = apiValue.moduleSpecifier,
|
|
36
|
+
actionableVersion = apiValue.actionableVersion;
|
|
37
|
+
|
|
38
|
+
// if actionableVersion is not provided in the deprecated APIs config, it is actionable on all versions
|
|
39
|
+
if (!actionableVersion) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
var installedVersion = (_rootPackageDependenc = rootPackageDependencies) === null || _rootPackageDependenc === void 0 ? void 0 : _rootPackageDependenc[moduleSpecifier];
|
|
43
|
+
var coercedInstalledVersion = _semver.default.coerce(installedVersion);
|
|
44
|
+
if (!coercedInstalledVersion) {
|
|
45
|
+
throw new Error("No valid ".concat(moduleSpecifier, " found in the dependencies of root package.json."));
|
|
46
|
+
}
|
|
47
|
+
var coercedActionableVersion = _semver.default.coerce(actionableVersion);
|
|
48
|
+
if (!coercedActionableVersion) {
|
|
49
|
+
throw new Error("Actionable version is invalid for ".concat(moduleSpecifier, " in the deprecated APIs config."));
|
|
50
|
+
}
|
|
51
|
+
return _semver.default.gte(coercedInstalledVersion, coercedActionableVersion);
|
|
52
|
+
});
|
|
53
|
+
if (filterApiValues.length > 0) {
|
|
54
|
+
Object.assign(filteredConfig, (0, _defineProperty2.default)({}, apiKey, filterApiValues));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return JSON.stringify(filteredConfig);
|
|
58
|
+
};
|
|
59
|
+
exports.filterActionableDeprecations = filterActionableDeprecations;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.getValidatedConfig = void 0;
|
|
8
|
+
var _ajv = _interopRequireDefault(require("ajv"));
|
|
9
|
+
var deprecatedSchema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
patternProperties: {
|
|
12
|
+
'.+': {
|
|
13
|
+
type: 'array',
|
|
14
|
+
items: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
moduleSpecifier: {
|
|
18
|
+
type: 'string'
|
|
19
|
+
},
|
|
20
|
+
namedSpecifiers: {
|
|
21
|
+
type: 'array',
|
|
22
|
+
items: {
|
|
23
|
+
type: 'string'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
actionableVersion: {
|
|
27
|
+
type: 'string'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ['moduleSpecifier'],
|
|
31
|
+
additionalProperites: false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
allowMatchingProperties: true
|
|
36
|
+
};
|
|
37
|
+
var getValidatedConfig = function getValidatedConfig(originalDeprecatedConfig) {
|
|
38
|
+
var parsedDeprecatedConfig = {};
|
|
39
|
+
try {
|
|
40
|
+
parsedDeprecatedConfig = JSON.parse(originalDeprecatedConfig);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
var error = e;
|
|
43
|
+
throw new Error("Failed to parse JSON string: ".concat(error.message));
|
|
44
|
+
}
|
|
45
|
+
var ajv = new _ajv.default({
|
|
46
|
+
allErrors: true
|
|
47
|
+
});
|
|
48
|
+
var validate = ajv.compile(deprecatedSchema);
|
|
49
|
+
var valid = validate(parsedDeprecatedConfig);
|
|
50
|
+
if (!valid) {
|
|
51
|
+
var errors = validate.errors;
|
|
52
|
+
if (errors && errors.length) {
|
|
53
|
+
throw new Error("Deprecated APIs config is invalid: ".concat(errors));
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error('Failed to validate deprecated APIs config with unknown error.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return parsedDeprecatedConfig;
|
|
59
|
+
};
|
|
60
|
+
exports.getValidatedConfig = getValidatedConfig;
|
|
@@ -1,78 +1,154 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
3
4
|
Object.defineProperty(exports, "__esModule", {
|
|
4
5
|
value: true
|
|
5
6
|
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
exports.noDeprecatedJSXAttributeMessageId = exports.name = exports.default = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
var _utils = require("@typescript-eslint/utils");
|
|
12
|
+
var createRule = _utils.ESLintUtils.RuleCreator(function (name) {
|
|
13
|
+
return name;
|
|
14
|
+
});
|
|
15
|
+
var noDeprecatedJSXAttributeMessageId = 'noDeprecatedJSXAttributes';
|
|
16
|
+
exports.noDeprecatedJSXAttributeMessageId = noDeprecatedJSXAttributeMessageId;
|
|
17
|
+
var isNodeOfType = function isNodeOfType(node, nodeType) {
|
|
18
|
+
return _utils.ASTUtils.isNodeOfType(nodeType)(node);
|
|
19
|
+
};
|
|
20
|
+
var isImportDeclaration = function isImportDeclaration(programStatement) {
|
|
21
|
+
return (programStatement === null || programStatement === void 0 ? void 0 : programStatement.type) === 'ImportDeclaration';
|
|
22
|
+
};
|
|
23
|
+
var findJSXElementName = function findJSXElementName(jsxAttributeNode) {
|
|
24
|
+
if (!jsxAttributeNode.parent || !isNodeOfType(jsxAttributeNode.parent, _utils.AST_NODE_TYPES.JSXOpeningElement)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
var openingElement = jsxAttributeNode.parent;
|
|
28
|
+
if (!isNodeOfType(openingElement.name, _utils.AST_NODE_TYPES.JSXIdentifier)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
return openingElement.name.name;
|
|
32
|
+
};
|
|
33
|
+
var getConfig = function getConfig() {
|
|
34
|
+
var configPath = _path.default.resolve(__dirname, '..', '..', '..', 'configs', 'deprecated.json');
|
|
35
|
+
var source = _fs.default.readFileSync(configPath, 'utf8');
|
|
36
|
+
var parsedConfig = JSON.parse(source);
|
|
37
|
+
return parsedConfig;
|
|
15
38
|
};
|
|
16
|
-
var
|
|
17
|
-
|
|
39
|
+
var name = 'no-deprecated-apis';
|
|
40
|
+
exports.name = name;
|
|
41
|
+
var rule = createRule({
|
|
42
|
+
name: name,
|
|
43
|
+
defaultOptions: [{
|
|
44
|
+
deprecatedConfig: getConfig()
|
|
45
|
+
}],
|
|
18
46
|
meta: {
|
|
19
47
|
type: 'suggestion',
|
|
20
48
|
docs: {
|
|
21
49
|
description: 'Disallow specified APIs that have been marked as deprecated and/or discouraged.',
|
|
22
|
-
recommended:
|
|
50
|
+
recommended: 'warn'
|
|
23
51
|
},
|
|
24
52
|
messages: {
|
|
25
|
-
|
|
26
|
-
}
|
|
53
|
+
noDeprecatedJSXAttributes: 'The JSX attribute {{propName}} has been deprecated.'
|
|
54
|
+
},
|
|
55
|
+
schema: [{
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
deprecatedConfig: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
'.+': {
|
|
62
|
+
type: 'array',
|
|
63
|
+
items: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
moduleSpecifier: {
|
|
67
|
+
type: 'string'
|
|
68
|
+
},
|
|
69
|
+
namedSpecifiers: {
|
|
70
|
+
type: 'array',
|
|
71
|
+
items: {
|
|
72
|
+
type: 'string'
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
actionableVersion: {
|
|
76
|
+
type: 'string'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: ['moduleSpecifier'],
|
|
80
|
+
additionalProperites: false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}]
|
|
27
87
|
},
|
|
28
|
-
create: function create(context) {
|
|
88
|
+
create: function create(context, _ref) {
|
|
89
|
+
var _context$options$;
|
|
90
|
+
var _ref2 = (0, _slicedToArray2.default)(_ref, 1),
|
|
91
|
+
options = _ref2[0];
|
|
92
|
+
// Get rule configuration
|
|
93
|
+
var defaultDeprecatedConfig = options.deprecatedConfig;
|
|
94
|
+
|
|
95
|
+
// Get the rule configuration specified otherwise use default config.
|
|
96
|
+
// A bit confusing as it seems that the default options have precedence over the user specified options.
|
|
97
|
+
var deprecatedConfig = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.deprecatedConfig) || defaultDeprecatedConfig;
|
|
29
98
|
return {
|
|
30
99
|
// find JSX atribute - find name of attribute - get source and find relevant identifiers.
|
|
31
100
|
JSXAttribute: function JSXAttribute(node) {
|
|
32
|
-
var
|
|
33
|
-
if (!(
|
|
101
|
+
var jsxAttributeIdentifier = node.name;
|
|
102
|
+
if (!isNodeOfType(jsxAttributeIdentifier, _utils.AST_NODE_TYPES.JSXIdentifier)) {
|
|
34
103
|
return;
|
|
35
104
|
}
|
|
36
|
-
|
|
105
|
+
var jsxAttributeName = jsxAttributeIdentifier.name;
|
|
106
|
+
if (!deprecatedConfig[jsxAttributeName]) {
|
|
37
107
|
return;
|
|
38
108
|
}
|
|
39
|
-
var
|
|
40
|
-
var bannedApi = node.name.name;
|
|
41
|
-
|
|
42
|
-
// traverse the tree to the nearest JSX Element and get its name
|
|
43
|
-
var closesetJSXElement = (0, _eslintCodemodUtils.closestOfType)(node, 'JSXOpeningElement');
|
|
44
|
-
var jsxElementName = (0, _eslintCodemodUtils.isNodeOfType)(closesetJSXElement === null || closesetJSXElement === void 0 ? void 0 : closesetJSXElement.name, 'JSXIdentifier') && closesetJSXElement.name.name;
|
|
109
|
+
var jsxElementName = findJSXElementName(node);
|
|
45
110
|
if (!jsxElementName) {
|
|
46
111
|
return;
|
|
47
112
|
}
|
|
113
|
+
var source = context.getSourceCode();
|
|
48
114
|
|
|
49
115
|
// find an import for the path of the banned api
|
|
50
|
-
|
|
51
|
-
var
|
|
116
|
+
deprecatedConfig[jsxAttributeName].forEach(function (importItem) {
|
|
117
|
+
var _importItem$namedSpec;
|
|
118
|
+
var importNode = source.ast.body.filter(isImportDeclaration).find(function (node) {
|
|
119
|
+
return node.source.value === importItem.moduleSpecifier;
|
|
120
|
+
});
|
|
52
121
|
if (!importNode) {
|
|
53
122
|
return;
|
|
54
123
|
}
|
|
55
124
|
|
|
56
125
|
// find an import that matches our JSX element
|
|
57
|
-
var
|
|
126
|
+
var targetNode = importNode.specifiers.find(function (node) {
|
|
58
127
|
return node.local.name === jsxElementName;
|
|
59
128
|
});
|
|
60
|
-
|
|
129
|
+
|
|
130
|
+
// check if the import exists
|
|
131
|
+
if (!targetNode) {
|
|
61
132
|
return;
|
|
62
133
|
}
|
|
63
134
|
|
|
64
|
-
// if
|
|
135
|
+
// if the import has named specifiers, check if the JSX element is one of them
|
|
136
|
+
if (importItem !== null && importItem !== void 0 && (_importItem$namedSpec = importItem.namedSpecifiers) !== null && _importItem$namedSpec !== void 0 && _importItem$namedSpec.length && !importItem.namedSpecifiers.includes(targetNode.local.name)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// if we're here, there is a valid lint error.
|
|
65
141
|
context.report({
|
|
66
142
|
node: node,
|
|
67
|
-
messageId: '
|
|
143
|
+
messageId: 'noDeprecatedJSXAttributes',
|
|
68
144
|
data: {
|
|
69
|
-
propName:
|
|
145
|
+
propName: jsxAttributeName
|
|
70
146
|
}
|
|
71
147
|
});
|
|
72
148
|
});
|
|
73
149
|
}
|
|
74
150
|
};
|
|
75
151
|
}
|
|
76
|
-
};
|
|
152
|
+
});
|
|
77
153
|
var _default = rule;
|
|
78
154
|
exports.default = _default;
|
package/dist/cjs/version.json
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cssFn": [{
|
|
3
|
+
"moduleSpecifier": "@atlaskit/menu"
|
|
4
|
+
},
|
|
5
|
+
{
|
|
6
|
+
"moduleSpecifier": "@atlaskit/side-navigation"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"overrides": [{
|
|
10
|
+
"moduleSpecifier": "@atlaskit/menu"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"moduleSpecifier": "@atlaskit/side-navigation"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"type": [{
|
|
17
|
+
"moduleSpecifier": "@atlaskit/inline-message"
|
|
18
|
+
}],
|
|
19
|
+
"textColor": [{
|
|
20
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
21
|
+
|
|
22
|
+
}],
|
|
23
|
+
"iconColor": [{
|
|
24
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
25
|
+
}],
|
|
26
|
+
"iconGradientStart": [{
|
|
27
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
28
|
+
}],
|
|
29
|
+
"iconGradientStop": [{
|
|
30
|
+
"moduleSpecifier": "@atlaskit/logo"
|
|
31
|
+
}]
|
|
32
|
+
|
|
33
|
+
}
|
package/dist/es2019/index.js
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import { getValidatedConfig } from './validate-deprecated-apis-config';
|
|
3
|
+
export const filterActionableDeprecations = (originalDeprecatedConfig, rootPackageJson) => {
|
|
4
|
+
// verify the config is valid and parse it to an object
|
|
5
|
+
const validatedDeprecatedConfig = getValidatedConfig(originalDeprecatedConfig);
|
|
6
|
+
|
|
7
|
+
// verify the root package.json is valid and parse it to an object
|
|
8
|
+
let rootPackageDependencies;
|
|
9
|
+
try {
|
|
10
|
+
rootPackageDependencies = JSON.parse(rootPackageJson).dependencies;
|
|
11
|
+
if (!rootPackageDependencies) {
|
|
12
|
+
throw new Error('No dependencies found in the provided root package.json');
|
|
13
|
+
}
|
|
14
|
+
} catch (e) {
|
|
15
|
+
const error = e;
|
|
16
|
+
throw new Error(`Failed to parse root package.json: ${error.message}`);
|
|
17
|
+
}
|
|
18
|
+
const filteredConfig = {};
|
|
19
|
+
// filter out the deprecated APIs that are not actionable
|
|
20
|
+
for (const [apiKey, apiValues] of Object.entries(validatedDeprecatedConfig)) {
|
|
21
|
+
const filterApiValues = apiValues.filter(apiValue => {
|
|
22
|
+
var _rootPackageDependenc;
|
|
23
|
+
const {
|
|
24
|
+
moduleSpecifier,
|
|
25
|
+
actionableVersion
|
|
26
|
+
} = apiValue;
|
|
27
|
+
|
|
28
|
+
// if actionableVersion is not provided in the deprecated APIs config, it is actionable on all versions
|
|
29
|
+
if (!actionableVersion) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
const installedVersion = (_rootPackageDependenc = rootPackageDependencies) === null || _rootPackageDependenc === void 0 ? void 0 : _rootPackageDependenc[moduleSpecifier];
|
|
33
|
+
const coercedInstalledVersion = semver.coerce(installedVersion);
|
|
34
|
+
if (!coercedInstalledVersion) {
|
|
35
|
+
throw new Error(`No valid ${moduleSpecifier} found in the dependencies of root package.json.`);
|
|
36
|
+
}
|
|
37
|
+
const coercedActionableVersion = semver.coerce(actionableVersion);
|
|
38
|
+
if (!coercedActionableVersion) {
|
|
39
|
+
throw new Error(`Actionable version is invalid for ${moduleSpecifier} in the deprecated APIs config.`);
|
|
40
|
+
}
|
|
41
|
+
return semver.gte(coercedInstalledVersion, coercedActionableVersion);
|
|
42
|
+
});
|
|
43
|
+
if (filterApiValues.length > 0) {
|
|
44
|
+
Object.assign(filteredConfig, {
|
|
45
|
+
[apiKey]: filterApiValues
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return JSON.stringify(filteredConfig);
|
|
50
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
const deprecatedSchema = {
|
|
3
|
+
type: 'object',
|
|
4
|
+
patternProperties: {
|
|
5
|
+
'.+': {
|
|
6
|
+
type: 'array',
|
|
7
|
+
items: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
moduleSpecifier: {
|
|
11
|
+
type: 'string'
|
|
12
|
+
},
|
|
13
|
+
namedSpecifiers: {
|
|
14
|
+
type: 'array',
|
|
15
|
+
items: {
|
|
16
|
+
type: 'string'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
actionableVersion: {
|
|
20
|
+
type: 'string'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
required: ['moduleSpecifier'],
|
|
24
|
+
additionalProperites: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
allowMatchingProperties: true
|
|
29
|
+
};
|
|
30
|
+
export const getValidatedConfig = originalDeprecatedConfig => {
|
|
31
|
+
let parsedDeprecatedConfig = {};
|
|
32
|
+
try {
|
|
33
|
+
parsedDeprecatedConfig = JSON.parse(originalDeprecatedConfig);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
const error = e;
|
|
36
|
+
throw new Error(`Failed to parse JSON string: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
const ajv = new Ajv({
|
|
39
|
+
allErrors: true
|
|
40
|
+
});
|
|
41
|
+
const validate = ajv.compile(deprecatedSchema);
|
|
42
|
+
const valid = validate(parsedDeprecatedConfig);
|
|
43
|
+
if (!valid) {
|
|
44
|
+
const {
|
|
45
|
+
errors
|
|
46
|
+
} = validate;
|
|
47
|
+
if (errors && errors.length) {
|
|
48
|
+
throw new Error(`Deprecated APIs config is invalid: ${errors}`);
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error('Failed to validate deprecated APIs config with unknown error.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return parsedDeprecatedConfig;
|
|
54
|
+
};
|
|
@@ -1,69 +1,135 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { AST_NODE_TYPES, ASTUtils, ESLintUtils } from '@typescript-eslint/utils';
|
|
4
|
+
const createRule = ESLintUtils.RuleCreator(name => name);
|
|
5
|
+
export const noDeprecatedJSXAttributeMessageId = 'noDeprecatedJSXAttributes';
|
|
6
|
+
const isNodeOfType = (node, nodeType) => ASTUtils.isNodeOfType(nodeType)(node);
|
|
7
|
+
const isImportDeclaration = programStatement => {
|
|
8
|
+
return (programStatement === null || programStatement === void 0 ? void 0 : programStatement.type) === 'ImportDeclaration';
|
|
9
9
|
};
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const findJSXElementName = jsxAttributeNode => {
|
|
11
|
+
if (!jsxAttributeNode.parent || !isNodeOfType(jsxAttributeNode.parent, AST_NODE_TYPES.JSXOpeningElement)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const openingElement = jsxAttributeNode.parent;
|
|
15
|
+
if (!isNodeOfType(openingElement.name, AST_NODE_TYPES.JSXIdentifier)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
return openingElement.name.name;
|
|
19
|
+
};
|
|
20
|
+
const getConfig = () => {
|
|
21
|
+
const configPath = path.resolve(__dirname, '..', '..', '..', 'configs', 'deprecated.json');
|
|
22
|
+
const source = fs.readFileSync(configPath, 'utf8');
|
|
23
|
+
const parsedConfig = JSON.parse(source);
|
|
24
|
+
return parsedConfig;
|
|
25
|
+
};
|
|
26
|
+
export const name = 'no-deprecated-apis';
|
|
27
|
+
const rule = createRule({
|
|
28
|
+
name,
|
|
29
|
+
defaultOptions: [{
|
|
30
|
+
deprecatedConfig: getConfig()
|
|
31
|
+
}],
|
|
12
32
|
meta: {
|
|
13
33
|
type: 'suggestion',
|
|
14
34
|
docs: {
|
|
15
35
|
description: 'Disallow specified APIs that have been marked as deprecated and/or discouraged.',
|
|
16
|
-
recommended:
|
|
36
|
+
recommended: 'warn'
|
|
17
37
|
},
|
|
18
38
|
messages: {
|
|
19
|
-
|
|
20
|
-
}
|
|
39
|
+
noDeprecatedJSXAttributes: 'The JSX attribute {{propName}} has been deprecated.'
|
|
40
|
+
},
|
|
41
|
+
schema: [{
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
deprecatedConfig: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
'.+': {
|
|
48
|
+
type: 'array',
|
|
49
|
+
items: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
moduleSpecifier: {
|
|
53
|
+
type: 'string'
|
|
54
|
+
},
|
|
55
|
+
namedSpecifiers: {
|
|
56
|
+
type: 'array',
|
|
57
|
+
items: {
|
|
58
|
+
type: 'string'
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
actionableVersion: {
|
|
62
|
+
type: 'string'
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
required: ['moduleSpecifier'],
|
|
66
|
+
additionalProperites: false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}]
|
|
21
73
|
},
|
|
22
|
-
create(context) {
|
|
74
|
+
create(context, [options]) {
|
|
75
|
+
var _context$options$;
|
|
76
|
+
// Get rule configuration
|
|
77
|
+
const {
|
|
78
|
+
deprecatedConfig: defaultDeprecatedConfig
|
|
79
|
+
} = options;
|
|
80
|
+
|
|
81
|
+
// Get the rule configuration specified otherwise use default config.
|
|
82
|
+
// A bit confusing as it seems that the default options have precedence over the user specified options.
|
|
83
|
+
const deprecatedConfig = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.deprecatedConfig) || defaultDeprecatedConfig;
|
|
23
84
|
return {
|
|
24
85
|
// find JSX atribute - find name of attribute - get source and find relevant identifiers.
|
|
25
86
|
JSXAttribute(node) {
|
|
26
|
-
|
|
27
|
-
if (!isNodeOfType(
|
|
87
|
+
const jsxAttributeIdentifier = node.name;
|
|
88
|
+
if (!isNodeOfType(jsxAttributeIdentifier, AST_NODE_TYPES.JSXIdentifier)) {
|
|
28
89
|
return;
|
|
29
90
|
}
|
|
30
|
-
|
|
91
|
+
const jsxAttributeName = jsxAttributeIdentifier.name;
|
|
92
|
+
if (!deprecatedConfig[jsxAttributeName]) {
|
|
31
93
|
return;
|
|
32
94
|
}
|
|
33
|
-
const
|
|
34
|
-
const bannedApi = node.name.name;
|
|
35
|
-
|
|
36
|
-
// traverse the tree to the nearest JSX Element and get its name
|
|
37
|
-
const closesetJSXElement = closestOfType(node, 'JSXOpeningElement');
|
|
38
|
-
const jsxElementName = isNodeOfType(closesetJSXElement === null || closesetJSXElement === void 0 ? void 0 : closesetJSXElement.name, 'JSXIdentifier') && closesetJSXElement.name.name;
|
|
95
|
+
const jsxElementName = findJSXElementName(node);
|
|
39
96
|
if (!jsxElementName) {
|
|
40
97
|
return;
|
|
41
98
|
}
|
|
99
|
+
const source = context.getSourceCode();
|
|
42
100
|
|
|
43
101
|
// find an import for the path of the banned api
|
|
44
|
-
|
|
45
|
-
|
|
102
|
+
deprecatedConfig[jsxAttributeName].forEach(importItem => {
|
|
103
|
+
var _importItem$namedSpec;
|
|
104
|
+
const importNode = source.ast.body.filter(isImportDeclaration).find(node => node.source.value === importItem.moduleSpecifier);
|
|
46
105
|
if (!importNode) {
|
|
47
106
|
return;
|
|
48
107
|
}
|
|
49
108
|
|
|
50
109
|
// find an import that matches our JSX element
|
|
51
|
-
const
|
|
52
|
-
|
|
110
|
+
const targetNode = importNode.specifiers.find(node => node.local.name === jsxElementName);
|
|
111
|
+
|
|
112
|
+
// check if the import exists
|
|
113
|
+
if (!targetNode) {
|
|
53
114
|
return;
|
|
54
115
|
}
|
|
55
116
|
|
|
56
|
-
// if
|
|
117
|
+
// if the import has named specifiers, check if the JSX element is one of them
|
|
118
|
+
if (importItem !== null && importItem !== void 0 && (_importItem$namedSpec = importItem.namedSpecifiers) !== null && _importItem$namedSpec !== void 0 && _importItem$namedSpec.length && !importItem.namedSpecifiers.includes(targetNode.local.name)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// if we're here, there is a valid lint error.
|
|
57
123
|
context.report({
|
|
58
|
-
node
|
|
59
|
-
messageId: '
|
|
124
|
+
node,
|
|
125
|
+
messageId: 'noDeprecatedJSXAttributes',
|
|
60
126
|
data: {
|
|
61
|
-
propName:
|
|
127
|
+
propName: jsxAttributeName
|
|
62
128
|
}
|
|
63
129
|
});
|
|
64
130
|
});
|
|
65
131
|
}
|
|
66
132
|
};
|
|
67
133
|
}
|
|
68
|
-
};
|
|
134
|
+
});
|
|
69
135
|
export default rule;
|
package/dist/es2019/version.json
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
|
+
import semver from 'semver';
|
|
4
|
+
import { getValidatedConfig } from './validate-deprecated-apis-config';
|
|
5
|
+
export var filterActionableDeprecations = function filterActionableDeprecations(originalDeprecatedConfig, rootPackageJson) {
|
|
6
|
+
// verify the config is valid and parse it to an object
|
|
7
|
+
var validatedDeprecatedConfig = getValidatedConfig(originalDeprecatedConfig);
|
|
8
|
+
|
|
9
|
+
// verify the root package.json is valid and parse it to an object
|
|
10
|
+
var rootPackageDependencies;
|
|
11
|
+
try {
|
|
12
|
+
rootPackageDependencies = JSON.parse(rootPackageJson).dependencies;
|
|
13
|
+
if (!rootPackageDependencies) {
|
|
14
|
+
throw new Error('No dependencies found in the provided root package.json');
|
|
15
|
+
}
|
|
16
|
+
} catch (e) {
|
|
17
|
+
var error = e;
|
|
18
|
+
throw new Error("Failed to parse root package.json: ".concat(error.message));
|
|
19
|
+
}
|
|
20
|
+
var filteredConfig = {};
|
|
21
|
+
// filter out the deprecated APIs that are not actionable
|
|
22
|
+
for (var _i = 0, _Object$entries = Object.entries(validatedDeprecatedConfig); _i < _Object$entries.length; _i++) {
|
|
23
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
24
|
+
apiKey = _Object$entries$_i[0],
|
|
25
|
+
apiValues = _Object$entries$_i[1];
|
|
26
|
+
var filterApiValues = apiValues.filter(function (apiValue) {
|
|
27
|
+
var _rootPackageDependenc;
|
|
28
|
+
var moduleSpecifier = apiValue.moduleSpecifier,
|
|
29
|
+
actionableVersion = apiValue.actionableVersion;
|
|
30
|
+
|
|
31
|
+
// if actionableVersion is not provided in the deprecated APIs config, it is actionable on all versions
|
|
32
|
+
if (!actionableVersion) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
var installedVersion = (_rootPackageDependenc = rootPackageDependencies) === null || _rootPackageDependenc === void 0 ? void 0 : _rootPackageDependenc[moduleSpecifier];
|
|
36
|
+
var coercedInstalledVersion = semver.coerce(installedVersion);
|
|
37
|
+
if (!coercedInstalledVersion) {
|
|
38
|
+
throw new Error("No valid ".concat(moduleSpecifier, " found in the dependencies of root package.json."));
|
|
39
|
+
}
|
|
40
|
+
var coercedActionableVersion = semver.coerce(actionableVersion);
|
|
41
|
+
if (!coercedActionableVersion) {
|
|
42
|
+
throw new Error("Actionable version is invalid for ".concat(moduleSpecifier, " in the deprecated APIs config."));
|
|
43
|
+
}
|
|
44
|
+
return semver.gte(coercedInstalledVersion, coercedActionableVersion);
|
|
45
|
+
});
|
|
46
|
+
if (filterApiValues.length > 0) {
|
|
47
|
+
Object.assign(filteredConfig, _defineProperty({}, apiKey, filterApiValues));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return JSON.stringify(filteredConfig);
|
|
51
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
var deprecatedSchema = {
|
|
3
|
+
type: 'object',
|
|
4
|
+
patternProperties: {
|
|
5
|
+
'.+': {
|
|
6
|
+
type: 'array',
|
|
7
|
+
items: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
moduleSpecifier: {
|
|
11
|
+
type: 'string'
|
|
12
|
+
},
|
|
13
|
+
namedSpecifiers: {
|
|
14
|
+
type: 'array',
|
|
15
|
+
items: {
|
|
16
|
+
type: 'string'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
actionableVersion: {
|
|
20
|
+
type: 'string'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
required: ['moduleSpecifier'],
|
|
24
|
+
additionalProperites: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
allowMatchingProperties: true
|
|
29
|
+
};
|
|
30
|
+
export var getValidatedConfig = function getValidatedConfig(originalDeprecatedConfig) {
|
|
31
|
+
var parsedDeprecatedConfig = {};
|
|
32
|
+
try {
|
|
33
|
+
parsedDeprecatedConfig = JSON.parse(originalDeprecatedConfig);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
var error = e;
|
|
36
|
+
throw new Error("Failed to parse JSON string: ".concat(error.message));
|
|
37
|
+
}
|
|
38
|
+
var ajv = new Ajv({
|
|
39
|
+
allErrors: true
|
|
40
|
+
});
|
|
41
|
+
var validate = ajv.compile(deprecatedSchema);
|
|
42
|
+
var valid = validate(parsedDeprecatedConfig);
|
|
43
|
+
if (!valid) {
|
|
44
|
+
var errors = validate.errors;
|
|
45
|
+
if (errors && errors.length) {
|
|
46
|
+
throw new Error("Deprecated APIs config is invalid: ".concat(errors));
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error('Failed to validate deprecated APIs config with unknown error.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return parsedDeprecatedConfig;
|
|
52
|
+
};
|
|
@@ -1,71 +1,144 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { AST_NODE_TYPES, ASTUtils, ESLintUtils } from '@typescript-eslint/utils';
|
|
5
|
+
var createRule = ESLintUtils.RuleCreator(function (name) {
|
|
6
|
+
return name;
|
|
7
|
+
});
|
|
8
|
+
export var noDeprecatedJSXAttributeMessageId = 'noDeprecatedJSXAttributes';
|
|
9
|
+
var isNodeOfType = function isNodeOfType(node, nodeType) {
|
|
10
|
+
return ASTUtils.isNodeOfType(nodeType)(node);
|
|
9
11
|
};
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
+
var isImportDeclaration = function isImportDeclaration(programStatement) {
|
|
13
|
+
return (programStatement === null || programStatement === void 0 ? void 0 : programStatement.type) === 'ImportDeclaration';
|
|
14
|
+
};
|
|
15
|
+
var findJSXElementName = function findJSXElementName(jsxAttributeNode) {
|
|
16
|
+
if (!jsxAttributeNode.parent || !isNodeOfType(jsxAttributeNode.parent, AST_NODE_TYPES.JSXOpeningElement)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
var openingElement = jsxAttributeNode.parent;
|
|
20
|
+
if (!isNodeOfType(openingElement.name, AST_NODE_TYPES.JSXIdentifier)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
return openingElement.name.name;
|
|
24
|
+
};
|
|
25
|
+
var getConfig = function getConfig() {
|
|
26
|
+
var configPath = path.resolve(__dirname, '..', '..', '..', 'configs', 'deprecated.json');
|
|
27
|
+
var source = fs.readFileSync(configPath, 'utf8');
|
|
28
|
+
var parsedConfig = JSON.parse(source);
|
|
29
|
+
return parsedConfig;
|
|
30
|
+
};
|
|
31
|
+
export var name = 'no-deprecated-apis';
|
|
32
|
+
var rule = createRule({
|
|
33
|
+
name: name,
|
|
34
|
+
defaultOptions: [{
|
|
35
|
+
deprecatedConfig: getConfig()
|
|
36
|
+
}],
|
|
12
37
|
meta: {
|
|
13
38
|
type: 'suggestion',
|
|
14
39
|
docs: {
|
|
15
40
|
description: 'Disallow specified APIs that have been marked as deprecated and/or discouraged.',
|
|
16
|
-
recommended:
|
|
41
|
+
recommended: 'warn'
|
|
17
42
|
},
|
|
18
43
|
messages: {
|
|
19
|
-
|
|
20
|
-
}
|
|
44
|
+
noDeprecatedJSXAttributes: 'The JSX attribute {{propName}} has been deprecated.'
|
|
45
|
+
},
|
|
46
|
+
schema: [{
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
deprecatedConfig: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
'.+': {
|
|
53
|
+
type: 'array',
|
|
54
|
+
items: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
moduleSpecifier: {
|
|
58
|
+
type: 'string'
|
|
59
|
+
},
|
|
60
|
+
namedSpecifiers: {
|
|
61
|
+
type: 'array',
|
|
62
|
+
items: {
|
|
63
|
+
type: 'string'
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
actionableVersion: {
|
|
67
|
+
type: 'string'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
required: ['moduleSpecifier'],
|
|
71
|
+
additionalProperites: false
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}]
|
|
21
78
|
},
|
|
22
|
-
create: function create(context) {
|
|
79
|
+
create: function create(context, _ref) {
|
|
80
|
+
var _context$options$;
|
|
81
|
+
var _ref2 = _slicedToArray(_ref, 1),
|
|
82
|
+
options = _ref2[0];
|
|
83
|
+
// Get rule configuration
|
|
84
|
+
var defaultDeprecatedConfig = options.deprecatedConfig;
|
|
85
|
+
|
|
86
|
+
// Get the rule configuration specified otherwise use default config.
|
|
87
|
+
// A bit confusing as it seems that the default options have precedence over the user specified options.
|
|
88
|
+
var deprecatedConfig = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.deprecatedConfig) || defaultDeprecatedConfig;
|
|
23
89
|
return {
|
|
24
90
|
// find JSX atribute - find name of attribute - get source and find relevant identifiers.
|
|
25
91
|
JSXAttribute: function JSXAttribute(node) {
|
|
26
|
-
var
|
|
27
|
-
if (!isNodeOfType(
|
|
92
|
+
var jsxAttributeIdentifier = node.name;
|
|
93
|
+
if (!isNodeOfType(jsxAttributeIdentifier, AST_NODE_TYPES.JSXIdentifier)) {
|
|
28
94
|
return;
|
|
29
95
|
}
|
|
30
|
-
|
|
96
|
+
var jsxAttributeName = jsxAttributeIdentifier.name;
|
|
97
|
+
if (!deprecatedConfig[jsxAttributeName]) {
|
|
31
98
|
return;
|
|
32
99
|
}
|
|
33
|
-
var
|
|
34
|
-
var bannedApi = node.name.name;
|
|
35
|
-
|
|
36
|
-
// traverse the tree to the nearest JSX Element and get its name
|
|
37
|
-
var closesetJSXElement = closestOfType(node, 'JSXOpeningElement');
|
|
38
|
-
var jsxElementName = isNodeOfType(closesetJSXElement === null || closesetJSXElement === void 0 ? void 0 : closesetJSXElement.name, 'JSXIdentifier') && closesetJSXElement.name.name;
|
|
100
|
+
var jsxElementName = findJSXElementName(node);
|
|
39
101
|
if (!jsxElementName) {
|
|
40
102
|
return;
|
|
41
103
|
}
|
|
104
|
+
var source = context.getSourceCode();
|
|
42
105
|
|
|
43
106
|
// find an import for the path of the banned api
|
|
44
|
-
|
|
45
|
-
var
|
|
107
|
+
deprecatedConfig[jsxAttributeName].forEach(function (importItem) {
|
|
108
|
+
var _importItem$namedSpec;
|
|
109
|
+
var importNode = source.ast.body.filter(isImportDeclaration).find(function (node) {
|
|
110
|
+
return node.source.value === importItem.moduleSpecifier;
|
|
111
|
+
});
|
|
46
112
|
if (!importNode) {
|
|
47
113
|
return;
|
|
48
114
|
}
|
|
49
115
|
|
|
50
116
|
// find an import that matches our JSX element
|
|
51
|
-
var
|
|
117
|
+
var targetNode = importNode.specifiers.find(function (node) {
|
|
52
118
|
return node.local.name === jsxElementName;
|
|
53
119
|
});
|
|
54
|
-
|
|
120
|
+
|
|
121
|
+
// check if the import exists
|
|
122
|
+
if (!targetNode) {
|
|
55
123
|
return;
|
|
56
124
|
}
|
|
57
125
|
|
|
58
|
-
// if
|
|
126
|
+
// if the import has named specifiers, check if the JSX element is one of them
|
|
127
|
+
if (importItem !== null && importItem !== void 0 && (_importItem$namedSpec = importItem.namedSpecifiers) !== null && _importItem$namedSpec !== void 0 && _importItem$namedSpec.length && !importItem.namedSpecifiers.includes(targetNode.local.name)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// if we're here, there is a valid lint error.
|
|
59
132
|
context.report({
|
|
60
133
|
node: node,
|
|
61
|
-
messageId: '
|
|
134
|
+
messageId: 'noDeprecatedJSXAttributes',
|
|
62
135
|
data: {
|
|
63
|
-
propName:
|
|
136
|
+
propName: jsxAttributeName
|
|
64
137
|
}
|
|
65
138
|
});
|
|
66
139
|
});
|
|
67
140
|
}
|
|
68
141
|
};
|
|
69
142
|
}
|
|
70
|
-
};
|
|
143
|
+
});
|
|
71
144
|
export default rule;
|
package/dist/esm/version.json
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export declare const rules: {
|
|
2
2
|
'ensure-design-token-usage': import("eslint").Rule.RuleModule;
|
|
3
3
|
'icon-label': import("eslint").Rule.RuleModule;
|
|
4
|
-
'no-deprecated-apis': import("eslint").
|
|
4
|
+
'no-deprecated-apis': import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<string, [{
|
|
5
|
+
deprecatedConfig: import("./rules/no-deprecated-apis").DeprecatedConfig;
|
|
6
|
+
}], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
5
7
|
'no-deprecated-design-token-usage': import("eslint").Rule.RuleModule;
|
|
6
8
|
'no-deprecated-imports': import("eslint").Rule.RuleModule;
|
|
7
9
|
'no-banned-imports': import("eslint").Rule.RuleModule;
|
|
@@ -34,3 +36,4 @@ export declare const configs: {
|
|
|
34
36
|
};
|
|
35
37
|
};
|
|
36
38
|
};
|
|
39
|
+
export { filterActionableDeprecations } from './rules/no-deprecated-apis/helpers/filter-actionable-deprecations';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const filterActionableDeprecations: (originalDeprecatedConfig: string, rootPackageJson: string) => string;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
interface DeprecatedConfigEntry {
|
|
3
|
+
moduleSpecifier: string;
|
|
4
|
+
namedSpecifiers?: string[];
|
|
5
|
+
actionableVersion?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DeprecatedConfig {
|
|
8
|
+
[key: string]: DeprecatedConfigEntry[];
|
|
9
|
+
}
|
|
10
|
+
export declare const noDeprecatedJSXAttributeMessageId = "noDeprecatedJSXAttributes";
|
|
11
|
+
export declare const name = "no-deprecated-apis";
|
|
12
|
+
declare const rule: TSESLint.RuleModule<string, [{
|
|
13
|
+
deprecatedConfig: DeprecatedConfig;
|
|
14
|
+
}], TSESLint.RuleListener>;
|
|
3
15
|
export default rule;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/eslint-plugin-design-system",
|
|
3
3
|
"description": "The essential plugin for use with the Atlassian Design System.",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.16.0",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@atlaskit/tokens": "^1.2.0",
|
|
36
36
|
"@babel/runtime": "^7.0.0",
|
|
37
|
-
"eslint
|
|
37
|
+
"@typescript-eslint/utils": "^5.48.1",
|
|
38
|
+
"ajv": "^6.12.6",
|
|
39
|
+
"eslint-codemod-utils": "^1.6.3",
|
|
40
|
+
"semver": "^7.3.0"
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
40
43
|
"@atlaskit/ds-lib": "^2.1.0",
|
|
@@ -48,6 +51,9 @@
|
|
|
48
51
|
"tsconfig-paths": "^3.9.0",
|
|
49
52
|
"typescript": "4.5.5"
|
|
50
53
|
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"ak-postbuild": "cp -r configs dist"
|
|
56
|
+
},
|
|
51
57
|
"techstack": {
|
|
52
58
|
"@atlassian/frontend": {
|
|
53
59
|
"import-structure": "atlassian-conventions",
|