From 8c63c8a98b82ed0f551219d0e7663e7605dab927 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 11:14:32 +0800 Subject: [PATCH 01/39] New integration test --- test/integration/config.js | 38 ------- test/integration/package.json | 10 -- test/integration/run-eslint.mjs | 98 +++++++++++++++++ test/integration/test.mjs | 188 +++++++++----------------------- 4 files changed, 147 insertions(+), 187 deletions(-) delete mode 100644 test/integration/config.js delete mode 100644 test/integration/package.json create mode 100644 test/integration/run-eslint.mjs diff --git a/test/integration/config.js b/test/integration/config.js deleted file mode 100644 index d9af78bf98..0000000000 --- a/test/integration/config.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -module.exports = { - root: true, - parser: '@babel/eslint-parser', - parserOptions: { - requireConfigFile: false, - babelOptions: { - babelrc: false, - configFile: false, - parserOpts: { - plugins: [ - 'jsx', - 'doExpressions', - 'exportDefaultFrom', - ], - }, - }, - }, - plugins: [ - 'unicorn', - ], - extends: 'plugin:unicorn/all', - rules: { - // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` - 'unicorn/string-content': 'off', - }, - overrides: [ - { - files: ['*.ts'], - parser: '@typescript-eslint/parser', - }, - { - files: ['*.vue'], - parser: 'vue-eslint-parser', - }, - ], -}; diff --git a/test/integration/package.json b/test/integration/package.json deleted file mode 100644 index ae5df13bef..0000000000 --- a/test/integration/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "private": true, - "name": "eslint-config-unicorn-tester", - "engines": { - "node": ">=12" - }, - "dependencies": { - "eslint-plugin-unicorn": "file:../.." - } -} diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs new file mode 100644 index 0000000000..c124c4feaf --- /dev/null +++ b/test/integration/run-eslint.mjs @@ -0,0 +1,98 @@ +import {ESLint} from 'eslint'; +import {codeFrameColumns} from '@babel/code-frame'; +import eslintPluginUnicorn from '../../index.js'; + +class UnicornIntegrationTestError extends AggregateError { + name = 'UnicornIntegrationTestError'; + + constructor(project, errors) { + super(errors, `Error thrown when linting '${project.name}' project.`); + + this.project = project; + } +} + +class UnicornEslintFatalError extends SyntaxError { + name = 'UnicornEslintFatalError'; + + constructor(message, file) { + super(message.message); + + this.eslintMessage = message; + this.eslintFile = file; + } + + get details() { + const {source} = this.eslintFile; + const {line, column, message} = this.eslintMessage; + + return codeFrameColumns( + source, + {start: {line, column}}, + {message, highlightCode: true}, + ); + } +} + +async function runEslint(project) { + const eslint = new ESLint({ + cwd: project.location, + baseConfig: eslintPluginUnicorn.configs.all, + useEslintrc: false, + extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], + plugins: { + unicorn: eslintPluginUnicorn, + }, + fix: true, + overrideConfig: { + parser: '@babel/eslint-parser', + parserOptions: { + requireConfigFile: false, + babelOptions: { + babelrc: false, + configFile: false, + parserOpts: { + plugins: [ + 'jsx', + 'doExpressions', + 'exportDefaultFrom', + ], + }, + }, + }, + ignorePatterns: project.ignore, + rules: { + // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` + 'unicorn/string-content': 'off', + }, + overrides: [ + { + files: ['*.ts', '*.mts', '*.cts', '*.tsx'], + parser: '@typescript-eslint/parser', + }, + { + files: ['*.vue'], + parser: 'vue-eslint-parser', + }, + ], + }, + }); + + const result = await eslint.lintFiles('.'); + + const errors = result + .filter(file => file.fatalErrorCount > 0) + .flatMap( + file => file.messages + .filter(message => message.fatal) + .map(message => new UnicornEslintFatalError(message, file)), + ); + + if (errors.length === 0) { + return; + } + + throw new UnicornIntegrationTestError(project, errors); +} + +export default runEslint; diff --git a/test/integration/test.mjs b/test/integration/test.mjs index ba29cbbc4c..fc9b057ef6 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -2,186 +2,96 @@ import process from 'node:process'; import fs from 'node:fs'; import path from 'node:path'; -import {fileURLToPath} from 'node:url'; import Listr from 'listr'; import {execa} from 'execa'; import chalk from 'chalk'; import {isCI} from 'ci-info'; import mem from 'mem'; import allProjects from './projects.mjs'; +import runEslint from './run-eslint.mjs'; -const dirname = path.dirname(fileURLToPath(import.meta.url)); const projectsArguments = process.argv.slice(2); const projects = projectsArguments.length === 0 ? allProjects : allProjects.filter(({name}) => projectsArguments.includes(name)); -const enrichErrors = (packageName, cliArguments, f) => async (...arguments_) => { - try { - return await f(...arguments_); - } catch (error) { - error.packageName = packageName; - error.cliArgs = cliArguments; - throw error; - } -}; - -const makeEslintTask = (project, destination) => { - const arguments_ = [ - 'eslint', - project.path || '.', - '--fix-dry-run', - '--no-eslintrc', - '--ext', - '.js,.ts,.vue', - '--format', - 'json', - '--config', - path.join(dirname, 'config.js'), - ]; - - for (const pattern of project.ignore) { - arguments_.push('--ignore-pattern', pattern); - } - - return enrichErrors(project.name, arguments_, async () => { - let stdout; - let processError; - try { - ({stdout} = await execa('npx', arguments_, {cwd: destination, localDir: dirname})); - } catch (error) { - ({stdout} = error); - processError = error; - - if (!stdout) { - throw error; - } - } - - let files; - try { - files = JSON.parse(stdout); - } catch (error) { - console.error('Error while parsing eslint output:', error); - - if (processError) { - throw processError; - } - - throw error; - } - - for (const file of files) { - for (const message of file.messages) { - if (message.fatal) { - const error = new Error(message.message); - error.eslintJob = { - destination, - project, - file, - }; - error.eslintMessage = message; - throw error; - } - } - } - }); -}; - const getBranch = mem(async dirname => { const {stdout} = await execa('git', ['branch', '--show-current'], {cwd: dirname}); return stdout; }); -const execute = project => { - const destination = project.location || path.join(dirname, 'fixtures', project.name); - - return new Listr([ +const execute = project => new Listr( + [ { title: 'Cloning', - skip: () => fs.existsSync(destination) ? 'Project already downloaded.' : false, + skip: () => fs.existsSync(project.location) ? 'Project already downloaded.' : false, task: () => execa('git', [ 'clone', project.repository, '--single-branch', '--depth', '1', - destination, - ]), + project.location, + ], {stdout: 'inherit', stderr: 'inherit'}), }, { title: 'Running eslint', - task: makeEslintTask(project, destination), + task: () => runEslint(project), }, ].map(({title, task, skip}) => ({ title: `${project.name} / ${title}`, skip, task, - })), { - exitOnError: false, - }); -}; - -const list = new Listr([ - { - title: 'Setup', - task: () => execa('npm', ['install'], {cwd: dirname, stdout: 'inherit', stderr: 'inherit'}), - }, - { - title: 'Integration tests', - task() { - const tests = new Listr({concurrent: true}); - - for (const project of projects) { - tests.add([ - { - title: project.name, - task: () => execute(project), - }, - ]); - } - - return tests; - }, - }, -], { - renderer: isCI ? 'verbose' : 'default', -}); - -async function logError(error) { - if (error.errors) { - for (const error2 of error.errors) { - console.error('\n', chalk.red.bold.underline(error2.packageName), chalk.gray('(' + error2.cliArgs.join(' ') + ')')); - console.error(error2.message); + })), + {exitOnError: false}, +); + +async function printEslintError(eslintError) { + const {message, project} = eslintError; + + console.error( + chalk.red.bold.underline(`[${project.name}]`), + message, + ); + + project.branch ??= await getBranch(project.location); + for (const error of eslintError.errors) { + let file = path.relative(project.location, error.eslintFile.filePath); + if (project.repository) { + file = `${project.repository}/blob/${project.branch}/${file}`; + } - if (error2.stderr) { - console.error(chalk.gray(error2.stderr)); - } + file += `#L${error.eslintMessage.line}`; + console.log(); + console.error(chalk.blue.bold.underline(file)); + console.error(error.details); + } +} - if (error2.eslintMessage) { - const {file, project, destination} = error2.eslintJob; - const {line} = error2.eslintMessage; +async function printListrError(listrError) { + process.exitCode = 1; - if (project.repository) { - // eslint-disable-next-line no-await-in-loop - const branch = await getBranch(destination); - console.error(chalk.gray(`${project.repository}/blob/${branch}/${path.relative(destination, file.filePath)}#L${line}`)); - } else { - console.error(chalk.gray(`${path.relative(destination, file.filePath)}#L${line}`)); - } + if (!listrError.errors) { + console.error(listrError); + return; + } - console.error(chalk.gray(JSON.stringify(error2.eslintMessage, undefined, 2))); - } + for (const error of listrError.errors) { + if (error.name !== 'UnicornIntegrationTestError') { + console.error(error); + continue; } - } else { - console.error(error); - } - process.exit(1); + // eslint-disable-next-line no-await-in-loop + await printEslintError(error); + } } try { - await list.run(); + await new Listr( + projects.map(project => ({title: project.name, task: () => execute(project)})), + {renderer: isCI ? 'verbose' : 'default'}, + ).run(); } catch (error) { - await logError(error); + await printListrError(error); } From 71e1ff4b38c0c18a95f9608db1dd0624ae142430 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 11:22:11 +0800 Subject: [PATCH 02/39] Missing file --- test/integration/projects.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index d1ee160bf9..622e348484 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -174,6 +174,7 @@ export default [ } = project; return { + location: path.join(dirname, 'fixtures', name), ...project, name, repository, From 569c31d98ef4521acee2f26929a1c80ff42a8859 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 11:26:03 +0800 Subject: [PATCH 03/39] [Expect failure] Break rule --- rules/prefer-node-protocol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/prefer-node-protocol.js b/rules/prefer-node-protocol.js index 55a44a35cd..c48a46708c 100644 --- a/rules/prefer-node-protocol.js +++ b/rules/prefer-node-protocol.js @@ -35,7 +35,7 @@ const create = () => ({ messageId: MESSAGE_ID, data: {moduleName: value}, /** @param {import('eslint').Rule.RuleFixer} fixer */ - fix: fixer => replaceStringLiteral(fixer, node, 'node:', 0, 0), + fix: fixer => replaceStringLiteral(fixer, node, '"\'', 0, 0), }; }, }); From 8c2ac66148e5deb2e1e8a7dd05e49ff5aaa4547a Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 11:34:12 +0800 Subject: [PATCH 04/39] Disable angular test --- test/integration/projects.mjs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 622e348484..9bc30a8195 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -61,17 +61,18 @@ export default [ 'tests/**', ], }, - { - repository: 'https://github.com/angular/angular', - ignore: [ - 'aio/content/examples/animations/src/app/open-close.component.3.ts', - 'aio/content/examples/router/src/app/app-routing.module.9.ts', - 'aio/tools/transforms/templates/data-module.template.js', - 'aio/tools/transforms/authors-package/index.js', // This file use `package` keyword as variable - 'packages/compiler-cli/test/**', - 'tools/**', - ], - }, + // Too slow + // { + // repository: 'https://github.com/angular/angular', + // ignore: [ + // 'aio/content/examples/animations/src/app/open-close.component.3.ts', + // 'aio/content/examples/router/src/app/app-routing.module.9.ts', + // 'aio/tools/transforms/templates/data-module.template.js', + // 'aio/tools/transforms/authors-package/index.js', // This file use `package` keyword as variable + // 'packages/compiler-cli/test/**', + // 'tools/**', + // ], + // }, { repository: 'https://github.com/microsoft/typescript', ignore: [ From f3908c38dc54cd457b3aaec834af766df7cf0928 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 12:46:07 +0800 Subject: [PATCH 05/39] Fix --- test/integration/projects.mjs | 8 +++++++- test/integration/run-eslint.mjs | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 9bc30a8195..c3d6abe969 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -94,7 +94,13 @@ export default [ 'https://github.com/sindresorhus/create-dmg', 'https://github.com/sindresorhus/cp-file', 'https://github.com/sindresorhus/capture-website', - 'https://github.com/sindresorhus/file-type', + { + repository: 'https://github.com/sindresorhus/file-type', + ignore: [ + // Not a text file + 'fixture/fixture-bdav.mts', + ] + }, 'https://github.com/sindresorhus/slugify', { repository: 'https://github.com/gatsbyjs/gatsby', diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index c124c4feaf..cebc5c38da 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -39,7 +39,9 @@ async function runEslint(project) { cwd: project.location, baseConfig: eslintPluginUnicorn.configs.all, useEslintrc: false, - extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], + extensions: ['.js', '.ts', '.vue'], + // TODO[@fisker]: Lint more files + // extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], plugins: { unicorn: eslintPluginUnicorn, }, From d2d1968b9936eab75fc4079e5a1a513f20c34f11 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 13:27:13 +0800 Subject: [PATCH 06/39] Linting --- test/integration/projects.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index c3d6abe969..dacd306522 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -99,7 +99,7 @@ export default [ ignore: [ // Not a text file 'fixture/fixture-bdav.mts', - ] + ], }, 'https://github.com/sindresorhus/slugify', { From c4beca69a5a737917a9bb526fa2695b64f249e60 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 13:32:20 +0800 Subject: [PATCH 07/39] Fix --- test/integration/run-eslint.mjs | 24 ++++-------------------- test/integration/test.mjs | 2 +- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index cebc5c38da..94e8a99ed4 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -22,12 +22,12 @@ class UnicornEslintFatalError extends SyntaxError { this.eslintFile = file; } - get details() { - const {source} = this.eslintFile; + get codeFrame() { + const {source, output} = this.eslintFile; const {line, column, message} = this.eslintMessage; return codeFrameColumns( - source, + source ?? output, {start: {line, column}}, {message, highlightCode: true}, ); @@ -39,29 +39,13 @@ async function runEslint(project) { cwd: project.location, baseConfig: eslintPluginUnicorn.configs.all, useEslintrc: false, - extensions: ['.js', '.ts', '.vue'], - // TODO[@fisker]: Lint more files - // extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], + extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], plugins: { unicorn: eslintPluginUnicorn, }, fix: true, overrideConfig: { parser: '@babel/eslint-parser', - parserOptions: { - requireConfigFile: false, - babelOptions: { - babelrc: false, - configFile: false, - parserOpts: { - plugins: [ - 'jsx', - 'doExpressions', - 'exportDefaultFrom', - ], - }, - }, - }, ignorePatterns: project.ignore, rules: { // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` diff --git a/test/integration/test.mjs b/test/integration/test.mjs index fc9b057ef6..94d2a0876b 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -64,7 +64,7 @@ async function printEslintError(eslintError) { file += `#L${error.eslintMessage.line}`; console.log(); console.error(chalk.blue.bold.underline(file)); - console.error(error.details); + console.error(error.codeFrame); } } From 2838c7c544250f6bf274d8e9cd1b289c951d5833 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 13:34:32 +0800 Subject: [PATCH 08/39] Fix --- test/integration/run-eslint.mjs | 12 ++++++++++++ test/integration/test.mjs | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 94e8a99ed4..4c24999bd6 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -46,6 +46,18 @@ async function runEslint(project) { fix: true, overrideConfig: { parser: '@babel/eslint-parser', + parserOptions: { + requireConfigFile: false, + babelOptions: { + babelrc: false, + configFile: false, + parserOpts: { + plugins: [ + 'jsx', + ], + }, + }, + }, ignorePatterns: project.ignore, rules: { // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` diff --git a/test/integration/test.mjs b/test/integration/test.mjs index 94d2a0876b..ac0f726e0b 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -61,7 +61,9 @@ async function printEslintError(eslintError) { file = `${project.repository}/blob/${project.branch}/${file}`; } - file += `#L${error.eslintMessage.line}`; + if (typeof error.eslintMessage.line == 'number') { + file += `#L${error.eslintMessage.line}`; + } console.log(); console.error(chalk.blue.bold.underline(file)); console.error(error.codeFrame); From 3bdcd2916b65229ac7d938f5adf34a94854a23e6 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 14:01:33 +0800 Subject: [PATCH 09/39] Revert rule change --- rules/prefer-node-protocol.js | 2 +- test/integration/test.mjs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rules/prefer-node-protocol.js b/rules/prefer-node-protocol.js index c48a46708c..55a44a35cd 100644 --- a/rules/prefer-node-protocol.js +++ b/rules/prefer-node-protocol.js @@ -35,7 +35,7 @@ const create = () => ({ messageId: MESSAGE_ID, data: {moduleName: value}, /** @param {import('eslint').Rule.RuleFixer} fixer */ - fix: fixer => replaceStringLiteral(fixer, node, '"\'', 0, 0), + fix: fixer => replaceStringLiteral(fixer, node, 'node:', 0, 0), }; }, }); diff --git a/test/integration/test.mjs b/test/integration/test.mjs index ac0f726e0b..9b6f3bc9b4 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -49,6 +49,7 @@ const execute = project => new Listr( async function printEslintError(eslintError) { const {message, project} = eslintError; + console.log(); console.error( chalk.red.bold.underline(`[${project.name}]`), message, @@ -66,6 +67,7 @@ async function printEslintError(eslintError) { } console.log(); console.error(chalk.blue.bold.underline(file)); + console.log(); console.error(error.codeFrame); } } @@ -92,7 +94,7 @@ async function printListrError(listrError) { try { await new Listr( projects.map(project => ({title: project.name, task: () => execute(project)})), - {renderer: isCI ? 'verbose' : 'default'}, + {renderer: 'verbose'}, ).run(); } catch (error) { await printListrError(error); From 1e911c6d64c60cf74aad79609c3c53eeeb7fd17f Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 14:02:44 +0800 Subject: [PATCH 10/39] Add angular back --- test/integration/projects.mjs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index dacd306522..b74baf0c23 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -61,18 +61,17 @@ export default [ 'tests/**', ], }, - // Too slow - // { - // repository: 'https://github.com/angular/angular', - // ignore: [ - // 'aio/content/examples/animations/src/app/open-close.component.3.ts', - // 'aio/content/examples/router/src/app/app-routing.module.9.ts', - // 'aio/tools/transforms/templates/data-module.template.js', - // 'aio/tools/transforms/authors-package/index.js', // This file use `package` keyword as variable - // 'packages/compiler-cli/test/**', - // 'tools/**', - // ], - // }, + { + repository: 'https://github.com/angular/angular', + ignore: [ + 'aio/content/examples/animations/src/app/open-close.component.3.ts', + 'aio/content/examples/router/src/app/app-routing.module.9.ts', + 'aio/tools/transforms/templates/data-module.template.js', + 'aio/tools/transforms/authors-package/index.js', // This file use `package` keyword as variable + 'packages/compiler-cli/test/**', + 'tools/**', + ], + }, { repository: 'https://github.com/microsoft/typescript', ignore: [ From 09f7890e6829fa28dfc9bbd1c750e3d6011a6287 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 14:05:56 +0800 Subject: [PATCH 11/39] Linting --- test/integration/test.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/integration/test.mjs b/test/integration/test.mjs index 9b6f3bc9b4..05aab0c1ce 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -62,9 +62,10 @@ async function printEslintError(eslintError) { file = `${project.repository}/blob/${project.branch}/${file}`; } - if (typeof error.eslintMessage.line == 'number') { + if (typeof error.eslintMessage.line === 'number') { file += `#L${error.eslintMessage.line}`; } + console.log(); console.error(chalk.blue.bold.underline(file)); console.log(); @@ -94,7 +95,7 @@ async function printListrError(listrError) { try { await new Listr( projects.map(project => ({title: project.name, task: () => execute(project)})), - {renderer: 'verbose'}, + {renderer: isCI ? 'verbose' : 'default'}, ).run(); } catch (error) { await printListrError(error); From e4dc5b9491f284b46d0e91e0418c73d3bcaaeabe Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 14:26:15 +0800 Subject: [PATCH 12/39] Run with worker --- package.json | 1 + test/integration/run-eslint.mjs | 47 ++++--------------------------- test/integration/worker.mjs | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 test/integration/worker.mjs diff --git a/package.json b/package.json index b384bf2523..a640daae3f 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "npm-package-json-lint": "^6.3.0", "npm-run-all": "^4.1.5", "outdent": "^0.8.0", + "piscina": "^3.2.0", "typescript": "^4.8.3", "vue-eslint-parser": "^9.1.0", "xo": "^0.52.3" diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 4c24999bd6..10d203c63f 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -1,5 +1,6 @@ import {ESLint} from 'eslint'; import {codeFrameColumns} from '@babel/code-frame'; +import Piscina from 'piscina'; import eslintPluginUnicorn from '../../index.js'; class UnicornIntegrationTestError extends AggregateError { @@ -34,49 +35,11 @@ class UnicornEslintFatalError extends SyntaxError { } } +const piscina = new Piscina({ + filename: new URL('./worker.mjs', import.meta.url).href +}); async function runEslint(project) { - const eslint = new ESLint({ - cwd: project.location, - baseConfig: eslintPluginUnicorn.configs.all, - useEslintrc: false, - extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], - plugins: { - unicorn: eslintPluginUnicorn, - }, - fix: true, - overrideConfig: { - parser: '@babel/eslint-parser', - parserOptions: { - requireConfigFile: false, - babelOptions: { - babelrc: false, - configFile: false, - parserOpts: { - plugins: [ - 'jsx', - ], - }, - }, - }, - ignorePatterns: project.ignore, - rules: { - // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` - 'unicorn/string-content': 'off', - }, - overrides: [ - { - files: ['*.ts', '*.mts', '*.cts', '*.tsx'], - parser: '@typescript-eslint/parser', - }, - { - files: ['*.vue'], - parser: 'vue-eslint-parser', - }, - ], - }, - }); - - const result = await eslint.lintFiles('.'); + const result = await piscina.run(project); const errors = result .filter(file => file.fatalErrorCount > 0) diff --git a/test/integration/worker.mjs b/test/integration/worker.mjs new file mode 100644 index 0000000000..118b3ca124 --- /dev/null +++ b/test/integration/worker.mjs @@ -0,0 +1,50 @@ +import {ESLint} from 'eslint'; +import {codeFrameColumns} from '@babel/code-frame'; +import eslintPluginUnicorn from '../../index.js'; + +function runEslint(project) { + const eslint = new ESLint({ + cwd: project.location, + baseConfig: eslintPluginUnicorn.configs.all, + useEslintrc: false, + extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], + plugins: { + unicorn: eslintPluginUnicorn, + }, + fix: true, + overrideConfig: { + parser: '@babel/eslint-parser', + parserOptions: { + requireConfigFile: false, + babelOptions: { + babelrc: false, + configFile: false, + parserOpts: { + plugins: [ + 'jsx', + ], + }, + }, + }, + ignorePatterns: project.ignore, + rules: { + // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` + 'unicorn/string-content': 'off', + }, + overrides: [ + { + files: ['*.ts', '*.mts', '*.cts', '*.tsx'], + parser: '@typescript-eslint/parser', + }, + { + files: ['*.vue'], + parser: 'vue-eslint-parser', + }, + ], + }, + }); + + return eslint.lintFiles('.'); +} + +export default runEslint; From 8448d39ed4a792e0d11391cd934d943afbd7524d Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 14:38:37 +0800 Subject: [PATCH 13/39] Linting --- test/integration/run-eslint.mjs | 4 +--- test/integration/worker.mjs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 10d203c63f..1715977984 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -1,7 +1,5 @@ -import {ESLint} from 'eslint'; import {codeFrameColumns} from '@babel/code-frame'; import Piscina from 'piscina'; -import eslintPluginUnicorn from '../../index.js'; class UnicornIntegrationTestError extends AggregateError { name = 'UnicornIntegrationTestError'; @@ -36,7 +34,7 @@ class UnicornEslintFatalError extends SyntaxError { } const piscina = new Piscina({ - filename: new URL('./worker.mjs', import.meta.url).href + filename: new URL('worker.mjs', import.meta.url).href, }); async function runEslint(project) { const result = await piscina.run(project); diff --git a/test/integration/worker.mjs b/test/integration/worker.mjs index 118b3ca124..fbfeb4297d 100644 --- a/test/integration/worker.mjs +++ b/test/integration/worker.mjs @@ -1,5 +1,4 @@ import {ESLint} from 'eslint'; -import {codeFrameColumns} from '@babel/code-frame'; import eslintPluginUnicorn from '../../index.js'; function runEslint(project) { From 9e009c2d693089378018126f59d8370a83ac6cab Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 14:56:32 +0800 Subject: [PATCH 14/39] `concurrent` --- test/integration/test.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/integration/test.mjs b/test/integration/test.mjs index 05aab0c1ce..793d7216a3 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -95,7 +95,10 @@ async function printListrError(listrError) { try { await new Listr( projects.map(project => ({title: project.name, task: () => execute(project)})), - {renderer: isCI ? 'verbose' : 'default'}, + { + renderer: isCI ? 'verbose' : 'default', + concurrent: true, + }, ).run(); } catch (error) { await printListrError(error); From c199a8fb26c10bd5e49946970b681b97a6d098f0 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 15:03:32 +0800 Subject: [PATCH 15/39] Enable `exportDefaultFrom` plugin --- test/integration/worker.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/worker.mjs b/test/integration/worker.mjs index fbfeb4297d..e48f249971 100644 --- a/test/integration/worker.mjs +++ b/test/integration/worker.mjs @@ -21,6 +21,7 @@ function runEslint(project) { parserOpts: { plugins: [ 'jsx', + 'exportDefaultFrom', ], }, }, From 4bcbb6cc56512eab360cbfe0d03ecd0f4a16c92e Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 15:23:19 +0800 Subject: [PATCH 16/39] Ignore --- test/integration/projects.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index b74baf0c23..b8348f433d 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -96,8 +96,8 @@ export default [ { repository: 'https://github.com/sindresorhus/file-type', ignore: [ - // Not a text file - 'fixture/fixture-bdav.mts', + // Contains non-text `.mts` file + 'fixture/**', ], }, 'https://github.com/sindresorhus/slugify', From 720e2d6b1a09e96b7ece2b421de953d7866f369f Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 15:52:17 +0800 Subject: [PATCH 17/39] Ignore --- test/integration/projects.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index b8348f433d..68487d7ce0 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -12,6 +12,7 @@ export default [ repository: 'https://github.com/avajs/ava', ignore: [ 'test/node_modules', + 'test-tap/fixture/report/edgecases/ast-syntax-error.cjs', ], }, 'https://github.com/chalk/chalk', From d97e9b4c09bf14ded54b65c581a632c4684850cc Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 15:57:07 +0800 Subject: [PATCH 18/39] Disable some huge projects --- test/integration/projects.mjs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 68487d7ce0..c7c415114b 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -73,13 +73,14 @@ export default [ 'tools/**', ], }, - { - repository: 'https://github.com/microsoft/typescript', - ignore: [ - // These file use `'\033'` - 'build/**', - ], - }, + // Too slow + // { + // repository: 'https://github.com/microsoft/typescript', + // ignore: [ + // // These file use `'\033'` + // 'build/**', + // ], + // }, // This repo use `override` keyword which is not avaiable before TS4.3, temporary disable // https://github.com/microsoft/vscode/pull/120690/files // { @@ -161,7 +162,8 @@ export default [ ], }, // #903 - 'https://github.com/mattermost/mattermost-webapp', + // Too slow + // 'https://github.com/mattermost/mattermost-webapp', // #1030 'https://github.com/astrofox-io/astrofox', // #1075 From 5825e595d1fa3e205f651e44211c309d29d43f18 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 16:34:54 +0800 Subject: [PATCH 19/39] Split into MANY groups --- .github/workflows/main.yml | 24 +++- test/integration/projects.mjs | 200 +++++++++++++++++----------------- test/integration/readme.md | 2 +- test/integration/test.mjs | 31 +++++- 4 files changed, 155 insertions(+), 102 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c7513eb405..5993df5ea0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,9 +49,31 @@ jobs: - run: npm install - run: npm run run-rules-on-codebase integration: + name: Integration test (${{ matrix.group }}) + strategy: + fail-fast: false + matrix: + group: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + - 16 + - 17 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npm install - - run: npm run integration + - run: npm run integration --group ${{ matrix.group }} diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index c7c415114b..0e1d9a986b 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -3,58 +3,91 @@ import {fileURLToPath} from 'node:url'; const dirname = path.dirname(fileURLToPath(import.meta.url)); +function normalizeProject(project) { + if (typeof project === 'string') { + project = {repository: project}; + } + + const { + repository, + name = repository.split('/').pop(), + ignore = [], + } = project; + + return { + location: path.join(dirname, 'fixtures', name), + ...project, + name, + repository, + ignore, + }; +} + export default [ - { - name: 'fixtures-local', - location: path.join(dirname, 'fixtures-local'), - }, - { - repository: 'https://github.com/avajs/ava', - ignore: [ - 'test/node_modules', - 'test-tap/fixture/report/edgecases/ast-syntax-error.cjs', - ], - }, - 'https://github.com/chalk/chalk', - 'https://github.com/chalk/wrap-ansi', - 'https://github.com/sindresorhus/np', - 'https://github.com/sindresorhus/ora', - 'https://github.com/sindresorhus/p-map', - 'https://github.com/sindresorhus/os-locale', - 'https://github.com/sindresorhus/execa', - 'https://github.com/sindresorhus/pify', - 'https://github.com/sindresorhus/boxen', - 'https://github.com/sindresorhus/make-dir', - 'https://github.com/SamVerschueren/listr', - 'https://github.com/SamVerschueren/listr-update-renderer', - 'https://github.com/SamVerschueren/clinton', - 'https://github.com/SamVerschueren/bragg', - 'https://github.com/SamVerschueren/bragg-router', - 'https://github.com/SamVerschueren/dev-time', - 'https://github.com/SamVerschueren/decode-uri-component', - 'https://github.com/kevva/to-ico', - 'https://github.com/kevva/download', - 'https://github.com/kevva/brightness', - 'https://github.com/kevva/decompress', - 'https://github.com/kevva/npm-conf', - 'https://github.com/imagemin/imagemin', - 'https://github.com/qix-/color-convert', - 'https://github.com/sindresorhus/ky', - 'https://github.com/sindresorhus/query-string', - 'https://github.com/sindresorhus/meow', - 'https://github.com/sindresorhus/globby', - 'https://github.com/sindresorhus/emittery', - 'https://github.com/sindresorhus/p-queue', - 'https://github.com/sindresorhus/pretty-bytes', - 'https://github.com/sindresorhus/normalize-url', - 'https://github.com/sindresorhus/pageres', - { - repository: 'https://github.com/sindresorhus/got', - ignore: [ - // This file use `package` keyword as variable - 'documentation/examples/gh-got.js', - ], - }, + [ + { + name: 'fixtures-local', + location: path.join(dirname, 'fixtures-local'), + }, + { + repository: 'https://github.com/avajs/ava', + ignore: [ + 'test/node_modules', + 'test-tap/fixture/report/edgecases/ast-syntax-error.cjs', + ], + }, + 'https://github.com/chalk/chalk', + 'https://github.com/chalk/wrap-ansi', + 'https://github.com/sindresorhus/np', + 'https://github.com/sindresorhus/ora', + 'https://github.com/sindresorhus/p-map', + 'https://github.com/sindresorhus/os-locale', + 'https://github.com/sindresorhus/execa', + 'https://github.com/sindresorhus/pify', + 'https://github.com/sindresorhus/boxen', + 'https://github.com/sindresorhus/make-dir', + 'https://github.com/sindresorhus/ky', + 'https://github.com/sindresorhus/query-string', + 'https://github.com/sindresorhus/meow', + 'https://github.com/sindresorhus/globby', + 'https://github.com/sindresorhus/emittery', + 'https://github.com/sindresorhus/p-queue', + 'https://github.com/sindresorhus/pretty-bytes', + 'https://github.com/sindresorhus/normalize-url', + 'https://github.com/sindresorhus/pageres', + { + repository: 'https://github.com/sindresorhus/got', + ignore: [ + // This file use `package` keyword as variable + 'documentation/examples/gh-got.js', + ], + }, + 'https://github.com/sindresorhus/create-dmg', + 'https://github.com/sindresorhus/cp-file', + 'https://github.com/sindresorhus/capture-website', + { + repository: 'https://github.com/sindresorhus/file-type', + ignore: [ + // Contains non-text `.mts` file + 'fixture/**', + ], + }, + 'https://github.com/sindresorhus/slugify', + 'https://github.com/SamVerschueren/listr', + 'https://github.com/SamVerschueren/listr-update-renderer', + 'https://github.com/SamVerschueren/clinton', + 'https://github.com/SamVerschueren/bragg', + 'https://github.com/SamVerschueren/bragg-router', + 'https://github.com/SamVerschueren/dev-time', + 'https://github.com/SamVerschueren/decode-uri-component', + 'https://github.com/kevva/to-ico', + 'https://github.com/kevva/download', + 'https://github.com/kevva/brightness', + 'https://github.com/kevva/decompress', + 'https://github.com/kevva/npm-conf', + 'https://github.com/imagemin/imagemin', + 'https://github.com/qix-/color-convert', + ], // 'https://github.com/eslint/eslint', { repository: 'https://github.com/prettier/prettier', @@ -73,36 +106,22 @@ export default [ 'tools/**', ], }, - // Too slow - // { - // repository: 'https://github.com/microsoft/typescript', - // ignore: [ - // // These file use `'\033'` - // 'build/**', - // ], - // }, - // This repo use `override` keyword which is not avaiable before TS4.3, temporary disable - // https://github.com/microsoft/vscode/pull/120690/files - // { - // repository: 'https://github.com/microsoft/vscode', - // ignore: [ - // // This file use `'\033'` - // 'build/**' - // ] - // }, - // 'https://github.com/ElemeFE/element', - // 'https://github.com/iview/iview', - 'https://github.com/sindresorhus/create-dmg', - 'https://github.com/sindresorhus/cp-file', - 'https://github.com/sindresorhus/capture-website', { - repository: 'https://github.com/sindresorhus/file-type', + repository: 'https://github.com/microsoft/typescript', ignore: [ - // Contains non-text `.mts` file - 'fixture/**', + // These file use `'\033'` + 'build/**', ], }, - 'https://github.com/sindresorhus/slugify', + { + repository: 'https://github.com/microsoft/vscode', + ignore: [ + // This file use `'\033'` + 'build/**' + ] + }, + 'https://github.com/element-plus/element-plus', + 'https://github.com/tusen-ai/naive-ui', { repository: 'https://github.com/gatsbyjs/gatsby', ignore: [ @@ -162,8 +181,7 @@ export default [ ], }, // #903 - // Too slow - // 'https://github.com/mattermost/mattermost-webapp', + 'https://github.com/mattermost/mattermost-webapp', // #1030 'https://github.com/astrofox-io/astrofox', // #1075 @@ -171,22 +189,8 @@ export default [ // These two project use `decorator`, try to enable when we use `@babel/eslint-parser` // 'https://github.com/untitled-labs/metabase-custom', // 'https://github.com/TheThingsNetwork/lorawan-stack', -].map(project => { - if (typeof project === 'string') { - project = {repository: project}; - } - - const { - repository, - name = repository.split('/').pop(), - ignore = [], - } = project; - - return { - location: path.join(dirname, 'fixtures', name), - ...project, - name, - repository, - ignore, - }; -}); +].flatMap((projectOrProjects, index) => + Array.isArray(projectOrProjects) + ? projectOrProjects.map(project => ({...normalizeProject(project), group: index})) + : [{...normalizeProject(projectOrProjects), group: index}] +); diff --git a/test/integration/readme.md b/test/integration/readme.md index 63931ce6aa..3494c13647 100644 --- a/test/integration/readme.md +++ b/test/integration/readme.md @@ -2,4 +2,4 @@ To run the integration tests, go to the project root, and run `$ npm run integration`. -To run tests on specific projects, run `$ npm run integration projectName1 projectName2 … projectNameN`. The project names can be found in [`projects.js`](projects.js). +To run tests on specific projects, run `$ npm run integration projectName1 projectName2 … projectNameN`. The project names can be found in [`projects.mjs`](projects.mjs). diff --git a/test/integration/test.mjs b/test/integration/test.mjs index 793d7216a3..a19abb4412 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -2,6 +2,7 @@ import process from 'node:process'; import fs from 'node:fs'; import path from 'node:path'; +import {parseArgs} from 'node:util'; import Listr from 'listr'; import {execa} from 'execa'; import chalk from 'chalk'; @@ -10,11 +11,37 @@ import mem from 'mem'; import allProjects from './projects.mjs'; import runEslint from './run-eslint.mjs'; -const projectsArguments = process.argv.slice(2); -const projects = projectsArguments.length === 0 +const { + values: { + group, + }, + positionals: projectsArguments, +} = parseArgs({ + options: { + group: { + type: 'string', + }, + }, + allowPositionals: true, +}); + +let projects = projectsArguments.length === 0 ? allProjects : allProjects.filter(({name}) => projectsArguments.includes(name)); +if (isCI && !group) { + throw new Error('"--group" is required'); +} + +if (group) { + projects = projects.filter(project => String(project.group + 1) === group); +} + +if (projects.length === 0) { + console.log('No project matched'); + process.exit(0); +} + const getBranch = mem(async dirname => { const {stdout} = await execa('git', ['branch', '--show-current'], {cwd: dirname}); return stdout; From e401ac17e9a51cf57836c390e7221e185902632d Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 16:38:04 +0800 Subject: [PATCH 20/39] Revert "Run with worker" This reverts commit e4dc5b9491f284b46d0e91e0418c73d3bcaaeabe. --- package.json | 1 - test/integration/run-eslint.mjs | 48 +++++++++++++++++++++++++++---- test/integration/worker.mjs | 50 --------------------------------- 3 files changed, 43 insertions(+), 56 deletions(-) delete mode 100644 test/integration/worker.mjs diff --git a/package.json b/package.json index a640daae3f..b384bf2523 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,6 @@ "npm-package-json-lint": "^6.3.0", "npm-run-all": "^4.1.5", "outdent": "^0.8.0", - "piscina": "^3.2.0", "typescript": "^4.8.3", "vue-eslint-parser": "^9.1.0", "xo": "^0.52.3" diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 1715977984..cfa106f1d2 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -1,5 +1,5 @@ import {codeFrameColumns} from '@babel/code-frame'; -import Piscina from 'piscina'; +import eslintPluginUnicorn from '../../index.js'; class UnicornIntegrationTestError extends AggregateError { name = 'UnicornIntegrationTestError'; @@ -33,11 +33,49 @@ class UnicornEslintFatalError extends SyntaxError { } } -const piscina = new Piscina({ - filename: new URL('worker.mjs', import.meta.url).href, -}); async function runEslint(project) { - const result = await piscina.run(project); + const eslint = new ESLint({ + cwd: project.location, + baseConfig: eslintPluginUnicorn.configs.all, + useEslintrc: false, + extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], + plugins: { + unicorn: eslintPluginUnicorn, + }, + fix: true, + overrideConfig: { + parser: '@babel/eslint-parser', + parserOptions: { + requireConfigFile: false, + babelOptions: { + babelrc: false, + configFile: false, + parserOpts: { + plugins: [ + 'jsx', + ], + }, + }, + }, + ignorePatterns: project.ignore, + rules: { + // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` + 'unicorn/string-content': 'off', + }, + overrides: [ + { + files: ['*.ts', '*.mts', '*.cts', '*.tsx'], + parser: '@typescript-eslint/parser', + }, + { + files: ['*.vue'], + parser: 'vue-eslint-parser', + }, + ], + }, + }); + + const result = await eslint.lintFiles('.'); const errors = result .filter(file => file.fatalErrorCount > 0) diff --git a/test/integration/worker.mjs b/test/integration/worker.mjs deleted file mode 100644 index e48f249971..0000000000 --- a/test/integration/worker.mjs +++ /dev/null @@ -1,50 +0,0 @@ -import {ESLint} from 'eslint'; -import eslintPluginUnicorn from '../../index.js'; - -function runEslint(project) { - const eslint = new ESLint({ - cwd: project.location, - baseConfig: eslintPluginUnicorn.configs.all, - useEslintrc: false, - extensions: ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx', '.vue'], - plugins: { - unicorn: eslintPluginUnicorn, - }, - fix: true, - overrideConfig: { - parser: '@babel/eslint-parser', - parserOptions: { - requireConfigFile: false, - babelOptions: { - babelrc: false, - configFile: false, - parserOpts: { - plugins: [ - 'jsx', - 'exportDefaultFrom', - ], - }, - }, - }, - ignorePatterns: project.ignore, - rules: { - // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` - 'unicorn/string-content': 'off', - }, - overrides: [ - { - files: ['*.ts', '*.mts', '*.cts', '*.tsx'], - parser: '@typescript-eslint/parser', - }, - { - files: ['*.vue'], - parser: 'vue-eslint-parser', - }, - ], - }, - }); - - return eslint.lintFiles('.'); -} - -export default runEslint; From 0f97a309b7e0ad45855cb845a596111d5d76d8fd Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 16:40:05 +0800 Subject: [PATCH 21/39] Fix --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5993df5ea0..8aaebe41b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,4 +76,4 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npm install - - run: npm run integration --group ${{ matrix.group }} + - run: npm run integration -- --group ${{ matrix.group }} From d9a31866d8479b48198e582349bea9b62efd7463 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 16:42:20 +0800 Subject: [PATCH 22/39] Fix --- test/integration/run-eslint.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index cfa106f1d2..7468c2758e 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -1,4 +1,5 @@ import {codeFrameColumns} from '@babel/code-frame'; +import {ESLint} from 'eslint'; import eslintPluginUnicorn from '../../index.js'; class UnicornIntegrationTestError extends AggregateError { From 1783efed8efc9fce2310345c7609f07c318e6a79 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 16:48:00 +0800 Subject: [PATCH 23/39] Support `exportDefaultFrom` --- test/integration/run-eslint.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 7468c2758e..11a586eb5b 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -54,6 +54,7 @@ async function runEslint(project) { parserOpts: { plugins: [ 'jsx', + 'exportDefaultFrom', ], }, }, From 8a8f72e030fa42d3039b312ff6c52c950954d81b Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 16:59:10 +0800 Subject: [PATCH 24/39] Output summary --- test/integration/run-eslint.mjs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 11a586eb5b..31719f96c2 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -1,5 +1,7 @@ import {codeFrameColumns} from '@babel/code-frame'; import {ESLint} from 'eslint'; +import chalk from 'chalk'; +import {outdent} from 'outdent'; import eslintPluginUnicorn from '../../index.js'; class UnicornIntegrationTestError extends AggregateError { @@ -34,6 +36,9 @@ class UnicornEslintFatalError extends SyntaxError { } } +const sum = (collection, fieldName) => + collection.reduce((total, {[fieldName]: value}) => total + value, 0); + async function runEslint(project) { const eslint = new ESLint({ cwd: project.location, @@ -77,9 +82,9 @@ async function runEslint(project) { }, }); - const result = await eslint.lintFiles('.'); + const results = await eslint.lintFiles('.'); - const errors = result + const errors = results .filter(file => file.fatalErrorCount > 0) .flatMap( file => file.messages @@ -87,11 +92,22 @@ async function runEslint(project) { .map(message => new UnicornEslintFatalError(message, file)), ); - if (errors.length === 0) { - return; + if (errors.length > 0) { + throw new UnicornIntegrationTestError(project, errors); } - throw new UnicornIntegrationTestError(project, errors); + const errorCount = sum(results, 'errorCount'); + const warningCount = sum(results, 'warningCount'); + const fixableErrorCount = sum(results, 'fixableErrorCount'); + const fixableWarningCount = sum(results, 'fixableWarningCount'); + console.log(); + console.log(outdent` + ${results.length} files linted: + - error: ${chalk.gray`${errorCount}`} + - warning: ${chalk.gray`${warningCount}`} + - fixable error: ${chalk.gray`${fixableErrorCount}`} + - fixable warning: ${chalk.gray`${fixableWarningCount}`} + `); } export default runEslint; From c08411a4ca0e8cb0a22d29d1c381fdd492632aa2 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:02:35 +0800 Subject: [PATCH 25/39] Use ts parser in .vue --- test/integration/run-eslint.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 31719f96c2..10599f9159 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -77,6 +77,9 @@ async function runEslint(project) { { files: ['*.vue'], parser: 'vue-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + }, }, ], }, From 6601ded969cc1b2b31621435a98421342381022a Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:05:20 +0800 Subject: [PATCH 26/39] Linting --- test/integration/projects.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 0e1d9a986b..6a71b438af 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -117,8 +117,8 @@ export default [ repository: 'https://github.com/microsoft/vscode', ignore: [ // This file use `'\033'` - 'build/**' - ] + 'build/**', + ], }, 'https://github.com/element-plus/element-plus', 'https://github.com/tusen-ai/naive-ui', @@ -192,5 +192,5 @@ export default [ ].flatMap((projectOrProjects, index) => Array.isArray(projectOrProjects) ? projectOrProjects.map(project => ({...normalizeProject(project), group: index})) - : [{...normalizeProject(projectOrProjects), group: index}] + : [{...normalizeProject(projectOrProjects), group: index}], ); From 83d24b0a0bf13070a2868337ac1646692cde5f47 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:19:19 +0800 Subject: [PATCH 27/39] Check CI config --- .github/workflows/main.yml | 34 +++++++++++++++++----------------- package.json | 3 ++- test/integration/test.mjs | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8aaebe41b6..5a9b741bfa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,23 +54,23 @@ jobs: fail-fast: false matrix: group: - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - - 13 - - 14 - - 15 - - 16 - - 17 + - "1" + - "2" + - "3" + - "4" + - "5" + - "6" + - "7" + - "8" + - "9" + - "10" + - "11" + - "12" + - "13" + - "14" + - "15" + - "16" + - "17" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/package.json b/package.json index b384bf2523..08fd891f55 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,8 @@ "outdent": "^0.8.0", "typescript": "^4.8.3", "vue-eslint-parser": "^9.1.0", - "xo": "^0.52.3" + "xo": "^0.52.3", + "yaml": "^1.10.2" }, "peerDependencies": { "eslint": ">=8.23.1" diff --git a/test/integration/test.mjs b/test/integration/test.mjs index a19abb4412..7e687ad2d3 100644 --- a/test/integration/test.mjs +++ b/test/integration/test.mjs @@ -6,11 +6,30 @@ import {parseArgs} from 'node:util'; import Listr from 'listr'; import {execa} from 'execa'; import chalk from 'chalk'; +import {outdent} from 'outdent'; import {isCI} from 'ci-info'; import mem from 'mem'; +import YAML from 'yaml'; import allProjects from './projects.mjs'; import runEslint from './run-eslint.mjs'; +if (isCI) { + const CI_CONFIG_FILE = new URL('../../.github/workflows/main.yml', import.meta.url); + const content = fs.readFileSync(CI_CONFIG_FILE, 'utf8'); + const config = YAML.parse(content).jobs.integration.strategy.matrix.group; + + const expected = [...new Set(allProjects.map(project => String(project.group + 1)))]; + if ( + config.length !== expected.length + || expected.some((group, index) => config[index] !== group) + ) { + throw new Error(outdent` + Expect 'jobs.integration.strategy.matrix.group' in '/.github/workflows/main.yml' to be: + ${YAML.stringify(expected)} + `); + } +} + const { values: { group, From 89039b7566d8575950934dfd074b3e3035978409 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:24:32 +0800 Subject: [PATCH 28/39] Tweak --- test/integration/run-eslint.mjs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 10599f9159..ffad8f0a3f 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -105,11 +105,11 @@ async function runEslint(project) { const fixableWarningCount = sum(results, 'fixableWarningCount'); console.log(); console.log(outdent` - ${results.length} files linted: - - error: ${chalk.gray`${errorCount}`} - - warning: ${chalk.gray`${warningCount}`} - - fixable error: ${chalk.gray`${fixableErrorCount}`} - - fixable warning: ${chalk.gray`${fixableWarningCount}`} + ${chalk.green.bold.underline(`[${project.name}]`)} ${results.length} files linted: + - error: ${chalk.gray(errorCount)} + - warning: ${chalk.gray(warningCount)} + - fixable error: ${chalk.gray(fixableErrorCount)} + - fixable warning: ${chalk.gray(fixableWarningCount)} `); } From 560bf9813268417c404ba84400fb199049516936 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:25:29 +0800 Subject: [PATCH 29/39] Missing group --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5a9b741bfa..d6f2531f6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -71,6 +71,7 @@ jobs: - "15" - "16" - "17" + - "18" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From d1577a64692d575bb90e245bcd6fe483c3e0cd52 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:32:50 +0800 Subject: [PATCH 30/39] Reduce groups --- .github/workflows/main.yml | 6 ---- test/integration/projects.mjs | 66 +++++++++++++++++------------------ 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d6f2531f6f..047ce5263a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,12 +66,6 @@ jobs: - "10" - "11" - "12" - - "13" - - "14" - - "15" - - "16" - - "17" - - "18" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 6a71b438af..7599fc37ee 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -87,14 +87,41 @@ export default [ 'https://github.com/kevva/npm-conf', 'https://github.com/imagemin/imagemin', 'https://github.com/qix-/color-convert', + { + repository: 'https://github.com/prettier/prettier', + ignore: [ + 'tests/**', + ], + }, + { + repository: 'https://github.com/puppeteer/puppeteer', + ignore: [ + // Parser error on `await page.evaluate(() => delete Node);` + // https://github.com/puppeteer/puppeteer/blob/0b1a9ceee2f05f534f0d50079ece172d627a93c7/test/jshandle.spec.js#L151 + 'test/jshandle.spec.js', + + // `package` keyword + // https://github.com/puppeteer/puppeteer/blob/0b1a9ceee2f05f534f0d50079ece172d627a93c7/utils/apply_next_version.js#L17 + 'utils/apply_next_version.js', + + // Global return + 'utils/fetch_devices.js', + ], + }, + 'https://github.com/ReactTraining/react-router', + // #902 + { + repository: 'https://github.com/reakit/reakit', + ignore: [ + 'packages/reakit/jest.config.js', // This file use `package` keyword as variable + ], + }, + // #1030 + 'https://github.com/astrofox-io/astrofox', + // #1075 + 'https://github.com/jaredLunde/masonic', ], // 'https://github.com/eslint/eslint', - { - repository: 'https://github.com/prettier/prettier', - ignore: [ - 'tests/**', - ], - }, { repository: 'https://github.com/angular/angular', ignore: [ @@ -129,21 +156,6 @@ export default [ '**/*.js', ], }, - { - repository: 'https://github.com/puppeteer/puppeteer', - ignore: [ - // Parser error on `await page.evaluate(() => delete Node);` - // https://github.com/puppeteer/puppeteer/blob/0b1a9ceee2f05f534f0d50079ece172d627a93c7/test/jshandle.spec.js#L151 - 'test/jshandle.spec.js', - - // `package` keyword - // https://github.com/puppeteer/puppeteer/blob/0b1a9ceee2f05f534f0d50079ece172d627a93c7/utils/apply_next_version.js#L17 - 'utils/apply_next_version.js', - - // Global return - 'utils/fetch_devices.js', - ], - }, { repository: 'https://github.com/vercel/next.js', ignore: [ @@ -159,7 +171,6 @@ export default [ 'scripts/create-package.js', // This file use `package` keyword as variable ], }, - 'https://github.com/ReactTraining/react-router', 'https://github.com/mozilla/pdf.js', // #912 { @@ -173,19 +184,8 @@ export default [ 'scripts/cypress.js', ], }, - // #902 - { - repository: 'https://github.com/reakit/reakit', - ignore: [ - 'packages/reakit/jest.config.js', // This file use `package` keyword as variable - ], - }, // #903 'https://github.com/mattermost/mattermost-webapp', - // #1030 - 'https://github.com/astrofox-io/astrofox', - // #1075 - 'https://github.com/jaredLunde/masonic', // These two project use `decorator`, try to enable when we use `@babel/eslint-parser` // 'https://github.com/untitled-labs/metabase-custom', // 'https://github.com/TheThingsNetwork/lorawan-stack', From 9bed82ce4a39f7f9f73c87ae5b5d144e68b1597e Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 17:40:14 +0800 Subject: [PATCH 31/39] Enable jsx --- test/integration/run-eslint.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index ffad8f0a3f..fbf26081ab 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -78,7 +78,9 @@ async function runEslint(project) { files: ['*.vue'], parser: 'vue-eslint-parser', parserOptions: { + ecmaVersion: 'latest', parser: '@typescript-eslint/parser', + jsx: true, }, }, ], From ce92b1aa122776820aa95528c63a5e591912dcb7 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 18:04:19 +0800 Subject: [PATCH 32/39] Fix --- test/integration/run-eslint.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index fbf26081ab..d24b69834f 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -78,9 +78,10 @@ async function runEslint(project) { files: ['*.vue'], parser: 'vue-eslint-parser', parserOptions: { - ecmaVersion: 'latest', parser: '@typescript-eslint/parser', - jsx: true, + ecmaFeatures: { + jsx: true, + }, }, }, ], From b5f0294d553f6821ee4bfa07a138a75382281ec6 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 18:43:13 +0800 Subject: [PATCH 33/39] Enable globalReturn --- test/integration/run-eslint.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index d24b69834f..094def9cc8 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -56,6 +56,7 @@ async function runEslint(project) { babelOptions: { babelrc: false, configFile: false, + allowReturnOutsideFunction: true, parserOpts: { plugins: [ 'jsx', From f0e5c9579c8349fbc8da6b5280e2b24966753daa Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 18:47:41 +0800 Subject: [PATCH 34/39] Fix --- test/integration/run-eslint.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 094def9cc8..bb90e6c4a7 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -56,8 +56,8 @@ async function runEslint(project) { babelOptions: { babelrc: false, configFile: false, - allowReturnOutsideFunction: true, parserOpts: { + allowReturnOutsideFunction: true, plugins: [ 'jsx', 'exportDefaultFrom', From 9eb23d8cef18841582ee780631b5ea57b9dd738b Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 19:06:52 +0800 Subject: [PATCH 35/39] Add `project: []` --- test/integration/run-eslint.mjs | 4 ++++ test/smoke/eslint-remote-tester.config.js | 1 + test/utils/parsers.mjs | 1 + 3 files changed, 6 insertions(+) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index bb90e6c4a7..6655d40617 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -74,6 +74,9 @@ async function runEslint(project) { { files: ['*.ts', '*.mts', '*.cts', '*.tsx'], parser: '@typescript-eslint/parser', + parserOptions: { + project: [], + }, }, { files: ['*.vue'], @@ -83,6 +86,7 @@ async function runEslint(project) { ecmaFeatures: { jsx: true, }, + project: [], }, }, ], diff --git a/test/smoke/eslint-remote-tester.config.js b/test/smoke/eslint-remote-tester.config.js index 93b39a0330..7dbfba9971 100644 --- a/test/smoke/eslint-remote-tester.config.js +++ b/test/smoke/eslint-remote-tester.config.js @@ -29,6 +29,7 @@ module.exports = { ecmaFeatures: { jsx: true, }, + project: [], }, extends: ['plugin:unicorn/all'], }, diff --git a/test/utils/parsers.mjs b/test/utils/parsers.mjs index b63e7ba04c..6f1e4cb959 100644 --- a/test/utils/parsers.mjs +++ b/test/utils/parsers.mjs @@ -48,6 +48,7 @@ const typescript = { mergeParserOptions(options) { return { ...defaultOptions.parserOptions, + project: [], ...options, }; }, From ce2bad6e038a15e6d1f6134daf64ea0343fda79b Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 19:17:22 +0800 Subject: [PATCH 36/39] Add `TIMING` --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 047ce5263a..8d8f80c958 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,6 +66,8 @@ jobs: - "10" - "11" - "12" + env: + TIMING: 1 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From fcccbb8038330f6c692778c4535ee29fc3ce7d19 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 20:09:10 +0800 Subject: [PATCH 37/39] Add ruleId to codeFrame --- test/integration/run-eslint.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/integration/run-eslint.mjs b/test/integration/run-eslint.mjs index 6655d40617..f70a9beae1 100644 --- a/test/integration/run-eslint.mjs +++ b/test/integration/run-eslint.mjs @@ -26,12 +26,15 @@ class UnicornEslintFatalError extends SyntaxError { get codeFrame() { const {source, output} = this.eslintFile; - const {line, column, message} = this.eslintMessage; + const {line, column, message, ruleId} = this.eslintMessage; return codeFrameColumns( source ?? output, {start: {line, column}}, - {message, highlightCode: true}, + { + message: ruleId ? `[${ruleId}]: ${message}` : message, + highlightCode: true, + }, ); } } From abf67ea7ca03f8bd544b300deec5693a29cf9f56 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 21:07:54 +0800 Subject: [PATCH 38/39] Ignore --- test/integration/projects.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 7599fc37ee..4611702bf9 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -145,6 +145,8 @@ export default [ ignore: [ // This file use `'\033'` 'build/**', + // Unknown parse error + 'src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts', ], }, 'https://github.com/element-plus/element-plus', From 4e3b4db3019e7e057e7d8fe1e1bad75d188eba83 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 20 Sep 2022 21:32:39 +0800 Subject: [PATCH 39/39] Update projects.mjs --- test/integration/projects.mjs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/integration/projects.mjs b/test/integration/projects.mjs index 4611702bf9..7599fc37ee 100644 --- a/test/integration/projects.mjs +++ b/test/integration/projects.mjs @@ -145,8 +145,6 @@ export default [ ignore: [ // This file use `'\033'` 'build/**', - // Unknown parse error - 'src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts', ], }, 'https://github.com/element-plus/element-plus',