@atlaskit/eslint-plugin-design-system 10.20.0 → 10.22.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 +16 -0
- package/README.md +1 -1
- package/dist/cjs/rules/no-html-anchor/index.js +1 -0
- package/dist/cjs/rules/no-html-anchor/node-types/jsx-element/index.js +126 -4
- package/dist/cjs/rules/use-latest-xcss-syntax-typography/config/index.js +1 -1
- package/dist/cjs/rules/use-latest-xcss-syntax-typography/index.js +13 -0
- package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/common.js +6 -1
- package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/index.js +7 -0
- package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/restricted-capitalisation.js +37 -0
- package/dist/cjs/rules/use-latest-xcss-syntax-typography/linters/restricted-property.js +2 -1
- package/dist/es2019/rules/no-html-anchor/index.js +1 -0
- package/dist/es2019/rules/no-html-anchor/node-types/jsx-element/index.js +97 -2
- package/dist/es2019/rules/use-latest-xcss-syntax-typography/config/index.js +1 -1
- package/dist/es2019/rules/use-latest-xcss-syntax-typography/index.js +10 -1
- package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/common.js +4 -1
- package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/index.js +1 -0
- package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/restricted-capitalisation.js +33 -0
- package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/restricted-property.js +2 -1
- package/dist/esm/rules/no-html-anchor/index.js +1 -0
- package/dist/esm/rules/no-html-anchor/node-types/jsx-element/index.js +126 -2
- package/dist/esm/rules/use-latest-xcss-syntax-typography/config/index.js +1 -1
- package/dist/esm/rules/use-latest-xcss-syntax-typography/index.js +14 -1
- package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/common.js +4 -1
- package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/index.js +1 -0
- package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/restricted-capitalisation.js +31 -0
- package/dist/esm/rules/use-latest-xcss-syntax-typography/linters/restricted-property.js +2 -1
- package/dist/types/rules/use-latest-xcss-syntax-typography/config/index.d.ts +1 -1
- package/dist/types/rules/use-latest-xcss-syntax-typography/linters/common.d.ts +1 -0
- package/dist/types/rules/use-latest-xcss-syntax-typography/linters/index.d.ts +1 -0
- package/dist/types/rules/use-latest-xcss-syntax-typography/linters/restricted-capitalisation.d.ts +6 -0
- package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/config/index.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/common.d.ts +1 -0
- package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/index.d.ts +1 -0
- package/dist/types-ts4.5/rules/use-latest-xcss-syntax-typography/linters/restricted-capitalisation.d.ts +6 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 10.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#147366](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/147366)
|
|
8
|
+
[`7f5bab6a1ebd1`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/7f5bab6a1ebd1) -
|
|
9
|
+
Add fixes for html anchors in the no-html-anchor rule
|
|
10
|
+
|
|
11
|
+
## 10.21.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [#142522](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/142522)
|
|
16
|
+
[`42f1b0abdb783`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/42f1b0abdb783) -
|
|
17
|
+
Disallow ALL CAPS styles in XCSS using the `use-latest-xcss-syntax-typography` rule.
|
|
18
|
+
|
|
3
19
|
## 10.20.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ module.exports = {
|
|
|
65
65
|
| <a href="./src/rules/no-empty-styled-expression/README.md">no-empty-styled-expression</a> | Forbids any styled expression to be used when passing empty arguments to styled.div() (or other JSX elements). | | | |
|
|
66
66
|
| <a href="./src/rules/no-exported-css/README.md">no-exported-css</a> | Forbid exporting `css` function calls. Exporting `css` function calls can result in unexpected behaviour at runtime, and is not statically analysable. | | | |
|
|
67
67
|
| <a href="./src/rules/no-exported-keyframes/README.md">no-exported-keyframes</a> | Forbid exporting `keyframes` function calls. Exporting `css` function calls can result in unexpected behaviour at runtime, and is not statically analysable. | | | |
|
|
68
|
-
| <a href="./src/rules/no-html-anchor/README.md">no-html-anchor</a> | Discourage direct usage of HTML anchor elements in favor of Atlassian Design System link components. | Yes | |
|
|
68
|
+
| <a href="./src/rules/no-html-anchor/README.md">no-html-anchor</a> | Discourage direct usage of HTML anchor elements in favor of Atlassian Design System link components. | Yes | | Yes |
|
|
69
69
|
| <a href="./src/rules/no-html-button/README.md">no-html-button</a> | Discourage direct usage of HTML button elements in favor of Atlassian Design System button components. | Yes | | |
|
|
70
70
|
| <a href="./src/rules/no-invalid-css-map/README.md">no-invalid-css-map</a> | Checks the validity of a CSS map created through cssMap. This is intended to be used alongside TypeScript's type-checking. | Yes | | |
|
|
71
71
|
| <a href="./src/rules/no-keyframes-tagged-template-expression/README.md">no-keyframes-tagged-template-expression</a> | Disallows any `keyframe` tagged template expressions that originate from Emotion, Styled Components or Compiled | | Yes | |
|
|
@@ -10,6 +10,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
10
10
|
meta: {
|
|
11
11
|
name: 'no-html-anchor',
|
|
12
12
|
type: 'suggestion',
|
|
13
|
+
hasSuggestions: true,
|
|
13
14
|
docs: {
|
|
14
15
|
description: 'Discourage direct usage of HTML anchor elements in favor of Atlassian Design System link components.',
|
|
15
16
|
recommended: true,
|
|
@@ -9,20 +9,142 @@ var ast = _interopRequireWildcard(require("../../../../ast-nodes"));
|
|
|
9
9
|
var _supported = require("../supported");
|
|
10
10
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
11
11
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
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; } } }; }
|
|
13
|
+
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; } }
|
|
14
|
+
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; } /* eslint-disable @repo/internal/react/require-jsdoc */
|
|
15
|
+
function isImportDeclaration(node) {
|
|
16
|
+
return node.type === 'ImportDeclaration';
|
|
17
|
+
}
|
|
14
18
|
var JSXElement = exports.JSXElement = {
|
|
15
19
|
lint: function lint(node, _ref) {
|
|
16
20
|
var context = _ref.context;
|
|
17
21
|
if (!(0, _supported.isSupportedForLint)(node)) {
|
|
18
22
|
return;
|
|
19
23
|
}
|
|
24
|
+
var nodeName = ast.JSXElement.getName(node);
|
|
25
|
+
var sourceCode = context.getSourceCode();
|
|
26
|
+
var importDeclarations = sourceCode.ast.body.filter(isImportDeclaration);
|
|
27
|
+
var existingLinkName = null;
|
|
28
|
+
var existingLinkButtonName = null;
|
|
29
|
+
var usedNames = new Set();
|
|
30
|
+
|
|
31
|
+
// Check for existing imports
|
|
32
|
+
var _iterator = _createForOfIteratorHelper(importDeclarations),
|
|
33
|
+
_step;
|
|
34
|
+
try {
|
|
35
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
36
|
+
var declaration = _step.value;
|
|
37
|
+
var _iterator2 = _createForOfIteratorHelper(declaration.specifiers),
|
|
38
|
+
_step2;
|
|
39
|
+
try {
|
|
40
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
41
|
+
var specifier = _step2.value;
|
|
42
|
+
usedNames.add(specifier.local.name);
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
_iterator2.e(err);
|
|
46
|
+
} finally {
|
|
47
|
+
_iterator2.f();
|
|
48
|
+
}
|
|
49
|
+
if (declaration.source.value === '@atlaskit/link') {
|
|
50
|
+
var defaultSpecifier = declaration.specifiers.find(function (specifier) {
|
|
51
|
+
return specifier.type === 'ImportDefaultSpecifier';
|
|
52
|
+
});
|
|
53
|
+
if (defaultSpecifier) {
|
|
54
|
+
existingLinkName = defaultSpecifier.local.name;
|
|
55
|
+
}
|
|
56
|
+
} else if (declaration.source.value === '@atlaskit/button/new') {
|
|
57
|
+
var namedSpecifier = declaration.specifiers.find(function (specifier) {
|
|
58
|
+
return specifier.type === 'ImportSpecifier' && specifier.imported.name === 'LinkButton';
|
|
59
|
+
});
|
|
60
|
+
if (namedSpecifier) {
|
|
61
|
+
existingLinkButtonName = namedSpecifier.local.name;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
_iterator.e(err);
|
|
67
|
+
} finally {
|
|
68
|
+
_iterator.f();
|
|
69
|
+
}
|
|
70
|
+
var generateUniqueName = function generateUniqueName(baseName) {
|
|
71
|
+
var index = 1;
|
|
72
|
+
var newName = baseName;
|
|
73
|
+
while (usedNames.has(newName)) {
|
|
74
|
+
newName = "".concat(baseName).concat(index);
|
|
75
|
+
index++;
|
|
76
|
+
}
|
|
77
|
+
return newName;
|
|
78
|
+
};
|
|
79
|
+
var linkName = existingLinkName || generateUniqueName('Link');
|
|
80
|
+
var linkButtonName = existingLinkButtonName || generateUniqueName('LinkButton');
|
|
20
81
|
context.report({
|
|
21
82
|
node: node.openingElement,
|
|
22
83
|
messageId: 'noHtmlAnchor',
|
|
23
84
|
data: {
|
|
24
|
-
name:
|
|
25
|
-
}
|
|
85
|
+
name: nodeName
|
|
86
|
+
},
|
|
87
|
+
suggest: [{
|
|
88
|
+
desc: 'Replace with Link component from @atlaskit/link',
|
|
89
|
+
fix: function fix(fixer) {
|
|
90
|
+
var _node$closingElement;
|
|
91
|
+
var openingTagRange = node.openingElement.range;
|
|
92
|
+
var closingTagRange = (_node$closingElement = node.closingElement) === null || _node$closingElement === void 0 ? void 0 : _node$closingElement.range;
|
|
93
|
+
var attributesText = node.openingElement.attributes.map(function (attr) {
|
|
94
|
+
return sourceCode.getText(attr);
|
|
95
|
+
}).join(' ');
|
|
96
|
+
var fixers = [];
|
|
97
|
+
|
|
98
|
+
// Replace <a> with <Link> and retain attributes
|
|
99
|
+
if (openingTagRange) {
|
|
100
|
+
if (node.openingElement.selfClosing) {
|
|
101
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkName).concat(attributesText ? " ".concat(attributesText) : '', " /")));
|
|
102
|
+
} else {
|
|
103
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkName).concat(attributesText ? " ".concat(attributesText) : '')));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (closingTagRange && !node.openingElement.selfClosing) {
|
|
107
|
+
fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], linkName));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add import if not present
|
|
111
|
+
if (!existingLinkName) {
|
|
112
|
+
var importStatement = "import ".concat(linkName, " from '@atlaskit/link';\n");
|
|
113
|
+
fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
|
|
114
|
+
}
|
|
115
|
+
return fixers;
|
|
116
|
+
}
|
|
117
|
+
}, {
|
|
118
|
+
desc: 'Replace with LinkButton component from @atlaskit/button/new',
|
|
119
|
+
fix: function fix(fixer) {
|
|
120
|
+
var _node$closingElement2;
|
|
121
|
+
var openingTagRange = node.openingElement.range;
|
|
122
|
+
var closingTagRange = (_node$closingElement2 = node.closingElement) === null || _node$closingElement2 === void 0 ? void 0 : _node$closingElement2.range;
|
|
123
|
+
var attributesText = node.openingElement.attributes.map(function (attr) {
|
|
124
|
+
return sourceCode.getText(attr);
|
|
125
|
+
}).join(' ');
|
|
126
|
+
var fixers = [];
|
|
127
|
+
|
|
128
|
+
// Replace <a> with <LinkButton> and retain attributes
|
|
129
|
+
if (openingTagRange) {
|
|
130
|
+
if (node.openingElement.selfClosing) {
|
|
131
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkButtonName).concat(attributesText ? " ".concat(attributesText) : '', " /")));
|
|
132
|
+
} else {
|
|
133
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkButtonName).concat(attributesText ? " ".concat(attributesText) : '')));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (closingTagRange && !node.openingElement.selfClosing) {
|
|
137
|
+
fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], linkButtonName));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Add import if not present
|
|
141
|
+
if (!existingLinkButtonName) {
|
|
142
|
+
var importStatement = "import { ".concat(linkButtonName, " } from '@atlaskit/button/new';\n");
|
|
143
|
+
fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
|
|
144
|
+
}
|
|
145
|
+
return fixers;
|
|
146
|
+
}
|
|
147
|
+
}]
|
|
26
148
|
});
|
|
27
149
|
}
|
|
28
150
|
};
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getConfig = void 0;
|
|
7
7
|
var defaults = {
|
|
8
8
|
failSilently: false,
|
|
9
|
-
patterns: ['restricted-property', 'wrapped-token-value']
|
|
9
|
+
patterns: ['restricted-property', 'wrapped-token-value', 'restricted-capitalisation']
|
|
10
10
|
};
|
|
11
11
|
var getConfig = exports.getConfig = function getConfig(overrides) {
|
|
12
12
|
return Object.assign({}, defaults, overrides);
|
|
@@ -23,6 +23,7 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
23
23
|
messages: {
|
|
24
24
|
noRestrictedTypographyProperties: "Don't set '{{ property }}' on xcss as it allows invalid combinations of typography tokens. ".concat(typescriptErrorMessage),
|
|
25
25
|
noRestrictedTypographyPropertiesHeading: "Don't set '{{ property }}' on xcss in combination with 'font' heading tokens. ".concat(typescriptErrorMessage),
|
|
26
|
+
noRestrictedCapitalisation: "Avoid using ALL CAPS as it reduces readability and is bad for accessibility.",
|
|
26
27
|
noWrappedTokenTypographyValues: "Don't wrap typography tokens in xcss. ".concat(typescriptErrorMessage)
|
|
27
28
|
}
|
|
28
29
|
},
|
|
@@ -41,6 +42,18 @@ var rule = (0, _createRule.createLintRule)({
|
|
|
41
42
|
config: config
|
|
42
43
|
});
|
|
43
44
|
},
|
|
45
|
+
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=textTransform]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameTextTransform(node) {
|
|
46
|
+
return _linters.RestrictedCapitalisation.lint(node, {
|
|
47
|
+
context: context,
|
|
48
|
+
config: config
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=textTransform]': function CallExpressionCalleeNameXcssObjectExpressionPropertyLiteralValueTextTransform(node) {
|
|
52
|
+
return _linters.RestrictedCapitalisation.lint(node, {
|
|
53
|
+
context: context,
|
|
54
|
+
config: config
|
|
55
|
+
});
|
|
56
|
+
},
|
|
44
57
|
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(font|fontFamily|fontWeight)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameFontFontFamilyFontWeight(node) {
|
|
45
58
|
return _linters.WrappedTokenValue.lint(node, {
|
|
46
59
|
context: context,
|
|
@@ -2,4 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
|
-
});
|
|
5
|
+
});
|
|
6
|
+
exports.isPropertyName = isPropertyName;
|
|
7
|
+
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
8
|
+
function isPropertyName(node, name) {
|
|
9
|
+
return (0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier') && node.name === name || (0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') && node.value === name;
|
|
10
|
+
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "RestrictedCapitalisation", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _restrictedCapitalisation.RestrictedCapitalisation;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
6
12
|
Object.defineProperty(exports, "RestrictedProperty", {
|
|
7
13
|
enumerable: true,
|
|
8
14
|
get: function get() {
|
|
@@ -16,4 +22,5 @@ Object.defineProperty(exports, "WrappedTokenValue", {
|
|
|
16
22
|
}
|
|
17
23
|
});
|
|
18
24
|
var _restrictedProperty = require("./restricted-property");
|
|
25
|
+
var _restrictedCapitalisation = require("./restricted-capitalisation");
|
|
19
26
|
var _wrappedTokenValue = require("./wrapped-token-value");
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.RestrictedCapitalisation = void 0;
|
|
7
|
+
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
8
|
+
var _common = require("./common");
|
|
9
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
10
|
+
|
|
11
|
+
var RestrictedCapitalisation = exports.RestrictedCapitalisation = {
|
|
12
|
+
lint: function lint(node, _ref) {
|
|
13
|
+
var context = _ref.context,
|
|
14
|
+
config = _ref.config;
|
|
15
|
+
if (RestrictedCapitalisation._check(node, {
|
|
16
|
+
context: context,
|
|
17
|
+
config: config
|
|
18
|
+
})) {
|
|
19
|
+
context.report({
|
|
20
|
+
node: node,
|
|
21
|
+
messageId: 'noRestrictedCapitalisation'
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
_check: function _check(node, _ref2) {
|
|
26
|
+
var config = _ref2.config;
|
|
27
|
+
if (!config.patterns.includes('restricted-capitalisation')) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Prevent text transform being used to uppercase all characters
|
|
32
|
+
if ((0, _common.isPropertyName)(node, 'textTransform') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'Property') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.value, 'Literal')) {
|
|
33
|
+
return node.parent.value.value === 'uppercase';
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.RestrictedProperty = void 0;
|
|
7
7
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
8
|
+
var _common = require("./common");
|
|
8
9
|
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; } } }; }
|
|
9
10
|
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; } }
|
|
10
11
|
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; } /* eslint-disable @repo/internal/react/require-jsdoc */
|
|
@@ -38,7 +39,7 @@ var RestrictedProperty = exports.RestrictedProperty = {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
// Prevent font weight being used in combination with heading tokens
|
|
41
|
-
if ((0,
|
|
42
|
+
if ((0, _common.isPropertyName)(node, 'fontWeight')) {
|
|
42
43
|
if ((0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent, 'ObjectExpression')) {
|
|
43
44
|
var _iterator = _createForOfIteratorHelper(node.parent.parent.properties),
|
|
44
45
|
_step;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import * as ast from '../../../../ast-nodes';
|
|
4
4
|
import { isSupportedForLint } from '../supported';
|
|
5
|
+
function isImportDeclaration(node) {
|
|
6
|
+
return node.type === 'ImportDeclaration';
|
|
7
|
+
}
|
|
5
8
|
export const JSXElement = {
|
|
6
9
|
lint(node, {
|
|
7
10
|
context
|
|
@@ -9,12 +12,104 @@ export const JSXElement = {
|
|
|
9
12
|
if (!isSupportedForLint(node)) {
|
|
10
13
|
return;
|
|
11
14
|
}
|
|
15
|
+
const nodeName = ast.JSXElement.getName(node);
|
|
16
|
+
const sourceCode = context.getSourceCode();
|
|
17
|
+
const importDeclarations = sourceCode.ast.body.filter(isImportDeclaration);
|
|
18
|
+
let existingLinkName = null;
|
|
19
|
+
let existingLinkButtonName = null;
|
|
20
|
+
const usedNames = new Set();
|
|
21
|
+
|
|
22
|
+
// Check for existing imports
|
|
23
|
+
for (const declaration of importDeclarations) {
|
|
24
|
+
for (const specifier of declaration.specifiers) {
|
|
25
|
+
usedNames.add(specifier.local.name);
|
|
26
|
+
}
|
|
27
|
+
if (declaration.source.value === '@atlaskit/link') {
|
|
28
|
+
const defaultSpecifier = declaration.specifiers.find(specifier => specifier.type === 'ImportDefaultSpecifier');
|
|
29
|
+
if (defaultSpecifier) {
|
|
30
|
+
existingLinkName = defaultSpecifier.local.name;
|
|
31
|
+
}
|
|
32
|
+
} else if (declaration.source.value === '@atlaskit/button/new') {
|
|
33
|
+
const namedSpecifier = declaration.specifiers.find(specifier => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'LinkButton');
|
|
34
|
+
if (namedSpecifier) {
|
|
35
|
+
existingLinkButtonName = namedSpecifier.local.name;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const generateUniqueName = baseName => {
|
|
40
|
+
let index = 1;
|
|
41
|
+
let newName = baseName;
|
|
42
|
+
while (usedNames.has(newName)) {
|
|
43
|
+
newName = `${baseName}${index}`;
|
|
44
|
+
index++;
|
|
45
|
+
}
|
|
46
|
+
return newName;
|
|
47
|
+
};
|
|
48
|
+
const linkName = existingLinkName || generateUniqueName('Link');
|
|
49
|
+
const linkButtonName = existingLinkButtonName || generateUniqueName('LinkButton');
|
|
12
50
|
context.report({
|
|
13
51
|
node: node.openingElement,
|
|
14
52
|
messageId: 'noHtmlAnchor',
|
|
15
53
|
data: {
|
|
16
|
-
name:
|
|
17
|
-
}
|
|
54
|
+
name: nodeName
|
|
55
|
+
},
|
|
56
|
+
suggest: [{
|
|
57
|
+
desc: 'Replace with Link component from @atlaskit/link',
|
|
58
|
+
fix(fixer) {
|
|
59
|
+
var _node$closingElement;
|
|
60
|
+
const openingTagRange = node.openingElement.range;
|
|
61
|
+
const closingTagRange = (_node$closingElement = node.closingElement) === null || _node$closingElement === void 0 ? void 0 : _node$closingElement.range;
|
|
62
|
+
const attributesText = node.openingElement.attributes.map(attr => sourceCode.getText(attr)).join(' ');
|
|
63
|
+
const fixers = [];
|
|
64
|
+
|
|
65
|
+
// Replace <a> with <Link> and retain attributes
|
|
66
|
+
if (openingTagRange) {
|
|
67
|
+
if (node.openingElement.selfClosing) {
|
|
68
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], `${linkName}${attributesText ? ` ${attributesText}` : ''} /`));
|
|
69
|
+
} else {
|
|
70
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], `${linkName}${attributesText ? ` ${attributesText}` : ''}`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (closingTagRange && !node.openingElement.selfClosing) {
|
|
74
|
+
fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], linkName));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Add import if not present
|
|
78
|
+
if (!existingLinkName) {
|
|
79
|
+
const importStatement = `import ${linkName} from '@atlaskit/link';\n`;
|
|
80
|
+
fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
|
|
81
|
+
}
|
|
82
|
+
return fixers;
|
|
83
|
+
}
|
|
84
|
+
}, {
|
|
85
|
+
desc: 'Replace with LinkButton component from @atlaskit/button/new',
|
|
86
|
+
fix(fixer) {
|
|
87
|
+
var _node$closingElement2;
|
|
88
|
+
const openingTagRange = node.openingElement.range;
|
|
89
|
+
const closingTagRange = (_node$closingElement2 = node.closingElement) === null || _node$closingElement2 === void 0 ? void 0 : _node$closingElement2.range;
|
|
90
|
+
const attributesText = node.openingElement.attributes.map(attr => sourceCode.getText(attr)).join(' ');
|
|
91
|
+
const fixers = [];
|
|
92
|
+
|
|
93
|
+
// Replace <a> with <LinkButton> and retain attributes
|
|
94
|
+
if (openingTagRange) {
|
|
95
|
+
if (node.openingElement.selfClosing) {
|
|
96
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], `${linkButtonName}${attributesText ? ` ${attributesText}` : ''} /`));
|
|
97
|
+
} else {
|
|
98
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], `${linkButtonName}${attributesText ? ` ${attributesText}` : ''}`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (closingTagRange && !node.openingElement.selfClosing) {
|
|
102
|
+
fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], linkButtonName));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Add import if not present
|
|
106
|
+
if (!existingLinkButtonName) {
|
|
107
|
+
const importStatement = `import { ${linkButtonName} } from '@atlaskit/button/new';\n`;
|
|
108
|
+
fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
|
|
109
|
+
}
|
|
110
|
+
return fixers;
|
|
111
|
+
}
|
|
112
|
+
}]
|
|
18
113
|
});
|
|
19
114
|
}
|
|
20
115
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const defaults = {
|
|
2
2
|
failSilently: false,
|
|
3
|
-
patterns: ['restricted-property', 'wrapped-token-value']
|
|
3
|
+
patterns: ['restricted-property', 'wrapped-token-value', 'restricted-capitalisation']
|
|
4
4
|
};
|
|
5
5
|
export const getConfig = overrides => {
|
|
6
6
|
return Object.assign({}, defaults, overrides);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLintRule } from '../utils/create-rule';
|
|
2
2
|
import { errorBoundary } from '../utils/error-boundary';
|
|
3
3
|
import { getConfig } from './config';
|
|
4
|
-
import { RestrictedProperty, WrappedTokenValue } from './linters';
|
|
4
|
+
import { RestrictedCapitalisation, RestrictedProperty, WrappedTokenValue } from './linters';
|
|
5
5
|
const typescriptErrorMessage = 'There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway.';
|
|
6
6
|
const rule = createLintRule({
|
|
7
7
|
meta: {
|
|
@@ -17,6 +17,7 @@ const rule = createLintRule({
|
|
|
17
17
|
messages: {
|
|
18
18
|
noRestrictedTypographyProperties: `Don't set '{{ property }}' on xcss as it allows invalid combinations of typography tokens. ${typescriptErrorMessage}`,
|
|
19
19
|
noRestrictedTypographyPropertiesHeading: `Don't set '{{ property }}' on xcss in combination with 'font' heading tokens. ${typescriptErrorMessage}`,
|
|
20
|
+
noRestrictedCapitalisation: `Avoid using ALL CAPS as it reduces readability and is bad for accessibility.`,
|
|
20
21
|
noWrappedTokenTypographyValues: `Don't wrap typography tokens in xcss. ${typescriptErrorMessage}`
|
|
21
22
|
}
|
|
22
23
|
},
|
|
@@ -31,6 +32,14 @@ const rule = createLintRule({
|
|
|
31
32
|
context,
|
|
32
33
|
config
|
|
33
34
|
}),
|
|
35
|
+
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=textTransform]': node => RestrictedCapitalisation.lint(node, {
|
|
36
|
+
context,
|
|
37
|
+
config
|
|
38
|
+
}),
|
|
39
|
+
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=textTransform]': node => RestrictedCapitalisation.lint(node, {
|
|
40
|
+
context,
|
|
41
|
+
config
|
|
42
|
+
}),
|
|
34
43
|
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(font|fontFamily|fontWeight)/]': node => WrappedTokenValue.lint(node, {
|
|
35
44
|
context,
|
|
36
45
|
config
|
package/dist/es2019/rules/use-latest-xcss-syntax-typography/linters/restricted-capitalisation.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import { isPropertyName } from './common';
|
|
5
|
+
export const RestrictedCapitalisation = {
|
|
6
|
+
lint(node, {
|
|
7
|
+
context,
|
|
8
|
+
config
|
|
9
|
+
}) {
|
|
10
|
+
if (RestrictedCapitalisation._check(node, {
|
|
11
|
+
context,
|
|
12
|
+
config
|
|
13
|
+
})) {
|
|
14
|
+
context.report({
|
|
15
|
+
node,
|
|
16
|
+
messageId: 'noRestrictedCapitalisation'
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
_check(node, {
|
|
21
|
+
config
|
|
22
|
+
}) {
|
|
23
|
+
if (!config.patterns.includes('restricted-capitalisation')) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Prevent text transform being used to uppercase all characters
|
|
28
|
+
if (isPropertyName(node, 'textTransform') && isNodeOfType(node.parent, 'Property') && isNodeOfType(node.parent.value, 'Literal')) {
|
|
29
|
+
return node.parent.value.value === 'uppercase';
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
2
|
|
|
3
3
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import { isPropertyName } from './common';
|
|
4
5
|
export const RestrictedProperty = {
|
|
5
6
|
lint(node, {
|
|
6
7
|
context,
|
|
@@ -33,7 +34,7 @@ export const RestrictedProperty = {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
// Prevent font weight being used in combination with heading tokens
|
|
36
|
-
if (
|
|
37
|
+
if (isPropertyName(node, 'fontWeight')) {
|
|
37
38
|
if (isNodeOfType(node.parent.parent, 'ObjectExpression')) {
|
|
38
39
|
for (const property of node.parent.parent.properties) {
|
|
39
40
|
var _property$value$value;
|
|
@@ -1,19 +1,143 @@
|
|
|
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; }
|
|
1
4
|
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
5
|
|
|
3
6
|
import * as ast from '../../../../ast-nodes';
|
|
4
7
|
import { isSupportedForLint } from '../supported';
|
|
8
|
+
function isImportDeclaration(node) {
|
|
9
|
+
return node.type === 'ImportDeclaration';
|
|
10
|
+
}
|
|
5
11
|
export var JSXElement = {
|
|
6
12
|
lint: function lint(node, _ref) {
|
|
7
13
|
var context = _ref.context;
|
|
8
14
|
if (!isSupportedForLint(node)) {
|
|
9
15
|
return;
|
|
10
16
|
}
|
|
17
|
+
var nodeName = ast.JSXElement.getName(node);
|
|
18
|
+
var sourceCode = context.getSourceCode();
|
|
19
|
+
var importDeclarations = sourceCode.ast.body.filter(isImportDeclaration);
|
|
20
|
+
var existingLinkName = null;
|
|
21
|
+
var existingLinkButtonName = null;
|
|
22
|
+
var usedNames = new Set();
|
|
23
|
+
|
|
24
|
+
// Check for existing imports
|
|
25
|
+
var _iterator = _createForOfIteratorHelper(importDeclarations),
|
|
26
|
+
_step;
|
|
27
|
+
try {
|
|
28
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
29
|
+
var declaration = _step.value;
|
|
30
|
+
var _iterator2 = _createForOfIteratorHelper(declaration.specifiers),
|
|
31
|
+
_step2;
|
|
32
|
+
try {
|
|
33
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
34
|
+
var specifier = _step2.value;
|
|
35
|
+
usedNames.add(specifier.local.name);
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
_iterator2.e(err);
|
|
39
|
+
} finally {
|
|
40
|
+
_iterator2.f();
|
|
41
|
+
}
|
|
42
|
+
if (declaration.source.value === '@atlaskit/link') {
|
|
43
|
+
var defaultSpecifier = declaration.specifiers.find(function (specifier) {
|
|
44
|
+
return specifier.type === 'ImportDefaultSpecifier';
|
|
45
|
+
});
|
|
46
|
+
if (defaultSpecifier) {
|
|
47
|
+
existingLinkName = defaultSpecifier.local.name;
|
|
48
|
+
}
|
|
49
|
+
} else if (declaration.source.value === '@atlaskit/button/new') {
|
|
50
|
+
var namedSpecifier = declaration.specifiers.find(function (specifier) {
|
|
51
|
+
return specifier.type === 'ImportSpecifier' && specifier.imported.name === 'LinkButton';
|
|
52
|
+
});
|
|
53
|
+
if (namedSpecifier) {
|
|
54
|
+
existingLinkButtonName = namedSpecifier.local.name;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
_iterator.e(err);
|
|
60
|
+
} finally {
|
|
61
|
+
_iterator.f();
|
|
62
|
+
}
|
|
63
|
+
var generateUniqueName = function generateUniqueName(baseName) {
|
|
64
|
+
var index = 1;
|
|
65
|
+
var newName = baseName;
|
|
66
|
+
while (usedNames.has(newName)) {
|
|
67
|
+
newName = "".concat(baseName).concat(index);
|
|
68
|
+
index++;
|
|
69
|
+
}
|
|
70
|
+
return newName;
|
|
71
|
+
};
|
|
72
|
+
var linkName = existingLinkName || generateUniqueName('Link');
|
|
73
|
+
var linkButtonName = existingLinkButtonName || generateUniqueName('LinkButton');
|
|
11
74
|
context.report({
|
|
12
75
|
node: node.openingElement,
|
|
13
76
|
messageId: 'noHtmlAnchor',
|
|
14
77
|
data: {
|
|
15
|
-
name:
|
|
16
|
-
}
|
|
78
|
+
name: nodeName
|
|
79
|
+
},
|
|
80
|
+
suggest: [{
|
|
81
|
+
desc: 'Replace with Link component from @atlaskit/link',
|
|
82
|
+
fix: function fix(fixer) {
|
|
83
|
+
var _node$closingElement;
|
|
84
|
+
var openingTagRange = node.openingElement.range;
|
|
85
|
+
var closingTagRange = (_node$closingElement = node.closingElement) === null || _node$closingElement === void 0 ? void 0 : _node$closingElement.range;
|
|
86
|
+
var attributesText = node.openingElement.attributes.map(function (attr) {
|
|
87
|
+
return sourceCode.getText(attr);
|
|
88
|
+
}).join(' ');
|
|
89
|
+
var fixers = [];
|
|
90
|
+
|
|
91
|
+
// Replace <a> with <Link> and retain attributes
|
|
92
|
+
if (openingTagRange) {
|
|
93
|
+
if (node.openingElement.selfClosing) {
|
|
94
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkName).concat(attributesText ? " ".concat(attributesText) : '', " /")));
|
|
95
|
+
} else {
|
|
96
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkName).concat(attributesText ? " ".concat(attributesText) : '')));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (closingTagRange && !node.openingElement.selfClosing) {
|
|
100
|
+
fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], linkName));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Add import if not present
|
|
104
|
+
if (!existingLinkName) {
|
|
105
|
+
var importStatement = "import ".concat(linkName, " from '@atlaskit/link';\n");
|
|
106
|
+
fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
|
|
107
|
+
}
|
|
108
|
+
return fixers;
|
|
109
|
+
}
|
|
110
|
+
}, {
|
|
111
|
+
desc: 'Replace with LinkButton component from @atlaskit/button/new',
|
|
112
|
+
fix: function fix(fixer) {
|
|
113
|
+
var _node$closingElement2;
|
|
114
|
+
var openingTagRange = node.openingElement.range;
|
|
115
|
+
var closingTagRange = (_node$closingElement2 = node.closingElement) === null || _node$closingElement2 === void 0 ? void 0 : _node$closingElement2.range;
|
|
116
|
+
var attributesText = node.openingElement.attributes.map(function (attr) {
|
|
117
|
+
return sourceCode.getText(attr);
|
|
118
|
+
}).join(' ');
|
|
119
|
+
var fixers = [];
|
|
120
|
+
|
|
121
|
+
// Replace <a> with <LinkButton> and retain attributes
|
|
122
|
+
if (openingTagRange) {
|
|
123
|
+
if (node.openingElement.selfClosing) {
|
|
124
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkButtonName).concat(attributesText ? " ".concat(attributesText) : '', " /")));
|
|
125
|
+
} else {
|
|
126
|
+
fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(linkButtonName).concat(attributesText ? " ".concat(attributesText) : '')));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (closingTagRange && !node.openingElement.selfClosing) {
|
|
130
|
+
fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], linkButtonName));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add import if not present
|
|
134
|
+
if (!existingLinkButtonName) {
|
|
135
|
+
var importStatement = "import { ".concat(linkButtonName, " } from '@atlaskit/button/new';\n");
|
|
136
|
+
fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
|
|
137
|
+
}
|
|
138
|
+
return fixers;
|
|
139
|
+
}
|
|
140
|
+
}]
|
|
17
141
|
});
|
|
18
142
|
}
|
|
19
143
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var defaults = {
|
|
2
2
|
failSilently: false,
|
|
3
|
-
patterns: ['restricted-property', 'wrapped-token-value']
|
|
3
|
+
patterns: ['restricted-property', 'wrapped-token-value', 'restricted-capitalisation']
|
|
4
4
|
};
|
|
5
5
|
export var getConfig = function getConfig(overrides) {
|
|
6
6
|
return Object.assign({}, defaults, overrides);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLintRule } from '../utils/create-rule';
|
|
2
2
|
import { errorBoundary } from '../utils/error-boundary';
|
|
3
3
|
import { getConfig } from './config';
|
|
4
|
-
import { RestrictedProperty, WrappedTokenValue } from './linters';
|
|
4
|
+
import { RestrictedCapitalisation, RestrictedProperty, WrappedTokenValue } from './linters';
|
|
5
5
|
var typescriptErrorMessage = 'There is ongoing work to make this a TypeScript error. Once that happens, you will have to delete/refactor anyway.';
|
|
6
6
|
var rule = createLintRule({
|
|
7
7
|
meta: {
|
|
@@ -17,6 +17,7 @@ var rule = createLintRule({
|
|
|
17
17
|
messages: {
|
|
18
18
|
noRestrictedTypographyProperties: "Don't set '{{ property }}' on xcss as it allows invalid combinations of typography tokens. ".concat(typescriptErrorMessage),
|
|
19
19
|
noRestrictedTypographyPropertiesHeading: "Don't set '{{ property }}' on xcss in combination with 'font' heading tokens. ".concat(typescriptErrorMessage),
|
|
20
|
+
noRestrictedCapitalisation: "Avoid using ALL CAPS as it reduces readability and is bad for accessibility.",
|
|
20
21
|
noWrappedTokenTypographyValues: "Don't wrap typography tokens in xcss. ".concat(typescriptErrorMessage)
|
|
21
22
|
}
|
|
22
23
|
},
|
|
@@ -35,6 +36,18 @@ var rule = createLintRule({
|
|
|
35
36
|
config: config
|
|
36
37
|
});
|
|
37
38
|
},
|
|
39
|
+
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=textTransform]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameTextTransform(node) {
|
|
40
|
+
return RestrictedCapitalisation.lint(node, {
|
|
41
|
+
context: context,
|
|
42
|
+
config: config
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Literal[value=textTransform]': function CallExpressionCalleeNameXcssObjectExpressionPropertyLiteralValueTextTransform(node) {
|
|
46
|
+
return RestrictedCapitalisation.lint(node, {
|
|
47
|
+
context: context,
|
|
48
|
+
config: config
|
|
49
|
+
});
|
|
50
|
+
},
|
|
38
51
|
'CallExpression[callee.name="xcss"] ObjectExpression > Property > Identifier[name=/(font|fontFamily|fontWeight)/]': function CallExpressionCalleeNameXcssObjectExpressionPropertyIdentifierNameFontFontFamilyFontWeight(node) {
|
|
39
52
|
return WrappedTokenValue.lint(node, {
|
|
40
53
|
context: context,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import { isPropertyName } from './common';
|
|
5
|
+
export var RestrictedCapitalisation = {
|
|
6
|
+
lint: function lint(node, _ref) {
|
|
7
|
+
var context = _ref.context,
|
|
8
|
+
config = _ref.config;
|
|
9
|
+
if (RestrictedCapitalisation._check(node, {
|
|
10
|
+
context: context,
|
|
11
|
+
config: config
|
|
12
|
+
})) {
|
|
13
|
+
context.report({
|
|
14
|
+
node: node,
|
|
15
|
+
messageId: 'noRestrictedCapitalisation'
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
_check: function _check(node, _ref2) {
|
|
20
|
+
var config = _ref2.config;
|
|
21
|
+
if (!config.patterns.includes('restricted-capitalisation')) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Prevent text transform being used to uppercase all characters
|
|
26
|
+
if (isPropertyName(node, 'textTransform') && isNodeOfType(node.parent, 'Property') && isNodeOfType(node.parent.value, 'Literal')) {
|
|
27
|
+
return node.parent.value.value === 'uppercase';
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -4,6 +4,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
4
4
|
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
5
5
|
|
|
6
6
|
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
7
|
+
import { isPropertyName } from './common';
|
|
7
8
|
export var RestrictedProperty = {
|
|
8
9
|
lint: function lint(node, _ref) {
|
|
9
10
|
var context = _ref.context,
|
|
@@ -34,7 +35,7 @@ export var RestrictedProperty = {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// Prevent font weight being used in combination with heading tokens
|
|
37
|
-
if (
|
|
38
|
+
if (isPropertyName(node, 'fontWeight')) {
|
|
38
39
|
if (isNodeOfType(node.parent.parent, 'ObjectExpression')) {
|
|
39
40
|
var _iterator = _createForOfIteratorHelper(node.parent.parent.properties),
|
|
40
41
|
_step;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/eslint-plugin-design-system",
|
|
3
3
|
"description": "The essential plugin for use with the Atlassian Design System.",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.22.0",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"publishConfig": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@atlaskit/eslint-utils": "^1.7.0",
|
|
44
|
-
"@atlaskit/icon": "^22.
|
|
44
|
+
"@atlaskit/icon": "^22.20.0",
|
|
45
45
|
"@atlaskit/tokens": "*",
|
|
46
46
|
"@babel/runtime": "^7.0.0",
|
|
47
47
|
"@typescript-eslint/utils": "^5.48.1",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@af/formatting": "*",
|
|
57
|
-
"@atlaskit/ds-lib": "^2.
|
|
57
|
+
"@atlaskit/ds-lib": "^2.6.0",
|
|
58
58
|
"@atlaskit/theme": "^13.0.0",
|
|
59
59
|
"@atlassian/codegen": "*",
|
|
60
60
|
"@atlassian/eslint-utils": "^0.5.0",
|