@atlaskit/eslint-plugin-design-system
Version:
The essential plugin for use with the Atlassian Design System.
123 lines (118 loc) • 4.64 kB
JavaScript
import { isNodeOfType } from 'eslint-codemod-utils';
import { getSourceCode } from '@atlaskit/eslint-utils/context-compat';
import { createLintRule } from '../utils/create-lint-rule';
import { getConfig } from '../utils/get-deprecated-config';
import { isDeprecatedJSXAttributeConfig } from '../utils/is-deprecated-jsx-attribute-config';
export const noDeprecatedJSXAttributeMessageId = 'noDeprecatedJSXAttributes';
const isImportDeclaration = programStatement => {
return (programStatement === null || programStatement === void 0 ? void 0 : programStatement.type) === 'ImportDeclaration';
};
const findJSXElementName = jsxAttributeNode => {
if (!jsxAttributeNode.parent || !isNodeOfType(jsxAttributeNode === null || jsxAttributeNode === void 0 ? void 0 : jsxAttributeNode.parent, 'JSXOpeningElement')) {
return;
}
const openingElement = jsxAttributeNode.parent;
if (!isNodeOfType(openingElement.name, 'JSXIdentifier')) {
return;
}
return openingElement.name.name;
};
export const name = 'no-deprecated-apis';
const rule = createLintRule({
meta: {
name,
type: 'suggestion',
docs: {
description: 'Disallow using deprecated APIs.',
recommended: true,
severity: 'error'
},
messages: {
noDeprecatedJSXAttributes: 'The JSX attribute {{propName}} has been deprecated.'
},
schema: [{
type: 'object',
properties: {
deprecatedConfig: {
type: 'object',
properties: {
'.+': {
type: 'array',
items: {
type: 'object',
properties: {
moduleSpecifier: {
type: 'string'
},
namedSpecifiers: {
type: 'array',
items: {
type: 'string'
}
},
actionableVersion: {
type: 'string'
}
},
required: ['moduleSpecifier'],
additionalProperties: false
}
}
}
}
}
}]
},
create(context) {
var _context$options$;
// Get the rule configuration specified otherwise use default config.
// A bit confusing as it seems that the default options have precedence over the user specified options.
const deprecatedConfig =
// TODO: JFP-2823 - this type cast was added due to Jira's ESLint v9 migration
((_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.deprecatedConfig) || getConfig('jsxAttributes');
return {
// find JSX attribute - find name of attribute - get source and find relevant identifiers.
JSXAttribute(node) {
if (!isNodeOfType(node, 'JSXAttribute') || !isNodeOfType(node.name, 'JSXIdentifier')) {
return;
}
const jsxAttributeName = node.name.name;
if (!isDeprecatedJSXAttributeConfig(deprecatedConfig) || !deprecatedConfig[jsxAttributeName]) {
return;
}
const jsxElementName = findJSXElementName(node);
if (!jsxElementName) {
return;
}
const source = getSourceCode(context);
// find an import for the path of the banned api
deprecatedConfig[jsxAttributeName].forEach(importItem => {
var _importItem$namedSpec;
const importNode = source.ast.body.filter(isImportDeclaration).find(node => node && node.source.value && typeof node.source.value === 'string' && node.source.value.includes(importItem.moduleSpecifier));
if (!importNode) {
return;
}
// find an import that matches our JSX element
const targetNode = importNode.specifiers.find(node => node.local.name === jsxElementName);
// check if the import exists
if (!targetNode) {
return;
}
// if the import has named specifiers, check if the JSX element is one of them
if (importItem !== null && importItem !== void 0 && (_importItem$namedSpec = importItem.namedSpecifiers) !== null && _importItem$namedSpec !== void 0 && _importItem$namedSpec.length && !importItem.namedSpecifiers.includes(targetNode.local.name)) {
return;
}
// if we're here, there is a valid lint error.
context.report({
node,
messageId: 'noDeprecatedJSXAttributes',
data: {
propName: jsxAttributeName
}
});
});
}
};
}
});
export default rule;