From 031fee68a32ef99be645376e3a0008c079feb507 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Mon, 6 Dec 2021 10:58:24 +0100 Subject: [PATCH] Pass current directory to formatter function This replicates a new feature in ESLint 8.4: https://github.com/eslint/eslint/pull/13392 --- index.js | 55 ++++++++++++++++++++++----------------------- test/format.spec.js | 6 ++--- test/util.spec.js | 28 ++++++++++++----------- util.js | 19 ++++++++-------- 4 files changed, 55 insertions(+), 53 deletions(-) diff --git a/index.js b/index.js index dc09118..b45ec63 100644 --- a/index.js +++ b/index.js @@ -15,15 +15,15 @@ const { const { ESLint } = require('eslint'); const { promisify } = require('util'); -function getESLintInstance(file) { - const eslintInstance = file._eslintInstance; - if (eslintInstance != null) { - return eslintInstance; +function getESLintInfo(file) { + const eslintInfo = file._eslintInfo; + if (eslintInfo != null) { + return eslintInfo; } - throw createPluginError({ fileName: file.path, message: 'ESLint instance not found' }); + throw createPluginError({ fileName: file.path, message: 'ESLint information not available' }); } -async function lintFile(eslintInstance, file, cwd, quiet, warnIgnored) { +async function lintFile(eslintInfo, file, quiet, warnIgnored) { if (file.isNull()) { return; } @@ -32,49 +32,48 @@ async function lintFile(eslintInstance, file, cwd, quiet, warnIgnored) { throw 'gulp-eslint-new doesn\'t support Vinyl files with Stream contents.'; } + const { eslint } = eslintInfo; // The "path" property of a Vinyl file should be always an absolute path. // See https://gulpjs.com/docs/en/api/vinyl/#instance-properties. const filePath = file.path; - if (await eslintInstance.isPathIgnored(filePath)) { + if (await eslint.isPathIgnored(filePath)) { // Note: ESLint doesn't adjust file paths relative to an ancestory .eslintignore path. // E.g., If ../.eslintignore has "foo/*.js", ESLint will ignore ./foo/*.js, instead of ../foo/*.js. // ESLint rolls this into `ESLint.prototype.lintText`. So, gulp-eslint-new must account for this limitation. if (warnIgnored) { // Warn that gulp.src is needlessly reading files that ESLint ignores. - file.eslint = createIgnoreResult(filePath, cwd); + file.eslint = createIgnoreResult(filePath, eslintInfo.cwd); } return; } - const [result] = await eslintInstance.lintText(file.contents.toString(), { filePath }); + let [result] = await eslint.lintText(file.contents.toString(), { filePath }); // Note: Fixes are applied as part of `lintText`. // Any applied fix messages have been removed from the result. - let eslint; if (quiet) { // Ignore some messages. const filter = typeof quiet === 'function' ? quiet : isErrorMessage; - eslint = filterResult(result, filter); - } else { - eslint = result; + result = filterResult(result, filter); } - file.eslint = eslint; - file._eslintInstance = eslintInstance; + file.eslint = result; + file._eslintInfo = eslintInfo; // Update the fixed output; otherwise, fixable messages are simply ignored. - if (hasOwn(eslint, 'output')) { - file.contents = Buffer.from(eslint.output); - eslint.fixed = true; + if (hasOwn(result, 'output')) { + file.contents = Buffer.from(result.output); + result.fixed = true; } } function gulpEslint(options) { const { eslintOptions, quiet, warnIgnored } = migrateOptions(options); - const eslintInstance = new ESLint(eslintOptions); const cwd = eslintOptions.cwd || process.cwd(); + const eslint = new ESLint(eslintOptions); + const eslintInfo = { cwd, eslint }; return createTransform( - file => lintFile(eslintInstance, file, cwd, quiet, warnIgnored) + file => lintFile(eslintInfo, file, quiet, warnIgnored) ); } @@ -156,8 +155,8 @@ gulpEslint.formatEach = (formatter, writable) => { return createTransform(async file => { const { eslint } = file; if (eslint) { - const eslintInstance = getESLintInstance(file); - await writeResults([eslint], eslintInstance, formatter, writable); + const eslintInfo = getESLintInfo(file); + await writeResults([eslint], eslintInfo, formatter, writable); } }); }; @@ -165,16 +164,16 @@ gulpEslint.formatEach = (formatter, writable) => { gulpEslint.format = (formatter, writable) => { writable = resolveWritable(writable); const results = []; - let commonInstance; + let commonInfo; return createTransform( file => { const { eslint } = file; if (eslint) { - const eslintInstance = getESLintInstance(file); - if (commonInstance == null) { - commonInstance = eslintInstance; + const eslintInfo = getESLintInfo(file); + if (commonInfo == null) { + commonInfo = eslintInfo; } else { - if (eslintInstance !== commonInstance) { + if (eslintInfo !== commonInfo) { throw createPluginError({ name: 'ESLintError', message: 'The files in the stream were not processes by the same ' @@ -187,7 +186,7 @@ gulpEslint.format = (formatter, writable) => { }, async () => { if (results.length) { - await writeResults(results, commonInstance, formatter, writable); + await writeResults(results, commonInfo, formatter, writable); } } ); diff --git a/test/format.spec.js b/test/format.spec.js index 46c95ae..ea8c4af 100644 --- a/test/format.spec.js +++ b/test/format.spec.js @@ -116,7 +116,7 @@ describe('gulp-eslint-new format function', () => { .format() .on('error', err => { assert.equal(err.fileName, file.path); - assert.equal(err.message, 'ESLint instance not found'); + assert.equal(err.message, 'ESLint information not available'); assert.equal(err.plugin, 'gulp-eslint-new'); done(); }) @@ -139,7 +139,7 @@ describe('gulp-eslint-new format function', () => { function addFile(path) { const file = createVinylFile(path, ''); file.eslint = { }; - file._eslintInstance = new ESLint(); + file._eslintInfo = { cwd: process.cwd(), eslint: new ESLint() }; formatStream.write(file); } @@ -216,7 +216,7 @@ describe('gulp-eslint-new format function', () => { .formatEach() .on('error', err => { assert.equal(err.fileName, file.path); - assert.equal(err.message, 'ESLint instance not found'); + assert.equal(err.message, 'ESLint information not available'); assert.equal(err.plugin, 'gulp-eslint-new'); done(); }) diff --git a/test/util.spec.js b/test/util.spec.js index 413d4e8..92810e6 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -380,8 +380,8 @@ describe('utility methods', () => { ]; it('should default to the "stylish" formatter', async () => { - const eslintInstance = new ESLint(); - const formatter = await util.resolveFormatter(eslintInstance); + const eslintInfo = { eslint: new ESLint() }; + const formatter = await util.resolveFormatter(eslintInfo); const text = formatter.format(testResults); assert.equal( text.replace(/\x1b\[\d+m/g, ''), // eslint-disable-line no-control-regex @@ -390,8 +390,8 @@ describe('utility methods', () => { }); it('should resolve a predefined formatter', async () => { - const eslintInstance = new ESLint(); - const formatter = await util.resolveFormatter(eslintInstance, 'compact'); + const eslintInfo = { eslint: new ESLint() }; + const formatter = await util.resolveFormatter(eslintInfo, 'compact'); const text = formatter.format(testResults); assert.equal( text.replace(/\x1b\[\d+m/g, ''), // eslint-disable-line no-control-regex @@ -400,30 +400,32 @@ describe('utility methods', () => { }); it('should resolve a custom formatter', async () => { - const eslintInstance = new ESLint({ cwd: __dirname }); - const formatter = await util.resolveFormatter(eslintInstance, './custom-formatter'); + const eslintInfo = { eslint: new ESLint({ cwd: __dirname }) }; + const formatter = await util.resolveFormatter(eslintInfo, './custom-formatter'); formatter.format(testResults); const { args } = require('./custom-formatter'); assert.equal(args[0], testResults); + assert.equal(args[1].cwd, __dirname); assert(args[1].rulesMeta); }); it('should wrap an ESLint 6 style formatter function into a formatter', async () => { - const eslintInstance = new ESLint(); + const eslintInfo = { cwd: 'TEST CWD', eslint: new ESLint() }; const legacyFormatter = (actualResults, data) => { assert.equal(actualResults, testResults); assert(data.rulesMeta); + assert.equal(data.cwd, 'TEST CWD'); return 'foo'; }; - const formatter = await util.resolveFormatter(eslintInstance, legacyFormatter); + const formatter = await util.resolveFormatter(eslintInfo, legacyFormatter); const text = await formatter.format(testResults); assert.equal(text, 'foo'); }); it('should throw an error if a formatter cannot be resolved', async () => { - const eslintInstance = new ESLint(); + const eslintInfo = { eslint: new ESLint() }; await assert.rejects( - () => util.resolveFormatter(eslintInstance, 'missing-formatter'), + () => util.resolveFormatter(eslintInfo, 'missing-formatter'), /\bThere was a problem loading formatter\b/ ); }); @@ -476,7 +478,7 @@ describe('utility methods', () => { const formattedText = 'something happened'; await util.writeResults( testResults, - testInstance, + { cwd: process.cwd(), eslint: testInstance }, (results, { rulesMeta }) => { assert(results); assert.equal(results, testResults); @@ -495,7 +497,7 @@ describe('utility methods', () => { it('should not write an empty formatted text', async () => { await util.writeResults( testResults, - testInstance, + { cwd: process.cwd(), eslint: testInstance }, (results, { rulesMeta }) => { assert(results); assert.equal(results, testResults); @@ -509,7 +511,7 @@ describe('utility methods', () => { it('should not write an undefined', async () => { await util.writeResults( testResults, - testInstance, + { cwd: process.cwd(), eslint: testInstance }, (results, { rulesMeta }) => { assert(results); assert.equal(results, testResults); diff --git a/util.js b/util.js index 1366d80..239af50 100644 --- a/util.js +++ b/util.js @@ -339,15 +339,15 @@ const { defineProperty } = Object; * If a function is specified, it will be treated as an ESLint 6 style formatter function and * wrapped into an object appropriately. * - * @param {ESLint} eslintInstance - * Instance of ESLint used to load the formatter. + * @param {{ cwd: string, eslint: ESLint }} eslintInfo + * Current directory and instance of ESLint used to load and configure the formatter. * * @param {string|CLIEngine.Formatter} [formatter] * A name or path of a formatter, or an ESLint 6 style formatter function to resolve as a formatter. * * @returns {Promise} An ESLint formatter. */ -async function resolveFormatter(eslintInstance, formatter) { +async function resolveFormatter({ cwd, eslint }, formatter) { if (typeof formatter === 'function') { return { format: results => { @@ -355,8 +355,9 @@ async function resolveFormatter(eslintInstance, formatter) { return formatter( results, { + cwd, get rulesMeta() { - const rulesMeta = eslintInstance.getRulesMetaForResults(results); + const rulesMeta = eslint.getRulesMetaForResults(results); defineProperty(this, 'rulesMeta', { value: rulesMeta }); return rulesMeta; } @@ -366,7 +367,7 @@ async function resolveFormatter(eslintInstance, formatter) { }; } // Use ESLint to look up formatter references. - return eslintInstance.loadFormatter(formatter); + return eslint.loadFormatter(formatter); } exports.resolveFormatter = resolveFormatter; @@ -390,8 +391,8 @@ exports.resolveWritable = (writable = fancyLog) => { * @param {ESLint.LintResult[]} results * A list of ESLint results. * - * @param {ESLint} eslintInstance - * Instance of ESLint, used to resolve the formatter. + * @param {{ cwd: string, eslint: ESLint }} eslintInfo + * Current directory and instance of ESLint used to load and configure the formatter. * * @param {string|CLIEngine.Formatter} [formatter] * A name or path of a formatter, or an ESLint 6 style formatter function to resolve as a formatter. @@ -399,8 +400,8 @@ exports.resolveWritable = (writable = fancyLog) => { * @param {Function} [writable] * A function used to write formatted ESLint results. */ -exports.writeResults = async (results, eslintInstance, formatter, writable) => { - const formatterObj = await resolveFormatter(eslintInstance, formatter); +exports.writeResults = async (results, eslintInfo, formatter, writable) => { + const formatterObj = await resolveFormatter(eslintInfo, formatter); const message = formatterObj.format(results); if (writable && message != null && message !== '') { writable(message);