Skip to content

Instantly share code, notes, and snippets.

@donaldpipowitch
Last active March 10, 2019 08:03
Show Gist options
  • Save donaldpipowitch/826f3758180b4efe07676a4652903a15 to your computer and use it in GitHub Desktop.
Save donaldpipowitch/826f3758180b4efe07676a4652903a15 to your computer and use it in GitHub Desktop.
Use @ts-check for custom ESLint rules

This is a very basic example which shows how you can create a simple ESLint rule with @ts-check support. This example features the rule and a test. The rule checks, if you pass an absolute URL to a history.push function or not.

If you want to use this rule in your ESLint configuration without publishing the rule there is a caveat. AFAIK you can't simply include the path to your rule in your .eslintrc.js (correct me if I'm wrong). You need to pass the directory of this rule to the CLI as --rulesdir "./path/to/rules" and if you use VS Code with the ESLint extension you need to set "eslint.options": { "rulePaths": ["./path/to/rules"] }, in your settings.json as well. Only then you can add the rule to your config:

module.exports = {
  // ...yourCurrentConfig,
  rules: {
    // ...yourCurrentConfig.rules,
    'some-rule': 'error'
  }
};
{
"name": "eslint-rules",
"version": "1.0.0",
"private": true,
"scripts": {
"lint": "tsc"
},
"dependencies": {
"@typescript-eslint/parser": "^1.4.2",
"eslint": "^5.15.0",
"typescript": "^3.0.3"
},
"devDependencies": {
"@types/eslint": "^4.16.6"
}
}
// @ts-check
const message = 'Only absolute URLs are valid.';
/** @type {import("eslint").Rule.RuleModule} */
const rule = {
create(context) {
return {
// e.g. `history.push('./test')`
CallExpression(node) {
if (
node.type === 'CallExpression' &&
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'history' &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === 'push'
) {
const argument = node.arguments[0];
if (
argument &&
argument.type === 'Literal' &&
!argument.value.toString().startsWith('/')
) {
context.report({ node, message });
}
}
}
};
}
};
module.exports = rule;
// @ts-check
// run "$ node some-test.js" to test this
const rule = require('./some-rules');
const RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester({
// in case you need TypeScript and JSX support
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
});
ruleTester.run('Check for absolute URLs.', rule, {
valid: ['history.push("/test")'],
invalid: [
{
code: 'history.push("./test")',
errors: [{ type: 'CallExpression' }]
}
]
});
{
"compilerOptions": {
"allowJs": true,
"lib": ["es2015"],
"resolveJsonModule": true,
"noEmit": true,
"types": ["node"]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment