Skip to content

Commit 97908fc

Browse files
authored
Merge pull request #578 from github/filenames-fix
Update filenames
2 parents 491ca33 + b5369f3 commit 97908fc

18 files changed

+164
-17
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ This config will be interpreted in the following way:
120120
| [async-currenttarget](docs/rules/async-currenttarget.md) | disallow `event.currentTarget` calls inside of async functions | 🔍 | | |
121121
| [async-preventdefault](docs/rules/async-preventdefault.md) | disallow `event.preventDefault` calls inside of async functions | 🔍 | | |
122122
| [authenticity-token](docs/rules/authenticity-token.md) | disallow usage of CSRF tokens in JavaScript | 🔐 | | |
123+
| [filenames-match-regex](docs/rules/filenames-match-regex.md) | ensure filenames match a regex naming convention || | |
123124
| [get-attribute](docs/rules/get-attribute.md) | disallow wrong usage of attribute names | 🔍 | 🔧 | |
124125
| [js-class-name](docs/rules/js-class-name.md) | enforce a naming convention for js- prefixed classes | 🔐 | | |
125126
| [no-blur](docs/rules/no-blur.md) | disallow usage of `Element.prototype.blur()` | 🔍 | | |

docs/rules/filenames-match-regex.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Ensure filenames match a regex naming convention (`github/filenames-match-regex`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
## Rule Details
8+
9+
Rule to ensure that filenames match a convention, with a default of camelCase.
10+
11+
👎 Examples of **incorrect** filename for this default rule:
12+
13+
`file-name.js`
14+
15+
👍 Examples of **correct** code for this rule:
16+
17+
`fileName.js`
18+
19+
## Options
20+
21+
regex - Regex to match the filename structure. Defaults to camelCase.
22+
23+
24+
```json
25+
{
26+
"filenames-match-regex": [
27+
"error",
28+
"^[a-z0-9-]+(.[a-z0-9-]+)?$"
29+
]
30+
}
31+
```
32+
33+
## Version
34+
35+
4.3.2

eslint.config.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const globals = require('globals')
22
const eslintPlugin = require('eslint-plugin-eslint-plugin')
33
const importPlugin = require('eslint-plugin-import')
4-
const filenames = require('eslint-plugin-filenames')
54
const i18nTextPlugin = require('eslint-plugin-i18n-text')
65
const recommendedGitHub = require('./lib/configs/flat/recommended')
76
const {fixupPluginRules} = require('@eslint/compat')
@@ -23,13 +22,12 @@ module.exports = [
2322
plugins: {
2423
eslintPlugin,
2524
importPlugin,
26-
filenames,
2725
'i18n-text': fixupPluginRules(i18nTextPlugin),
2826
},
2927
rules: {
3028
'importPlugin/extensions': 'off',
3129
'importPlugin/no-commonjs': 'off',
32-
'filenamesPlugin/match-regex': 'off',
30+
'github/filenames-match-regex': 'off',
3331
'i18n-text/no-en': 'off',
3432
'eslint-plugin/prefer-placeholders': 'off',
3533
'eslint-plugin/test-case-shorthand-strings': 'off',

lib/configs/flat/browser.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const globals = require('globals')
22
const github = require('../../plugin')
33
const importPlugin = require('eslint-plugin-import')
44
const escompatPlugin = require('eslint-plugin-escompat')
5+
const {fixupPluginRules} = require('@eslint/compat')
56

67
module.exports = {
78
...escompatPlugin.configs['flat/recommended'],
@@ -10,7 +11,7 @@ module.exports = {
1011
...globals.browser,
1112
},
1213
},
13-
plugins: {importPlugin, escompatPlugin, github},
14+
plugins: {importPlugin, escompatPlugin, github: fixupPluginRules(github)},
1415
rules: {
1516
'escompatPlugin/no-dynamic-imports': 'off',
1617
'github/async-currenttarget': 'error',

lib/configs/flat/internal.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
const github = require('../../plugin')
2+
const {fixupPluginRules} = require('@eslint/compat')
23

34
module.exports = {
4-
plugins: {github},
5+
plugins: {github: fixupPluginRules(github)},
56
rules: {
67
'github/authenticity-token': 'error',
78
'github/js-class-name': 'error',

lib/configs/flat/react.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const github = require('../../plugin')
22
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y')
3+
const {fixupPluginRules} = require('@eslint/compat')
34

45
module.exports = {
56
...jsxA11yPlugin.flatConfigs.recommended,
@@ -11,7 +12,7 @@ module.exports = {
1112
},
1213
},
1314
},
14-
plugins: {github, jsxA11yPlugin},
15+
plugins: {github: fixupPluginRules(github), jsxA11yPlugin},
1516
rules: {
1617
'jsxA11yPlugin/role-supports-aria-props': 'off', // Override with github/a11y-role-supports-aria-props until https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/910 is resolved
1718
'github/a11y-aria-label-is-well-formatted': 'error',

lib/configs/flat/recommended.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const github = require('../../plugin')
33
const prettierPlugin = require('eslint-plugin-prettier')
44
const eslintComments = require('eslint-plugin-eslint-comments')
55
const importPlugin = require('eslint-plugin-import')
6-
const filenames = require('eslint-plugin-filenames')
76
const i18nTextPlugin = require('eslint-plugin-i18n-text')
87
const noOnlyTestsPlugin = require('eslint-plugin-no-only-tests')
98
const {fixupPluginRules} = require('@eslint/compat')
@@ -17,13 +16,12 @@ module.exports = {
1716
},
1817
},
1918
plugins: {
20-
filenamesPlugin: fixupPluginRules(filenames),
2119
prettierPlugin,
2220
eslintComments,
2321
importPlugin,
2422
'i18n-text': fixupPluginRules(i18nTextPlugin),
2523
noOnlyTestsPlugin,
26-
github,
24+
github: fixupPluginRules(github),
2725
},
2826
rules: {
2927
'constructor-super': 'error',
@@ -34,7 +32,7 @@ module.exports = {
3432
'eslintComments/no-unused-disable': 'error',
3533
'eslintComments/no-unused-enable': 'error',
3634
'eslintComments/no-use': ['error', {allow: ['eslint', 'eslint-disable-next-line', 'eslint-env', 'globals']}],
37-
'filenamesPlugin/match-regex': ['error', '^[a-z0-9-]+(.[a-z0-9-]+)?$'],
35+
'github/filenames-match-regex': ['error', '^[a-z0-9-]+(.[a-z0-9-]+)?$'],
3836
'func-style': ['error', 'declaration', {allowArrowFunctions: true}],
3937
'github/array-foreach': 'error',
4038
'github/no-implicit-buggy-globals': 'error',

lib/configs/flat/typescript.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ const eslint = require('@eslint/js')
22
const tseslint = require('typescript-eslint')
33
const escompatPlugin = require('eslint-plugin-escompat')
44
const github = require('../../plugin')
5+
const {fixupPluginRules} = require('@eslint/compat')
56

67
module.exports = tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended, {
78
languageOptions: {
89
parser: tseslint.parser,
910
},
10-
plugins: {'@typescript-eslint': tseslint.plugin, escompatPlugin, github},
11+
plugins: {'@typescript-eslint': tseslint.plugin, escompatPlugin, github: fixupPluginRules(github)},
1112
rules: {
1213
camelcase: 'off',
1314
'no-unused-vars': 'off',

lib/configs/recommended.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = {
88
env: {
99
es6: true,
1010
},
11-
plugins: ['github', 'prettier', 'eslint-comments', 'import', 'filenames', 'i18n-text', 'no-only-tests'],
11+
plugins: ['github', 'prettier', 'eslint-comments', 'import', 'i18n-text', 'no-only-tests'],
1212
rules: {
1313
'constructor-super': 'error',
1414
'eslint-comments/disable-enable-pair': 'off',
@@ -18,7 +18,7 @@ module.exports = {
1818
'eslint-comments/no-unused-disable': 'error',
1919
'eslint-comments/no-unused-enable': 'error',
2020
'eslint-comments/no-use': ['error', {allow: ['eslint', 'eslint-disable-next-line', 'eslint-env', 'globals']}],
21-
'filenames/match-regex': ['error', '^[a-z0-9-]+(.[a-z0-9-]+)?$'],
21+
'github/filenames-match-regex': ['error', '^[a-z0-9-]+(.[a-z0-9-]+)?$'],
2222
'func-style': ['error', 'declaration', {allowArrowFunctions: true}],
2323
'github/array-foreach': 'error',
2424
'github/no-implicit-buggy-globals': 'error',

lib/plugin.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = {
1313
'async-currenttarget': require('./rules/async-currenttarget'),
1414
'async-preventdefault': require('./rules/async-preventdefault'),
1515
'authenticity-token': require('./rules/authenticity-token'),
16+
'filenames-match-regex': require('./rules/filenames-match-regex'),
1617
'get-attribute': require('./rules/get-attribute'),
1718
'js-class-name': require('./rules/js-class-name'),
1819
'no-blur': require('./rules/no-blur'),

lib/rules/filenames-match-regex.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// This is adapted from https://github.com/selaux/eslint-plugin-filenames since it's no longer actively maintained
2+
// and needed a fix for eslint v9
3+
const path = require('path')
4+
const parseFilename = require('../utils/parse-filename')
5+
const getExportedName = require('../utils/get-exported-name')
6+
const isIgnoredFilename = require('../utils/is-ignored-filename')
7+
8+
module.exports = {
9+
meta: {
10+
type: 'problem',
11+
docs: {
12+
description: 'ensure filenames match a regex naming convention',
13+
url: require('../url')(module),
14+
},
15+
schema: {
16+
type: 'array',
17+
minItems: 0,
18+
maxItems: 1,
19+
items: [
20+
{
21+
type: 'string',
22+
},
23+
],
24+
},
25+
},
26+
27+
create(context) {
28+
const defaultRegexp = /^([a-z0-9]+)([A-Z][a-z0-9]+)*$/g
29+
const conventionRegexp = context.options[0] ? new RegExp(context.options[0]) : defaultRegexp
30+
const ignoreExporting = context.options[1] ? context.options[1] : false
31+
32+
return {
33+
Program(node) {
34+
const filename = context.getFilename()
35+
const absoluteFilename = path.resolve(filename)
36+
const parsed = parseFilename(absoluteFilename)
37+
const shouldIgnore = isIgnoredFilename(filename)
38+
const isExporting = Boolean(getExportedName(node))
39+
const matchesRegex = conventionRegexp.test(parsed.name)
40+
41+
if (shouldIgnore) return
42+
if (ignoreExporting && isExporting) return
43+
if (!matchesRegex) {
44+
context.report(node, "Filename '{{name}}' does not match the regex naming convention.", {
45+
name: parsed.base,
46+
})
47+
}
48+
},
49+
}
50+
},
51+
}

lib/utils/get-exported-name.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function getNodeName(node, options) {
2+
const op = options || []
3+
4+
if (node.type === 'Identifier') {
5+
return node.name
6+
}
7+
8+
if (node.id && node.id.type === 'Identifier') {
9+
return node.id.name
10+
}
11+
12+
if (op[2] && node.type === 'CallExpression' && node.callee.type === 'Identifier') {
13+
return node.callee.name
14+
}
15+
}
16+
17+
module.exports = function getExportedName(programNode, options) {
18+
for (let i = 0; i < programNode.body.length; i += 1) {
19+
const node = programNode.body[i]
20+
21+
if (node.type === 'ExportDefaultDeclaration') {
22+
return getNodeName(node.declaration, options)
23+
}
24+
25+
if (
26+
node.type === 'ExpressionStatement' &&
27+
node.expression.type === 'AssignmentExpression' &&
28+
node.expression.left.type === 'MemberExpression' &&
29+
node.expression.left.object.type === 'Identifier' &&
30+
node.expression.left.object.name === 'module' &&
31+
node.expression.left.property.type === 'Identifier' &&
32+
node.expression.left.property.name === 'exports'
33+
) {
34+
return getNodeName(node.expression.right, options)
35+
}
36+
}
37+
}

lib/utils/is-ignored-filename.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const ignoredFilenames = ['<text>', '<input>']
2+
3+
module.exports = function isIgnoredFilename(filename) {
4+
return ignoredFilenames.indexOf(filename) !== -1
5+
}

lib/utils/parse-filename.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const path = require('path')
2+
3+
module.exports = function parseFilename(filename) {
4+
const ext = path.extname(filename)
5+
6+
return {
7+
dir: path.dirname(filename),
8+
base: path.basename(filename),
9+
ext,
10+
name: path.basename(filename, ext),
11+
}
12+
}

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
"eslint-config-prettier": ">=8.0.0",
4040
"eslint-plugin-escompat": "^3.11.3",
4141
"eslint-plugin-eslint-comments": "^3.2.0",
42-
"eslint-plugin-filenames": "^1.3.2",
4342
"eslint-plugin-i18n-text": "^1.0.1",
4443
"eslint-plugin-import": "^2.25.2",
4544
"eslint-plugin-jsx-a11y": "^6.7.1",

test-examples/flat/eslint.config.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import github from 'eslint-plugin-github'
22

33
export default [
4-
github.getFlatConfigs().browser,
54
github.getFlatConfigs().recommended,
5+
github.getFlatConfigs().browser,
66
github.getFlatConfigs().react,
77
...github.getFlatConfigs().typescript,
88
{
99
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
1010
ignores: ['eslint.config.mjs'],
1111
rules: {
1212
'github/array-foreach': 'error',
13-
'github/async-preventdefault': 'warn',
1413
'github/no-then': 'error',
1514
'github/no-blur': 'error',
15+
'github/async-preventdefault': 'error',
1616
},
1717
},
1818
]

test-examples/flat/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"@eslint/js": "^9.5.0",
1010
"@types/node": "^20.14.5",
1111
"cross-env": "^7.0.3",
12-
"eslint": "^8.57.0",
12+
"eslint": "^9.14.0",
1313
"eslint-plugin-github": "file:../..",
1414
"typescript": "^5.6.3",
1515
"typescript-eslint": "^8.12.2"

test-examples/flat/src/getAttribute.js

+6
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ const title = document.createElement('h1')
66
title.textContent = `${title}!`
77

88
foobar(title)
9+
10+
document.addEventListener('click', async function (event) {
11+
const data = await fetch()
12+
13+
event.preventDefault()
14+
})

0 commit comments

Comments
 (0)