Skip to content
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

feat: target ESLint v9 #1076

Merged
merged 1 commit into from
Oct 22, 2024
Merged
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
9 changes: 0 additions & 9 deletions .eslintignore

This file was deleted.

20 changes: 20 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

const HapiPlugin = require('@hapi/eslint-plugin');

module.exports = [
{
ignores: [
'node_modules/',
'test_runner/',
'test/coverage/',
'test/cli/',
'test/cli_*/',
'test/lint/',
'test/override/',
'test/plan/',
'test/transform/'
]
},
...HapiPlugin.configs.module
];
6 changes: 3 additions & 3 deletions lib/linter/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use strict';

module.exports = {
extends: 'plugin:@hapi/module'
};
const HapiPlugin = require('@hapi/eslint-plugin');

module.exports = [...HapiPlugin.configs.module];
52 changes: 37 additions & 15 deletions lib/linter/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const Fs = require('fs');
const Path = require('path');

const Eslint = require('eslint');
const Hoek = require('@hapi/hoek');
Expand All @@ -18,31 +17,47 @@ exports.lint = async function () {

const options = process.argv[2] ? JSON.parse(process.argv[2]) : undefined;

if (!Fs.existsSync('.eslintrc.js') &&
!Fs.existsSync('.eslintrc.cjs') && // Needed for projects with "type": "module"
!Fs.existsSync('.eslintrc.yaml') &&
!Fs.existsSync('.eslintrc.yml') &&
!Fs.existsSync('.eslintrc.json') &&
!Fs.existsSync('.eslintrc')) {
configuration.overrideConfigFile = Path.join(__dirname, '.eslintrc.js');
let usingDefault = false;

if (!Fs.existsSync('eslint.config.js') &&
!Fs.existsSync('eslint.config.cjs') &&
!Fs.existsSync('eslint.config.mjs')) {
// No configuration file found, using the default one
usingDefault = true;
configuration.baseConfig = require('./.eslintrc.js');
configuration.overrideConfigFile = true;
}

if (options) {
Hoek.merge(configuration, options, true, false);
}

if (!configuration.extensions) {
configuration.extensions = ['.js', '.cjs', '.mjs'];
// Only the default configuration should be altered, otherwise the user's configuration should be used as is
if (usingDefault) {
if (!configuration.extensions) {
const extensions = ['js', 'cjs', 'mjs'];

if (configuration.typescript) {
extensions.push('ts');
}

if (configuration.typescript) {
configuration.extensions.push('.ts');
configuration.baseConfig.unshift({
files: extensions.map((ext) => `**/*.${ext}`)
});
}
}

if (configuration.typescript) {
delete configuration.typescript;
if (configuration.ignores) {
configuration.baseConfig.unshift({
ignores: configuration.ignores
});
}
}

delete configuration.extensions;
delete configuration.typescript;
delete configuration.ignores;


let results;
try {
const eslint = new Eslint.ESLint(configuration);
Expand All @@ -66,6 +81,13 @@ exports.lint = async function () {

transformed.errors = result.messages.map((err) => {

if (err.messageTemplate === 'all-matched-files-ignored') {
return {
severity: 'ERROR',
message: err.message
};
}

return {
line: err.line,
severity: err.severity === 1 ? 'WARNING' : 'ERROR',
Expand Down
40 changes: 34 additions & 6 deletions lib/modules/coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ const SourceMap = require('../source-map');
const Transform = require('./transform');

const internals = {
_state: Symbol.for('@hapi/lab/coverage/_state'),
eslint: new ESLint.ESLint({ baseConfig: Eslintrc })
_state: Symbol.for('@hapi/lab/coverage/_state')
};


Expand Down Expand Up @@ -111,7 +110,7 @@ internals.prime = function (extension, ctx) {
require.extensions[extension] = function (localModule, filename) {

// We never want to instrument eslint configs in order to avoid infinite recursion
if (Path.basename(filename, extension) !== '.eslintrc') {
if (!['.eslintrc', 'eslint.config'].includes(Path.basename(filename, extension))) {
for (let i = 0; i < internals.state.patterns.length; ++i) {
if (internals.state.patterns[i].test(filename.replace(/\\/g, '/'))) {
return localModule._compile(internals.instrument(filename, ctx), filename);
Expand Down Expand Up @@ -761,11 +760,40 @@ internals.file = async function (filename, data, options) {

internals.context = async (options) => {

const filePath = Path.join(options.coveragePath || '', 'x.js');
let calculated;

// The parserOptions are shared by all files for coverage purposes, based on
// the effective eslint config for a hypothetical file {coveragePath}/x.js
const { parserOptions } = await internals.eslint.calculateConfigForFile(
Path.join(options.coveragePath || '', 'x.js')
);
try {
// Let's try first with eslint's native configuration detection
const eslint = new ESLint.ESLint({
ignore: false
});

calculated = await eslint.calculateConfigForFile(filePath);
}
catch (err) {
/* $lab:coverage:off$ */
if (err.messageTemplate !== 'config-file-missing') {
throw err;
}

// If the eslint config file is missing, we'll use the one provided by lab
const eslint = new ESLint.ESLint({
overrideConfig: Eslintrc,
overrideConfigFile: true,
ignore: false
});

calculated = await eslint.calculateConfigForFile(filePath);
/* $lab:coverage:on$ */
}

const parserOptions = {
...calculated.languageOptions,
...calculated.languageOptions?.parserOptions
};

return { parserOptions };
};
2 changes: 1 addition & 1 deletion lib/modules/lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ exports.lint = function (settings) {
try {
linterOptions = JSON.parse(settings['lint-options'] || '{}');
}
catch (err) {
catch {
return reject(new Error('lint-options could not be parsed'));
}

Expand Down
2 changes: 1 addition & 1 deletion lib/modules/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ exports.retrieveFile = function (path) {
try {
contents = Fs.readFileSync(path, 'utf8');
}
catch (e) {
catch {
contents = null;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/modules/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internals.transform = function (content, fileName) {
try {
var { config, error } = Typescript.readConfigFile(configFile, Typescript.sys.readFile);
}
catch (err) {
catch {
throw new Error(`Cannot find a tsconfig file for ${fileName}`);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ const internals = {};

// Prevent libraries like Sinon from clobbering global time functions

/* eslint-disable no-redeclare */
const Date = global.Date;
const setTimeout = global.setTimeout;
const clearTimeout = global.clearTimeout;
const setImmediate = global.setImmediate;
/* eslint-enable no-redeclare */


Error.stackTraceLimit = Infinity; // Set Error stack size
Expand Down
18 changes: 7 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,14 @@
"bin/lab",
"lib"
],
"eslintConfig": {
"extends": [
"plugin:@hapi/module"
]
},
"dependencies": {
"@babel/core": "^7.16.0",
"@babel/eslint-parser": "^7.16.0",
"@babel/eslint-parser": "^7.25.1",
"@hapi/bossy": "^6.0.0",
"@hapi/eslint-plugin": "^6.0.0",
"@hapi/eslint-plugin": "^7.0.0",
"@hapi/hoek": "^11.0.2",
"diff": "^5.0.0",
"eslint": "8.x.x",
"eslint": "9.x.x",
"find-rc": "4.x.x",
"globby": "^11.1.0",
"handlebars": "4.x.x",
Expand All @@ -37,7 +32,7 @@
"will-call": "1.x.x"
},
"peerDependencies": {
"@hapi/eslint-plugin": "^6.0.0",
"@hapi/eslint-plugin": "^7.0.0",
"typescript": ">=3.6.5"
},
"peerDependenciesMeta": {
Expand All @@ -48,13 +43,14 @@
"devDependencies": {
"@hapi/code": "^9.0.0",
"@hapi/somever": "^4.0.0",
"@types/eslint": "^9.6.0",
"@types/node": "^18.11.17",
"@typescript-eslint/parser": "^5.62.0",
"cpr": "3.x.x",
"lab-event-reporter": "1.x.x",
"semver": "7.x.x",
"tsconfig-paths": "^4.0.0",
"typescript": "^4.5.4"
"typescript": "^4.5.4",
"typescript-eslint": "^8.1.0"
},
"bin": {
"lab": "./bin/lab"
Expand Down
3 changes: 2 additions & 1 deletion test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Load modules

const ChildProcess = require('child_process');
// eslint-disable-next-line no-redeclare
const Crypto = require('crypto');
const Fs = require('fs');
const Http = require('http');
Expand Down Expand Up @@ -702,7 +703,7 @@ describe('CLI', () => {
try {
await unlink(outputPath);
}
catch (err) {
catch {

// Error is ok here
}
Expand Down
6 changes: 3 additions & 3 deletions test/coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -566,19 +566,19 @@ describe('Coverage', () => {
it('sorts file paths in report', async () => {

const files = global.__$$labCov.files;
const paths = ['/a/b', '/a/b/c', '/a/c/b', '/a/c', '/a/b/c', '/a/b/a'];
const paths = ['./a/b', './a/b/c', './a/c/b', './a/c', './a/b/c', './a/b/a'];
paths.forEach((path) => {

files[path] = { source: [] };
});

const cov = await Lab.coverage.analyze({ coveragePath: '/a' });
const cov = await Lab.coverage.analyze({ coveragePath: './a' });
const sorted = cov.files.map((file) => {

return file.filename;
});

expect(sorted).to.equal(['/a/b', '/a/c', '/a/b/a', '/a/b/c', '/a/c/b']);
expect(sorted).to.equal(['./a/b', './a/c', './a/b/a', './a/b/c', './a/c/b']);
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
'use strict';

const HapiPlugin = require('@hapi/eslint-plugin');

// this is a deliberately unused function that will reduce coverage percentage
// if it ends up getting instrumented, giving us something to assert against
const unusedMethod = () => {
console.log('hello world')
}

module.exports = {
extends: 'plugin:@hapi/module'
}
module.exports = [...HapiPlugin.configs.module]
4 changes: 2 additions & 2 deletions test/coverage/test-folder/test-name.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

// Load modules

const EslintConfig = require('./eslint.config');

// Declare internals

Expand All @@ -10,5 +10,5 @@ const internals = {};

exports.method = function () {

return;
return EslintConfig;
};
13 changes: 0 additions & 13 deletions test/lint/eslint/esm/.eslintrc.cjs

This file was deleted.

22 changes: 22 additions & 0 deletions test/lint/eslint/esm/eslint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

const HapiPlugin = require('@hapi/eslint-plugin');

module.exports = [
...HapiPlugin.configs.module,
{
languageOptions: {
parserOptions: {
sourceType: 'module'
}
}
},
{
files: ['*.cjs'],
languageOptions: {
parserOptions: {
sourceType: 'script'
}
}
}
];
5 changes: 0 additions & 5 deletions test/lint/eslint/typescript/.eslintrc.cjs

This file was deleted.

Loading