From 1883f0b2deb7e6866fa5f9f465d912748f297b70 Mon Sep 17 00:00:00 2001 From: Kate Higa Date: Wed, 20 Jul 2022 11:53:39 -0700 Subject: [PATCH 1/5] Add custom element mapping support --- lib/rules/a11y-no-generic-link-text.js | 7 +- lib/utils/get-element-type.js | 29 +++++ package-lock.json | 148 +++++++++++++++++++++++++ package.json | 3 +- tests/a11y-no-generic-link-text.js | 58 +++++++++- tests/utils/get-element-type.js | 110 ++++++++++++++++++ 6 files changed, 351 insertions(+), 4 deletions(-) create mode 100644 lib/utils/get-element-type.js create mode 100644 tests/utils/get-element-type.js diff --git a/lib/rules/a11y-no-generic-link-text.js b/lib/rules/a11y-no-generic-link-text.js index a262b43e..49938e37 100644 --- a/lib/rules/a11y-no-generic-link-text.js +++ b/lib/rules/a11y-no-generic-link-text.js @@ -1,4 +1,5 @@ -const {elementType, getProp, getPropValue} = require('jsx-ast-utils') +const {getProp, getPropValue} = require('jsx-ast-utils') +const {getElementType} = require('../utils/get-element-type.js') const bannedLinkText = ['read more', 'here', 'click here', 'learn more', 'more', 'here'] @@ -23,7 +24,9 @@ module.exports = { create(context) { return { JSXOpeningElement: node => { - if (elementType(node) !== 'a') return + const elementType = getElementType(context, node) + + if (elementType !== 'a') return if (getProp(node.attributes, 'aria-labelledby')) return let cleanTextContent // text content we can reliably fetch diff --git a/lib/utils/get-element-type.js b/lib/utils/get-element-type.js new file mode 100644 index 00000000..ce7d5b94 --- /dev/null +++ b/lib/utils/get-element-type.js @@ -0,0 +1,29 @@ +const {elementType, getProp, getPropValue} = require('jsx-ast-utils') + +function getElementType(context, node) { + const {settings} = context + const rawElement = elementType(node) + if (!settings) return rawElement + + const componentMap = settings['github']?.components + if (!componentMap) return rawElement + const component = componentMap[rawElement] + if (!component) return rawElement + let element = component.default ? component.default : rawElement + + if (component.props) { + const props = Object.entries(component.props) + for (const [key, value] of props) { + const propMap = value + let propValue = getPropValue(getProp(node.attributes, key)) + let mapValue = propMap[propValue] + + if (mapValue) { + element = mapValue + } + } + } + return element +} + +module.exports = {getElementType} diff --git a/package-lock.json b/package-lock.json index ccab3919..5290290f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ }, "devDependencies": { "@github/prettier-config": "0.0.4", + "chai": "^4.3.6", "eslint": "^8.0.1", "eslint-plugin-eslint-plugin": "^5.0.0", "mocha": "^10.0.0" @@ -551,6 +552,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -683,6 +693,24 @@ } ] }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -698,6 +726,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -848,6 +885,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1605,6 +1654,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -2165,6 +2223,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2552,6 +2619,15 @@ "node": ">=8" } }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2986,6 +3062,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -3566,6 +3651,12 @@ "es-abstract": "^1.19.0" } }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -3651,6 +3742,21 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz", "integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==" }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3660,6 +3766,12 @@ "supports-color": "^7.1.0" } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -3769,6 +3881,15 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4342,6 +4463,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -4728,6 +4855,15 @@ "is-unicode-supported": "^0.1.0" } }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -5004,6 +5140,12 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -5274,6 +5416,12 @@ "prelude-ls": "^1.2.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", diff --git a/package.json b/package.json index 29837104..28126be6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "pretest": "mkdir -p node_modules/ && ln -fs $(pwd) node_modules/", "eslint-check": "eslint-config-prettier .eslintrc.js", - "test": "npm run eslint-check && eslint . && mocha tests/" + "test": "mocha ./tests/**/*.js ./tests/*.js" }, "repository": { "type": "git", @@ -51,6 +51,7 @@ ], "devDependencies": { "@github/prettier-config": "0.0.4", + "chai": "^4.3.6", "eslint": "^8.0.1", "eslint-plugin-eslint-plugin": "^5.0.0", "mocha": "^10.0.0" diff --git a/tests/a11y-no-generic-link-text.js b/tests/a11y-no-generic-link-text.js index 7ef4a7b4..5a106737 100644 --- a/tests/a11y-no-generic-link-text.js +++ b/tests/a11y-no-generic-link-text.js @@ -19,9 +19,65 @@ ruleTester.run('a11y-no-generic-link-text', rule, { {code: "GitHub Home;"}, {code: "GitHub Home;"}, {code: "Read more;"}, - {code: "Read more;"} + {code: "Read more;"}, + {code: 'Read more;'}, + { + code: 'Read more', + settings: { + github: { + components: { + Link: { + props: { as: {undefined: 'a'} } + } + } + } + } + }, ], invalid: [ + { + code: 'Read more', + settings: { + github: { + components: { + ButtonLink: { + default: 'a' + } + } + } + }, + errors: [{message: errorMessage}] + }, + { + code: 'Read more', + settings: { + github: { + components: { + Link: { + props: { as: {undefined: 'a'} } + } + } + } + }, + errors: [{message: errorMessage}] + }, + { + code: 'Read more', + settings: { + github: { + components: { + Test: { + props: { as: {'a': 'a'} } + } + } + } + }, + errors: [{message: errorMessage}] + }, + { + code: "Click here;", + errors: [{message: errorMessage}] + }, {code: 'Click here*;', errors: [{message: errorMessage}]}, {code: 'Learn more.;', errors: [{message: errorMessage}]}, {code: ";", errors: [{message: errorMessage}]}, diff --git a/tests/utils/get-element-type.js b/tests/utils/get-element-type.js new file mode 100644 index 00000000..bbcdce8f --- /dev/null +++ b/tests/utils/get-element-type.js @@ -0,0 +1,110 @@ +const {getElementType} = require('../../lib/utils/get-element-type') +const expect = require('chai').expect + +function mockJSXAttribute(prop, propValue) { + return { + type: 'JSXAttribute', + name: { + type: 'JSXIdentifier', + name: prop + }, + value: { + type: 'Literal', + value: propValue + } + } +} + +function mockJSXOpeningElement(tagName, attributes = [], children = []) { + return { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: tagName + }, + attributes + } +} + +function mockSetting(componentSetting = {}) { + return { + settings: { + github: { + components: componentSetting + } + } + } +} + +describe('getElementType', function () { + it('gets element type', function () { + const node = mockJSXOpeningElement('a') + expect(getElementType({}, node)).to.equal('a') + }) + + it('gets element type from default', function () { + const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'summary')]) + const setting = mockSetting({ + Link: { + default: 'button' + } + }) + expect(getElementType(setting, node)).to.equal('button') + }) + + it('gets element type from matching props setting', function () { + const setting = mockSetting({ + Link: { + default: 'a', + props: { + as: {summary: 'summary'} + } + } + }) + const node_1 = mockJSXOpeningElement('Link') + expect(getElementType(setting, node_1)).to.equal('a') + + const node_2 = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'p')]) + expect(getElementType(setting, node_2)).to.equal('a') + + const node_3 = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'summary')]) + expect(getElementType(setting, node_3)).to.equal('summary') + }) + + it('uses original type if no default or matching prop setting', function () { + const setting = mockSetting({ + Link: { + props: { + as: {summary: 'summary'} + } + } + }) + const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'p')]) + expect(getElementType(setting, node)).to.equal('Link') + }) + + it('allows undefined prop to be mapped to a type', function () { + const setting = mockSetting({ + Link: { + props: { + as: {undefined: 'a'} + } + } + }) + const node = mockJSXOpeningElement('Link') + expect(getElementType(setting, node)).to.equal('a') + }) + + it('falls back to original type if no default and prop does not match props setting', function () { + const setting = mockSetting({ + Link: { + props: { + as: {undefined: 'a'} + } + } + }) + + const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'p')]) + expect(getElementType(setting, node)).to.equal('Link') + }) +}) From f0b18b53911821217de61f1d0ce06064ed91225b Mon Sep 17 00:00:00 2001 From: Kate Higa Date: Wed, 20 Jul 2022 12:04:19 -0700 Subject: [PATCH 2/5] all tests pass --- lib/rules/a11y-no-generic-link-text.js | 2 +- lib/utils/get-element-type.js | 4 ++-- package.json | 3 +-- tests/a11y-no-generic-link-text.js | 20 ++++++++++---------- tests/utils/get-element-type.js | 24 +++++++++++------------- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/lib/rules/a11y-no-generic-link-text.js b/lib/rules/a11y-no-generic-link-text.js index 49938e37..8c98d19d 100644 --- a/lib/rules/a11y-no-generic-link-text.js +++ b/lib/rules/a11y-no-generic-link-text.js @@ -1,5 +1,5 @@ const {getProp, getPropValue} = require('jsx-ast-utils') -const {getElementType} = require('../utils/get-element-type.js') +const {getElementType} = require('../utils/get-element-type') const bannedLinkText = ['read more', 'here', 'click here', 'learn more', 'more', 'here'] diff --git a/lib/utils/get-element-type.js b/lib/utils/get-element-type.js index ce7d5b94..0aaea8e9 100644 --- a/lib/utils/get-element-type.js +++ b/lib/utils/get-element-type.js @@ -15,8 +15,8 @@ function getElementType(context, node) { const props = Object.entries(component.props) for (const [key, value] of props) { const propMap = value - let propValue = getPropValue(getProp(node.attributes, key)) - let mapValue = propMap[propValue] + const propValue = getPropValue(getProp(node.attributes, key)) + const mapValue = propMap[propValue] if (mapValue) { element = mapValue diff --git a/package.json b/package.json index 28126be6..6381e42b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "pretest": "mkdir -p node_modules/ && ln -fs $(pwd) node_modules/", "eslint-check": "eslint-config-prettier .eslintrc.js", - "test": "mocha ./tests/**/*.js ./tests/*.js" + "test": "npm run eslint-check && eslint . && mocha tests/**/*.js tests/" }, "repository": { "type": "git", @@ -51,7 +51,6 @@ ], "devDependencies": { "@github/prettier-config": "0.0.4", - "chai": "^4.3.6", "eslint": "^8.0.1", "eslint-plugin-eslint-plugin": "^5.0.0", "mocha": "^10.0.0" diff --git a/tests/a11y-no-generic-link-text.js b/tests/a11y-no-generic-link-text.js index 5a106737..39eeda45 100644 --- a/tests/a11y-no-generic-link-text.js +++ b/tests/a11y-no-generic-link-text.js @@ -27,16 +27,17 @@ ruleTester.run('a11y-no-generic-link-text', rule, { github: { components: { Link: { - props: { as: {undefined: 'a'} } + props: {as: {undefined: 'a'}} } } } } - }, + } ], invalid: [ { code: 'Read more', + errors: [{message: errorMessage}], settings: { github: { components: { @@ -45,34 +46,33 @@ ruleTester.run('a11y-no-generic-link-text', rule, { } } } - }, - errors: [{message: errorMessage}] + } }, { code: 'Read more', + errors: [{message: errorMessage}], settings: { github: { components: { Link: { - props: { as: {undefined: 'a'} } + props: {as: {undefined: 'a'}} } } } - }, - errors: [{message: errorMessage}] + } }, { code: 'Read more', + errors: [{message: errorMessage}], settings: { github: { components: { Test: { - props: { as: {'a': 'a'} } + props: {as: {a: 'a'}} } } } - }, - errors: [{message: errorMessage}] + } }, { code: "Click here;", diff --git a/tests/utils/get-element-type.js b/tests/utils/get-element-type.js index bbcdce8f..47115550 100644 --- a/tests/utils/get-element-type.js +++ b/tests/utils/get-element-type.js @@ -1,4 +1,7 @@ const {getElementType} = require('../../lib/utils/get-element-type') +const mocha = require('mocha') +const describe = mocha.describe +const it = mocha.it const expect = require('chai').expect function mockJSXAttribute(prop, propValue) { @@ -15,7 +18,7 @@ function mockJSXAttribute(prop, propValue) { } } -function mockJSXOpeningElement(tagName, attributes = [], children = []) { +function mockJSXOpeningElement(tagName, attributes = []) { return { type: 'JSXOpeningElement', name: { @@ -37,12 +40,12 @@ function mockSetting(componentSetting = {}) { } describe('getElementType', function () { - it('gets element type', function () { + it('returns raw element type', function () { const node = mockJSXOpeningElement('a') expect(getElementType({}, node)).to.equal('a') }) - it('gets element type from default', function () { + it('returns element type from default if set', function () { const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'summary')]) const setting = mockSetting({ Link: { @@ -52,7 +55,7 @@ describe('getElementType', function () { expect(getElementType(setting, node)).to.equal('button') }) - it('gets element type from matching props setting', function () { + it('returns element type from matching props setting if set', function () { const setting = mockSetting({ Link: { default: 'a', @@ -61,17 +64,12 @@ describe('getElementType', function () { } } }) - const node_1 = mockJSXOpeningElement('Link') - expect(getElementType(setting, node_1)).to.equal('a') - const node_2 = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'p')]) - expect(getElementType(setting, node_2)).to.equal('a') - - const node_3 = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'summary')]) - expect(getElementType(setting, node_3)).to.equal('summary') + const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'summary')]) + expect(getElementType(setting, node)).to.equal('summary') }) - it('uses original type if no default or matching prop setting', function () { + it('returns raw type if no default or matching prop setting', function () { const setting = mockSetting({ Link: { props: { @@ -95,7 +93,7 @@ describe('getElementType', function () { expect(getElementType(setting, node)).to.equal('a') }) - it('falls back to original type if no default and prop does not match props setting', function () { + it('returns raw type if prop does not match props setting and no default type', function () { const setting = mockSetting({ Link: { props: { From da905a9813355bb8c2ecda9d5cd39c35655c390b Mon Sep 17 00:00:00 2001 From: Kate Higa Date: Wed, 20 Jul 2022 12:11:24 -0700 Subject: [PATCH 3/5] Add method description --- lib/utils/get-element-type.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/utils/get-element-type.js b/lib/utils/get-element-type.js index 0aaea8e9..1fd4b096 100644 --- a/lib/utils/get-element-type.js +++ b/lib/utils/get-element-type.js @@ -1,5 +1,12 @@ const {elementType, getProp, getPropValue} = require('jsx-ast-utils') +/* +Allows custom component to be mapped to an element type. +When a default is set, all instances of the component will be mapped to the default. +If a prop determines the type, it can be specified with `props`. + +For now, we only support the mapping of one prop type to an element type, rather than combinations of props. +*/ function getElementType(context, node) { const {settings} = context const rawElement = elementType(node) From bfbbfbe7243ab76e3d0e7864390df43d26110989 Mon Sep 17 00:00:00 2001 From: Kate Higa Date: Wed, 20 Jul 2022 12:15:13 -0700 Subject: [PATCH 4/5] Add chai for assertion --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 6381e42b..65564edc 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ ], "devDependencies": { "@github/prettier-config": "0.0.4", + "chai": "^4.3.6", "eslint": "^8.0.1", "eslint-plugin-eslint-plugin": "^5.0.0", "mocha": "^10.0.0" From 7e976bf41e64e18f73e90788e5b35c46ccb4f9ae Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Wed, 20 Jul 2022 13:39:21 -0700 Subject: [PATCH 5/5] Update lib/utils/get-element-type.js Co-authored-by: Ian Sanders --- lib/utils/get-element-type.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/get-element-type.js b/lib/utils/get-element-type.js index 1fd4b096..60ce6f68 100644 --- a/lib/utils/get-element-type.js +++ b/lib/utils/get-element-type.js @@ -12,7 +12,7 @@ function getElementType(context, node) { const rawElement = elementType(node) if (!settings) return rawElement - const componentMap = settings['github']?.components + const componentMap = settings.github && settings.github.components if (!componentMap) return rawElement const component = componentMap[rawElement] if (!component) return rawElement