Skip to content

Commit

Permalink
Rework everything for ESLint v9
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanblock committed Apr 18, 2024
1 parent 862451a commit 8844eb0
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 38 deletions.
6 changes: 5 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

---

## [3.0.0] 2024-03-26
## [3.0.0] 2024-04-18

### Added

- Added ESLint v9 support
- Added new rules:
- `block-spacing` - https://eslint.style/rules/default/block-spacing
- `comma-dangle` (multiline) - https://eslint.style/rules/default/comma-dangle
Expand All @@ -15,7 +16,10 @@

### Changed

- Breaking change: now using ESLint v9's flat config format
- Updated to ES2022 (v13)
- Breaking change: `eslint-plugin-import` is not yet ESLint v9 compatible, so that has been disabled
- This will be restored as soon as the plugin has been updated, see: https://github.com/import-js/eslint-plugin-import/issues/2948
- Breaking change: disabled `global-require` erroring
- Breaking change: removed support for Node.js 14.x (now EOL)

Expand Down
80 changes: 53 additions & 27 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
let off = 'off'
let err = 'error'
let s = '@stylistic/js/'
const eslint = require('@eslint/js')
const recommended = eslint.configs.recommended.rules
const stylistic = require('@stylistic/eslint-plugin-js')
const arc = require('./src/rules')
const fp = require('eslint-plugin-fp')
// TODO: re-enable eslint-plugin-import once eslint-plugin-import#2948 is fixed
// const importPlugin = require('eslint-plugin-import')

