From 945df920a7423e35485211c5c9055441e42a1ac0 Mon Sep 17 00:00:00 2001 From: Francisco Alejandro Garcia Cebada <93944312+frgarc@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:10:04 -0800 Subject: [PATCH] Support for ESLint Flat Config (#15) * - Support for ESLint Flat Config * Change files * - Updating just-scripts version * - Converting to Flat Config * Change files * Change files * - Fixing legacy file name --- ...-da7d6e11-184c-4135-b8bc-a3626a674a63.json | 7 +++ ...-53db88ef-0630-4f25-900a-cab0514b466e.json | 7 +++ ...-7b130e35-6c54-4c46-bf1e-4398f642ef8a.json | 7 +++ libraries/math/.eslintrc.js | 8 --- libraries/math/eslint.config.mjs | 3 + package-lock.json | 4 +- tools/core-build-tasks/package.json | 4 +- tools/core-build-tasks/src/tasks/coreLint.ts | 54 +++++++++++++--- .../index.js | 35 ----------- .../index.mjs | 63 +++++++++++++++++++ .../package.json | 2 +- .../.eslintrc.js | 8 --- .../eslint.config.mjs | 3 + .../package.json | 4 +- .../src/index.ts | 7 +++ 15 files changed, 151 insertions(+), 65 deletions(-) create mode 100644 change/@minecraft-core-build-tasks-da7d6e11-184c-4135-b8bc-a3626a674a63.json create mode 100644 change/@minecraft-math-53db88ef-0630-4f25-900a-cab0514b466e.json create mode 100644 change/eslint-plugin-minecraft-linting-7b130e35-6c54-4c46-bf1e-4398f642ef8a.json delete mode 100644 libraries/math/.eslintrc.js create mode 100644 libraries/math/eslint.config.mjs delete mode 100644 tools/eslint-config-minecraft-scripting/index.js create mode 100644 tools/eslint-config-minecraft-scripting/index.mjs delete mode 100644 tools/eslint-plugin-minecraft-linting/.eslintrc.js create mode 100644 tools/eslint-plugin-minecraft-linting/eslint.config.mjs diff --git a/change/@minecraft-core-build-tasks-da7d6e11-184c-4135-b8bc-a3626a674a63.json b/change/@minecraft-core-build-tasks-da7d6e11-184c-4135-b8bc-a3626a674a63.json new file mode 100644 index 0000000..dd0bbff --- /dev/null +++ b/change/@minecraft-core-build-tasks-da7d6e11-184c-4135-b8bc-a3626a674a63.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Support for ESLint Flat Config", + "packageName": "@minecraft/core-build-tasks", + "email": "frgarc@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@minecraft-math-53db88ef-0630-4f25-900a-cab0514b466e.json b/change/@minecraft-math-53db88ef-0630-4f25-900a-cab0514b466e.json new file mode 100644 index 0000000..b3791cc --- /dev/null +++ b/change/@minecraft-math-53db88ef-0630-4f25-900a-cab0514b466e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Using flat config", + "packageName": "@minecraft/math", + "email": "frgarc@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/eslint-plugin-minecraft-linting-7b130e35-6c54-4c46-bf1e-4398f642ef8a.json b/change/eslint-plugin-minecraft-linting-7b130e35-6c54-4c46-bf1e-4398f642ef8a.json new file mode 100644 index 0000000..b3aee65 --- /dev/null +++ b/change/eslint-plugin-minecraft-linting-7b130e35-6c54-4c46-bf1e-4398f642ef8a.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Support for ESLint Flat Config", + "packageName": "eslint-plugin-minecraft-linting", + "email": "frgarc@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/libraries/math/.eslintrc.js b/libraries/math/.eslintrc.js deleted file mode 100644 index 2c87c27..0000000 --- a/libraries/math/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - root: true, - // This tells ESLint to load the config from the package `eslint-config-minecraft-scripting` - extends: ['minecraft-scripting'], - parserOptions: { - tsconfigRootDir: __dirname, - }, -}; diff --git a/libraries/math/eslint.config.mjs b/libraries/math/eslint.config.mjs new file mode 100644 index 0000000..270d82c --- /dev/null +++ b/libraries/math/eslint.config.mjs @@ -0,0 +1,3 @@ +import configMinecraftScripting from 'eslint-config-minecraft-scripting'; + +export default [...configMinecraftScripting]; diff --git a/package-lock.json b/package-lock.json index 9587f64..a9dc33a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6015,6 +6015,7 @@ "@rushstack/node-core-library": "^3.59.6", "dotenv": "^16.0.2", "esbuild": "^0.20.1", + "just-scripts": "^2.2.1", "prettier": "^2.8.2", "rimraf": "^3.0.2", "vitest": "^0.34.6", @@ -6024,7 +6025,6 @@ "devDependencies": { "@types/node": "^14.0.0 || ^16.0.0 || ^18.0.0", "@types/rimraf": "^3.0.2", - "just-scripts": "^2.1.3", "ts-node": "^10.9.1", "tsconfig": "*", "typescript": "^5.2.2" @@ -6451,7 +6451,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-unicorn": "^42.0.0", - "just-scripts": "^2.1.3", + "just-scripts": "^2.2.1", "tsconfig": "*" } }, diff --git a/tools/core-build-tasks/package.json b/tools/core-build-tasks/package.json index a1907b9..730a48b 100644 --- a/tools/core-build-tasks/package.json +++ b/tools/core-build-tasks/package.json @@ -18,6 +18,7 @@ "@microsoft/api-extractor": "^7.38.3", "esbuild": "^0.20.1", "dotenv": "^16.0.2", + "just-scripts": "^2.2.1", "prettier": "^2.8.2", "rimraf": "^3.0.2", "vitest": "^0.34.6", @@ -27,9 +28,8 @@ "devDependencies": { "@types/node": "^14.0.0 || ^16.0.0 || ^18.0.0", "@types/rimraf": "^3.0.2", - "just-scripts": "^2.1.3", "ts-node": "^10.9.1", "tsconfig": "*", "typescript": "^5.2.2" } -} +} \ No newline at end of file diff --git a/tools/core-build-tasks/src/tasks/coreLint.ts b/tools/core-build-tasks/src/tasks/coreLint.ts index 5d50efe..900f492 100644 --- a/tools/core-build-tasks/src/tasks/coreLint.ts +++ b/tools/core-build-tasks/src/tasks/coreLint.ts @@ -1,21 +1,61 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { execSync } from 'child_process'; import { existsSync } from 'fs'; -import type { TaskFunction } from 'just-scripts'; -import { condition, eslintTask, prettierCheckTask, prettierTask, series, task } from 'just-scripts'; +import { TaskFunction, condition, logger, prettierCheckTask, prettierTask, series, task } from 'just-scripts'; import path from 'path'; import process from 'process'; +const LEGACY_CONFIG_FILES = ['.eslintrc.js']; +const FLAT_CONFIG_FILES = ['eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs']; +const POSSIBLE_CONFIG_FILES = [...LEGACY_CONFIG_FILES, ...FLAT_CONFIG_FILES]; + +function getConfigFilePath(): string | undefined { + for (const file of POSSIBLE_CONFIG_FILES) { + const configPath = path.resolve(process.cwd(), file); + if (existsSync(configPath)) { + return configPath; + } + } + + return undefined; +} + +function eslintTask(files: string[], fix?: boolean): TaskFunction { + return () => { + const configFilePath = getConfigFilePath(); + if (configFilePath) { + // Setting ESLINT_USE_FLAT_CONFIG environment variable to indicate if the config file is flat or not. + // ESLint is not able to determine the type in all the cases, so we need to help it. + process.env['ESLINT_USE_FLAT_CONFIG'] = FLAT_CONFIG_FILES.some(file => configFilePath.endsWith(file)) + ? 'true' + : 'false'; + const cmd = ['eslint', ...files, '--config', configFilePath, ...(fix ? ['--fix'] : []), '--color'].join( + ' ' + ); + logger.info(`Running command: ${cmd}`); + return execSync(cmd, { stdio: 'inherit' }); + } + + // no-op if the config file does not exist. + return Promise.resolve(); + }; +} + export function coreLint(files: string[], fix?: boolean): TaskFunction { task('verify-lint', () => { - // If the process working directory does not have an `.eslintrc.js` file, fail the build - const lintConfig = path.resolve(process.cwd(), '.eslintrc.js'); - if (!existsSync(lintConfig)) { - throw new Error(`.eslintrc.js not found at ${lintConfig}.`); + // If the process working directory does not have an eslint configuration file, fail the build: + // https://eslint.org/docs/latest/use/configure/configuration-files-new + if (!getConfigFilePath()) { + throw new Error( + `ESLint config file not found at ${process.cwd()}. Possible values: [${POSSIBLE_CONFIG_FILES.join( + ', ' + )}]` + ); } }); - task('eslint', eslintTask({ files, fix })); + task('eslint', eslintTask(files, fix)); task('prettier-fix', prettierTask({ files })); task('prettier-check', prettierCheckTask({ files })); diff --git a/tools/eslint-config-minecraft-scripting/index.js b/tools/eslint-config-minecraft-scripting/index.js deleted file mode 100644 index 29cb07c..0000000 --- a/tools/eslint-config-minecraft-scripting/index.js +++ /dev/null @@ -1,35 +0,0 @@ -module.exports = { - extends: [ - 'prettier', - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/recommended-requiring-type-checking', - ], - parser: '@typescript-eslint/parser', - parserOptions: { project: ['./tsconfig.json'] }, - plugins: ['header', '@typescript-eslint', 'unicorn', 'minecraft-linting'], - overrides: [{ files: ['**/*.{ts,tsx,js,jsx}', '*.ts'] }], - rules: { - 'unicorn/no-abusive-eslint-disable': 'error', - 'unicorn/no-null': ['error', { checkStrictEquality: true }], - '@typescript-eslint/no-empty-function': 'off', - 'no-unused-vars': 'off', - '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_', - }, - ], - '@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true, allowBoolean: true }], - eqeqeq: ['error', 'always'], - '@typescript-eslint/no-unsafe-assignment': 'error', - '@typescript-eslint/no-unsafe-member-access': 'error', - '@typescript-eslint/no-unsafe-call': 'error', - 'header/header': [2, 'line', [' Copyright (c) Microsoft Corporation.', ` Licensed under the MIT License.`], 1], - 'minecraft-linting/avoid-unnecessary-command': 'error', - }, -}; diff --git a/tools/eslint-config-minecraft-scripting/index.mjs b/tools/eslint-config-minecraft-scripting/index.mjs new file mode 100644 index 0000000..91822e3 --- /dev/null +++ b/tools/eslint-config-minecraft-scripting/index.mjs @@ -0,0 +1,63 @@ +import eslintConfigPrettier from 'eslint-config-prettier'; +import eslintPluginUnicorn from 'eslint-plugin-unicorn'; +import eslint from '@eslint/js'; +import globals from 'globals'; +import header from 'eslint-plugin-header'; +import minecraftLinting from 'eslint-plugin-minecraft-linting'; +import tsEslint from '@typescript-eslint/eslint-plugin'; +import tsParser from '@typescript-eslint/parser'; + +export default [ + eslintConfigPrettier, + eslint.configs.recommended, + { + files: ['**/*.{ts,tsx,js,jsx}', '*.ts'], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 'latest', + project: './tsconfig.json', + }, + globals: { + ...globals.node, + }, + }, + plugins: { + header, + tsEslint, + '@typescript-eslint': tsEslint, + unicorn: eslintPluginUnicorn, + 'minecraft-linting': minecraftLinting, + }, + rules: { + ...tsEslint.configs['eslint-recommended'].rules, + ...tsEslint.configs['recommended'].rules, + 'unicorn/no-abusive-eslint-disable': 'error', + 'unicorn/no-null': ['error', { checkStrictEquality: true }], + '@typescript-eslint/no-empty-function': 'off', + 'no-unused-vars': 'off', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true, allowBoolean: true }], + eqeqeq: ['error', 'always'], + '@typescript-eslint/no-unsafe-assignment': 'error', + '@typescript-eslint/no-unsafe-member-access': 'error', + '@typescript-eslint/no-unsafe-call': 'error', + 'header/header': [ + 2, + 'line', + [' Copyright (c) Microsoft Corporation.', ` Licensed under the MIT License.`], + 1, + ], + 'minecraft-linting/avoid-unnecessary-command': 'error', + }, + }, +]; diff --git a/tools/eslint-config-minecraft-scripting/package.json b/tools/eslint-config-minecraft-scripting/package.json index 5f29d73..792d3eb 100644 --- a/tools/eslint-config-minecraft-scripting/package.json +++ b/tools/eslint-config-minecraft-scripting/package.json @@ -1,7 +1,7 @@ { "name": "eslint-config-minecraft-scripting", "version": "0.0.1", - "main": "index.js", + "main": "index.mjs", "license": "MIT", "private": true, "dependencies": { diff --git a/tools/eslint-plugin-minecraft-linting/.eslintrc.js b/tools/eslint-plugin-minecraft-linting/.eslintrc.js deleted file mode 100644 index 2c87c27..0000000 --- a/tools/eslint-plugin-minecraft-linting/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - root: true, - // This tells ESLint to load the config from the package `eslint-config-minecraft-scripting` - extends: ['minecraft-scripting'], - parserOptions: { - tsconfigRootDir: __dirname, - }, -}; diff --git a/tools/eslint-plugin-minecraft-linting/eslint.config.mjs b/tools/eslint-plugin-minecraft-linting/eslint.config.mjs new file mode 100644 index 0000000..270d82c --- /dev/null +++ b/tools/eslint-plugin-minecraft-linting/eslint.config.mjs @@ -0,0 +1,3 @@ +import configMinecraftScripting from 'eslint-config-minecraft-scripting'; + +export default [...configMinecraftScripting]; diff --git a/tools/eslint-plugin-minecraft-linting/package.json b/tools/eslint-plugin-minecraft-linting/package.json index 9a8894f..f77bf63 100644 --- a/tools/eslint-plugin-minecraft-linting/package.json +++ b/tools/eslint-plugin-minecraft-linting/package.json @@ -25,10 +25,10 @@ "eslint-plugin-header": "^3.1.1", "eslint-config-prettier": "^9.0.0", "eslint-plugin-unicorn": "^42.0.0", - "just-scripts": "^2.1.3", + "just-scripts": "^2.2.1", "tsconfig": "*" }, "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/tools/eslint-plugin-minecraft-linting/src/index.ts b/tools/eslint-plugin-minecraft-linting/src/index.ts index 6db7af2..44609f2 100644 --- a/tools/eslint-plugin-minecraft-linting/src/index.ts +++ b/tools/eslint-plugin-minecraft-linting/src/index.ts @@ -3,7 +3,14 @@ import AvoidUnnecessaryCommand from './Rules/AvoidUnnecessaryCommand'; +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires +const { name, version }: { name: string; version: string } = require('../package.json'); + module.exports = { + meta: { + name, + version, + }, rules: { 'avoid-unnecessary-command': AvoidUnnecessaryCommand, },