Skip to content

Conversation

hyoban
Copy link

@hyoban hyoban commented Sep 28, 2023

According to the doc from ESLint, eslint.config.js is the configuration file format of the next version of eslint, and ESLint's new config system, Part 3: Developer preview mentions how developers should migrate.

Therefore, in this PR, I try to make next lint can support eslint.config.js and execute lint correctly.
If I made a mistake, I'll do my best to fix it.

@hyoban hyoban marked this pull request as draft October 6, 2023 08:45
Copy link

@AlexisSossa AlexisSossa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi maybe this works!!

// Function to load ESLint dependencies and check if ESLint is installed
async function loadESLintDependencies(baseDir) {
    const deps = await hasNecessaryDependencies(baseDir, requiredPackages);
    if (deps.missing.some((dep) => dep.pkg === 'eslint')) {
        throw new Error(`ESLint is not installed. Please install ESLint in your project.`);
    }
    return deps;
}

// Function to get the ESLint module and its associated unsupported API
async function getESLintModule(baseDir) {
    const deps = await loadESLintDependencies(baseDir);
    const modPath = deps.resolved.get('eslint');
    const unsupportedApiPath = path.resolve(path.dirname(modPath), './unsupported-api.js');
    
    const mod = require(modPath);
    const unsupportedApi = require(unsupportedApiPath);

    return { mod, unsupportedApi };
}

// Function to determine the configuration type for ESLint
async function determineConfigType({ mod, unsupportedApi }) {
    let shouldUseFlatConfig = await unsupportedApi.shouldUseFlatConfig();
    return shouldUseFlatConfig ? unsupportedApi.FlatESLint : mod.ESLint;
}

// Function to configure ESLint with the appropriate options
async function configureESLint(baseDir, opts) {
    const { mod, unsupportedApi } = await getESLintModule(baseDir);
    const ESLint = await determineConfigType({ mod, unsupportedApi });
    
    const eslintVersion = ESLint.version ?? mod.CLIEngine.version;
    if (!eslintVersion || semver.lt(eslintVersion, '7.0.0')) {
        throw new Error(`Your project is using an older version of ESLint (${eslintVersion}). Please upgrade to version 7 or above.`);
    }

    let options = getESLintOptions(unsupportedApi, opts);
    return new ESLint(options);
}

// Function to get ESLint options based on configuration type
function getESLintOptions(unsupportedApi, opts) {
    let shouldUseFlatConfig = unsupportedApi.shouldUseFlatConfig();
    return {
        baseConfig: {},
        ...(!shouldUseFlatConfig ? {
            useEslintrc: true,
            errorOnUnmatchedPattern: false,
            extensions: ['.js', '.jsx', '.ts', '.tsx'],
            cache: true,
            ...opts.eslintOptions,
        } : {}),
    };
}

// Main function to handle the linting process
async function mainLintFunction(baseDir, opts) {
    try {
        const eslint = await configureESLint(baseDir, opts);
        // Rest of the linting logic here...
    } catch (error) {
        Log.error(error.message);
        return null;
    }
}

// Main execution
mainLintFunction(baseDir, opts);

@hyoban
Copy link
Author

hyoban commented Dec 9, 2023

Close this PR for now. Anyone who needs it can patch next according to the Files changed.

Here is a patch example:

diff --git a/dist/lib/eslint/runLintCheck.js b/dist/lib/eslint/runLintCheck.js
index 080bd27878721805f16d104255ba4a8b1a9a1410..ab431e64e746f97ef23ba799c20005a3caf728fe 100644
--- a/dist/lib/eslint/runLintCheck.js
+++ b/dist/lib/eslint/runLintCheck.js
@@ -122,34 +122,56 @@ async function lint(baseDir, lintDirs, eslintrcFile, pkgJsonPath, { lintDuringBu
             _log.error(`ESLint must be installed${lintDuringBuild ? " in order to run during builds:" : ":"} ${(0, _picocolors.bold)((0, _picocolors.cyan)((packageManager === "yarn" ? "yarn add --dev" : packageManager === "pnpm" ? "pnpm install --save-dev" : "npm install --save-dev") + " eslint"))}`);
             return null;
         }
-        const mod = await Promise.resolve(require(deps.resolved.get("eslint")));
-        const { ESLint } = mod;
+        const modPath = deps.resolved.get('eslint')
+        const unsupportedApiPath = _path.default.resolve(
+            _path.default.dirname(modPath),
+            './unsupported-api.js'
+        )
+
+        const mod = await Promise.resolve(require(modPath))
+        const unsupportedApi = await new Promise((resolve) => {
+            try {
+                resolve(require(unsupportedApiPath))
+            } catch (err) {
+                resolve(null)
+            }
+        })
+
+        let { ESLint } = mod
+        let shouldUseFlatConfig = false
+        if (unsupportedApi) {
+            shouldUseFlatConfig = await unsupportedApi.shouldUseFlatConfig?.()
+            if (shouldUseFlatConfig) {
+                ESLint = unsupportedApi.FlatESLint
+            }
+        }
+
         let eslintVersion = (ESLint == null ? void 0 : ESLint.version) ?? ((_mod_CLIEngine = mod.CLIEngine) == null ? void 0 : _mod_CLIEngine.version);
         if (!eslintVersion || _semver.default.lt(eslintVersion, "7.0.0")) {
             return `${(0, _picocolors.red)("error")} - Your project has an older version of ESLint installed${eslintVersion ? " (" + eslintVersion + ")" : ""}. Please upgrade to ESLint version 7 or above`;
         }
         let options = {
-            useEslintrc: true,
             baseConfig: {},
-            errorOnUnmatchedPattern: false,
-            extensions: [
-                ".js",
-                ".jsx",
-                ".ts",
-                ".tsx"
-            ],
-            cache: true,
-            ...eslintOptions
+            ...(!shouldUseFlatConfig
+                ? {
+                    useEslintrc: true,
+                    errorOnUnmatchedPattern: false,
+                    extensions: ['.js', '.jsx', '.ts', '.tsx'],
+                    cache: true,
+                    ...eslintOptions,
+                  }
+                : {}),
         };
         let eslint = new ESLint(options);
         let nextEslintPluginIsEnabled = false;
         const nextRulesEnabled = new Map();
+
         for (const configFile of [
             eslintrcFile,
             pkgJsonPath
         ]){
             var _completeConfig_plugins;
-            if (!configFile) continue;
+            if (!configFile || shouldUseFlatConfig) continue;
             const completeConfig = await eslint.calculateConfigForFile(configFile);
             if ((_completeConfig_plugins = completeConfig.plugins) == null ? void 0 : _completeConfig_plugins.includes("@next/next")) {
                 nextEslintPluginIsEnabled = true;
@@ -189,8 +211,10 @@ async function lint(baseDir, lintDirs, eslintrcFile, pkgJsonPath, { lintDuringBu
                 eslint = new ESLint(options);
             }
         } else {
-            _log.warn("");
-            _log.warn("The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config");
+            if (!shouldUseFlatConfig) {
+                _log.warn("");
+                _log.warn("The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config");
+            }
         }
         const lintStart = process.hrtime();
         let results = await eslint.lintFiles(lintDirs);
@@ -237,7 +261,8 @@ async function runLintCheck(baseDir, lintDirs, opts) {
             ".eslintrc.yaml",
             ".eslintrc.yml",
             ".eslintrc.json",
-            ".eslintrc"
+            ".eslintrc",
+            "eslint.config.js"
         ], {
             cwd: baseDir
         }) ?? null;

@hyoban hyoban closed this Dec 9, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants