Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Handlebars partials are not supported yet
# @see https://github.com/prettier/prettier/issues/11834
*.hbs

# Test fixtures are intentionally invalid/non-standard and shouldn't be reformatted
**/__tests__/__fixtures__/**
3 changes: 3 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import optionalBaseConfig from './packages/eslint-config-base/optional.js';
import jestConfig from './packages/eslint-config-jest/index.js';

export default [
{
ignores: ['**/__tests__/__fixtures__/**'],
},
...baseConfig,
...optionalBaseConfig,
...jestConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var mask = 1 & 2;

if (mask == 1) mask = 0;

for (const key in { a: 1 }) {
mask += key.length;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var sum = function sum (a, b) {

'use strict';

return a + b;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here should not be the empty line

};

sum(1, 2);
28 changes: 28 additions & 0 deletions packages/eslint-config-base/__tests__/__fixtures__/valid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export function sum (a, b) {

return a + b;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here should not be an empty line

}

export class Counter {

constructor (start) {

this.value = start;

}

increment () {

this.value += 1;

return this.value;

}

}

const numbers = [1, 2, 3];
const doubled = numbers.map((number) => number * 2);

export default doubled;
115 changes: 115 additions & 0 deletions packages/eslint-config-base/__tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { describe, it } from 'node:test';
import { ESLint } from 'eslint';
import baseConfig from '../index.js';
import legacyConfig from '../legacy.js';
import optionalConfig from '../optional.js';
import whitespaceConfig from '../whitespace.js';

const readFixture = (name) => fs.readFileSync(`./__tests__/__fixtures__/${name}`, 'utf-8');

const lint = async (config, code) => {
const eslint = new ESLint({
overrideConfigFile: true,
overrideConfig: config,
});

const [result] = await eslint.lintText(code, { filePath: 'fixture.js' });

return result;
};

describe('@alma-oss/eslint-config-base', () => {
describe('flags no problems with valid code', () => {
it('has no errors or warnings', async () => {
const result = await lint(baseConfig, readFixture('valid.js'));

assert.equal(result.errorCount, 0);
assert.equal(result.warningCount, 0);
});
});

describe('flags problems with invalid code', () => {
it('flags airbnb rules inherited as-is', async () => {
const result = await lint(baseConfig, readFixture('invalid.js'));
const ruleIds = result.messages.map((message) => message.ruleId);

assert.ok(ruleIds.includes('no-var'));
assert.ok(ruleIds.includes('eqeqeq'));
assert.ok(ruleIds.includes('guard-for-in'));
});

it('flags rules restored during the ESLint v9 style.js migration', async () => {
const result = await lint(baseConfig, readFixture('invalid.js'));
const noBitwise = result.messages.find((message) => message.ruleId === 'no-bitwise');

assert.ok(noBitwise, 'expected no-bitwise to be reported');
assert.equal(noBitwise.severity, 2);
});

it('respects ALMA severity overrides instead of airbnb defaults', async () => {
const result = await lint(baseConfig, readFixture('invalid.js'));
const curly = result.messages.find((message) => message.ruleId === 'curly');

assert.ok(curly, 'expected curly to be reported');
assert.equal(curly.severity, 1, 'curly should be a warning, not an airbnb error');
});

it('does not forbid for-in loops, unlike airbnb', async () => {
const result = await lint(baseConfig, readFixture('invalid.js'));
const ruleIds = result.messages.map((message) => message.ruleId);

assert.ok(!ruleIds.includes('no-restricted-syntax'));
});
});
});

describe('@alma-oss/eslint-config-base/legacy', () => {
it('lints ES5 code with no errors or warnings', async () => {
const result = await lint(legacyConfig, readFixture('legacy-valid.js'));

assert.equal(result.errorCount, 0);
assert.equal(result.warningCount, 0);
});

it('requires strict mode, unlike the modern config', async () => {
const result = await lint(legacyConfig, 'var sum = function sum(a, b) { return a + b; };\n');
const ruleIds = result.messages.map((message) => message.ruleId);

assert.ok(ruleIds.includes('strict'));
});
});

describe('@alma-oss/eslint-config-base/optional', () => {
it('loads and lints without throwing', async () => {
const result = await lint([...baseConfig, ...optionalConfig], readFixture('valid.js'));

assert.equal(typeof result.errorCount, 'number');
});

it('flags additional insights not enabled by the base config', async () => {
const result = await lint([...baseConfig, ...optionalConfig], 'const foo = Symbol();\n\nexport default foo;\n');
const ruleIds = result.messages.map((message) => message.ruleId);

assert.ok(ruleIds.includes('symbol-description'));
});
Comment on lines +91 to +96
});

describe('@alma-oss/eslint-config-base/whitespace', () => {
it('keeps whitespace rules at error severity', async () => {
const result = await lint(whitespaceConfig, 'if (true) {\n\tconsole.log(1);\n}\n');
const indent = result.messages.find((message) => message.ruleId === 'indent');

assert.ok(indent, 'expected indent to be reported');
assert.equal(indent.severity, 2);
});

it('downgrades non-whitespace rules to warnings', async () => {
const result = await lint(whitespaceConfig, readFixture('invalid.js'));
const noVar = result.messages.find((message) => message.ruleId === 'no-var');

assert.ok(noVar, 'expected no-var to be reported');
assert.equal(noVar.severity, 1, 'no-var is not a whitespace rule, so it should be a warning');
});
});
17 changes: 11 additions & 6 deletions packages/eslint-config-base/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { FlatCompat } from '@eslint/eslintrc';
import { fixupConfigRules } from '@eslint/compat';
import importPlugin from 'eslint-plugin-import';
import bestPractices from './rules/best-practices.js';
import errors from './rules/errors.js';
import node from './rules/node.js';
Expand All @@ -11,12 +10,18 @@ import strict from './rules/strict.js';
import globs from './globs.js';
import settings from './settings.js';

const compat = new FlatCompat();

export default [
...fixupConfigRules(compat.extends('eslint-config-airbnb-base')),
{
name: '@lmc-eu/eslint-config-base',
name: '@alma-oss/eslint-config-base',

plugins: {
import: importPlugin,
},

languageOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},

settings,

Expand Down
77 changes: 71 additions & 6 deletions packages/eslint-config-base/legacy.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,78 @@
import { FlatCompat } from '@eslint/eslintrc';
import { fixupConfigRules } from '@eslint/compat';

const compat = new FlatCompat();
import importPlugin from 'eslint-plugin-import';
import bestPractices from './rules/best-practices.js';
import errors from './rules/errors.js';
import node from './rules/node.js';
import style from './rules/style.js';
import variables from './rules/variables.js';
import globs from './globs.js';
import settings from './settings.js';

export default [
...fixupConfigRules(compat.extends('eslint-config-airbnb-base/legacy')),
{
name: '@lmc-eu/eslint-config-base/legacy',
name: '@alma-oss/eslint-config-base/legacy',

plugins: {
import: importPlugin,
},

languageOptions: {
ecmaVersion: 5,
sourceType: 'script',
},

settings,

rules: {},
},
bestPractices,
errors,
node,
style,
variables,
{
name: '@alma-oss/eslint-config-base/legacy-overrides',
rules: {
// === AIRBNB LEGACY OVERRIDES ===
// Legacy requires never trailing commas
'comma-dangle': ['error', 'never'],

// Prefer numeric literals (off in legacy)
'prefer-numeric-literals': 'off',

// Restrict some object properties
'no-restricted-properties': [
'error',
{
object: 'arguments',
property: 'callee',
message: 'arguments.callee is deprecated',
},
{
property: '__defineGetter__',
message: 'Please use Object.defineProperty instead.',
},
{
property: '__defineSetter__',
message: 'Please use Object.defineProperty instead.',
},
],

// Allow var in legacy code
'no-var': 'off',

// Allow prefer-object-spread in legacy code
'prefer-object-spread': 'off',

// Require strict mode ('safe' allows function or global)
strict: ['error', 'safe'],
},
},
{
files: globs.configs,

rules: {
// Using process.env is encouraged in configuration files
'node/no-process-env': 'off',
},
Comment on lines +73 to +76
},
];
6 changes: 1 addition & 5 deletions packages/eslint-config-base/optional.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import globs from './globs.js';
export default [
jsdoc.configs['flat/recommended'],
{
name: '@lmc-eu/eslint-config-base/optional',
name: '@alma-oss/eslint-config-base/optional',
plugins: {
jsdoc,
},
Expand Down Expand Up @@ -106,10 +106,6 @@ export default [
// https://eslint.org/docs/rules/strict
strict: 'error', // airbnb error

// Require an empty line before return statements
// https://eslint.org/docs/rules/newline-before-return
'newline-before-return': 'error', // airbnb off

// Disallow duplicate module imports
// https://eslint.org/docs/rules/no-duplicate-imports
'no-duplicate-imports': 'error', // airbnb off
Expand Down
7 changes: 4 additions & 3 deletions packages/eslint-config-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"engines": {
"node": ">=20"
},
"scripts": {
"test": "node --test"
},
"exports": {
".": "./index.js",
"./optional": "./optional.js",
Expand All @@ -34,9 +37,7 @@
"url": "https://github.com/lmc-eu/code-quality-tools/issues"
},
"dependencies": {
"@eslint/compat": "^1.3.1",
"@eslint/eslintrc": "^3.3.1",
"eslint-config-airbnb-base": "^15.0.0",
"confusing-browser-globals": "^1.0.10",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsdoc": "^51.4.1"
},
Expand Down
Loading
Loading