@atlaskit/eslint-plugin-design-system 10.25.0 → 10.26.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 +13 -0
- package/README.md +1 -1
- package/dist/cjs/rules/no-deprecated-imports/checks.js +201 -0
- package/dist/cjs/rules/no-deprecated-imports/constants.js +8 -0
- package/dist/cjs/rules/no-deprecated-imports/handlers/icon.js +204 -0
- package/dist/cjs/rules/no-deprecated-imports/index.js +25 -118
- package/dist/cjs/rules/utils/get-deprecated-config.js +9 -1
- package/dist/es2019/rules/no-deprecated-imports/checks.js +175 -0
- package/dist/es2019/rules/no-deprecated-imports/constants.js +2 -0
- package/dist/es2019/rules/no-deprecated-imports/handlers/icon.js +153 -0
- package/dist/es2019/rules/no-deprecated-imports/index.js +23 -103
- package/dist/es2019/rules/utils/get-deprecated-config.js +12 -1
- package/dist/esm/rules/no-deprecated-imports/checks.js +195 -0
- package/dist/esm/rules/no-deprecated-imports/constants.js +2 -0
- package/dist/esm/rules/no-deprecated-imports/handlers/icon.js +197 -0
- package/dist/esm/rules/no-deprecated-imports/index.js +23 -117
- package/dist/esm/rules/utils/get-deprecated-config.js +9 -1
- package/dist/types/index.codegen.d.ts +3 -9
- package/dist/types/rules/index.codegen.d.ts +1 -3
- package/dist/types/rules/no-deprecated-imports/checks.d.ts +11 -0
- package/dist/types/rules/no-deprecated-imports/constants.d.ts +2 -0
- package/dist/types/rules/no-deprecated-imports/handlers/icon.d.ts +38 -0
- package/dist/types/rules/no-deprecated-imports/index.d.ts +1 -7
- package/dist/types/rules/utils/types.d.ts +1 -0
- package/dist/types-ts4.5/index.codegen.d.ts +3 -15
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -5
- package/dist/types-ts4.5/rules/no-deprecated-imports/checks.d.ts +11 -0
- package/dist/types-ts4.5/rules/no-deprecated-imports/constants.d.ts +2 -0
- package/dist/types-ts4.5/rules/no-deprecated-imports/handlers/icon.d.ts +38 -0
- package/dist/types-ts4.5/rules/no-deprecated-imports/index.d.ts +1 -9
- package/dist/types-ts4.5/rules/utils/types.d.ts +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { getConfig } from '../utils/get-deprecated-config';
|
|
3
|
+
import { isDeprecatedImportConfig } from '../utils/types';
|
|
4
|
+
import { importNameWithCustomMessageId, pathWithCustomMessageId } from './constants';
|
|
5
|
+
import { getDeprecationIconHandler } from './handlers/icon';
|
|
6
|
+
export const createChecks = context => {
|
|
7
|
+
const deprecatedIconHandler = getDeprecationIconHandler(context);
|
|
8
|
+
const throwErrors = () => {
|
|
9
|
+
deprecatedIconHandler.throwErrors();
|
|
10
|
+
};
|
|
11
|
+
const getHandler = importSource => {
|
|
12
|
+
if (importSource.startsWith('@atlaskit/icon')) {
|
|
13
|
+
return deprecatedIconHandler;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Report a restricted path.
|
|
19
|
+
* @param {string} importSource path of the import
|
|
20
|
+
* @param {string} type whether the node is an import or export
|
|
21
|
+
* @param {node} node representing the restricted path reference
|
|
22
|
+
* @param {Map<string,TSESTree.Node>} importNames Map of import names that are being imported
|
|
23
|
+
* @returns {void}
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
function checkRestrictedPathAndReport({
|
|
27
|
+
importSource,
|
|
28
|
+
type,
|
|
29
|
+
node,
|
|
30
|
+
importNames
|
|
31
|
+
}) {
|
|
32
|
+
var _context$options$;
|
|
33
|
+
const restrictedPathMessages = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.deprecatedConfig) || getConfig('imports');
|
|
34
|
+
if (!isDeprecatedImportConfig(restrictedPathMessages)) {
|
|
35
|
+
throw new Error('Config is invalid for deprecated imports');
|
|
36
|
+
}
|
|
37
|
+
if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const config = restrictedPathMessages[importSource];
|
|
41
|
+
const handler = getHandler(importSource);
|
|
42
|
+
if (handler) {
|
|
43
|
+
if (type === 'import') {
|
|
44
|
+
handler.createImportError({
|
|
45
|
+
node,
|
|
46
|
+
importSource,
|
|
47
|
+
config
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
handler.createExportError({
|
|
51
|
+
node,
|
|
52
|
+
importSource,
|
|
53
|
+
config
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
// Default behaviour
|
|
58
|
+
// The message will only exist if the import is completely banned,
|
|
59
|
+
// eg a deprecated package
|
|
60
|
+
if ('message' in config) {
|
|
61
|
+
context.report({
|
|
62
|
+
node,
|
|
63
|
+
messageId: pathWithCustomMessageId,
|
|
64
|
+
data: {
|
|
65
|
+
importSource,
|
|
66
|
+
customMessage: config.message
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// if there are specific named exports that are banned,
|
|
72
|
+
// iterate through and check if they're being imported
|
|
73
|
+
if ('importSpecifiers' in config) {
|
|
74
|
+
var _config$importSpecifi;
|
|
75
|
+
(_config$importSpecifi = config.importSpecifiers) === null || _config$importSpecifi === void 0 ? void 0 : _config$importSpecifi.forEach(restrictedImport => {
|
|
76
|
+
if (importNames.has(restrictedImport.importName)) {
|
|
77
|
+
context.report({
|
|
78
|
+
node: importNames.get(restrictedImport.importName),
|
|
79
|
+
messageId: importNameWithCustomMessageId,
|
|
80
|
+
data: {
|
|
81
|
+
importName: restrictedImport.importName,
|
|
82
|
+
importSource: importSource,
|
|
83
|
+
customMessage: restrictedImport.message
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Checks a node to see if any problems should be reported.
|
|
94
|
+
* @param {ASTNode} node The node to check.
|
|
95
|
+
* @returns {void}
|
|
96
|
+
* @private
|
|
97
|
+
*/
|
|
98
|
+
const checkImportNode = node => {
|
|
99
|
+
const importSource = node.source.value.trim();
|
|
100
|
+
const importNames = new Map();
|
|
101
|
+
if ('specifiers' in node) {
|
|
102
|
+
for (const specifier of node.specifiers) {
|
|
103
|
+
let name;
|
|
104
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
105
|
+
name = 'default';
|
|
106
|
+
} else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
107
|
+
name = '*';
|
|
108
|
+
} else if (specifier.type === 'ImportSpecifier') {
|
|
109
|
+
name = specifier.imported.name;
|
|
110
|
+
}
|
|
111
|
+
if (name) {
|
|
112
|
+
importNames.set(name, specifier);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
checkRestrictedPathAndReport({
|
|
117
|
+
importSource,
|
|
118
|
+
type: 'import',
|
|
119
|
+
node,
|
|
120
|
+
importNames
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Checks a node to see if any problems should be reported.
|
|
126
|
+
* @param {ASTNode} node The node to check.
|
|
127
|
+
* @returns {void}
|
|
128
|
+
* @private
|
|
129
|
+
*/
|
|
130
|
+
const checkExportNode = node => {
|
|
131
|
+
if (!node.source || !node.source.value) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const importSource = node.source.value.trim();
|
|
135
|
+
const importNames = new Map();
|
|
136
|
+
if ('specifiers' in node) {
|
|
137
|
+
for (const specifier of node.specifiers) {
|
|
138
|
+
let name;
|
|
139
|
+
if (specifier.local) {
|
|
140
|
+
name = specifier.local.name;
|
|
141
|
+
}
|
|
142
|
+
if (name) {
|
|
143
|
+
importNames.set(name, specifier);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
checkRestrictedPathAndReport({
|
|
148
|
+
importSource,
|
|
149
|
+
type: 'export',
|
|
150
|
+
node,
|
|
151
|
+
importNames
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create a mapping of JSX elements by their name so they can be processed later.
|
|
157
|
+
* @param node The JSX node found by ESLint
|
|
158
|
+
*/
|
|
159
|
+
const checkJSXElement = node => {
|
|
160
|
+
if (!('openingElement' in node) || !isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
deprecatedIconHandler.checkJSXElement(node);
|
|
164
|
+
};
|
|
165
|
+
const checkIdentifier = node => {
|
|
166
|
+
deprecatedIconHandler.checkIdentifier(node);
|
|
167
|
+
};
|
|
168
|
+
return {
|
|
169
|
+
checkImportNode,
|
|
170
|
+
checkExportNode,
|
|
171
|
+
checkJSXElement,
|
|
172
|
+
checkIdentifier,
|
|
173
|
+
throwErrors
|
|
174
|
+
};
|
|
175
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { isNodeOfType, literal } from 'eslint-codemod-utils';
|
|
2
|
+
import coreIconLabMetadata from '@atlaskit/icon-lab/metadata';
|
|
3
|
+
import { coreIconMetadata, utilityIconMetadata } from '@atlaskit/icon/metadata';
|
|
4
|
+
import { pathWithCustomMessageId } from '../constants';
|
|
5
|
+
/**
|
|
6
|
+
* __Deprecation icon handler__
|
|
7
|
+
*
|
|
8
|
+
* A deprecation icon handler which is responsible for displaying an error for deprecated icons.
|
|
9
|
+
* It also includes a fixer to replace the deprecated icon with the new icon if a replacement exists.
|
|
10
|
+
*/
|
|
11
|
+
export const getDeprecationIconHandler = context => {
|
|
12
|
+
const jsxElements = new Map();
|
|
13
|
+
const identifiers = new Map();
|
|
14
|
+
const importErrors = {};
|
|
15
|
+
const exportErrors = {};
|
|
16
|
+
const getIconComponentName = name => {
|
|
17
|
+
return name.split(/\W/).map(part => `${part[0].toUpperCase()}${part.slice(1)}`).join('').concat('Icon');
|
|
18
|
+
};
|
|
19
|
+
const createImportError = ({
|
|
20
|
+
node,
|
|
21
|
+
importSource,
|
|
22
|
+
config
|
|
23
|
+
}) => {
|
|
24
|
+
if (config.message) {
|
|
25
|
+
const myError = {
|
|
26
|
+
node,
|
|
27
|
+
messageId: pathWithCustomMessageId,
|
|
28
|
+
data: {
|
|
29
|
+
importSource,
|
|
30
|
+
customMessage: config.message,
|
|
31
|
+
unfixable: config.unfixable ? 'true' : 'false'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
importErrors[node.source.value] = myError;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const createExportError = ({
|
|
38
|
+
node,
|
|
39
|
+
importSource,
|
|
40
|
+
config
|
|
41
|
+
}) => {
|
|
42
|
+
if (config.message) {
|
|
43
|
+
const myError = {
|
|
44
|
+
node,
|
|
45
|
+
messageId: pathWithCustomMessageId,
|
|
46
|
+
data: {
|
|
47
|
+
importSource,
|
|
48
|
+
customMessage: config.message
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
exportErrors[importSource] = myError;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const throwErrors = () => {
|
|
55
|
+
for (const [importSource, error] of Object.entries(importErrors)) {
|
|
56
|
+
if (importSource.includes('/migration/')) {
|
|
57
|
+
var _metadata$deprecatedI, _error$data;
|
|
58
|
+
const [_location, type, _migration, name] = importSource.split('/').slice(1);
|
|
59
|
+
const metadata = type === 'core' ? coreIconMetadata : utilityIconMetadata;
|
|
60
|
+
const [deprecatedIconName, legacyIconName] = name.split('--');
|
|
61
|
+
const replacement = metadata === null || metadata === void 0 ? void 0 : (_metadata$deprecatedI = metadata[deprecatedIconName]) === null || _metadata$deprecatedI === void 0 ? void 0 : _metadata$deprecatedI.replacement;
|
|
62
|
+
if (replacement && ((_error$data = error.data) === null || _error$data === void 0 ? void 0 : _error$data.unfixable) === 'false') {
|
|
63
|
+
const newIconName = getIconComponentName(replacement.name);
|
|
64
|
+
addAutoFix(error, importSource, `${replacement.location}/${replacement.type}/migration/${replacement.name}--${legacyIconName}`, newIconName);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
var _metadata, _metadata$name;
|
|
68
|
+
const [location, type, name] = importSource.split('/').slice(1);
|
|
69
|
+
let metadata;
|
|
70
|
+
if (location === 'icon') {
|
|
71
|
+
metadata = type === 'core' ? coreIconMetadata : utilityIconMetadata;
|
|
72
|
+
} else if (location === 'icon-lab') {
|
|
73
|
+
metadata = coreIconLabMetadata;
|
|
74
|
+
}
|
|
75
|
+
const replacement = (_metadata = metadata) === null || _metadata === void 0 ? void 0 : (_metadata$name = _metadata[name]) === null || _metadata$name === void 0 ? void 0 : _metadata$name.replacement;
|
|
76
|
+
if (replacement) {
|
|
77
|
+
const newIconName = getIconComponentName(replacement.name);
|
|
78
|
+
addAutoFix(error, importSource, `${replacement.location}/${replacement.type}/${replacement.name}`, newIconName);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
context.report(error);
|
|
82
|
+
}
|
|
83
|
+
for (const error of Object.values(exportErrors)) {
|
|
84
|
+
context.report(error);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const addAutoFix = (error, importSource, newImportSource, newIconName) => {
|
|
88
|
+
error.fix = fixer => {
|
|
89
|
+
const fixes = [];
|
|
90
|
+
|
|
91
|
+
//Find and update all usages of this icon in JSX with the replacement icon
|
|
92
|
+
const jsxUsageNodes = jsxElements.get(importSource);
|
|
93
|
+
if (jsxUsageNodes) {
|
|
94
|
+
for (const usageNode of jsxUsageNodes) {
|
|
95
|
+
fixes.push(fixer.replaceText(usageNode.openingElement.name, newIconName));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//Find and update all usages of this icon in identifiers with the replacement icon
|
|
100
|
+
const usageNodes = identifiers.get(importSource);
|
|
101
|
+
if (usageNodes) {
|
|
102
|
+
for (const usageNode of usageNodes) {
|
|
103
|
+
fixes.push(fixer.replaceText(usageNode.parent, `{${newIconName}}`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
fixes.push(fixer.replaceText(error.node, `${literal(`import ${newIconName} from '${newImportSource}'`)};`));
|
|
107
|
+
return fixes;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
const checkJSXElement = node => {
|
|
111
|
+
if (!('openingElement' in node) || !isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
for (const [importSource, error] of Object.entries(importErrors)) {
|
|
115
|
+
let importIconName = error.node.specifiers[0].local.name;
|
|
116
|
+
const iconName = node.openingElement.name.name;
|
|
117
|
+
if (iconName === importIconName) {
|
|
118
|
+
if (jsxElements.has(importSource)) {
|
|
119
|
+
var _jsxElements$get;
|
|
120
|
+
(_jsxElements$get = jsxElements.get(importSource)) === null || _jsxElements$get === void 0 ? void 0 : _jsxElements$get.push(node);
|
|
121
|
+
} else {
|
|
122
|
+
jsxElements.set(importSource, [node]);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const checkIdentifier = node => {
|
|
129
|
+
if (node.type !== 'Identifier' || node.parent.type !== 'JSXExpressionContainer') {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
for (const [importSource, error] of Object.entries(importErrors)) {
|
|
133
|
+
let importIconName = error.node.specifiers[0].local.name;
|
|
134
|
+
const iconName = node.name;
|
|
135
|
+
if (iconName === importIconName) {
|
|
136
|
+
if (identifiers.has(importSource)) {
|
|
137
|
+
var _identifiers$get;
|
|
138
|
+
(_identifiers$get = identifiers.get(importSource)) === null || _identifiers$get === void 0 ? void 0 : _identifiers$get.push(node);
|
|
139
|
+
} else {
|
|
140
|
+
identifiers.set(importSource, [node]);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
return {
|
|
147
|
+
createImportError,
|
|
148
|
+
createExportError,
|
|
149
|
+
checkJSXElement,
|
|
150
|
+
checkIdentifier,
|
|
151
|
+
throwErrors
|
|
152
|
+
};
|
|
153
|
+
};
|
|
@@ -25,22 +25,20 @@
|
|
|
25
25
|
* THE SOFTWARE.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
28
|
+
import { createLintRule } from '../utils/create-rule';
|
|
29
|
+
import { errorBoundary } from '../utils/error-boundary';
|
|
30
|
+
import { createChecks } from './checks';
|
|
31
31
|
export const name = 'no-deprecated-imports';
|
|
32
|
-
|
|
33
|
-
export const pathWithCustomMessageId = 'pathWithCustomMessage';
|
|
34
|
-
const rule = createRule({
|
|
35
|
-
name,
|
|
36
|
-
defaultOptions: [{
|
|
37
|
-
deprecatedConfig: getConfig('imports')
|
|
38
|
-
}],
|
|
32
|
+
const rule = createLintRule({
|
|
39
33
|
meta: {
|
|
34
|
+
name,
|
|
35
|
+
fixable: 'code',
|
|
36
|
+
hasSuggestions: true,
|
|
40
37
|
type: 'suggestion',
|
|
41
38
|
docs: {
|
|
42
39
|
description: 'Disallow importing deprecated modules.',
|
|
43
|
-
recommended:
|
|
40
|
+
recommended: true,
|
|
41
|
+
severity: 'error'
|
|
44
42
|
},
|
|
45
43
|
messages: {
|
|
46
44
|
pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
|
|
@@ -80,99 +78,21 @@ const rule = createRule({
|
|
|
80
78
|
}
|
|
81
79
|
}]
|
|
82
80
|
},
|
|
83
|
-
create(context
|
|
84
|
-
var _context$options$;
|
|
81
|
+
create(context) {
|
|
85
82
|
const {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* @private
|
|
100
|
-
*/
|
|
101
|
-
function checkRestrictedPathAndReport(importSource, node, importNames) {
|
|
102
|
-
if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const config = restrictedPathMessages[importSource];
|
|
106
|
-
|
|
107
|
-
// The message will only exist if the import is completely banned,
|
|
108
|
-
// eg a deprecated package
|
|
109
|
-
if ('message' in config) {
|
|
110
|
-
context.report({
|
|
111
|
-
node,
|
|
112
|
-
messageId: pathWithCustomMessageId,
|
|
113
|
-
data: {
|
|
114
|
-
importSource,
|
|
115
|
-
customMessage: config.message
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// if there are specific named exports that are banned,
|
|
121
|
-
// iterate through and check if they're being imported
|
|
122
|
-
if ('importSpecifiers' in config) {
|
|
123
|
-
var _config$importSpecifi;
|
|
124
|
-
(_config$importSpecifi = config.importSpecifiers) === null || _config$importSpecifi === void 0 ? void 0 : _config$importSpecifi.forEach(restrictedImport => {
|
|
125
|
-
if (importNames.has(restrictedImport.importName)) {
|
|
126
|
-
context.report({
|
|
127
|
-
node: importNames.get(restrictedImport.importName),
|
|
128
|
-
messageId: importNameWithCustomMessageId,
|
|
129
|
-
data: {
|
|
130
|
-
importName: restrictedImport.importName,
|
|
131
|
-
importSource: importSource,
|
|
132
|
-
customMessage: restrictedImport.message
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Checks a node to see if any problems should be reported.
|
|
142
|
-
* @param {ASTNode} node The node to check.
|
|
143
|
-
* @returns {void}
|
|
144
|
-
* @private
|
|
145
|
-
*/
|
|
146
|
-
const checkNode = node => {
|
|
147
|
-
const importSource = node.source.value.trim();
|
|
148
|
-
const importNames = new Map();
|
|
149
|
-
if ('specifiers' in node) {
|
|
150
|
-
for (const specifier of node.specifiers) {
|
|
151
|
-
let name;
|
|
152
|
-
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
153
|
-
name = 'default';
|
|
154
|
-
} else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
155
|
-
name = '*';
|
|
156
|
-
} else if (specifier.type === 'ImportSpecifier') {
|
|
157
|
-
name = specifier.imported.name;
|
|
158
|
-
} else if (specifier.local) {
|
|
159
|
-
name = specifier.local.name;
|
|
160
|
-
}
|
|
161
|
-
if (name) {
|
|
162
|
-
importNames.set(name, specifier);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
checkRestrictedPathAndReport(importSource, node, importNames);
|
|
167
|
-
};
|
|
168
|
-
return {
|
|
169
|
-
ImportDeclaration: checkNode,
|
|
170
|
-
ExportNamedDeclaration(node) {
|
|
171
|
-
if (node.source) {
|
|
172
|
-
checkNode(node);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
};
|
|
83
|
+
checkImportNode,
|
|
84
|
+
checkExportNode,
|
|
85
|
+
checkJSXElement,
|
|
86
|
+
checkIdentifier,
|
|
87
|
+
throwErrors
|
|
88
|
+
} = createChecks(context);
|
|
89
|
+
return errorBoundary({
|
|
90
|
+
ImportDeclaration: checkImportNode,
|
|
91
|
+
ExportNamedDeclaration: checkExportNode,
|
|
92
|
+
JSXElement: checkJSXElement,
|
|
93
|
+
Identifier: checkIdentifier,
|
|
94
|
+
'Program:exit': throwErrors
|
|
95
|
+
});
|
|
176
96
|
}
|
|
177
97
|
});
|
|
178
98
|
export default rule;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { deprecatedCore as deprecatedIconLabCore } from '@atlaskit/icon-lab/deprecated-map';
|
|
4
|
+
import { deprecatedCore as deprecatedIconCore, deprecatedUtility as deprecatedIconUtility } from '@atlaskit/icon/deprecated-map';
|
|
3
5
|
export const getConfig = specifier => {
|
|
4
6
|
const configPath = path.resolve(__dirname, '..', '..', '..', 'configs', 'deprecated.json');
|
|
5
7
|
const source = fs.readFileSync(configPath, 'utf8');
|
|
6
8
|
const parsedConfig = JSON.parse(source);
|
|
7
|
-
|
|
9
|
+
const combinedConfig = {
|
|
10
|
+
...parsedConfig,
|
|
11
|
+
imports: {
|
|
12
|
+
...parsedConfig.imports,
|
|
13
|
+
...deprecatedIconCore,
|
|
14
|
+
...deprecatedIconUtility,
|
|
15
|
+
...deprecatedIconLabCore
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return combinedConfig[specifier];
|
|
8
19
|
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
2
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
3
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
4
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
5
|
+
import { getConfig } from '../utils/get-deprecated-config';
|
|
6
|
+
import { isDeprecatedImportConfig } from '../utils/types';
|
|
7
|
+
import { importNameWithCustomMessageId, pathWithCustomMessageId } from './constants';
|
|
8
|
+
import { getDeprecationIconHandler } from './handlers/icon';
|
|
9
|
+
export var createChecks = function createChecks(context) {
|
|
10
|
+
var deprecatedIconHandler = getDeprecationIconHandler(context);
|
|
11
|
+
var throwErrors = function throwErrors() {
|
|
12
|
+
deprecatedIconHandler.throwErrors();
|
|
13
|
+
};
|
|
14
|
+
var getHandler = function getHandler(importSource) {
|
|
15
|
+
if (importSource.startsWith('@atlaskit/icon')) {
|
|
16
|
+
return deprecatedIconHandler;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Report a restricted path.
|
|
22
|
+
* @param {string} importSource path of the import
|
|
23
|
+
* @param {string} type whether the node is an import or export
|
|
24
|
+
* @param {node} node representing the restricted path reference
|
|
25
|
+
* @param {Map<string,TSESTree.Node>} importNames Map of import names that are being imported
|
|
26
|
+
* @returns {void}
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
function checkRestrictedPathAndReport(_ref) {
|
|
30
|
+
var _context$options$;
|
|
31
|
+
var importSource = _ref.importSource,
|
|
32
|
+
type = _ref.type,
|
|
33
|
+
node = _ref.node,
|
|
34
|
+
importNames = _ref.importNames;
|
|
35
|
+
var restrictedPathMessages = ((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.deprecatedConfig) || getConfig('imports');
|
|
36
|
+
if (!isDeprecatedImportConfig(restrictedPathMessages)) {
|
|
37
|
+
throw new Error('Config is invalid for deprecated imports');
|
|
38
|
+
}
|
|
39
|
+
if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
var config = restrictedPathMessages[importSource];
|
|
43
|
+
var handler = getHandler(importSource);
|
|
44
|
+
if (handler) {
|
|
45
|
+
if (type === 'import') {
|
|
46
|
+
handler.createImportError({
|
|
47
|
+
node: node,
|
|
48
|
+
importSource: importSource,
|
|
49
|
+
config: config
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
handler.createExportError({
|
|
53
|
+
node: node,
|
|
54
|
+
importSource: importSource,
|
|
55
|
+
config: config
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
// Default behaviour
|
|
60
|
+
// The message will only exist if the import is completely banned,
|
|
61
|
+
// eg a deprecated package
|
|
62
|
+
if ('message' in config) {
|
|
63
|
+
context.report({
|
|
64
|
+
node: node,
|
|
65
|
+
messageId: pathWithCustomMessageId,
|
|
66
|
+
data: {
|
|
67
|
+
importSource: importSource,
|
|
68
|
+
customMessage: config.message
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// if there are specific named exports that are banned,
|
|
74
|
+
// iterate through and check if they're being imported
|
|
75
|
+
if ('importSpecifiers' in config) {
|
|
76
|
+
var _config$importSpecifi;
|
|
77
|
+
(_config$importSpecifi = config.importSpecifiers) === null || _config$importSpecifi === void 0 || _config$importSpecifi.forEach(function (restrictedImport) {
|
|
78
|
+
if (importNames.has(restrictedImport.importName)) {
|
|
79
|
+
context.report({
|
|
80
|
+
node: importNames.get(restrictedImport.importName),
|
|
81
|
+
messageId: importNameWithCustomMessageId,
|
|
82
|
+
data: {
|
|
83
|
+
importName: restrictedImport.importName,
|
|
84
|
+
importSource: importSource,
|
|
85
|
+
customMessage: restrictedImport.message
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Checks a node to see if any problems should be reported.
|
|
96
|
+
* @param {ASTNode} node The node to check.
|
|
97
|
+
* @returns {void}
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
var checkImportNode = function checkImportNode(node) {
|
|
101
|
+
var importSource = node.source.value.trim();
|
|
102
|
+
var importNames = new Map();
|
|
103
|
+
if ('specifiers' in node) {
|
|
104
|
+
var _iterator = _createForOfIteratorHelper(node.specifiers),
|
|
105
|
+
_step;
|
|
106
|
+
try {
|
|
107
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
108
|
+
var specifier = _step.value;
|
|
109
|
+
var name = void 0;
|
|
110
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
111
|
+
name = 'default';
|
|
112
|
+
} else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
113
|
+
name = '*';
|
|
114
|
+
} else if (specifier.type === 'ImportSpecifier') {
|
|
115
|
+
name = specifier.imported.name;
|
|
116
|
+
}
|
|
117
|
+
if (name) {
|
|
118
|
+
importNames.set(name, specifier);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
_iterator.e(err);
|
|
123
|
+
} finally {
|
|
124
|
+
_iterator.f();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
checkRestrictedPathAndReport({
|
|
128
|
+
importSource: importSource,
|
|
129
|
+
type: 'import',
|
|
130
|
+
node: node,
|
|
131
|
+
importNames: importNames
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Checks a node to see if any problems should be reported.
|
|
137
|
+
* @param {ASTNode} node The node to check.
|
|
138
|
+
* @returns {void}
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
var checkExportNode = function checkExportNode(node) {
|
|
142
|
+
if (!node.source || !node.source.value) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
var importSource = node.source.value.trim();
|
|
146
|
+
var importNames = new Map();
|
|
147
|
+
if ('specifiers' in node) {
|
|
148
|
+
var _iterator2 = _createForOfIteratorHelper(node.specifiers),
|
|
149
|
+
_step2;
|
|
150
|
+
try {
|
|
151
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
152
|
+
var specifier = _step2.value;
|
|
153
|
+
var name = void 0;
|
|
154
|
+
if (specifier.local) {
|
|
155
|
+
name = specifier.local.name;
|
|
156
|
+
}
|
|
157
|
+
if (name) {
|
|
158
|
+
importNames.set(name, specifier);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
_iterator2.e(err);
|
|
163
|
+
} finally {
|
|
164
|
+
_iterator2.f();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
checkRestrictedPathAndReport({
|
|
168
|
+
importSource: importSource,
|
|
169
|
+
type: 'export',
|
|
170
|
+
node: node,
|
|
171
|
+
importNames: importNames
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Create a mapping of JSX elements by their name so they can be processed later.
|
|
177
|
+
* @param node The JSX node found by ESLint
|
|
178
|
+
*/
|
|
179
|
+
var checkJSXElement = function checkJSXElement(node) {
|
|
180
|
+
if (!('openingElement' in node) || !isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
deprecatedIconHandler.checkJSXElement(node);
|
|
184
|
+
};
|
|
185
|
+
var checkIdentifier = function checkIdentifier(node) {
|
|
186
|
+
deprecatedIconHandler.checkIdentifier(node);
|
|
187
|
+
};
|
|
188
|
+
return {
|
|
189
|
+
checkImportNode: checkImportNode,
|
|
190
|
+
checkExportNode: checkExportNode,
|
|
191
|
+
checkJSXElement: checkJSXElement,
|
|
192
|
+
checkIdentifier: checkIdentifier,
|
|
193
|
+
throwErrors: throwErrors
|
|
194
|
+
};
|
|
195
|
+
};
|