@atlaskit/eslint-plugin-design-system 13.6.0 → 13.7.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -0
  3. package/dist/cjs/presets/all-flat.codegen.js +2 -1
  4. package/dist/cjs/presets/all.codegen.js +2 -1
  5. package/dist/cjs/presets/recommended-flat.codegen.js +2 -1
  6. package/dist/cjs/presets/recommended.codegen.js +2 -1
  7. package/dist/cjs/rules/index.codegen.js +3 -1
  8. package/dist/cjs/rules/no-html-anchor/node-types/styled-component/index.js +2 -2
  9. package/dist/cjs/rules/no-html-button/node-types/styled-component/index.js +2 -2
  10. package/dist/cjs/rules/no-html-image/index.js +40 -0
  11. package/dist/cjs/rules/no-html-image/node-types/index.js +19 -0
  12. package/dist/cjs/rules/no-html-image/node-types/jsx-element/index.js +115 -0
  13. package/dist/cjs/rules/no-html-image/node-types/styled-component/index.js +42 -0
  14. package/dist/cjs/rules/no-html-image/node-types/supported.js +72 -0
  15. package/dist/cjs/rules/utils/get-styled-component-call.js +1 -1
  16. package/dist/es2019/presets/all-flat.codegen.js +2 -1
  17. package/dist/es2019/presets/all.codegen.js +2 -1
  18. package/dist/es2019/presets/recommended-flat.codegen.js +2 -1
  19. package/dist/es2019/presets/recommended.codegen.js +2 -1
  20. package/dist/es2019/rules/index.codegen.js +3 -1
  21. package/dist/es2019/rules/no-html-anchor/node-types/styled-component/index.js +2 -2
  22. package/dist/es2019/rules/no-html-button/node-types/styled-component/index.js +2 -2
  23. package/dist/es2019/rules/no-html-image/index.js +34 -0
  24. package/dist/es2019/rules/no-html-image/node-types/index.js +2 -0
  25. package/dist/es2019/rules/no-html-image/node-types/jsx-element/index.js +80 -0
  26. package/dist/es2019/rules/no-html-image/node-types/styled-component/index.js +37 -0
  27. package/dist/es2019/rules/no-html-image/node-types/supported.js +62 -0
  28. package/dist/es2019/rules/utils/get-styled-component-call.js +1 -1
  29. package/dist/esm/presets/all-flat.codegen.js +2 -1
  30. package/dist/esm/presets/all.codegen.js +2 -1
  31. package/dist/esm/presets/recommended-flat.codegen.js +2 -1
  32. package/dist/esm/presets/recommended.codegen.js +2 -1
  33. package/dist/esm/rules/index.codegen.js +3 -1
  34. package/dist/esm/rules/no-html-anchor/node-types/styled-component/index.js +2 -2
  35. package/dist/esm/rules/no-html-button/node-types/styled-component/index.js +2 -2
  36. package/dist/esm/rules/no-html-image/index.js +34 -0
  37. package/dist/esm/rules/no-html-image/node-types/index.js +2 -0
  38. package/dist/esm/rules/no-html-image/node-types/jsx-element/index.js +106 -0
  39. package/dist/esm/rules/no-html-image/node-types/styled-component/index.js +36 -0
  40. package/dist/esm/rules/no-html-image/node-types/supported.js +63 -0
  41. package/dist/esm/rules/utils/get-styled-component-call.js +1 -1
  42. package/dist/types/index.codegen.d.ts +9 -0
  43. package/dist/types/presets/all-flat.codegen.d.ts +1 -0
  44. package/dist/types/presets/all.codegen.d.ts +1 -0
  45. package/dist/types/presets/recommended-flat.codegen.d.ts +1 -0
  46. package/dist/types/presets/recommended.codegen.d.ts +1 -0
  47. package/dist/types/rules/index.codegen.d.ts +1 -0
  48. package/dist/types/rules/no-html-image/index.d.ts +3 -0
  49. package/dist/types/rules/no-html-image/node-types/index.d.ts +2 -0
  50. package/dist/types/rules/no-html-image/node-types/jsx-element/index.d.ts +8 -0
  51. package/dist/types/rules/no-html-image/node-types/styled-component/index.d.ts +8 -0
  52. package/dist/types/rules/no-html-image/node-types/supported.d.ts +7 -0
  53. package/dist/types-ts4.5/index.codegen.d.ts +9 -0
  54. package/dist/types-ts4.5/presets/all-flat.codegen.d.ts +1 -0
  55. package/dist/types-ts4.5/presets/all.codegen.d.ts +1 -0
  56. package/dist/types-ts4.5/presets/recommended-flat.codegen.d.ts +1 -0
  57. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +1 -0
  58. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  59. package/dist/types-ts4.5/rules/no-html-image/index.d.ts +3 -0
  60. package/dist/types-ts4.5/rules/no-html-image/node-types/index.d.ts +2 -0
  61. package/dist/types-ts4.5/rules/no-html-image/node-types/jsx-element/index.d.ts +8 -0
  62. package/dist/types-ts4.5/rules/no-html-image/node-types/styled-component/index.d.ts +8 -0
  63. package/dist/types-ts4.5/rules/no-html-image/node-types/supported.d.ts +7 -0
  64. package/package.json +1 -1
  65. package/dist/cjs/rules/no-html-anchor/node-types/styled-component/get-styled-component-call.js +0 -47
  66. package/dist/cjs/rules/no-html-button/node-types/styled-component/get-styled-component-call.js +0 -47
  67. package/dist/cjs/rules/no-html-button/utils/get-jsx-element-by-name.js +0 -53
  68. package/dist/es2019/rules/no-html-anchor/node-types/styled-component/get-styled-component-call.js +0 -42
  69. package/dist/es2019/rules/no-html-button/node-types/styled-component/get-styled-component-call.js +0 -42
  70. package/dist/es2019/rules/no-html-button/utils/get-jsx-element-by-name.js +0 -39
  71. package/dist/esm/rules/no-html-anchor/node-types/styled-component/get-styled-component-call.js +0 -42
  72. package/dist/esm/rules/no-html-button/node-types/styled-component/get-styled-component-call.js +0 -42
  73. package/dist/esm/rules/no-html-button/utils/get-jsx-element-by-name.js +0 -47
  74. package/dist/types/rules/no-html-anchor/node-types/styled-component/get-styled-component-call.d.ts +0 -6
  75. package/dist/types/rules/no-html-anchor/utils/get-jsx-element-by-name.d.ts +0 -6
  76. package/dist/types/rules/no-html-button/node-types/styled-component/get-styled-component-call.d.ts +0 -6
  77. package/dist/types/rules/no-html-button/utils/get-jsx-element-by-name.d.ts +0 -6
  78. package/dist/types-ts4.5/rules/no-html-anchor/node-types/styled-component/get-styled-component-call.d.ts +0 -6
  79. package/dist/types-ts4.5/rules/no-html-button/node-types/styled-component/get-styled-component-call.d.ts +0 -6
  80. /package/dist/cjs/rules/{no-html-anchor → no-html-image}/utils/get-jsx-element-by-name.js +0 -0
  81. /package/dist/es2019/rules/{no-html-anchor → no-html-image}/utils/get-jsx-element-by-name.js +0 -0
  82. /package/dist/esm/rules/{no-html-anchor → no-html-image}/utils/get-jsx-element-by-name.js +0 -0
  83. /package/dist/{types-ts4.5/rules/no-html-button → types/rules/no-html-image}/utils/get-jsx-element-by-name.d.ts +0 -0
  84. /package/dist/types-ts4.5/rules/{no-html-anchor → no-html-image}/utils/get-jsx-element-by-name.d.ts +0 -0
