-
Notifications
You must be signed in to change notification settings - Fork 150
Move rules settings to ESLint shared config: part 1 - utils detection #237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9c2cce9
c30a623
644d1c4
647ffc4
620e200
401ab97
71af089
15ae867
f35e95f
0ec9949
a229e44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package-lock=false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { ESLintUtils, TSESLint } from '@typescript-eslint/experimental-utils'; | ||
import { getDocsUrl } from './utils'; | ||
import { | ||
detectTestingLibraryUtils, | ||
DetectionHelpers, | ||
} from './detect-testing-library-utils'; | ||
|
||
// These 2 types are copied from @typescript-eslint/experimental-utils | ||
type CreateRuleMetaDocs = Omit<TSESLint.RuleMetaDataDocs, 'url'>; | ||
type CreateRuleMeta<TMessageIds extends string> = { | ||
docs: CreateRuleMetaDocs; | ||
} & Omit<TSESLint.RuleMetaData<TMessageIds>, 'docs'>; | ||
|
||
export function createTestingLibraryRule< | ||
TOptions extends readonly unknown[], | ||
TMessageIds extends string, | ||
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener | ||
>( | ||
config: Readonly<{ | ||
name: string; | ||
meta: CreateRuleMeta<TMessageIds>; | ||
defaultOptions: Readonly<TOptions>; | ||
create: ( | ||
context: Readonly<TSESLint.RuleContext<TMessageIds, TOptions>>, | ||
optionsWithDefault: Readonly<TOptions>, | ||
detectionHelpers: Readonly<DetectionHelpers> | ||
) => TRuleListener; | ||
}> | ||
): TSESLint.RuleModule<TMessageIds, TOptions, TRuleListener> { | ||
const { create, ...remainingConfig } = config; | ||
|
||
return ESLintUtils.RuleCreator(getDocsUrl)({ | ||
...remainingConfig, | ||
create: detectTestingLibraryUtils<TOptions, TMessageIds, TRuleListener>( | ||
create | ||
), | ||
Comment on lines
+34
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where all the magic happens now. It looks so simple after all the time I spent on it that I feel kinda stupid. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks great! Great job! |
||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; | ||
|
||
export type DetectionHelpers = { | ||
getIsImportingTestingLibrary: () => boolean; | ||
}; | ||
|
||
/** | ||
* Enhances a given rule `create` with helpers to detect Testing Library utils. | ||
*/ | ||
export function detectTestingLibraryUtils< | ||
TOptions extends readonly unknown[], | ||
TMessageIds extends string, | ||
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener | ||
>( | ||
ruleCreate: ( | ||
context: Readonly<TSESLint.RuleContext<TMessageIds, TOptions>>, | ||
optionsWithDefault: Readonly<TOptions>, | ||
detectionHelpers: Readonly<DetectionHelpers> | ||
) => TRuleListener | ||
) { | ||
return ( | ||
context: Readonly<TSESLint.RuleContext<TMessageIds, TOptions>>, | ||
optionsWithDefault: Readonly<TOptions> | ||
): TRuleListener => { | ||
let isImportingTestingLibrary = false; | ||
|
||
// TODO: init here options based on shared ESLint config | ||
timdeschryver marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// helpers for Testing Library detection | ||
const helpers: DetectionHelpers = { | ||
getIsImportingTestingLibrary() { | ||
return isImportingTestingLibrary; | ||
}, | ||
}; | ||
|
||
// instructions for Testing Library detection | ||
const detectionInstructions: TSESLint.RuleListener = { | ||
ImportDeclaration(node: TSESTree.ImportDeclaration) { | ||
isImportingTestingLibrary = /testing-library/g.test( | ||
node.source.value as string | ||
); | ||
}, | ||
}; | ||
|
||
// update given rule to inject Testing Library detection | ||
const ruleInstructions = ruleCreate(context, optionsWithDefault, helpers); | ||
const enhancedRuleInstructions = Object.assign({}, ruleInstructions); | ||
|
||
Object.keys(detectionInstructions).forEach((instruction) => { | ||
(enhancedRuleInstructions as TSESLint.RuleListener)[instruction] = ( | ||
node | ||
) => { | ||
if (instruction in detectionInstructions) { | ||
detectionInstructions[instruction](node); | ||
} | ||
|
||
if (ruleInstructions[instruction]) { | ||
return ruleInstructions[instruction](node); | ||
} | ||
}; | ||
}); | ||
|
||
return enhancedRuleInstructions; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils'; | ||
import { getDocsUrl, ALL_RETURNING_NODES } from '../utils'; | ||
import { TSESTree } from '@typescript-eslint/experimental-utils'; | ||
import { ALL_RETURNING_NODES } from '../utils'; | ||
import { isIdentifier } from '../node-utils'; | ||
import { createTestingLibraryRule } from '../create-testing-library-rule'; | ||
|
||
export const RULE_NAME = 'no-node-access'; | ||
export type MessageIds = 'noNodeAccess'; | ||
type Options = []; | ||
|
||
export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({ | ||
export default createTestingLibraryRule<Options, MessageIds>({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just creating rules like this everything is hooked and helpers are passed! |
||
name: RULE_NAME, | ||
meta: { | ||
type: 'problem', | ||
|
@@ -24,19 +25,11 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({ | |
}, | ||
defaultOptions: [], | ||
|
||
create(context) { | ||
let isImportingTestingLibrary = false; | ||
|
||
function checkTestingEnvironment(node: TSESTree.ImportDeclaration) { | ||
isImportingTestingLibrary = /testing-library/g.test( | ||
node.source.value as string | ||
); | ||
} | ||
|
||
create: (context, _, helpers) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This gets automatic types as I wanted originally 💯 |
||
function showErrorForNodeAccess(node: TSESTree.MemberExpression) { | ||
isIdentifier(node.property) && | ||
ALL_RETURNING_NODES.includes(node.property.name) && | ||
isImportingTestingLibrary && | ||
helpers.getIsImportingTestingLibrary() && | ||
context.report({ | ||
node: node, | ||
loc: node.property.loc.start, | ||
|
@@ -45,7 +38,6 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({ | |
} | ||
|
||
return { | ||
['ImportDeclaration']: checkTestingEnvironment, | ||
['ExpressionStatement MemberExpression']: showErrorForNodeAccess, | ||
['VariableDeclarator MemberExpression']: showErrorForNodeAccess, | ||
}; | ||
|
Uh oh!
There was an error while loading. Please reload this page.