eslint-plugin-baseui
Advanced tools
Comparing version 9.56.0 to 9.57.0
@@ -24,18 +24,20 @@ /* | ||
valid: [ | ||
// Accordion - renderPanelContent | ||
// Miss when Accordion is renamed | ||
// Spinner | ||
// Miss when StyledSpinnerNext is renamed | ||
{ | ||
code: ` | ||
import {Accordion as Acc} from "baseui/accordion"; | ||
const Foo = () => <Acc renderPanelContent />; | ||
import {StyledSpinnerNext as Spinner} from 'baseui/spinner'; | ||
export default function() { | ||
return <Spinner />; | ||
} | ||
`, | ||
}, | ||
// Checkbox - checkmarkType | ||
// Miss when STYLE_TYPE is renamed | ||
// Spinner | ||
// Miss when Spinner is not imported from baseui (imported elsewhere) | ||
{ | ||
code: ` | ||
import { Checkbox, STYLE_TYPE as STYLES } from "baseui/checkbox" | ||
import {Spinner} from './spinner.js'; | ||
export default function() { | ||
return <Checkbox checkmarkType={STYLES.toggle} /> | ||
return <Spinner />; | ||
} | ||
@@ -45,9 +47,11 @@ `, | ||
// Modal - Backdrop | ||
// Miss when Modal is renamed | ||
// Spinner | ||
// Miss when Spinner is not imported from baseui (created locally) | ||
{ | ||
code: ` | ||
import { Modal as Foo } from "baseui/modal" | ||
function Spinner() { | ||
return <span aria-label="loading" />; | ||
} | ||
export default function() { | ||
return <Foo overrides={{ Backdrop: {} }} /> | ||
return <Spinner />; | ||
} | ||
@@ -57,2 +61,35 @@ `, | ||
// Caption1 | ||
// Miss when Caption1 is not imported from baseui | ||
{ | ||
code: ` | ||
import {Caption1} from './typography.js'; | ||
export default function() { | ||
return <Caption1>Hello</Caption1>; | ||
} | ||
`, | ||
}, | ||
// Caption2 | ||
// Miss when Caption2 happens to be a rename from baseui | ||
{ | ||
code: ` | ||
import {LabelXSmall as Caption2} from 'baseui/typography'; | ||
export default function() { | ||
return <Caption2>Hello</Caption2>; | ||
} | ||
`, | ||
}, | ||
// Checkbox - checkmarkType | ||
// Miss when STYLE_TYPE is renamed | ||
{ | ||
code: ` | ||
import { Checkbox, STYLE_TYPE as STYLES } from "baseui/checkbox" | ||
export default function() { | ||
return <Checkbox checkmarkType={STYLES.toggle} /> | ||
} | ||
`, | ||
}, | ||
// Modal - Backdrop | ||
@@ -74,2 +111,3 @@ // Miss when overrides prop value is not an object expression | ||
code: ` | ||
import {Accordion} from "baseui/accordion"; | ||
const Foo = () => <Accordion renderPanelContent />; | ||
@@ -79,2 +117,3 @@ `, | ||
output: ` | ||
import {Accordion} from "baseui/accordion"; | ||
const Foo = () => <Accordion renderAll />; | ||
@@ -84,2 +123,16 @@ `, | ||
// Accordion - renderPanelContent | ||
// Accordion is renamed | ||
{ | ||
code: ` | ||
import {Accordion as Acc} from "baseui/accordion"; | ||
const Foo = () => <Acc renderPanelContent />; | ||
`, | ||
errors: [{messageId: MESSAGES.replace.id}], | ||
output: ` | ||
import {Accordion as Acc} from "baseui/accordion"; | ||
const Foo = () => <Acc renderAll />; | ||
`, | ||
}, | ||
// Checkbox - toggle checkmarkType | ||
@@ -146,9 +199,17 @@ // Prop as imported constant | ||
`, | ||
errors: [ | ||
{ | ||
message: `"Backdrop" has been deprecated as an override property. In v10 of baseui, "Backdrop" will be removed in favor of "DialogContainer".`, | ||
}, | ||
], | ||
errors: [{messageId: MESSAGES.modalBackdrop.id}], | ||
}, | ||
// Modal - Backdrop | ||
// Modal is renamed | ||
{ | ||
code: ` | ||
import { Modal as Foo } from "baseui/modal" | ||
export default function() { | ||
return <Foo overrides={{ Backdrop: {} }} /> | ||
} | ||
`, | ||
errors: [{messageId: MESSAGES.modalBackdrop.id}], | ||
}, | ||
// Spinner | ||
@@ -162,9 +223,16 @@ { | ||
`, | ||
errors: [ | ||
{ | ||
message: `The "Spinner" component has been deprecated in favor of "StyledSpinnerNext". In v10 of baseui, "Spinner" will be removed and "StyledSpinnerNext" will be renamed to "Spinner".`, | ||
}, | ||
], | ||
errors: [{messageId: MESSAGES.deprecateSpinner.id}], | ||
}, | ||
// Spinner renamed | ||
{ | ||
code: ` | ||
import { Spinner as BaseSpinner } from "baseui/spinner" | ||
export default function() { | ||
return <BaseSpinner /> | ||
} | ||
`, | ||
errors: [{messageId: MESSAGES.deprecateSpinner.id}], | ||
}, | ||
// Button - minimal kind | ||
@@ -183,7 +251,3 @@ // Prop as imported constant | ||
`, | ||
errors: [ | ||
{ | ||
message: `The "KIND.minimal" option for the Button "kind" prop is deprecated in favor of "KIND.tertiary". In v10 of baseui, "KIND.minimal" will be removed.`, | ||
}, | ||
], | ||
errors: [{messageId: MESSAGES.buttonKindMinimal.id}], | ||
}, | ||
@@ -195,3 +259,3 @@ | ||
code: ` | ||
import { Button, KIND } from "baseui/button" | ||
import { Button } from "baseui/button" | ||
export default () => { | ||
@@ -205,9 +269,21 @@ return ( | ||
`, | ||
errors: [ | ||
{ | ||
message: `The "minimal" option for the Button "kind" prop is deprecated in favor of "tertiary". In v10 of baseui, "minimal" will be removed.`, | ||
}, | ||
], | ||
errors: [{messageId: MESSAGES.buttonKindMinimal.id}], | ||
}, | ||
// Button - minimal kind | ||
// Button is aliased | ||
{ | ||
code: ` | ||
import { Button as BaseButton } from "baseui/button" | ||
export default () => { | ||
return ( | ||
<BaseButton kind="minimal"> | ||
Hello | ||
</BaseButton> | ||
) | ||
} | ||
`, | ||
errors: [{messageId: MESSAGES.buttonKindMinimal.id}], | ||
}, | ||
// Caption1 | ||
@@ -221,3 +297,6 @@ { | ||
`, | ||
errors: [{messageId: MESSAGES.replace.id}], | ||
errors: [ | ||
{messageId: MESSAGES.replace.id}, | ||
{messageId: MESSAGES.replace.id}, | ||
], | ||
output: ` | ||
@@ -231,2 +310,19 @@ import { ParagraphXSmall } from "baseui/typography" | ||
// Caption1, renamed | ||
{ | ||
code: ` | ||
import { Caption1 as Foo } from "baseui/typography" | ||
export default () => { | ||
return <Foo>Hello</Foo> | ||
} | ||
`, | ||
errors: [{messageId: MESSAGES.replace.id}], | ||
output: ` | ||
import { ParagraphXSmall as Foo } from "baseui/typography" | ||
export default () => { | ||
return <Foo>Hello</Foo> | ||
} | ||
`, | ||
}, | ||
// Caption2 | ||
@@ -240,3 +336,6 @@ { | ||
`, | ||
errors: [{messageId: MESSAGES.replace.id}], | ||
errors: [ | ||
{messageId: MESSAGES.replace.id}, | ||
{messageId: MESSAGES.replace.id}, | ||
], | ||
output: ` | ||
@@ -250,2 +349,19 @@ import { LabelXSmall } from "baseui/typography" | ||
// Caption2, renamed | ||
{ | ||
code: ` | ||
import { Caption2 as Foo } from "baseui/typography" | ||
export default () => { | ||
return <Foo>Hello</Foo> | ||
} | ||
`, | ||
errors: [{messageId: MESSAGES.replace.id}], | ||
output: ` | ||
import { LabelXSmall as Foo } from "baseui/typography" | ||
export default () => { | ||
return <Foo>Hello</Foo> | ||
} | ||
`, | ||
}, | ||
// Block - $style | ||
@@ -259,7 +375,3 @@ { | ||
`, | ||
errors: [ | ||
{ | ||
message: `The "$style" prop is not supported on the "Block" component. Please use "overrides.Block" to pass styles down to the root element.`, | ||
}, | ||
], | ||
errors: [{messageId: MESSAGES.styleOnBlock.id}], | ||
}, | ||
@@ -275,7 +387,3 @@ | ||
`, | ||
errors: [ | ||
{ | ||
message: `The "style" prop is not supported on the "Block" component. Please use "overrides.Block" to pass styles down to the root element.`, | ||
}, | ||
], | ||
errors: [{messageId: MESSAGES.styleOnBlock.id}], | ||
}, | ||
@@ -282,0 +390,0 @@ ], |
{ | ||
"name": "eslint-plugin-baseui", | ||
"version": "9.56.0", | ||
"version": "9.57.0", | ||
"description": "ESLint rules for Base Web", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -17,6 +17,82 @@ /* | ||
[MESSAGES.replace.id]: MESSAGES.replace.message, | ||
[MESSAGES.deprecateSpinner.id]: MESSAGES.deprecateSpinner.message, | ||
[MESSAGES.styleOnBlock.id]: MESSAGES.styleOnBlock.message, | ||
[MESSAGES.buttonKindMinimal.id]: MESSAGES.buttonKindMinimal.message, | ||
[MESSAGES.modalBackdrop.id]: MESSAGES.modalBackdrop.message, | ||
}, | ||
}, | ||
create(context) { | ||
let importState = {}; | ||
return { | ||
ImportSpecifier(node) { | ||
function isImporting(importName, importPath) { | ||
if ( | ||
node.imported.name === importName && | ||
node.parent.source.value === importPath | ||
) { | ||
importState[importName] = node.local.name; | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
// Spinner | ||
// Ex: import {Spinner} from "baseui/spinner"; | ||
// Note, we are not replacing Spinner because the new API | ||
// is not compatible. | ||
if (isImporting('Spinner', 'baseui/spinner')) { | ||
context.report({ | ||
node: node.imported, | ||
messageId: MESSAGES.deprecateSpinner.id, | ||
}); | ||
return; | ||
} | ||
// For Caption1 and Caption2, we want to potentially replace instances | ||
// of the component. We need to consider imports as well as instances | ||
// so that if people use the autofix flag, they don't end up with a | ||
// weird half-way fix. If we find a valid import here, we capture in | ||
// `importState` what the `new` value to use when we rename instances | ||
// later on. One consequence of this approach is that you have to fix | ||
// the import and instance separately if resolving lint warnings | ||
// manually. | ||
if (isImporting('Caption1', 'baseui/typography')) { | ||
context.report({ | ||
node: node.imported, | ||
messageId: MESSAGES.replace.id, | ||
data: { | ||
old: 'Caption1', | ||
new: 'ParagraphXSmall', | ||
}, | ||
fix: function(fixer) { | ||
return [fixer.replaceText(node.imported, 'ParagraphXSmall')]; | ||
}, | ||
}); | ||
return; | ||
} | ||
if (isImporting('Caption2', 'baseui/typography')) { | ||
context.report({ | ||
node: node.imported, | ||
messageId: MESSAGES.replace.id, | ||
data: { | ||
old: 'Caption2', | ||
new: 'LabelXSmall', | ||
}, | ||
fix: function(fixer) { | ||
return [fixer.replaceText(node.imported, 'LabelXSmall')]; | ||
}, | ||
}); | ||
return; | ||
} | ||
// These can be referenced later on by instances of components. | ||
if (isImporting('Accordion', 'baseui/accordion')) return; | ||
if (isImporting('Modal', 'baseui/modal')) return; | ||
if (isImporting('Block', 'baseui/block')) return; | ||
if (isImporting('Checkbox', 'baseui/checkbox')) return; | ||
if (isImporting('Button', 'baseui/button')) return; | ||
}, | ||
JSXIdentifier(node) { | ||
@@ -28,4 +104,5 @@ // ======= | ||
// isProp | ||
// Check if identifier is a prop with matching "name" and is used with "component". | ||
// Ex: prop("foo", "Boo") with <Boo foo={} /> => true | ||
// Check if identifier is a prop with matching "name" and is used with | ||
// "component". | ||
// Ex: isProp("foo", "Boo") with <Boo foo={} /> => true | ||
function isProp(name, component) { | ||
@@ -46,3 +123,3 @@ return ( | ||
// Check if identifier is a component matching "name". | ||
// Ex: prop("foo", "Boo") with <Boo foo={} /> => true | ||
// Ex: isComponent("Boo") with <Boo foo={} /> => true | ||
function isComponent(name) { | ||
@@ -59,3 +136,6 @@ return node.name === name && node.parent.type === 'JSXOpeningElement'; | ||
// Replacement: renderAll | ||
if (isProp('renderPanelContent', 'Accordion')) { | ||
if ( | ||
importState.Accordion && | ||
isProp('renderPanelContent', importState.Accordion) | ||
) { | ||
context.report({ | ||
@@ -78,3 +158,3 @@ node, | ||
// Replacement: autoFocus | ||
if (isProp('autofocus', 'Modal')) { | ||
if (importState.Modal && isProp('autofocus', importState.Modal)) { | ||
context.report({ | ||
@@ -101,10 +181,10 @@ node: node, | ||
// as expected. | ||
const isProp_$style = isProp('$style', 'Block'); | ||
const isProp_style = isProp('style', 'Block'); | ||
if (isProp_$style || isProp_style) { | ||
if ( | ||
importState.Block && | ||
(isProp('$style', importState.Block) || | ||
isProp('style', importState.Block)) | ||
) { | ||
context.report({ | ||
node, | ||
message: `The "${ | ||
isProp_$style ? '$style' : 'style' | ||
}" prop is not supported on the "Block" component. Please use "overrides.Block" to pass styles down to the root element.`, | ||
messageId: MESSAGES.styleOnBlock.id, | ||
}); | ||
@@ -121,3 +201,6 @@ } | ||
// Replacement: toggle_round | ||
if (isProp('checkmarkType', 'Checkbox')) { | ||
if ( | ||
importState.Checkbox && | ||
isProp('checkmarkType', importState.Checkbox) | ||
) { | ||
// The value can be a constant or a string literal. | ||
@@ -170,3 +253,3 @@ // We need to handle each a little differently. | ||
// Replacement: tertiary | ||
if (isProp('kind', 'Button')) { | ||
if (importState.Button && isProp('kind', importState.Button)) { | ||
// The value can be a constant or a string literal. | ||
@@ -181,3 +264,3 @@ // We need to handle each a little differently. | ||
node: node.parent.value, | ||
message: `The "minimal" option for the Button "kind" prop is deprecated in favor of "tertiary". In v10 of baseui, "minimal" will be removed.`, | ||
messageId: MESSAGES.buttonKindMinimal.id, | ||
}); | ||
@@ -194,3 +277,3 @@ return; | ||
node: node.parent.value.expression.property, | ||
message: `The "KIND.minimal" option for the Button "kind" prop is deprecated in favor of "KIND.tertiary". In v10 of baseui, "KIND.minimal" will be removed.`, | ||
messageId: MESSAGES.buttonKindMinimal.id, | ||
}); | ||
@@ -208,3 +291,3 @@ return; | ||
// Replacement: DialogContainer | ||
if (isProp('overrides', 'Modal')) { | ||
if (importState.Modal && isProp('overrides', importState.Modal)) { | ||
// Verify that an object is passed to overrides. | ||
@@ -219,3 +302,3 @@ if (node.parent.value.expression.type === 'ObjectExpression') { | ||
node: property, | ||
message: `"Backdrop" has been deprecated as an override property. In v10 of baseui, "Backdrop" will be removed in favor of "DialogContainer".`, | ||
messageId: MESSAGES.modalBackdrop.id, | ||
}); | ||
@@ -231,14 +314,4 @@ return; | ||
// Spinner | ||
// Ex: <Spinner /> | ||
// Replacement: SpinnerNext | ||
// Note, we are not replacing Spinner because the APIs are slightly different. | ||
// We can't swap the components in a way 100% safe way. | ||
if (isComponent('Spinner')) { | ||
context.report({ | ||
node: node, | ||
message: `The "Spinner" component has been deprecated in favor of "StyledSpinnerNext". In v10 of baseui, "Spinner" will be removed and "StyledSpinnerNext" will be renamed to "Spinner".`, | ||
}); | ||
return; | ||
} | ||
// See @ImportSpecifier function for how this importState.Caption1 | ||
// stuff works. | ||
@@ -248,18 +321,5 @@ // Caption1 | ||
// Replacement: ParagraphXSmall | ||
if (isComponent('Caption1')) { | ||
// Find import of Caption1 | ||
const program = context | ||
.getAncestors(node) | ||
.find(node => node.type === 'Program'); | ||
const importDeclaration = program.body.find( | ||
node => | ||
node.type === 'ImportDeclaration' && | ||
node.source.value === 'baseui/typography', | ||
); | ||
const importSpecifier = importDeclaration.specifiers.find( | ||
specifier => specifier.imported.name === 'Caption1', | ||
); | ||
if (importState.Caption1 && isComponent('Caption1')) { | ||
context.report({ | ||
node: node, | ||
node, | ||
messageId: MESSAGES.replace.id, | ||
@@ -272,3 +332,2 @@ data: { | ||
return [ | ||
fixer.replaceText(importSpecifier.imported, 'ParagraphXSmall'), | ||
fixer.replaceText(node, 'ParagraphXSmall'), | ||
@@ -288,18 +347,5 @@ fixer.replaceText( | ||
// Replacement: LabelXSmall | ||
if (isComponent('Caption2')) { | ||
// Find import of Caption2 | ||
const program = context | ||
.getAncestors(node) | ||
.find(node => node.type === 'Program'); | ||
const importDeclaration = program.body.find( | ||
node => | ||
node.type === 'ImportDeclaration' && | ||
node.source.value === 'baseui/typography', | ||
); | ||
const importSpecifier = importDeclaration.specifiers.find( | ||
specifier => specifier.imported.name === 'Caption2', | ||
); | ||
if (importState.Caption2 && isComponent('Caption2')) { | ||
context.report({ | ||
node: node, | ||
node, | ||
messageId: MESSAGES.replace.id, | ||
@@ -312,3 +358,2 @@ data: { | ||
return [ | ||
fixer.replaceText(importSpecifier.imported, 'LabelXSmall'), | ||
fixer.replaceText(node, 'LabelXSmall'), | ||
@@ -315,0 +360,0 @@ fixer.replaceText( |
@@ -23,2 +23,18 @@ /* | ||
}, | ||
deprecateSpinner: { | ||
id: 'deprecateSpinner', | ||
message: `The "Spinner" component has been deprecated in favor of "StyledSpinnerNext". In v10 of baseui, "Spinner" will be removed and "StyledSpinnerNext" will be renamed to "Spinner".`, | ||
}, | ||
styleOnBlock: { | ||
id: 'styleOnBlock', | ||
message: `"$style" and "style" are not supported props for the "Block" component. Please use "overrides.Block" to pass styles down to the root element.`, | ||
}, | ||
buttonKindMinimal: { | ||
id: 'buttonKindMinimal', | ||
message: `The "minimal" option for the Button "kind" prop is deprecated in favor of "tertiary". In v10 of baseui, "minimal" will be removed.`, | ||
}, | ||
modalBackdrop: { | ||
id: 'modalBackdrop', | ||
message: `"Backdrop" has been deprecated as an override property. In v10 of baseui, "Backdrop" will be removed in favor of "DialogContainer".`, | ||
}, | ||
}; |
68820
2294