@@ -0,0 +1,80 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { getSourceCode } from '@atlaskit/eslint-utils/context-compat';
3
+ import * as ast from '../../../../ast-nodes';
4
+ import { isSupportedForLint } from '../supported';
5
+ function isImportDeclaration(node) {
6
+ return node.type === 'ImportDeclaration';
7
+ }
8
+ export const JSXElement = {
9
+ lint(node, {
10
+ context
11
+ }) {
12
+ if (!isSupportedForLint(node)) {
13
+ return;
14
+ }
15
+ const nodeName = ast.JSXElement.getName(node);
16
+ const sourceCode = getSourceCode(context);
17
+ const importDeclarations = sourceCode.ast.body.filter(isImportDeclaration);
18
+ let existingImageName = null;
19
+ const usedNames = new Set();
20
+
21
+ // Check for existing imports
22
+ for (const declaration of importDeclarations) {
23
+ for (const specifier of declaration.specifiers) {
24
+ usedNames.add(specifier.local.name);
25
+ }
26
+ if (declaration.source.value === '@atlaskit/image') {
27
+ const defaultSpecifier = declaration.specifiers.find(specifier => specifier.type === 'ImportDefaultSpecifier');
28
+ if (defaultSpecifier) {
29
+ existingImageName = defaultSpecifier.local.name;
30
+ }
31
+ }
32
+ }
33
+ const generateUniqueName = baseName => {
34
+ let index = 1;
35
+ let newName = baseName;
36
+ while (usedNames.has(newName)) {
37
+ newName = `${baseName}${index}`;
38
+ index++;
39
+ }
40
+ return newName;
41
+ };
42
+ const imageName = existingImageName || generateUniqueName('Image');
43
+ context.report({
44
+ node: node.openingElement,
45
+ messageId: 'noHtmlImage',
46
+ data: {
47
+ name: nodeName
48
+ },
49
+ suggest: [{
50
+ desc: 'Replace with Image component from @atlaskit/image',
51
+ fix(fixer) {
52
+ var _node$closingElement;
53
+ const openingTagRange = node.openingElement.range;
54
+ const closingTagRange = (_node$closingElement = node.closingElement) === null || _node$closingElement === void 0 ? void 0 : _node$closingElement.range;
55
+ const attributesText = node.openingElement.attributes.filter(attr => !isNodeOfType(attr, 'JSXAttribute') || attr.name.name !== 'role').map(attr => sourceCode.getText(attr)).join(' ');
56
+ const fixers = [];
57
+
58
+ // Replace <img> with <Image> and retain attributes
59
+ if (openingTagRange) {
60
+ if (node.openingElement.selfClosing) {
61
+ fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], `${imageName}${attributesText ? ` ${attributesText}` : ''} /`));
62
+ } else {
63
+ fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], `${imageName}${attributesText ? ` ${attributesText}` : ''}`));
64
+ }
65
+ }
66
+ if (closingTagRange && !node.openingElement.selfClosing) {
67
+ fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], imageName));
68
+ }
69
+
70
+ // Add import if not present
71
+ if (!existingImageName) {
72
+ const importStatement = `import ${imageName} from '@atlaskit/image';\n`;
73
+ fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
74
+ }
75
+ return fixers;
76
+ }
77
+ }]
78
+ });
79
+ }
80
+ };
@@ -0,0 +1,37 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { isNodeOfType } from 'eslint-codemod-utils';
4
+ import { getScope } from '@atlaskit/eslint-utils/context-compat';
5
+ import { getStyledComponentCall } from '../../../utils/get-styled-component-call';
6
+ import { getJsxElementByName } from '../../utils/get-jsx-element-by-name';
7
+ import { isSupportedForLint } from '../supported';
8
+ export const StyledComponent = {
9
+ lint(node, {
10
+ context
11
+ }) {
12
+ var _getJsxElementByName;
13
+ if (!isNodeOfType(node, 'CallExpression') || !isNodeOfType(node.callee, 'MemberExpression') || !isNodeOfType(node.callee.object, 'Identifier') || !isNodeOfType(node.callee.property, 'Identifier')) {
14
+ return;
15
+ }
16
+ const styles = getStyledComponentCall(node);
17
+ const elementName = node.callee.property.name;
18
+ if (!styles || !isNodeOfType(styles.id, 'Identifier')) {
19
+ return;
20
+ }
21
+ const jsxElement = (_getJsxElementByName = getJsxElementByName(styles.id.name, getScope(context, node))) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
22
+ if (!jsxElement) {
23
+ // If there's no JSX element, we can't determine if it's being used as an image or not
24
+ return;
25
+ }
26
+ if (jsxElement && !isSupportedForLint(jsxElement, elementName)) {
27
+ return;
28
+ }
29
+ context.report({
30
+ node: styles,
31
+ messageId: 'noHtmlImage',
32
+ data: {
33
+ name: node.callee.property.name
34
+ }
35
+ });
36
+ }
37
+ };
@@ -0,0 +1,62 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import * as ast from '../../../ast-nodes';
3
+ const supportedElements = [{
4
+ name: 'img'
5
+ }, {
6
+ name: '*',
7
+ attributes: [{
8
+ name: 'role',
9
+ values: ['img']
10
+ }]
11
+ }];
12
+
13
+ /**
14
+ * Determines if the given JSX element is a supported element to lint with this rule.
15
+ */
16
+ export function isSupportedForLint(jsxNode, elementName) {
17
+ if (!isNodeOfType(jsxNode, 'JSXElement')) {
18
+ return false;
19
+ }
20
+
21
+ // Allow passing in the element name because the jsxNode doesn't
22
+ // represent the element name with styled components
23
+ const elName = elementName || ast.JSXElement.getName(jsxNode);
24
+ if (!elName) {
25
+ return false;
26
+ }
27
+
28
+ // Only check native HTML elements, not components
29
+ if (elName[0] !== elName[0].toLowerCase()) {
30
+ return false;
31
+ }
32
+ let supportedElement = supportedElements.find(({
33
+ name
34
+ }) => name === elName);
35
+ if (!supportedElement) {
36
+ supportedElement = supportedElements.find(({
37
+ name
38
+ }) => name === '*');
39
+ }
40
+ if (!supportedElement) {
41
+ return false;
42
+ }
43
+
44
+ // Check if the element has any attributes that are not supported
45
+ const attributes = ast.JSXElement.getAttributes(jsxNode);
46
+ if (supportedElement.attributes && !supportedElement.attributes.every(({
47
+ name,
48
+ values
49
+ }) => {
50
+ return attributes.some(attribute => {
51
+ if (attribute.type === 'JSXSpreadAttribute') {
52
+ return false;
53
+ }
54
+ const isMatchingName = attribute.name.name === name;
55
+ const isMatchingValues = values && attribute.value && attribute.value.type === 'Literal' && typeof attribute.value.value === 'string' && (values === null || values === void 0 ? void 0 : values.includes(attribute.value.value));
56
+ return isMatchingName && isMatchingValues;
57
+ });
58
+ })) {
59
+ return false;
60
+ }
61
+ return true;
62
+ }
@@ -23,7 +23,7 @@ export const getStyledComponentCall = node => {
23
23
 
24
24
  /**
25
25
  * Some verbose precondition checks but all it does is check
26
- * a call expression is of form `styled.a` or `styled2.a`
26
+ * a call expression is of form `styled.<element>` or `styled2.<element>`
27
27
  */
28
28
  const isStyledCallExpression = call => {
29
29
  if (!isNodeOfType(call, 'CallExpression')) {
@@ -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::c78819ddb772edbd8d5e2dc246c52d11>>
3
+ * @codegen <<SignedSource::9a179b7d6c19d5c7eafde6b3ae40233b>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -25,6 +25,7 @@ export default {
25
25
  '@atlaskit/design-system/no-html-anchor': 'warn',
26
26
  '@atlaskit/design-system/no-html-button': 'warn',
27
27
  '@atlaskit/design-system/no-html-checkbox': 'warn',
28
+ '@atlaskit/design-system/no-html-image': 'warn',
28
29
  '@atlaskit/design-system/no-invalid-css-map': ['error', {
29
30
  allowedFunctionCalls: [['@atlaskit/tokens', 'token']]
30
31
  }],
@@ -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::d35975a64956ac336de205b1272c43f1>>
3
+ * @codegen <<SignedSource::08dc1cc7cbbf7112f2f5733f57a9e2b1>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -24,6 +24,7 @@ export default {
24
24
  '@atlaskit/design-system/no-html-anchor': 'warn',
25
25
  '@atlaskit/design-system/no-html-button': 'warn',
26
26
  '@atlaskit/design-system/no-html-checkbox': 'warn',
27
+ '@atlaskit/design-system/no-html-image': 'warn',
27
28
  '@atlaskit/design-system/no-invalid-css-map': ['error', {
28
29
  allowedFunctionCalls: [['@atlaskit/tokens', 'token']]
29
30
  }],
@@ -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::b96889a18bb45c45fa23ed42442732ba>>
3
+ * @codegen <<SignedSource::81564fd37c062c0be3a14ca6ea1e4271>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -20,6 +20,7 @@ export default {
20
20
  '@atlaskit/design-system/no-html-anchor': 'warn',
21
21
  '@atlaskit/design-system/no-html-button': 'warn',
22
22
  '@atlaskit/design-system/no-html-checkbox': 'warn',
23
+ '@atlaskit/design-system/no-html-image': 'warn',
23
24
  '@atlaskit/design-system/no-invalid-css-map': ['error', {
24
25
  allowedFunctionCalls: [['@atlaskit/tokens', 'token']]
25
26
  }],
@@ -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::bd1d631a2db18da8a0726bec36044a51>>
3
+ * @codegen <<SignedSource::175936d329524b99169a5dc943011231>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
 
@@ -19,6 +19,7 @@ export default {
19
19
  '@atlaskit/design-system/no-html-anchor': 'warn',
20
20
  '@atlaskit/design-system/no-html-button': 'warn',
21
21
  '@atlaskit/design-system/no-html-checkbox': 'warn',
22
+ '@atlaskit/design-system/no-html-image': 'warn',
22
23
  '@atlaskit/design-system/no-invalid-css-map': ['error', {
23
24
  allowedFunctionCalls: [['@atlaskit/tokens', 'token']]
24
25
  }],
@@ -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::622bf05b9ade457d05e872b25c71ba5d>>
3
+ * @codegen <<SignedSource::3cefc1b277e5c633caf1fd4491d60d94>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -23,6 +23,7 @@ import noExportedKeyframes from './no-exported-keyframes';
23
23
  import noHtmlAnchor from './no-html-anchor';
24
24
  import noHtmlButton from './no-html-button';
25
25
  import noHtmlCheckbox from './no-html-checkbox';
26
+ import noHtmlImage from './no-html-image';
26
27
  import noInvalidCssMap from './no-invalid-css-map';
27
28
  import noKeyframesTaggedTemplateExpression from './no-keyframes-tagged-template-expression';
28
29
  import noLegacyIcons from './no-legacy-icons';
@@ -76,6 +77,7 @@ export var rules = {
76
77
  'no-html-anchor': noHtmlAnchor,
77
78
  'no-html-button': noHtmlButton,
78
79
  'no-html-checkbox': noHtmlCheckbox,
80
+ 'no-html-image': noHtmlImage,
79
81
  'no-invalid-css-map': noInvalidCssMap,
80
82
  'no-keyframes-tagged-template-expression': noKeyframesTaggedTemplateExpression,
81
83
  'no-legacy-icons': noLegacyIcons,
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import { getScope } from '@atlaskit/eslint-utils/context-compat';
5
- import { getJsxElementByName } from '../../utils/get-jsx-element-by-name';
5
+ import { getJsxElementByName } from '../../../utils/get-jsx-element-by-name';
6
+ import { getStyledComponentCall } from '../../../utils/get-styled-component-call';
6
7
  import { isSupportedForLint } from '../supported';
7
- import { getStyledComponentCall } from './get-styled-component-call';
8
8
  export var StyledComponent = {
9
9
  lint: function lint(node, _ref) {
10
10
  var _getJsxElementByName;
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import { getScope } from '@atlaskit/eslint-utils/context-compat';
5
- import { getJsxElementByName } from '../../utils/get-jsx-element-by-name';
5
+ import { getJsxElementByName } from '../../../utils/get-jsx-element-by-name';
6
+ import { getStyledComponentCall } from '../../../utils/get-styled-component-call';
6
7
  import { isSupportedForLint } from '../supported';
7
- import { getStyledComponentCall } from './get-styled-component-call';
8
8
  export var StyledComponent = {
9
9
  lint: function lint(node, _ref) {
10
10
  var _getJsxElementByName;
@@ -0,0 +1,34 @@
1
+ import { createLintRule } from '../utils/create-rule';
2
+ import { JSXElement as _JSXElement, StyledComponent } from './node-types';
3
+ var rule = createLintRule({
4
+ meta: {
5
+ name: 'no-html-image',
6
+ type: 'suggestion',
7
+ hasSuggestions: true,
8
+ docs: {
9
+ description: 'Discourage direct usage of HTML image elements in favor of the Atlassian Design System image component.',
10
+ recommended: true,
11
+ severity: 'warn'
12
+ },
13
+ messages: {
14
+ noHtmlImage: "This <{{ name }}> should be replaced with the image component from the Atlassian Design System. ADS images ensure accessible implementations, and provide access to ADS styling features like design tokens."
15
+ }
16
+ },
17
+ create: function create(context) {
18
+ return {
19
+ // transforms styled.<anchor>(...) usages
20
+ CallExpression: function CallExpression(node) {
21
+ StyledComponent.lint(node, {
22
+ context: context
23
+ });
24
+ },
25
+ // transforms <anchor css={...}> usages
26
+ JSXElement: function JSXElement(node) {
27
+ _JSXElement.lint(node, {
28
+ context: context
29
+ });
30
+ }
31
+ };
32
+ }
33
+ });
34
+ export default rule;
@@ -0,0 +1,2 @@
1
+ export { StyledComponent } from './styled-component';
2
+ export { JSXElement } from './jsx-element';
@@ -0,0 +1,106 @@
1
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
+ import { isNodeOfType } from 'eslint-codemod-utils';
5
+ import { getSourceCode } from '@atlaskit/eslint-utils/context-compat';
6
+ import * as ast from '../../../../ast-nodes';
7
+ import { isSupportedForLint } from '../supported';
8
+ function isImportDeclaration(node) {
9
+ return node.type === 'ImportDeclaration';
10
+ }
11
+ export var JSXElement = {
12
+ lint: function lint(node, _ref) {
13
+ var context = _ref.context;
14
+ if (!isSupportedForLint(node)) {
15
+ return;
16
+ }
17
+ var nodeName = ast.JSXElement.getName(node);
18
+ var sourceCode = getSourceCode(context);
19
+ var importDeclarations = sourceCode.ast.body.filter(isImportDeclaration);
20
+ var existingImageName = null;
21
+ var usedNames = new Set();
22
+
23
+ // Check for existing imports
24
+ var _iterator = _createForOfIteratorHelper(importDeclarations),
25
+ _step;
26
+ try {
27
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
28
+ var declaration = _step.value;
29
+ var _iterator2 = _createForOfIteratorHelper(declaration.specifiers),
30
+ _step2;
31
+ try {
32
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
33
+ var specifier = _step2.value;
34
+ usedNames.add(specifier.local.name);
35
+ }
36
+ } catch (err) {
37
+ _iterator2.e(err);
38
+ } finally {
39
+ _iterator2.f();
40
+ }
41
+ if (declaration.source.value === '@atlaskit/image') {
42
+ var defaultSpecifier = declaration.specifiers.find(function (specifier) {
43
+ return specifier.type === 'ImportDefaultSpecifier';
44
+ });
45
+ if (defaultSpecifier) {
46
+ existingImageName = defaultSpecifier.local.name;
47
+ }
48
+ }
49
+ }
50
+ } catch (err) {
51
+ _iterator.e(err);
52
+ } finally {
53
+ _iterator.f();
54
+ }
55
+ var generateUniqueName = function generateUniqueName(baseName) {
56
+ var index = 1;
57
+ var newName = baseName;
58
+ while (usedNames.has(newName)) {
59
+ newName = "".concat(baseName).concat(index);
60
+ index++;
61
+ }
62
+ return newName;
63
+ };
64
+ var imageName = existingImageName || generateUniqueName('Image');
65
+ context.report({
66
+ node: node.openingElement,
67
+ messageId: 'noHtmlImage',
68
+ data: {
69
+ name: nodeName
70
+ },
71
+ suggest: [{
72
+ desc: 'Replace with Image component from @atlaskit/image',
73
+ fix: function fix(fixer) {
74
+ var _node$closingElement;
75
+ var openingTagRange = node.openingElement.range;
76
+ var closingTagRange = (_node$closingElement = node.closingElement) === null || _node$closingElement === void 0 ? void 0 : _node$closingElement.range;
77
+ var attributesText = node.openingElement.attributes.filter(function (attr) {
78
+ return !isNodeOfType(attr, 'JSXAttribute') || attr.name.name !== 'role';
79
+ }).map(function (attr) {
80
+ return sourceCode.getText(attr);
81
+ }).join(' ');
82
+ var fixers = [];
83
+
84
+ // Replace <img> with <Image> and retain attributes
85
+ if (openingTagRange) {
86
+ if (node.openingElement.selfClosing) {
87
+ fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(imageName).concat(attributesText ? " ".concat(attributesText) : '', " /")));
88
+ } else {
89
+ fixers.push(fixer.replaceTextRange([openingTagRange[0] + 1, openingTagRange[1] - 1], "".concat(imageName).concat(attributesText ? " ".concat(attributesText) : '')));
90
+ }
91
+ }
92
+ if (closingTagRange && !node.openingElement.selfClosing) {
93
+ fixers.push(fixer.replaceTextRange([closingTagRange[0] + 2, closingTagRange[1] - 1], imageName));
94
+ }
95
+
96
+ // Add import if not present
97
+ if (!existingImageName) {
98
+ var importStatement = "import ".concat(imageName, " from '@atlaskit/image';\n");
99
+ fixers.push(fixer.insertTextBefore(sourceCode.ast, importStatement));
100
+ }
101
+ return fixers;
102
+ }
103
+ }]
104
+ });
105
+ }
106
+ };
@@ -0,0 +1,36 @@
1
+ /* eslint-disable @repo/internal/react/require-jsdoc */
2
+
3
+ import { isNodeOfType } from 'eslint-codemod-utils';
4
+ import { getScope } from '@atlaskit/eslint-utils/context-compat';
5
+ import { getStyledComponentCall } from '../../../utils/get-styled-component-call';
6
+ import { getJsxElementByName } from '../../utils/get-jsx-element-by-name';
7
+ import { isSupportedForLint } from '../supported';
8
+ export var StyledComponent = {
9
+ lint: function lint(node, _ref) {
10
+ var _getJsxElementByName;
11
+ var context = _ref.context;
12
+ if (!isNodeOfType(node, 'CallExpression') || !isNodeOfType(node.callee, 'MemberExpression') || !isNodeOfType(node.callee.object, 'Identifier') || !isNodeOfType(node.callee.property, 'Identifier')) {
13
+ return;
14
+ }
15
+ var styles = getStyledComponentCall(node);
16
+ var elementName = node.callee.property.name;
17
+ if (!styles || !isNodeOfType(styles.id, 'Identifier')) {
18
+ return;
19
+ }
20
+ var jsxElement = (_getJsxElementByName = getJsxElementByName(styles.id.name, getScope(context, node))) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
21
+ if (!jsxElement) {
22
+ // If there's no JSX element, we can't determine if it's being used as an image or not
23
+ return;
24
+ }
25
+ if (jsxElement && !isSupportedForLint(jsxElement, elementName)) {
26
+ return;
27
+ }
28
+ context.report({
29
+ node: styles,
30
+ messageId: 'noHtmlImage',
31
+ data: {
32
+ name: node.callee.property.name
33
+ }
34
+ });
35
+ }
36
+ };
@@ -0,0 +1,63 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import * as ast from '../../../ast-nodes';
3
+ var supportedElements = [{
4
+ name: 'img'
5
+ }, {
6
+ name: '*',
7
+ attributes: [{
8
+ name: 'role',
9
+ values: ['img']
10
+ }]
11
+ }];
12
+
13
+ /**
14
+ * Determines if the given JSX element is a supported element to lint with this rule.
15
+ */
16
+ export function isSupportedForLint(jsxNode, elementName) {
17
+ if (!isNodeOfType(jsxNode, 'JSXElement')) {
18
+ return false;
19
+ }
20
+
21
+ // Allow passing in the element name because the jsxNode doesn't
22
+ // represent the element name with styled components
23
+ var elName = elementName || ast.JSXElement.getName(jsxNode);
24
+ if (!elName) {
25
+ return false;
26
+ }
27
+
28
+ // Only check native HTML elements, not components
29
+ if (elName[0] !== elName[0].toLowerCase()) {
30
+ return false;
31
+ }
32
+ var supportedElement = supportedElements.find(function (_ref) {
33
+ var name = _ref.name;
34
+ return name === elName;
35
+ });
36
+ if (!supportedElement) {
37
+ supportedElement = supportedElements.find(function (_ref2) {
38
+ var name = _ref2.name;
39
+ return name === '*';
40
+ });
41
+ }
42
+ if (!supportedElement) {
43
+ return false;
44
+ }
45
+
46
+ // Check if the element has any attributes that are not supported
47
+ var attributes = ast.JSXElement.getAttributes(jsxNode);
48
+ if (supportedElement.attributes && !supportedElement.attributes.every(function (_ref3) {
49
+ var name = _ref3.name,
50
+ values = _ref3.values;
51
+ return attributes.some(function (attribute) {
52
+ if (attribute.type === 'JSXSpreadAttribute') {
53
+ return false;
54
+ }
55
+ var isMatchingName = attribute.name.name === name;
56
+ var isMatchingValues = values && attribute.value && attribute.value.type === 'Literal' && typeof attribute.value.value === 'string' && (values === null || values === void 0 ? void 0 : values.includes(attribute.value.value));
57
+ return isMatchingName && isMatchingValues;
58
+ });
59
+ })) {
60
+ return false;
61
+ }
62
+ return true;
63
+ }
@@ -23,7 +23,7 @@ export var getStyledComponentCall = function getStyledComponentCall(node) {
23
23
 
24
24
  /**
25
25
  * Some verbose precondition checks but all it does is check
26
- * a call expression is of form `styled.a` or `styled2.a`
26
+ * a call expression is of form `styled.<element>` or `styled2.<element>`
27
27
  */
28
28
  var isStyledCallExpression = function isStyledCallExpression(call) {
29
29
  if (!isNodeOfType(call, 'CallExpression')) {
@@ -35,6 +35,7 @@ export declare const plugin: {
35
35
  'no-html-anchor': import("eslint").Rule.RuleModule;
36
36
  'no-html-button': import("eslint").Rule.RuleModule;
37
37
  'no-html-checkbox': import("eslint").Rule.RuleModule;
38
+ 'no-html-image': import("eslint").Rule.RuleModule;
38
39
  'no-invalid-css-map': import("eslint").Rule.RuleModule;
39
40
  'no-keyframes-tagged-template-expression': import("eslint").Rule.RuleModule;
40
41
  'no-legacy-icons': import("eslint").Rule.RuleModule;
@@ -89,6 +90,7 @@ export declare const plugin: {
89
90
  '@atlaskit/design-system/no-html-anchor': "warn";
90
91
  '@atlaskit/design-system/no-html-button': "warn";
91
92
  '@atlaskit/design-system/no-html-checkbox': "warn";
93
+ '@atlaskit/design-system/no-html-image': "warn";
92
94
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
93
95
  allowedFunctionCalls: string[][];
94
96
  }];
@@ -147,6 +149,7 @@ export declare const plugin: {
147
149
  '@atlaskit/design-system/no-html-anchor': "warn";
148
150
  '@atlaskit/design-system/no-html-button': "warn";
149
151
  '@atlaskit/design-system/no-html-checkbox': "warn";
152
+ '@atlaskit/design-system/no-html-image': "warn";
150
153
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
151
154
  allowedFunctionCalls: string[][];
152
155
  }];
@@ -198,6 +201,7 @@ export declare const plugin: {
198
201
  '@atlaskit/design-system/no-html-anchor': "warn";
199
202
  '@atlaskit/design-system/no-html-button': "warn";
200
203
  '@atlaskit/design-system/no-html-checkbox': "warn";
204
+ '@atlaskit/design-system/no-html-image': "warn";
201
205
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
202
206
  allowedFunctionCalls: string[][];
203
207
  }];
@@ -240,6 +244,7 @@ export declare const plugin: {
240
244
  '@atlaskit/design-system/no-html-anchor': "warn";
241
245
  '@atlaskit/design-system/no-html-button': "warn";
242
246
  '@atlaskit/design-system/no-html-checkbox': "warn";
247
+ '@atlaskit/design-system/no-html-image': "warn";
243
248
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
244
249
  allowedFunctionCalls: string[][];
245
250
  }];
@@ -288,6 +293,7 @@ declare const configs: {
288
293
  '@atlaskit/design-system/no-html-anchor': "warn";
289
294
  '@atlaskit/design-system/no-html-button': "warn";
290
295
  '@atlaskit/design-system/no-html-checkbox': "warn";
296
+ '@atlaskit/design-system/no-html-image': "warn";
291
297
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
292
298
  allowedFunctionCalls: string[][];
293
299
  }];
@@ -346,6 +352,7 @@ declare const configs: {
346
352
  '@atlaskit/design-system/no-html-anchor': "warn";
347
353
  '@atlaskit/design-system/no-html-button': "warn";
348
354
  '@atlaskit/design-system/no-html-checkbox': "warn";
355
+ '@atlaskit/design-system/no-html-image': "warn";
349
356
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
350
357
  allowedFunctionCalls: string[][];
351
358
  }];
@@ -397,6 +404,7 @@ declare const configs: {
397
404
  '@atlaskit/design-system/no-html-anchor': "warn";
398
405
  '@atlaskit/design-system/no-html-button': "warn";
399
406
  '@atlaskit/design-system/no-html-checkbox': "warn";
407
+ '@atlaskit/design-system/no-html-image': "warn";
400
408
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
401
409
  allowedFunctionCalls: string[][];
402
410
  }];
@@ -439,6 +447,7 @@ declare const configs: {
439
447
  '@atlaskit/design-system/no-html-anchor': "warn";
440
448
  '@atlaskit/design-system/no-html-button': "warn";
441
449
  '@atlaskit/design-system/no-html-checkbox': "warn";
450
+ '@atlaskit/design-system/no-html-image': "warn";
442
451
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
443
452
  allowedFunctionCalls: string[][];
444
453
  }];
@@ -18,6 +18,7 @@ declare const _default: {
18
18
  '@atlaskit/design-system/no-html-anchor': "warn";
19
19
  '@atlaskit/design-system/no-html-button': "warn";
20
20
  '@atlaskit/design-system/no-html-checkbox': "warn";
21
+ '@atlaskit/design-system/no-html-image': "warn";
21
22
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
22
23
  allowedFunctionCalls: string[][];
23
24
  }];
@@ -18,6 +18,7 @@ declare const _default: {
18
18
  '@atlaskit/design-system/no-html-anchor': "warn";
19
19
  '@atlaskit/design-system/no-html-button': "warn";
20
20
  '@atlaskit/design-system/no-html-checkbox': "warn";
21
+ '@atlaskit/design-system/no-html-image': "warn";
21
22
  '@atlaskit/design-system/no-invalid-css-map': ["error", {
22
23
  allowedFunctionCalls: string[][];
23
24
  }];