@atlaskit/eslint-plugin-design-system 10.24.1 → 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 +23 -0
- package/README.md +2 -1
- package/dist/cjs/ast-nodes/jsx-attribute.js +8 -0
- package/dist/cjs/presets/all-flat.codegen.js +2 -1
- package/dist/cjs/presets/all.codegen.js +2 -1
- package/dist/cjs/presets/recommended-flat.codegen.js +2 -1
- package/dist/cjs/presets/recommended.codegen.js +2 -1
- package/dist/cjs/rules/index.codegen.js +3 -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/use-datetime-picker-calendar-button/index.js +187 -0
- package/dist/cjs/rules/utils/get-deprecated-config.js +9 -1
- package/dist/es2019/ast-nodes/jsx-attribute.js +8 -0
- package/dist/es2019/presets/all-flat.codegen.js +2 -1
- package/dist/es2019/presets/all.codegen.js +2 -1
- package/dist/es2019/presets/recommended-flat.codegen.js +2 -1
- package/dist/es2019/presets/recommended.codegen.js +2 -1
- package/dist/es2019/rules/index.codegen.js +3 -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/use-datetime-picker-calendar-button/index.js +173 -0
- package/dist/es2019/rules/utils/get-deprecated-config.js +12 -1
- package/dist/esm/ast-nodes/jsx-attribute.js +8 -0
- package/dist/esm/presets/all-flat.codegen.js +2 -1
- package/dist/esm/presets/all.codegen.js +2 -1
- package/dist/esm/presets/recommended-flat.codegen.js +2 -1
- package/dist/esm/presets/recommended.codegen.js +2 -1
- package/dist/esm/rules/index.codegen.js +3 -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/use-datetime-picker-calendar-button/index.js +181 -0
- package/dist/esm/rules/utils/get-deprecated-config.js +9 -1
- package/dist/types/ast-nodes/jsx-attribute.d.ts +3 -0
- package/dist/types/index.codegen.d.ts +14 -9
- package/dist/types/presets/all-flat.codegen.d.ts +2 -1
- package/dist/types/presets/all.codegen.d.ts +2 -1
- package/dist/types/presets/recommended-flat.codegen.d.ts +2 -1
- package/dist/types/presets/recommended.codegen.d.ts +2 -1
- package/dist/types/rules/index.codegen.d.ts +2 -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/use-datetime-picker-calendar-button/index.d.ts +7 -0
- package/dist/types/rules/utils/types.d.ts +1 -0
- package/dist/types-ts4.5/ast-nodes/jsx-attribute.d.ts +3 -0
- package/dist/types-ts4.5/index.codegen.d.ts +14 -15
- package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +2 -1
- package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
- package/dist/types-ts4.5/presets/recommended-flat.codegen.d.ts +2 -1
- package/dist/types-ts4.5/presets/recommended.codegen.d.ts +2 -1
- package/dist/types-ts4.5/rules/index.codegen.d.ts +2 -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/use-datetime-picker-calendar-button/index.d.ts +7 -0
- package/dist/types-ts4.5/rules/utils/types.d.ts +1 -0
- package/package.json +3 -2
|
@@ -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;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
|
|
3
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import { JSXAttribute } from '../../ast-nodes/jsx-attribute';
|
|
5
|
+
import { JSXElementHelper } from '../../ast-nodes/jsx-element';
|
|
6
|
+
import { createLintRule } from '../utils/create-rule';
|
|
7
|
+
const DATE_PICKER = 'DatePicker';
|
|
8
|
+
const DATETIME_PICKER = 'DateTimePicker';
|
|
9
|
+
const PROP_NAME = 'shouldShowCalendarButton';
|
|
10
|
+
|
|
11
|
+
// Lint rule message
|
|
12
|
+
const message = '`shouldShowCalendarButton` should be set to `true` to make date picker accessible.';
|
|
13
|
+
|
|
14
|
+
// Fix messages
|
|
15
|
+
export const addCalendarButtonProp = 'Add `shouldShowCalendarButton` prop.';
|
|
16
|
+
export const setCalendarButtonPropToTrue = 'Set `shouldShowCalendarButton` prop to `true`.';
|
|
17
|
+
export const addCalendarButtonProperty = 'Add `shouldShowCalendarButton: true` to `datePickerProps`.';
|
|
18
|
+
export const setCalendarButtonPropertyToTrue = 'Set `shouldShowCalendarButton` property in `datePickerProps` to `true`.';
|
|
19
|
+
const datePickerJSXElement = (node, context) => {
|
|
20
|
+
const prop = JSXElementHelper.getAttributeByName(node, PROP_NAME);
|
|
21
|
+
|
|
22
|
+
// If the prop exists
|
|
23
|
+
if (prop) {
|
|
24
|
+
const attrValue = JSXAttribute.getValue(prop);
|
|
25
|
+
// If the value is a boolean with value `false`
|
|
26
|
+
if ((attrValue === null || attrValue === void 0 ? void 0 : attrValue.type) === 'ExpressionStatement Literal' && (attrValue === null || attrValue === void 0 ? void 0 : attrValue.value) === false) {
|
|
27
|
+
return context.report({
|
|
28
|
+
node: prop,
|
|
29
|
+
messageId: 'datePickerCalendarButtonShouldBeShown',
|
|
30
|
+
suggest: [{
|
|
31
|
+
desc: setCalendarButtonPropToTrue,
|
|
32
|
+
fix: fixer => [fixer.replaceText(prop, PROP_NAME)]
|
|
33
|
+
}]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// If the prop does not exist
|
|
37
|
+
} else {
|
|
38
|
+
return context.report({
|
|
39
|
+
node: node.openingElement,
|
|
40
|
+
messageId: 'datePickerMissingCalendarButtonProp',
|
|
41
|
+
suggest: [{
|
|
42
|
+
desc: addCalendarButtonProp,
|
|
43
|
+
fix: fixer => [fixer.insertTextAfter(node.openingElement.name, ` ${PROP_NAME}`)]
|
|
44
|
+
}]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const dateTimePickerJSXElement = (node, context) => {
|
|
49
|
+
var _datePickerProp$value, _expression$propertie;
|
|
50
|
+
const datePickerProp = JSXElementHelper.getAttributeByName(node, 'datePickerProps');
|
|
51
|
+
|
|
52
|
+
// If the `datePickerProps` prop does not exist or is not an expression
|
|
53
|
+
// container (the latter being essentially unprecedented, against type
|
|
54
|
+
// guidelines, and useless)
|
|
55
|
+
if (!datePickerProp || ((_datePickerProp$value = datePickerProp.value) === null || _datePickerProp$value === void 0 ? void 0 : _datePickerProp$value.type) !== 'JSXExpressionContainer') {
|
|
56
|
+
return context.report({
|
|
57
|
+
node: node.openingElement,
|
|
58
|
+
messageId: 'dateTimePickerMissingCalendarButtonProp',
|
|
59
|
+
suggest: [{
|
|
60
|
+
desc: addCalendarButtonProperty,
|
|
61
|
+
fix: fixer => [fixer.insertTextAfter(node.openingElement.name, ` datePickerProps={{ ${PROP_NAME}: true }}`)]
|
|
62
|
+
}]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Had to cast all these things because ESLint can't hang here. The
|
|
67
|
+
// types are just abjectly wrong from what I can log.
|
|
68
|
+
const expression = datePickerProp.value.expression;
|
|
69
|
+
// If it is not an analyzable expression, like a variable or something, skip
|
|
70
|
+
// it.
|
|
71
|
+
if (!isNodeOfType(datePickerProp.value.expression, 'ObjectExpression')) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const prop = expression === null || expression === void 0 ? void 0 : (_expression$propertie = expression.properties) === null || _expression$propertie === void 0 ? void 0 : _expression$propertie.find(property => property.type === 'Property' && property.key.name === PROP_NAME);
|
|
75
|
+
|
|
76
|
+
// If the `shouldShowCalendarButton` prop does not exist in `datePickerProps`
|
|
77
|
+
if (!prop) {
|
|
78
|
+
return context.report({
|
|
79
|
+
node: datePickerProp,
|
|
80
|
+
messageId: 'dateTimePickerMissingCalendarButtonProp',
|
|
81
|
+
suggest: [{
|
|
82
|
+
desc: addCalendarButtonProperty,
|
|
83
|
+
fix: fixer => {
|
|
84
|
+
// If it has existing properties
|
|
85
|
+
if (expression.properties.length > 0) {
|
|
86
|
+
// Needs following comma to not disrupt existing properties inside `datePickerProps`
|
|
87
|
+
return [fixer.insertTextBefore(expression.properties[0], `${PROP_NAME}: true,`)];
|
|
88
|
+
// Else it's an empty object
|
|
89
|
+
} else {
|
|
90
|
+
return [fixer.replaceText(expression, `{ ${PROP_NAME}: true }`)];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}]
|
|
94
|
+
});
|
|
95
|
+
// If the `shouldShowCalendarButton` property exists and it's value is `false`
|
|
96
|
+
} else if (isNodeOfType(prop.value, 'Literal') && prop.value.value !== true) {
|
|
97
|
+
return context.report({
|
|
98
|
+
node: datePickerProp,
|
|
99
|
+
messageId: 'dateTimePickerCalendarButtonShouldBeShown',
|
|
100
|
+
suggest: [{
|
|
101
|
+
desc: setCalendarButtonPropertyToTrue,
|
|
102
|
+
// Needs following comma to not disrupt existing properties inside `datePickerProps`
|
|
103
|
+
fix: fixer => [fixer.replaceText(prop, `${PROP_NAME}: true,`)]
|
|
104
|
+
}]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const rule = createLintRule({
|
|
109
|
+
meta: {
|
|
110
|
+
name: 'use-datetime-picker-calendar-button',
|
|
111
|
+
type: 'suggestion',
|
|
112
|
+
fixable: 'code',
|
|
113
|
+
hasSuggestions: true,
|
|
114
|
+
docs: {
|
|
115
|
+
description: "Encourages makers to use calendar button in Atlassian Design System's date picker and datetime picker components.",
|
|
116
|
+
recommended: true,
|
|
117
|
+
severity: 'warn'
|
|
118
|
+
},
|
|
119
|
+
messages: {
|
|
120
|
+
dateTimePickerMissingCalendarButtonProp: `In \`datePickerProps\`, ${message}`,
|
|
121
|
+
datePickerMissingCalendarButtonProp: message,
|
|
122
|
+
dateTimePickerCalendarButtonShouldBeShown: `In \`datePickerProps\`, ${message}`,
|
|
123
|
+
datePickerCalendarButtonShouldBeShown: message
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
create(context) {
|
|
127
|
+
// List of component's locally imported names that match
|
|
128
|
+
const contextLocalIdentifier = [];
|
|
129
|
+
const contextImportedIdentifier = [];
|
|
130
|
+
return {
|
|
131
|
+
// Only run rule in files where the package is imported
|
|
132
|
+
ImportDeclaration(node) {
|
|
133
|
+
var _node$specifiers;
|
|
134
|
+
const datetimePickerIdentifier = (_node$specifiers = node.specifiers) === null || _node$specifiers === void 0 ? void 0 : _node$specifiers.filter(spec => {
|
|
135
|
+
if (node.source.value === '@atlaskit/datetime-picker') {
|
|
136
|
+
var _spec$imported;
|
|
137
|
+
return isNodeOfType(spec, 'ImportSpecifier') && [DATE_PICKER, DATETIME_PICKER].includes((_spec$imported = spec.imported) === null || _spec$imported === void 0 ? void 0 : _spec$imported.name);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
datetimePickerIdentifier.forEach(identifier => {
|
|
141
|
+
const {
|
|
142
|
+
imported,
|
|
143
|
+
local
|
|
144
|
+
} = identifier;
|
|
145
|
+
contextLocalIdentifier.push(local.name);
|
|
146
|
+
contextImportedIdentifier.push(imported.name);
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
JSXElement(node) {
|
|
150
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const name = node.openingElement.name.name;
|
|
157
|
+
const localIndex = contextLocalIdentifier.indexOf(name);
|
|
158
|
+
|
|
159
|
+
// If this component does not match what we're looking for, quit early
|
|
160
|
+
if (localIndex === -1) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const importedName = contextImportedIdentifier[localIndex];
|
|
164
|
+
if (importedName === DATE_PICKER) {
|
|
165
|
+
return datePickerJSXElement(node, context);
|
|
166
|
+
} else if (importedName === DATETIME_PICKER) {
|
|
167
|
+
return dateTimePickerJSXElement(node, context);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
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
|
};
|
|
@@ -46,6 +46,14 @@ var HelperJSXAttribute = {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// handle `css={true}`
|
|
50
|
+
if (isNodeOfType(node.value, 'JSXExpressionContainer') && isNodeOfType(node.value.expression, 'Literal')) {
|
|
51
|
+
return {
|
|
52
|
+
type: 'ExpressionStatement Literal',
|
|
53
|
+
value: node.value.expression.value
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
// handle `css='myStyles'`
|
|
50
58
|
if (isNodeOfType(node.value, 'Literal') && node.value.value) {
|
|
51
59
|
var _node$value$value;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::72c978e91d268fe1463898bd263385bc>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -37,6 +37,7 @@ export default {
|
|
|
37
37
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
38
38
|
'@atlaskit/design-system/prefer-primitives': 'warn',
|
|
39
39
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
40
|
+
'@atlaskit/design-system/use-datetime-picker-calendar-button': 'warn',
|
|
40
41
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
41
42
|
'@atlaskit/design-system/use-heading': 'warn',
|
|
42
43
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::5bfdac53d9eb0cc8f885ce996861719e>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -36,6 +36,7 @@ export default {
|
|
|
36
36
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
37
37
|
'@atlaskit/design-system/prefer-primitives': 'warn',
|
|
38
38
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
39
|
+
'@atlaskit/design-system/use-datetime-picker-calendar-button': 'warn',
|
|
39
40
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
40
41
|
'@atlaskit/design-system/use-heading': 'warn',
|
|
41
42
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::2c1f5cd2494143a83d590243d7ebf539>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -26,6 +26,7 @@ export default {
|
|
|
26
26
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
27
27
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
28
28
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
29
|
+
'@atlaskit/design-system/use-datetime-picker-calendar-button': 'warn',
|
|
29
30
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
30
31
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
31
32
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::9aeb2fd0b5bde21f6ef61b407d0d32ab>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -25,6 +25,7 @@ export default {
|
|
|
25
25
|
'@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
|
|
26
26
|
'@atlaskit/design-system/no-unsupported-drag-and-drop-libraries': 'error',
|
|
27
27
|
'@atlaskit/design-system/use-button-group-label': 'warn',
|
|
28
|
+
'@atlaskit/design-system/use-datetime-picker-calendar-button': 'warn',
|
|
28
29
|
'@atlaskit/design-system/use-drawer-label': 'warn',
|
|
29
30
|
'@atlaskit/design-system/use-heading-level-in-spotlight-card': 'warn',
|
|
30
31
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::50c700a7a1e85949b0e5136a8c576c72>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import consistentCssPropUsage from './consistent-css-prop-usage';
|
|
@@ -34,6 +34,7 @@ import noUnsafeStyleOverrides from './no-unsafe-style-overrides';
|
|
|
34
34
|
import noUnsupportedDragAndDropLibraries from './no-unsupported-drag-and-drop-libraries';
|
|
35
35
|
import preferPrimitives from './prefer-primitives';
|
|
36
36
|
import useButtonGroupLabel from './use-button-group-label';
|
|
37
|
+
import useDatetimePickerCalendarButton from './use-datetime-picker-calendar-button';
|
|
37
38
|
import useDrawerLabel from './use-drawer-label';
|
|
38
39
|
import useHeading from './use-heading';
|
|
39
40
|
import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-card';
|
|
@@ -81,6 +82,7 @@ export var rules = {
|
|
|
81
82
|
'no-unsupported-drag-and-drop-libraries': noUnsupportedDragAndDropLibraries,
|
|
82
83
|
'prefer-primitives': preferPrimitives,
|
|
83
84
|
'use-button-group-label': useButtonGroupLabel,
|
|
85
|
+
'use-datetime-picker-calendar-button': useDatetimePickerCalendarButton,
|
|
84
86
|
'use-drawer-label': useDrawerLabel,
|
|
85
87
|
'use-heading': useHeading,
|
|
86
88
|
'use-heading-level-in-spotlight-card': useHeadingLevelInSpotlightCard,
|