@atlaskit/eslint-plugin-design-system 13.32.0 → 13.34.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 +14 -0
- package/README.md +2 -0
- package/dist/cjs/presets/all-flat.codegen.js +3 -1
- package/dist/cjs/presets/all.codegen.js +3 -1
- package/dist/cjs/presets/recommended-flat.codegen.js +3 -1
- package/dist/cjs/presets/recommended.codegen.js +3 -1
- package/dist/cjs/rules/index.codegen.js +5 -1
- package/dist/cjs/rules/use-simple-field/index.js +138 -0
- package/dist/cjs/rules/use-simple-form/index.js +162 -0
- package/dist/es2019/presets/all-flat.codegen.js +3 -1
- package/dist/es2019/presets/all.codegen.js +3 -1
- package/dist/es2019/presets/recommended-flat.codegen.js +3 -1
- package/dist/es2019/presets/recommended.codegen.js +3 -1
- package/dist/es2019/rules/index.codegen.js +5 -1
- package/dist/es2019/rules/use-simple-field/index.js +124 -0
- package/dist/es2019/rules/use-simple-form/index.js +150 -0
- package/dist/esm/presets/all-flat.codegen.js +3 -1
- package/dist/esm/presets/all.codegen.js +3 -1
- package/dist/esm/presets/recommended-flat.codegen.js +3 -1
- package/dist/esm/presets/recommended.codegen.js +3 -1
- package/dist/esm/rules/index.codegen.js +5 -1
- package/dist/esm/rules/use-simple-field/index.js +132 -0
- package/dist/esm/rules/use-simple-form/index.js +156 -0
- package/dist/types/presets/all-flat.codegen.d.ts +1 -1
- package/dist/types/presets/all.codegen.d.ts +1 -1
- package/dist/types/presets/recommended-flat.codegen.d.ts +1 -1
- package/dist/types/presets/recommended.codegen.d.ts +1 -1
- package/dist/types/rules/index.codegen.d.ts +1 -1
- package/dist/types/rules/use-simple-field/index.d.ts +4 -0
- package/dist/types/rules/use-simple-form/index.d.ts +5 -0
- package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +1 -1
- package/dist/types-ts4.5/presets/all.codegen.d.ts +1 -1
- package/dist/types-ts4.5/presets/recommended-flat.codegen.d.ts +1 -1
- package/dist/types-ts4.5/presets/recommended.codegen.d.ts +1 -1
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-simple-field/index.d.ts +4 -0
- package/dist/types-ts4.5/rules/use-simple-form/index.d.ts +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { createLintRule } from '../utils/create-rule';
|
|
3
|
+
const FORM_PACKAGE = '@atlaskit/form';
|
|
4
|
+
export const topLevelAttributeNames = ['autocomplete', 'id', 'label', 'labelId', 'onKeyDown', 'onSubmit', 'name', 'noValidate', 'ref', 'xcss'];
|
|
5
|
+
export const convertForm = 'Convert form to simple form';
|
|
6
|
+
const rule = createLintRule({
|
|
7
|
+
meta: {
|
|
8
|
+
name: 'use-simple-form',
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
hasSuggestions: true,
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Encourage use of simple form for better developer experience and accessibility.',
|
|
13
|
+
recommended: true,
|
|
14
|
+
severity: 'warn'
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
useSimpleForm: 'The simplified form implementation can be used here.'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
let formImport;
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration(node) {
|
|
24
|
+
const source = node.source.value;
|
|
25
|
+
|
|
26
|
+
// Ignore anomalies
|
|
27
|
+
if (typeof source !== 'string') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!node.specifiers.length) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// If it's not from our package, ignore.
|
|
35
|
+
if (source !== FORM_PACKAGE) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const defaultImportSpecifiers = node.specifiers.filter(spec => isNodeOfType(spec, 'ImportDefaultSpecifier'));
|
|
39
|
+
if (defaultImportSpecifiers.length > 0) {
|
|
40
|
+
formImport = defaultImportSpecifiers[0].local.name;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
JSXElement(node) {
|
|
44
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// if no form import exists, skip
|
|
52
|
+
if (!formImport) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// if component is not field, skip
|
|
57
|
+
if (node.openingElement.name.name !== formImport) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// if children is not a render prop, skip
|
|
62
|
+
const renderProps = node.children.find(child => isNodeOfType(child, 'JSXExpressionContainer'));
|
|
63
|
+
if (!renderProps) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const renderPropExpression = renderProps.expression;
|
|
67
|
+
// If not an arrow func, ignore
|
|
68
|
+
if (!isNodeOfType(renderPropExpression, 'ArrowFunctionExpression')) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const renderPropParams = renderPropExpression.params;
|
|
72
|
+
// if it is not an object pattern, skip
|
|
73
|
+
if (!isNodeOfType(renderPropParams[0], 'ObjectPattern')) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// if component uses more than just formProps in render props, skip
|
|
77
|
+
const renderPropArgProperties = renderPropParams[0].properties;
|
|
78
|
+
const formPropsProp = renderPropArgProperties.find(property => isNodeOfType(property, 'Property') && isNodeOfType(property.key, 'Identifier') && property.key.name === 'formProps');
|
|
79
|
+
if (renderPropArgProperties.length > 1 || !formPropsProp) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const attributes = node.openingElement.attributes;
|
|
83
|
+
const lastProp = attributes.slice(-1)[0] || node.openingElement.name;
|
|
84
|
+
const sourceCode = context.sourceCode;
|
|
85
|
+
|
|
86
|
+
// if html form is not first child, don't give a fix
|
|
87
|
+
const renderPropBody = renderPropExpression.body;
|
|
88
|
+
const firstChildIsHTMLForm = isNodeOfType(renderPropBody, 'JSXElement') && isNodeOfType(renderPropBody.openingElement.name, 'JSXIdentifier') && renderPropBody.openingElement.name.name === 'form';
|
|
89
|
+
|
|
90
|
+
// If this is not an HTML form. skip because we can't convert
|
|
91
|
+
if (!firstChildIsHTMLForm) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
context.report({
|
|
95
|
+
node: node,
|
|
96
|
+
messageId: 'useSimpleForm',
|
|
97
|
+
suggest: [{
|
|
98
|
+
desc: convertForm,
|
|
99
|
+
fix: fixer => {
|
|
100
|
+
const fixes = [];
|
|
101
|
+
|
|
102
|
+
// add each attribute in the HTML form inside to the AK form
|
|
103
|
+
const attrs = {
|
|
104
|
+
topLevel: [],
|
|
105
|
+
formProps: []
|
|
106
|
+
};
|
|
107
|
+
const htmlFormAttributes = renderPropBody.openingElement.attributes;
|
|
108
|
+
htmlFormAttributes.forEach(attr => {
|
|
109
|
+
if (isNodeOfType(attr, 'JSXAttribute') && isNodeOfType(attr.name, 'JSXIdentifier')) {
|
|
110
|
+
if (topLevelAttributeNames.includes(attr.name.name)) {
|
|
111
|
+
attrs.topLevel.push(attr);
|
|
112
|
+
} else {
|
|
113
|
+
attrs.formProps.push(attr);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
attrs.topLevel.forEach(attr => {
|
|
118
|
+
fixes.push(fixer.insertTextAfter(lastProp, ` ${sourceCode.getText(attr)}`));
|
|
119
|
+
});
|
|
120
|
+
if (attrs.formProps.length > 0) {
|
|
121
|
+
const formPropsText = attrs.formProps.map(attr => {
|
|
122
|
+
return sourceCode.getText(attr).replace(/^([^=]*)/, "'$1'").replace('=', ': ');
|
|
123
|
+
}).join(',\n');
|
|
124
|
+
fixes.push(fixer.insertTextAfter(lastProp, ` formProps={{\n${formPropsText}\n}} `));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// remove the body of the AK form
|
|
128
|
+
fixes.push(fixer.remove(renderProps));
|
|
129
|
+
|
|
130
|
+
// Get all children text of HTML form to be added to AK form
|
|
131
|
+
let childrenText = '<>';
|
|
132
|
+
renderPropBody.children.forEach(child => {
|
|
133
|
+
// Doing `as any` because it doesn't like spread props. I
|
|
134
|
+
// added a test that verifies that this does indeed work,
|
|
135
|
+
// though, so it's fine.
|
|
136
|
+
childrenText += sourceCode.getText(child);
|
|
137
|
+
});
|
|
138
|
+
childrenText += '</>';
|
|
139
|
+
|
|
140
|
+
// Add the children of the HTML form to the children of the AK form
|
|
141
|
+
node.closingElement && fixes.push(fixer.insertTextBefore(node.closingElement, childrenText));
|
|
142
|
+
return fixes;
|
|
143
|
+
}
|
|
144
|
+
}]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
export default rule;
|
|
@@ -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::1e1e5efbb1ffcefdebdd86064e50fd4a>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -74,6 +74,8 @@ var rules = {
|
|
|
74
74
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
75
75
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
76
76
|
'@atlaskit/design-system/use-should-render-to-parent': 'warn',
|
|
77
|
+
'@atlaskit/design-system/use-simple-field': 'warn',
|
|
78
|
+
'@atlaskit/design-system/use-simple-form': 'warn',
|
|
77
79
|
'@atlaskit/design-system/use-spotlight-package': 'warn',
|
|
78
80
|
'@atlaskit/design-system/use-tag-group-label': 'warn',
|
|
79
81
|
'@atlaskit/design-system/use-tokens-shape': 'error',
|
|
@@ -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::474fa7fcdd540003aa6622a9e52497fa>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -73,6 +73,8 @@ var rules = {
|
|
|
73
73
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
74
74
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
75
75
|
'@atlaskit/design-system/use-should-render-to-parent': 'warn',
|
|
76
|
+
'@atlaskit/design-system/use-simple-field': 'warn',
|
|
77
|
+
'@atlaskit/design-system/use-simple-form': 'warn',
|
|
76
78
|
'@atlaskit/design-system/use-spotlight-package': 'warn',
|
|
77
79
|
'@atlaskit/design-system/use-tag-group-label': 'warn',
|
|
78
80
|
'@atlaskit/design-system/use-tokens-shape': 'error',
|
|
@@ -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::cb96c76e14fd1bfeec1ecf16f2787d10>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -56,6 +56,8 @@ var rules = {
|
|
|
56
56
|
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
57
57
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
58
58
|
'@atlaskit/design-system/use-should-render-to-parent': 'warn',
|
|
59
|
+
'@atlaskit/design-system/use-simple-field': 'warn',
|
|
60
|
+
'@atlaskit/design-system/use-simple-form': 'warn',
|
|
59
61
|
'@atlaskit/design-system/use-tag-group-label': 'warn',
|
|
60
62
|
'@atlaskit/design-system/use-tokens-typography': 'warn',
|
|
61
63
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
@@ -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::7d6723309ffe428e466de19a384d02c9>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -55,6 +55,8 @@ var rules = {
|
|
|
55
55
|
'@atlaskit/design-system/use-popup-label': 'warn',
|
|
56
56
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
57
57
|
'@atlaskit/design-system/use-should-render-to-parent': 'warn',
|
|
58
|
+
'@atlaskit/design-system/use-simple-field': 'warn',
|
|
59
|
+
'@atlaskit/design-system/use-simple-form': 'warn',
|
|
58
60
|
'@atlaskit/design-system/use-tag-group-label': 'warn',
|
|
59
61
|
'@atlaskit/design-system/use-tokens-typography': 'warn',
|
|
60
62
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
@@ -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::f9f7263c6ed88b95a13cbfbe56cfc380>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -71,6 +71,8 @@ import usePopupLabel from './use-popup-label';
|
|
|
71
71
|
import usePrimitives from './use-primitives';
|
|
72
72
|
import usePrimitivesText from './use-primitives-text';
|
|
73
73
|
import useShouldRenderToParent from './use-should-render-to-parent';
|
|
74
|
+
import useSimpleField from './use-simple-field';
|
|
75
|
+
import useSimpleForm from './use-simple-form';
|
|
74
76
|
import useSpotlightPackage from './use-spotlight-package';
|
|
75
77
|
import useTagGroupLabel from './use-tag-group-label';
|
|
76
78
|
import useTokensShape from './use-tokens-shape';
|
|
@@ -145,6 +147,8 @@ export var rules = {
|
|
|
145
147
|
'use-primitives': usePrimitives,
|
|
146
148
|
'use-primitives-text': usePrimitivesText,
|
|
147
149
|
'use-should-render-to-parent': useShouldRenderToParent,
|
|
150
|
+
'use-simple-field': useSimpleField,
|
|
151
|
+
'use-simple-form': useSimpleForm,
|
|
148
152
|
'use-spotlight-package': useSpotlightPackage,
|
|
149
153
|
'use-tag-group-label': useTagGroupLabel,
|
|
150
154
|
'use-tokens-shape': useTokensShape,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { createLintRule } from '../utils/create-rule';
|
|
3
|
+
var FORM_PACKAGE = '@atlaskit/form';
|
|
4
|
+
var MESSAGE_COMPONENTS = ['ErrorMessage', 'HelperMessage', 'ValidMessage'];
|
|
5
|
+
export var convertField = 'Convert field to simple field';
|
|
6
|
+
var rule = createLintRule({
|
|
7
|
+
meta: {
|
|
8
|
+
name: 'use-simple-field',
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
hasSuggestions: true,
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Encourage use of simple field for better developer experience and accessibility.',
|
|
13
|
+
recommended: true,
|
|
14
|
+
severity: 'warn'
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
useSimpleField: 'The simplified field implementation can be used here.'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create: function create(context) {
|
|
21
|
+
var fieldImport;
|
|
22
|
+
var messageImports = [];
|
|
23
|
+
return {
|
|
24
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
25
|
+
var source = node.source.value;
|
|
26
|
+
|
|
27
|
+
// Ignore anomalies
|
|
28
|
+
if (typeof source !== 'string') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!node.specifiers.length) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If it's not from our package, ignore.
|
|
36
|
+
if (source !== FORM_PACKAGE) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
var namedImportSpecifiers = node.specifiers.filter(function (spec) {
|
|
40
|
+
return isNodeOfType(spec, 'ImportSpecifier');
|
|
41
|
+
});
|
|
42
|
+
namedImportSpecifiers.forEach(function (spec) {
|
|
43
|
+
if (spec.type === 'ImportSpecifier' && 'name' in spec.imported) {
|
|
44
|
+
var name = spec.imported.name;
|
|
45
|
+
var local = spec.local;
|
|
46
|
+
if (MESSAGE_COMPONENTS.includes(name)) {
|
|
47
|
+
messageImports.push(local);
|
|
48
|
+
} else if (name === 'Field') {
|
|
49
|
+
fieldImport = local.name;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
JSXElement: function JSXElement(node) {
|
|
55
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// if no field import exists, skip
|
|
63
|
+
if (!fieldImport) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// if component is not field, skip
|
|
68
|
+
if (node.openingElement.name.name !== fieldImport) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// if message imports exist, skip because we can't really dig around
|
|
73
|
+
// inside the field to see if they exist because of memory constraints
|
|
74
|
+
if (messageImports.length > 0) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// if component exists as a prop, skip because it's already simple
|
|
79
|
+
var attributes = node.openingElement.attributes;
|
|
80
|
+
var componentAttr = attributes.find(function (attr) {
|
|
81
|
+
return isNodeOfType(attr, 'JSXAttribute') && isNodeOfType(attr.name, 'JSXIdentifier') && attr.name.name === 'component';
|
|
82
|
+
});
|
|
83
|
+
if (componentAttr) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// if children is not a render prop, skip
|
|
88
|
+
var renderProps = node.children.find(function (child) {
|
|
89
|
+
return isNodeOfType(child, 'JSXExpressionContainer');
|
|
90
|
+
});
|
|
91
|
+
if (!renderProps) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
var renderPropExpression = renderProps.expression;
|
|
95
|
+
// If not an arrow func, ignore
|
|
96
|
+
if (!isNodeOfType(renderPropExpression, 'ArrowFunctionExpression')) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
var renderPropParams = renderPropExpression.params;
|
|
100
|
+
// if it is not an object pattern, skip
|
|
101
|
+
if (!isNodeOfType(renderPropParams[0], 'ObjectPattern')) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// if component uses more than just fieldProps in render props, skip
|
|
105
|
+
var renderPropArgProperties = renderPropParams[0].properties;
|
|
106
|
+
var fieldPropsProp = renderPropArgProperties.find(function (property) {
|
|
107
|
+
return isNodeOfType(property, 'Property') && isNodeOfType(property.key, 'Identifier') && property.key.name === 'fieldProps';
|
|
108
|
+
});
|
|
109
|
+
if (renderPropArgProperties.length > 1 || !fieldPropsProp) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
var lastProp = attributes.slice(-1)[0] || node.openingElement.name;
|
|
113
|
+
var sourceCode = context.sourceCode;
|
|
114
|
+
var renderPropsText = sourceCode.getText(renderProps);
|
|
115
|
+
context.report({
|
|
116
|
+
node: node,
|
|
117
|
+
messageId: 'useSimpleField',
|
|
118
|
+
suggest: [{
|
|
119
|
+
desc: convertField,
|
|
120
|
+
fix: function fix(fixer) {
|
|
121
|
+
var fixes = [];
|
|
122
|
+
fixes.push(fixer.insertTextAfter(lastProp, " component=".concat(renderPropsText, " ")));
|
|
123
|
+
fixes.push(fixer.remove(renderProps));
|
|
124
|
+
return fixes;
|
|
125
|
+
}
|
|
126
|
+
}]
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
export default rule;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
2
|
+
import { createLintRule } from '../utils/create-rule';
|
|
3
|
+
var FORM_PACKAGE = '@atlaskit/form';
|
|
4
|
+
export var topLevelAttributeNames = ['autocomplete', 'id', 'label', 'labelId', 'onKeyDown', 'onSubmit', 'name', 'noValidate', 'ref', 'xcss'];
|
|
5
|
+
export var convertForm = 'Convert form to simple form';
|
|
6
|
+
var rule = createLintRule({
|
|
7
|
+
meta: {
|
|
8
|
+
name: 'use-simple-form',
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
hasSuggestions: true,
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Encourage use of simple form for better developer experience and accessibility.',
|
|
13
|
+
recommended: true,
|
|
14
|
+
severity: 'warn'
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
useSimpleForm: 'The simplified form implementation can be used here.'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create: function create(context) {
|
|
21
|
+
var formImport;
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration: function ImportDeclaration(node) {
|
|
24
|
+
var source = node.source.value;
|
|
25
|
+
|
|
26
|
+
// Ignore anomalies
|
|
27
|
+
if (typeof source !== 'string') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!node.specifiers.length) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// If it's not from our package, ignore.
|
|
35
|
+
if (source !== FORM_PACKAGE) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
var defaultImportSpecifiers = node.specifiers.filter(function (spec) {
|
|
39
|
+
return isNodeOfType(spec, 'ImportDefaultSpecifier');
|
|
40
|
+
});
|
|
41
|
+
if (defaultImportSpecifiers.length > 0) {
|
|
42
|
+
formImport = defaultImportSpecifiers[0].local.name;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
JSXElement: function JSXElement(node) {
|
|
46
|
+
if (!isNodeOfType(node, 'JSXElement')) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// if no form import exists, skip
|
|
54
|
+
if (!formImport) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// if component is not field, skip
|
|
59
|
+
if (node.openingElement.name.name !== formImport) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// if children is not a render prop, skip
|
|
64
|
+
var renderProps = node.children.find(function (child) {
|
|
65
|
+
return isNodeOfType(child, 'JSXExpressionContainer');
|
|
66
|
+
});
|
|
67
|
+
if (!renderProps) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
var renderPropExpression = renderProps.expression;
|
|
71
|
+
// If not an arrow func, ignore
|
|
72
|
+
if (!isNodeOfType(renderPropExpression, 'ArrowFunctionExpression')) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
var renderPropParams = renderPropExpression.params;
|
|
76
|
+
// if it is not an object pattern, skip
|
|
77
|
+
if (!isNodeOfType(renderPropParams[0], 'ObjectPattern')) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// if component uses more than just formProps in render props, skip
|
|
81
|
+
var renderPropArgProperties = renderPropParams[0].properties;
|
|
82
|
+
var formPropsProp = renderPropArgProperties.find(function (property) {
|
|
83
|
+
return isNodeOfType(property, 'Property') && isNodeOfType(property.key, 'Identifier') && property.key.name === 'formProps';
|
|
84
|
+
});
|
|
85
|
+
if (renderPropArgProperties.length > 1 || !formPropsProp) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
var attributes = node.openingElement.attributes;
|
|
89
|
+
var lastProp = attributes.slice(-1)[0] || node.openingElement.name;
|
|
90
|
+
var sourceCode = context.sourceCode;
|
|
91
|
+
|
|
92
|
+
// if html form is not first child, don't give a fix
|
|
93
|
+
var renderPropBody = renderPropExpression.body;
|
|
94
|
+
var firstChildIsHTMLForm = isNodeOfType(renderPropBody, 'JSXElement') && isNodeOfType(renderPropBody.openingElement.name, 'JSXIdentifier') && renderPropBody.openingElement.name.name === 'form';
|
|
95
|
+
|
|
96
|
+
// If this is not an HTML form. skip because we can't convert
|
|
97
|
+
if (!firstChildIsHTMLForm) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
context.report({
|
|
101
|
+
node: node,
|
|
102
|
+
messageId: 'useSimpleForm',
|
|
103
|
+
suggest: [{
|
|
104
|
+
desc: convertForm,
|
|
105
|
+
fix: function fix(fixer) {
|
|
106
|
+
var fixes = [];
|
|
107
|
+
|
|
108
|
+
// add each attribute in the HTML form inside to the AK form
|
|
109
|
+
var attrs = {
|
|
110
|
+
topLevel: [],
|
|
111
|
+
formProps: []
|
|
112
|
+
};
|
|
113
|
+
var htmlFormAttributes = renderPropBody.openingElement.attributes;
|
|
114
|
+
htmlFormAttributes.forEach(function (attr) {
|
|
115
|
+
if (isNodeOfType(attr, 'JSXAttribute') && isNodeOfType(attr.name, 'JSXIdentifier')) {
|
|
116
|
+
if (topLevelAttributeNames.includes(attr.name.name)) {
|
|
117
|
+
attrs.topLevel.push(attr);
|
|
118
|
+
} else {
|
|
119
|
+
attrs.formProps.push(attr);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
attrs.topLevel.forEach(function (attr) {
|
|
124
|
+
fixes.push(fixer.insertTextAfter(lastProp, " ".concat(sourceCode.getText(attr))));
|
|
125
|
+
});
|
|
126
|
+
if (attrs.formProps.length > 0) {
|
|
127
|
+
var formPropsText = attrs.formProps.map(function (attr) {
|
|
128
|
+
return sourceCode.getText(attr).replace(/^([^=]*)/, "'$1'").replace('=', ': ');
|
|
129
|
+
}).join(',\n');
|
|
130
|
+
fixes.push(fixer.insertTextAfter(lastProp, " formProps={{\n".concat(formPropsText, "\n}} ")));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// remove the body of the AK form
|
|
134
|
+
fixes.push(fixer.remove(renderProps));
|
|
135
|
+
|
|
136
|
+
// Get all children text of HTML form to be added to AK form
|
|
137
|
+
var childrenText = '<>';
|
|
138
|
+
renderPropBody.children.forEach(function (child) {
|
|
139
|
+
// Doing `as any` because it doesn't like spread props. I
|
|
140
|
+
// added a test that verifies that this does indeed work,
|
|
141
|
+
// though, so it's fine.
|
|
142
|
+
childrenText += sourceCode.getText(child);
|
|
143
|
+
});
|
|
144
|
+
childrenText += '</>';
|
|
145
|
+
|
|
146
|
+
// Add the children of the HTML form to the children of the AK form
|
|
147
|
+
node.closingElement && fixes.push(fixer.insertTextBefore(node.closingElement, childrenText));
|
|
148
|
+
return fixes;
|
|
149
|
+
}
|
|
150
|
+
}]
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
export default rule;
|
|
@@ -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::1e1e5efbb1ffcefdebdd86064e50fd4a>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { Linter } from 'eslint';
|
|
@@ -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::474fa7fcdd540003aa6622a9e52497fa>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { ESLint } from 'eslint';
|
|
@@ -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::cb96c76e14fd1bfeec1ecf16f2787d10>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { Linter } from 'eslint';
|
|
@@ -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::7d6723309ffe428e466de19a384d02c9>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { ESLint } from 'eslint';
|
|
@@ -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::f9f7263c6ed88b95a13cbfbe56cfc380>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { Rule } from 'eslint';
|
|
@@ -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::1e1e5efbb1ffcefdebdd86064e50fd4a>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { Linter } from 'eslint';
|
|
@@ -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::474fa7fcdd540003aa6622a9e52497fa>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { ESLint } from 'eslint';
|
|
@@ -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::cb96c76e14fd1bfeec1ecf16f2787d10>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { Linter } from 'eslint';
|
|
@@ -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::7d6723309ffe428e466de19a384d02c9>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { ESLint } from 'eslint';
|
|
@@ -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::f9f7263c6ed88b95a13cbfbe56cfc380>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import type { Rule } from 'eslint';
|
package/package.json
CHANGED