module.exports = {
parserOptions: {
ecmaVersion: 13, // 2022
},
// parserOptions default source type is `script`; override to `module` because eslint bailed on #12675
overrides: [ {
files: [ '*.mjs' ],
parserOptions: {
sourceType: 'module',
},
} ],
env: {
es6: true,
node: true,
const ecmaVersion = 13 // 2022
const ignores = [ './node_modules/' ]
const off = 'off'
const err = 'error'
const s = '@stylistic/js/'

const config = {
plugins: {
recommended,
arc,
'@stylistic/js': stylistic,
fp,
// importPlugin,
},
extends: 'eslint:recommended',
plugins: [
'@stylistic/js',
'filenames',
'fp',
'import',
],
rules: {
// Previously on `eslint:recommended`:
...recommended,

// Disable rules from base configurations
'arrow-body-style': off,
'no-console': off,
'no-inner-declarations': off,
'no-redeclare': off,
'no-useless-escape': off,

// Enable additional rules
'global-require': off,
'handle-callback-err': err,
[s + 'linebreak-style']: [ err, 'unix' ],
'no-cond-assign': [ err, 'always' ],

// Style
[s + 'array-bracket-spacing']: [ err, 'always' ],
[s + 'arrow-spacing']: err,
Expand All @@ -61,9 +62,34 @@ module.exports = {
[s + 'space-infix-ops']: err,
[s + 'spaced-comment']: err,
[s + 'template-curly-spacing']: err,
// Modules must resolve
'import/no-unresolved': [ err, { commonjs: true, amd: true } ],

// Ensure filesystem safe filenames
'filenames/match-regex': [ err, '^[a-z0-9-_.]+$', true ],
'arc/match-regex': [ err, '^[a-z0-9-_.]+$', true ],

// Modules must resolve
// 'import/no-unresolved': [ err, { commonjs: true, amd: true } ],
},
}


module.exports = [
// As of ESLint v9 flat config, *.js is assumed to be ESM, so undo that:
{
files: [ '**/*.js', '**/*.cjs' ],
ignores,
languageOptions: {
ecmaVersion,
sourceType: 'commonjs',
},
...config,
},
{
files: [ '**/*.mjs' ],
ignores,
languageOptions: {
ecmaVersion,
sourceType: 'module',
},
...config,
},
]
12 changes: 2 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "@architect/eslint-config",
"version": "3.0.0-RC.0",
"description": "Architect standard ESLint configuration",
"main": "index.js",
"scripts": {
"lint": "eslint . --fix",
"test": "npm run lint",
Expand All @@ -15,16 +14,9 @@
"homepage": "https://github.com/architect/eslint-config",
"bugs": "https://github.com/architect/architect/issues",
"dependencies": {
"@eslint/js": "^9.0.0",
"@stylistic/eslint-plugin-js": "^1.7.2",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-fp": "^2.3.0",
"eslint-plugin-import": "^2.29.1"
},
"peerDependencies": {
"@stylistic/eslint-plugin-js": "^1.7.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-fp": "^2.3.0",
"eslint-plugin-import": "^2.27.4"
"eslint-plugin-fp": "^2.3.0"
},
"devDependencies": {
"eslint": "^9.0.0"
Expand Down
40 changes: 40 additions & 0 deletions src/lib/get-exported-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Adapted with gratitude from eslint-plugin-filenames (https://github.com/selaux/eslint-plugin-filenames) and used under an MIT license

function getNodeName (node, options) {
const op = options || []

if (node.type === 'Identifier') {
return node.name
}

if (node.id && node.id.type === 'Identifier') {
return node.id.name
}

if (op[2] && node.type === 'CallExpression' && node.callee.type === 'Identifier') {
return node.callee.name
}
}

module.exports = function getExportedName (programNode, options) {
for (let i = 0; i < programNode.body.length; i += 1) {
const node = programNode.body[i]

// export default ...
if (node.type === 'ExportDefaultDeclaration') {
return getNodeName(node.declaration, options)
}

// module.exports = ...
if (node.type === 'ExpressionStatement' &&
node.expression.type === 'AssignmentExpression' &&
node.expression.left.type === 'MemberExpression' &&
node.expression.left.object.type === 'Identifier' &&
node.expression.left.object.name === 'module' &&
node.expression.left.property.type === 'Identifier' &&
node.expression.left.property.name === 'exports'
) {
return getNodeName(node.expression.right, options)
}
}
}
7 changes: 7 additions & 0 deletions src/lib/is-ignored-filename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Adapted with gratitude from eslint-plugin-filenames (https://github.com/selaux/eslint-plugin-filenames) and used under an MIT license

const ignoredFilenames = [ '<text>', '<input>' ]

module.exports = function isIgnoredFilename (filename) {
return ignoredFilenames.indexOf(filename) !== -1
}
14 changes: 14 additions & 0 deletions src/lib/parse-filename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Adapted with gratitude from eslint-plugin-filenames (https://github.com/selaux/eslint-plugin-filenames) and used under an MIT license

const path = require('path')

module.exports = function parseFilename (filename) {
const ext = path.extname(filename)

return {
dir: path.dirname(filename),
base: path.basename(filename),
ext: ext,
name: path.basename(filename, ext),
}
}
7 changes: 7 additions & 0 deletions src/rules/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Adapted with gratitude from eslint-plugin-filenames (https://github.com/selaux/eslint-plugin-filenames) and used under an MIT license

module.exports = {
rules: {
'match-regex': require('./match-regex'),
},
}
50 changes: 50 additions & 0 deletions src/rules/match-regex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Adapted with gratitude from eslint-plugin-filenames (https://github.com/selaux/eslint-plugin-filenames) and used under an MIT license

/**
* @fileoverview Rule to ensure that filenames match a convention (default: camelCase)
* @author Stefan Lau
*/

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

const path = require('path'),
parseFilename = require('../lib/parse-filename'),
getExportedName = require('../lib/get-exported-name'),
isIgnoredFilename = require('../lib/is-ignored-filename')

module.exports = {
meta: {
schema: {
type: 'array',
minItems: 0,
maxItems: 3,
},
},
create: function (context) {
const defaultRegexp = /^([a-z0-9]+)([A-Z][a-z0-9]+)*$/g,
conventionRegexp = context.options[0] ? new RegExp(context.options[0]) : defaultRegexp,
ignoreExporting = context.options[1] ? context.options[1] : false

return {
'Program': function (node) {
const filename = context.getFilename(),
absoluteFilename = path.resolve(filename),
parsed = parseFilename(absoluteFilename),
shouldIgnore = isIgnoredFilename(filename),
isExporting = Boolean(getExportedName(node)),
matchesRegex = conventionRegexp.test(parsed.name)

if (shouldIgnore) return
if (ignoreExporting && isExporting) return
if (!matchesRegex) {
context.report(node,
`Filename '{{name}}' does not match the naming convention.`,
{ name: parsed.base },
)
}
},
}
},
}

0 comments on commit 8844eb0

Please sign in to comment.