diff --git a/package.json b/package.json index 88f90e0..49b5eeb 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "fix:js": "npm run lint:js -- --fix", "fix:prettier": "npm run lint:prettier -- --write", "fix": "npm-run-all -l fix:js fix:prettier", - "test:only": "cross-env NODE_ENV=test jest --testTimeout=60000", + "test:only": "cross-env NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --testTimeout=60000", "test:watch": "npm run test:only -- --watch", "test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage", "pretest": "npm run lint", diff --git a/src/getESLint.js b/src/getESLint.js index 7e49d61..b386101 100644 --- a/src/getESLint.js +++ b/src/getESLint.js @@ -2,6 +2,8 @@ const { cpus } = require('os'); const { Worker: JestWorker } = require('jest-worker'); +// @ts-ignore +const { setup, lintFiles } = require('./worker'); const { getESLintOptions } = require('./options'); const { jsonStringifyReplacerSortKeys } = require('./utils'); @@ -14,7 +16,7 @@ const cache = {}; /** @typedef {import('./linter').File} File */ /** @typedef {() => Promise} AsyncTask */ /** @typedef {(files: File[]) => Promise} LintTask */ -/** @typedef {{threads: number, ESLint: ESLint, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask}} Linter */ +/** @typedef {{threads: number, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask}} Linter */ /** @typedef {JestWorker & {lintFiles: LintTask}} Worker */ /** @@ -23,40 +25,16 @@ const cache = {}; */ function loadESLint(options) { const { eslintPath } = options; - - const { ESLint } = require(eslintPath || 'eslint'); - - // Filter out loader options before passing the options to ESLint. - /** @type {ESLint} */ - const eslint = new ESLint(getESLintOptions(options)); + const eslint = setup({ + eslintPath, + configType: options.configType, + eslintOptions: getESLintOptions(options), + }); return { threads: 1, - ESLint, + lintFiles, eslint, - lintFiles: async (files) => { - /** @type {LintResult[]} */ - const results = []; - - await Promise.all( - files.map(async (file) => { - const result = await eslint.lintText(file.content, { - filePath: file.path, - }); - // istanbul ignore else - if (result) { - results.push(...result); - } - }) - ); - - // istanbul ignore else - if (options.fix) { - await ESLint.outputFixes(results); - } - - return results; - }, // no-op for non-threaded cleanup: async () => {}, }; @@ -75,7 +53,13 @@ function loadESLintThreaded(key, poolSize, options) { const workerOptions = { enableWorkerThreads: true, numWorkers: poolSize, - setupArgs: [{ eslintPath, eslintOptions: getESLintOptions(options) }], + setupArgs: [ + { + eslintPath, + configType: options.configType, + eslintOptions: getESLintOptions(options), + }, + ], }; const local = loadESLint(options); diff --git a/src/index.js b/src/index.js index fd381a3..3003522 100644 --- a/src/index.js +++ b/src/index.js @@ -142,7 +142,7 @@ class ESLintWebpackPlugin { // @ts-ignore // eslint-disable-next-line no-underscore-dangle module._source - ).source() + ).source(), ); const file = { path: filePath, content }; files.push(file); diff --git a/src/options.js b/src/options.js index 4d1fce6..7d153cd 100644 --- a/src/options.js +++ b/src/options.js @@ -37,6 +37,7 @@ const schema = require('./options.json'); * @property {OutputReport=} outputReport * @property {number|boolean=} threads * @property {RegExp|RegExp[]=} resourceQueryExclude + * @property {string=} configType */ /** @typedef {PluginOptions & ESLintOptions} Options */ @@ -84,6 +85,11 @@ function getESLintOptions(loaderOptions) { delete eslintOptions[option]; } + // Some options aren't available in flat mode + if (loaderOptions.configType === 'flat') { + delete eslintOptions.extensions; + } + return eslintOptions; } diff --git a/src/options.json b/src/options.json index caa39ff..963044c 100644 --- a/src/options.json +++ b/src/options.json @@ -58,6 +58,10 @@ "description": "Will process and report errors only and ignore warnings, if set to `true`.", "type": "boolean" }, + "configType": { + "description": "Enable flat config by setting this value to `flat`.", + "type": "string" + }, "outputReport": { "description": "Write the output of the errors to a file, for example a checkstyle xml file for use for reporting on Jenkins CI", "anyOf": [ diff --git a/src/worker.js b/src/worker.js index 0833263..98e8498 100644 --- a/src/worker.js +++ b/src/worker.js @@ -20,14 +20,37 @@ let fix; /** * @typedef {object} setupOptions * @property {string=} eslintPath - import path of eslint - * @property {ESLintOptions=} eslintOptions - linter options + * @property {string=} configType + * @property {ESLintOptions} eslintOptions - linter options * * @param {setupOptions} arg0 - setup worker */ -function setup({ eslintPath, eslintOptions = {} }) { +function setup({ eslintPath, configType, eslintOptions }) { fix = !!(eslintOptions && eslintOptions.fix); - ({ ESLint } = require(eslintPath || 'eslint')); - eslint = new ESLint(eslintOptions); + const eslintModule = require(eslintPath || 'eslint'); + + let FlatESLint; + + if (eslintModule.LegacyESLint) { + ESLint = eslintModule.LegacyESLint; + ({ FlatESLint } = eslintModule); + } else { + ({ ESLint } = eslintModule); + + if (configType === 'flat') { + throw new Error( + "Couldn't find FlatESLint, you might need to set eslintPath to 'eslint/use-at-your-own-risk'", + ); + } + } + + if (configType === 'flat') { + eslint = new FlatESLint(eslintOptions); + } else { + eslint = new ESLint(eslintOptions); + } + + return eslint; } /** @@ -46,7 +69,7 @@ async function lintFiles(files) { if (result) { results.push(...result); } - }) + }), ); // istanbul ignore else diff --git a/test/autofix-stop.test.js b/test/autofix-stop.test.js index c58ada7..5634286 100644 --- a/test/autofix-stop.test.js +++ b/test/autofix-stop.test.js @@ -25,12 +25,10 @@ describe('autofix stop', () => { removeSync(entry); }); - it('should not change file if there are no fixable errors/warnings', (done) => { + it('should not change file if there are no fixable errors/warnings', async () => { const compiler = pack('nonfixable-clone', { fix: true }); - compiler.run(() => { - expect(changed).toBe(false); - done(); - }); + await compiler.runAsync(); + expect(changed).toBe(false); }); }); diff --git a/test/autofix.test.js b/test/autofix.test.js index d1ce57a..3b0cc58 100644 --- a/test/autofix.test.js +++ b/test/autofix.test.js @@ -17,7 +17,7 @@ describe('autofix stop', () => { test.each([[{}], [{ threads: false }]])( 'should not throw error if file ok after auto-fixing', - (cfg, done) => { + async (cfg) => { const compiler = pack('fixable-clone', { ...cfg, fix: true, @@ -27,11 +27,10 @@ describe('autofix stop', () => { }, }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - expect(readFileSync(entry).toString('utf8')).toMatchInlineSnapshot(` + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); + expect(readFileSync(entry).toString('utf8')).toMatchInlineSnapshot(` "function foo() { return true; } @@ -39,8 +38,6 @@ describe('autofix stop', () => { foo(); " `); - done(); - }); }, ); }); diff --git a/test/context.test.js b/test/context.test.js index 1f28828..84fb54d 100644 --- a/test/context.test.js +++ b/test/context.test.js @@ -3,25 +3,19 @@ import { join } from 'path'; import pack from './utils/pack'; describe('context', () => { - it('absolute', (done) => { + it('absolute', async () => { const compiler = pack('good', { context: join(__dirname, 'fixtures') }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); - it('relative', (done) => { + it('relative', async () => { const compiler = pack('good', { context: '../fixtures/' }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/emit-error.test.js b/test/emit-error.test.js index 6048964..ca82903 100644 --- a/test/emit-error.test.js +++ b/test/emit-error.test.js @@ -1,58 +1,43 @@ import pack from './utils/pack'; describe('emit error', () => { - it('should not emit errors if emitError is false', (done) => { + it('should not emit errors if emitError is false', async () => { const compiler = pack('error', { emitError: false }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(false); }); - it('should emit errors if emitError is undefined', (done) => { + it('should emit errors if emitError is undefined', async () => { const compiler = pack('error', {}); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(true); }); - it('should emit errors if emitError is true', (done) => { + it('should emit errors if emitError is true', async () => { const compiler = pack('error', { emitError: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(true); }); - it('should emit errors, but not warnings if emitError is true and emitWarning is false', (done) => { + it('should emit errors, but not warnings if emitError is true and emitWarning is false', async () => { const compiler = pack('full-of-problems', { emitError: true, emitWarning: false, }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); - it('should emit errors and warnings if emitError is true and emitWarning is undefined', (done) => { + it('should emit errors and warnings if emitError is true and emitWarning is undefined', async () => { const compiler = pack('full-of-problems', { emitError: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(true); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(true); + expect(stats.hasErrors()).toBe(true); }); }); diff --git a/test/emit-warning.test.js b/test/emit-warning.test.js index 1eb0feb..4cd2100 100644 --- a/test/emit-warning.test.js +++ b/test/emit-warning.test.js @@ -1,58 +1,43 @@ import pack from './utils/pack'; describe('emit warning', () => { - it('should not emit warnings if emitWarning is false', (done) => { + it('should not emit warnings if emitWarning is false', async () => { const compiler = pack('warn', { emitWarning: false }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); }); - it('should emit warnings if emitWarning is undefined', (done) => { + it('should emit warnings if emitWarning is undefined', async () => { const compiler = pack('warn', {}); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(true); }); - it('should emit warnings if emitWarning is true', (done) => { + it('should emit warnings if emitWarning is true', async () => { const compiler = pack('warn', { emitWarning: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(true); }); - it('should emit warnings, but not warnings if emitWarning is true and emitError is false', (done) => { + it('should emit warnings, but not warnings if emitWarning is true and emitError is false', async () => { const compiler = pack('full-of-problems', { emitWarning: true, emitError: false, }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(true); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(true); + expect(stats.hasErrors()).toBe(false); }); - it('should emit warnings and errors if emitWarning is true and emitError is undefined', (done) => { + it('should emit warnings and errors if emitWarning is true and emitError is undefined', async () => { const compiler = pack('full-of-problems', { emitWarning: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(true); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(true); + expect(stats.hasErrors()).toBe(true); }); }); diff --git a/test/error.test.js b/test/error.test.js index 816ff2f..c802698 100644 --- a/test/error.test.js +++ b/test/error.test.js @@ -5,18 +5,15 @@ describe('error', () => { jest.restoreAllMocks(); }); - it('should return error if file is bad', (done) => { + it('should return error if file is bad', async () => { const compiler = pack('error'); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); - it('should propagate eslint exceptions as errors', (done) => { + it('should propagate eslint exceptions as errors', async () => { jest.mock('eslint', () => { return { ESLint: function ESLint() { @@ -27,11 +24,8 @@ describe('error', () => { const compiler = pack('good', { threads: false }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); }); diff --git a/test/eslint-lint.test.js b/test/eslint-lint.test.js index 4d52d9d..e1e5aa9 100644 --- a/test/eslint-lint.test.js +++ b/test/eslint-lint.test.js @@ -17,33 +17,24 @@ describe('eslint lint', () => { mockLintFiles.mockClear(); }); - it('should lint one file', (done) => { + it('should lint one file', async () => { const compiler = pack('lint-one', { threads: false }); - compiler.run((err) => { - expect(mockLintFiles).toHaveBeenCalledTimes(1); - expect(err).toBeNull(); - done(); - }); + await compiler.runAsync(); + expect(mockLintFiles).toHaveBeenCalledTimes(1); }); - it('should lint two files', (done) => { + it('should lint two files', async () => { const compiler = pack('lint-two', { threads: false }); - compiler.run((err) => { - expect(mockLintFiles).toHaveBeenCalledTimes(2); - expect(err).toBeNull(); - done(); - }); + await compiler.runAsync(); + expect(mockLintFiles).toHaveBeenCalledTimes(2); }); - it('should lint more files', (done) => { + it('should lint more files', async () => { const compiler = pack('lint-more', { threads: false }); - compiler.run((err) => { - expect(mockLintFiles).toHaveBeenCalledTimes(3); - expect(err).toBeNull(); - done(); - }); + await compiler.runAsync(); + expect(mockLintFiles).toHaveBeenCalledTimes(3); }); }); diff --git a/test/eslint-path.test.js b/test/eslint-path.test.js index 940fb08..63d87dd 100644 --- a/test/eslint-path.test.js +++ b/test/eslint-path.test.js @@ -3,16 +3,13 @@ import { join } from 'path'; import pack from './utils/pack'; describe('eslint path', () => { - it('should use another instance of eslint via eslintPath config', (done) => { + it('should use another instance of eslint via eslintPath config', async () => { const eslintPath = join(__dirname, 'mock/eslint'); const compiler = pack('good', { eslintPath }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - expect(stats.compilation.errors[0].message).toContain('Fake error'); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); + expect(stats.compilation.errors[0].message).toContain('Fake error'); }); }); diff --git a/test/eslintignore.test.js b/test/eslintignore.test.js index 1d04646..b37f92a 100644 --- a/test/eslintignore.test.js +++ b/test/eslintignore.test.js @@ -3,17 +3,13 @@ import ESLintError from '../src/ESLintError'; import pack from './utils/pack'; describe('eslintignore', () => { - it('should ignores files present in .eslintignore', (done) => { + it('should ignores files present in .eslintignore', async () => { const compiler = pack('ignore', { ignore: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect( - stats.compilation.errors.filter((x) => x instanceof ESLintError), - ).toEqual([]); - - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect( + stats.compilation.errors.filter((x) => x instanceof ESLintError), + ).toEqual([]); }); }); diff --git a/test/exclude.test.js b/test/exclude.test.js index d4b6810..6ee306d 100644 --- a/test/exclude.test.js +++ b/test/exclude.test.js @@ -1,36 +1,27 @@ import pack from './utils/pack'; describe('exclude', () => { - it('should exclude with globs', (done) => { + it('should exclude with globs', async () => { const compiler = pack('exclude', { exclude: ['*error*'] }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); - it('should exclude files', (done) => { + it('should exclude files', async () => { const compiler = pack('exclude', { exclude: ['error.js'] }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); - it('should exclude folders', (done) => { + it('should exclude folders', async () => { const compiler = pack('exclude-folder', { exclude: ['folder'] }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/fail-on-config.test.js b/test/fail-on-config.test.js index d9a947c..6f40a04 100644 --- a/test/fail-on-config.test.js +++ b/test/fail-on-config.test.js @@ -3,19 +3,17 @@ import { join } from 'path'; import pack from './utils/pack'; describe('fail on config', () => { - it('fails when .eslintrc is not a proper format', (done) => { + it('fails when .eslintrc is not a proper format', async () => { const overrideConfigFile = join(__dirname, '.badeslintrc'); const compiler = pack('error', { overrideConfigFile }); - compiler.run((err, stats) => { - const { errors } = stats.compilation; - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - expect(errors).toHaveLength(1); - expect(errors[0].message).toMatch( - /ESLint configuration in --config is invalid/i, - ); - done(); - }); + const stats = await compiler.runAsync(); + const { errors } = stats.compilation; + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); + expect(errors).toHaveLength(1); + expect(errors[0].message).toMatch( + /ESLint configuration in --config is invalid/i, + ); }); }); diff --git a/test/fail-on-error.test.js b/test/fail-on-error.test.js index d5bfc6b..f6fa866 100644 --- a/test/fail-on-error.test.js +++ b/test/fail-on-error.test.js @@ -1,33 +1,24 @@ import pack from './utils/pack'; describe('fail on error', () => { - it('should emits errors', (done) => { + it('should emits errors', async () => { const compiler = pack('error', { failOnError: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(true); }); - it('should emit warnings when disabled', (done) => { + it('should emit warnings when disabled', async () => { const compiler = pack('error', { failOnError: false }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasErrors()).toBe(false); - expect(stats.hasWarnings()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(false); + expect(stats.hasWarnings()).toBe(true); }); - it('should correctly identifies a success', (done) => { + it('should correctly identifies a success', async () => { const compiler = pack('good', { failOnError: true }); - compiler.run((err) => { - expect(err).toBeNull(); - done(); - }); + await compiler.runAsync(); }); }); diff --git a/test/fail-on-warning.test.js b/test/fail-on-warning.test.js index d03d9c9..9141aff 100644 --- a/test/fail-on-warning.test.js +++ b/test/fail-on-warning.test.js @@ -1,22 +1,17 @@ import pack from './utils/pack'; describe('fail on warning', () => { - it('should emits errors', (done) => { + it('should emits errors', async () => { const compiler = pack('warn', { failOnWarning: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(true); }); - it('should correctly identifies a success', (done) => { + it('should correctly identifies a success', async () => { const compiler = pack('good', { failOnWarning: true }); - compiler.run((err) => { - expect(err).toBeNull(); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/fixtures/flat-config.js b/test/fixtures/flat-config.js new file mode 100644 index 0000000..c213271 --- /dev/null +++ b/test/fixtures/flat-config.js @@ -0,0 +1,7 @@ + +module.exports = [ + { + files: ["*.js"], + rules: {} + } +]; diff --git a/test/flat-config.test.js b/test/flat-config.test.js new file mode 100644 index 0000000..f80c485 --- /dev/null +++ b/test/flat-config.test.js @@ -0,0 +1,42 @@ +import { join } from 'path'; + +import pack from './utils/pack'; + +describe('succeed on flat-configuration', () => { + it('cannot load FlatESLint class on default ESLint module', async () => { + const overrideConfigFile = join(__dirname, 'fixtures', 'flat-config.js'); + const compiler = pack('full-of-problems', { + configType: 'flat', + overrideConfigFile, + threads: 1, + }); + + const stats = await compiler.runAsync(); + const { errors } = stats.compilation; + + expect(stats.hasErrors()).toBe(true); + expect(errors).toHaveLength(1); + expect(errors[0].message).toMatch( + /Couldn't find FlatESLint, you might need to set eslintPath to 'eslint\/use-at-your-own-risk'/i, + ); + }); + + it('finds errors on files', async () => { + const overrideConfigFile = join(__dirname, 'fixtures', 'flat-config.js'); + const compiler = pack('full-of-problems', { + configType: 'flat', + // needed for now + eslintPath: 'eslint/use-at-your-own-risk', + overrideConfigFile, + threads: 1, + }); + + const stats = await compiler.runAsync(); + const { errors } = stats.compilation; + + expect(stats.hasErrors()).toBe(true); + expect(errors).toHaveLength(1); + expect(errors[0].message).toMatch(/test\/fixtures\/full-of-problems\.js/i); + expect(stats.hasWarnings()).toBe(true); + }); +}); diff --git a/test/formatter-custom.test.js b/test/formatter-custom.test.js index b37ab45..25979c2 100644 --- a/test/formatter-custom.test.js +++ b/test/formatter-custom.test.js @@ -1,39 +1,33 @@ import pack from './utils/pack'; describe('formatter eslint', () => { - it('should use custom formatter as function', (done) => { + it('should use custom formatter as function', async () => { const formatter = require('./mock/formatter'); const compiler = pack('error', { formatter }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - expect(stats.compilation.errors[0].message).toBeTruthy(); - const message = JSON.parse( - stats.compilation.errors[0].message.replace('[eslint] ', ''), - ); - expect(message.formatter).toEqual('mock'); - expect(message.results).toBeTruthy(); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); + expect(stats.compilation.errors[0].message).toBeTruthy(); + const message = JSON.parse( + stats.compilation.errors[0].message.replace('[eslint] ', ''), + ); + expect(message.formatter).toEqual('mock'); + expect(message.results).toBeTruthy(); }); - it('should use custom formatter as string', (done) => { + it('should use custom formatter as string', async () => { const formatter = './test/mock/formatter'; const compiler = pack('error', { formatter }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - expect(stats.compilation.errors[0].message).toBeTruthy(); - const message = JSON.parse( - stats.compilation.errors[0].message.replace('[eslint] ', ''), - ); - expect(message.formatter).toEqual('mock'); - expect(message.results).toBeTruthy(); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); + expect(stats.compilation.errors[0].message).toBeTruthy(); + const message = JSON.parse( + stats.compilation.errors[0].message.replace('[eslint] ', ''), + ); + expect(message.formatter).toEqual('mock'); + expect(message.results).toBeTruthy(); }); }); diff --git a/test/formatter-eslint.test.js b/test/formatter-eslint.test.js index 6b016ff..7b991e0 100644 --- a/test/formatter-eslint.test.js +++ b/test/formatter-eslint.test.js @@ -1,15 +1,12 @@ import pack from './utils/pack'; describe('formatter eslint', () => { - it('should use eslint formatter', (done) => { + it('should use eslint formatter', async () => { const compiler = pack('error'); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - expect(stats.compilation.errors[0].message).toBeTruthy(); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); + expect(stats.compilation.errors[0].message).toBeTruthy(); }); }); diff --git a/test/formatter-official.test.js b/test/formatter-official.test.js index 62ddbb6..814a666 100644 --- a/test/formatter-official.test.js +++ b/test/formatter-official.test.js @@ -1,15 +1,12 @@ import pack from './utils/pack'; describe('formatter official', () => { - it('should use official formatter', (done) => { + it('should use official formatter', async () => { const compiler = pack('error', { formatter: 'json' }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - expect(stats.compilation.errors[0].message).toBeTruthy(); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); + expect(stats.compilation.errors[0].message).toBeTruthy(); }); }); diff --git a/test/multiple-instances.test.js b/test/multiple-instances.test.js index 77d51e9..fc9fd80 100644 --- a/test/multiple-instances.test.js +++ b/test/multiple-instances.test.js @@ -3,7 +3,7 @@ import ESLintPlugin from '../src'; import pack from './utils/pack'; describe('multiple instances', () => { - it("should don't fail", (done) => { + it("should don't fail", async () => { const compiler = pack( 'multiple', {}, @@ -15,15 +15,12 @@ describe('multiple instances', () => { }, ); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); - it('should fail on first instance', (done) => { + it('should fail on first instance', async () => { const compiler = pack( 'multiple', {}, @@ -35,15 +32,12 @@ describe('multiple instances', () => { }, ); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); - it('should fail on second instance', (done) => { + it('should fail on second instance', async () => { const compiler = pack( 'multiple', {}, @@ -55,11 +49,8 @@ describe('multiple instances', () => { }, ); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); }); diff --git a/test/ok.test.js b/test/ok.test.js index 1820088..4336711 100644 --- a/test/ok.test.js +++ b/test/ok.test.js @@ -1,14 +1,11 @@ import pack from './utils/pack'; describe('ok', () => { - it("should don't throw error if file is ok", (done) => { + it("should don't throw error if file is ok", async () => { const compiler = pack('good'); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/parameters.test.js b/test/parameters.test.js index 35575de..1c8044f 100644 --- a/test/parameters.test.js +++ b/test/parameters.test.js @@ -1,7 +1,7 @@ import pack from './utils/pack'; describe('parameters', () => { - it('should supports query strings parameters', (done) => { + it('should supports query strings parameters', async () => { const loaderOptions = { overrideConfig: { rules: { semi: 0 }, @@ -9,11 +9,8 @@ describe('parameters', () => { }; const compiler = pack('good', loaderOptions); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/query.test.js b/test/query.test.js index 386750b..0210ebf 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1,7 +1,7 @@ import pack from './utils/pack'; describe('query', () => { - it('should correctly resolve file despite query path', (done) => { + it('should correctly resolve file despite query path', async () => { const compiler = pack( 'query', {}, @@ -14,12 +14,8 @@ describe('query', () => { }, ); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/quiet.test.js b/test/quiet.test.js index c29dd5c..11c3cb8 100644 --- a/test/quiet.test.js +++ b/test/quiet.test.js @@ -1,25 +1,19 @@ import pack from './utils/pack'; describe('quiet', () => { - it('should not emit warnings if quiet is set', (done) => { + it('should not emit warnings if quiet is set', async () => { const compiler = pack('warn', { quiet: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); - it('should emit errors, but not emit warnings if quiet is set', (done) => { + it('should emit errors, but not emit warnings if quiet is set', async () => { const compiler = pack('full-of-problems', { quiet: true }); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); }); diff --git a/test/resource-query.test.js b/test/resource-query.test.js index 7c342eb..8f02804 100644 --- a/test/resource-query.test.js +++ b/test/resource-query.test.js @@ -1,7 +1,7 @@ import pack from './utils/pack'; describe('resource-query', () => { - it('should exclude the match resource query', (done) => { + it('should exclude the match resource query', async () => { const compiler = pack( 'resource-query', { @@ -15,11 +15,8 @@ describe('resource-query', () => { }, ); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/test/symbols.test.js b/test/symbols.test.js index 194ac24..34ce638 100644 --- a/test/symbols.test.js +++ b/test/symbols.test.js @@ -7,18 +7,15 @@ describe('symbols', () => { jest.restoreAllMocks(); }); - it('should return error', (done) => { + it('should return error', async () => { const compiler = pack( 'symbols', {}, { context: join(__dirname, 'fixtures/[symbols]') }, ); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(false); - expect(stats.hasErrors()).toBe(true); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(false); + expect(stats.hasErrors()).toBe(true); }); }); diff --git a/test/utils/pack.js b/test/utils/pack.js index 37276fb..acba716 100644 --- a/test/utils/pack.js +++ b/test/utils/pack.js @@ -9,5 +9,23 @@ import conf from './conf'; * @param {webpack.Configuration} webpackConf * @returns {ReturnType} */ -export default (context, pluginConf = {}, webpackConf = {}) => - webpack(conf(context, pluginConf, webpackConf)); +export default (context, pluginConf = {}, webpackConf = {}) => { + const compiler = webpack(conf(context, pluginConf, webpackConf)); + + return { + runAsync() { + return new Promise((resolve, reject) => { + compiler.run((err, stats) => { + if (err) { + reject(err); + } else { + resolve(stats); + } + }); + }); + }, + watch(options, fn) { + return compiler.watch(options, fn); + }, + }; +}; diff --git a/test/warning.test.js b/test/warning.test.js index bd523e6..d378f4c 100644 --- a/test/warning.test.js +++ b/test/warning.test.js @@ -1,14 +1,11 @@ import pack from './utils/pack'; describe('warning', () => { - it('should emit warnings', (done) => { + it('should emit warnings', async () => { const compiler = pack('warn'); - compiler.run((err, stats) => { - expect(err).toBeNull(); - expect(stats.hasWarnings()).toBe(true); - expect(stats.hasErrors()).toBe(false); - done(); - }); + const stats = await compiler.runAsync(); + expect(stats.hasWarnings()).toBe(true); + expect(stats.hasErrors()).toBe(false); }); }); diff --git a/types/utils.d.ts b/types/utils.d.ts index 1ece4b9..e2b7d7e 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -18,12 +18,12 @@ export function arrify( ): T extends null | undefined ? [] : T extends string - ? [string] - : T extends readonly unknown[] - ? T - : T extends Iterable - ? T_1[] - : [T]; + ? [string] + : T extends readonly unknown[] + ? T + : T extends Iterable + ? T_1[] + : [T]; /** * @param {string|string[]} files * @param {string} context