From f9d2dfd0d579c6a98e4415ec77d7398b312a9c1c Mon Sep 17 00:00:00 2001 From: Brody McKee Date: Tue, 10 Aug 2021 16:04:43 +0300 Subject: [PATCH 1/4] Add rootDir setting to eslint-plugin-next --- .../eslint-plugin-next/@types/eslint.d.ts | 9 +++++ packages/eslint-plugin-next/README.md | 4 ++ packages/eslint-plugin-next/jsconfig.json | 7 ++++ .../lib/rules/google-font-display.js | 2 +- .../lib/rules/google-font-preconnect.js | 2 +- .../lib/rules/link-passhref.js | 2 +- .../lib/rules/no-html-link-for-pages.js | 31 ++++++++++---- .../lib/rules/no-page-custom-font.js | 2 +- .../lib/utils/get-root-dirs.js | 40 +++++++++++++++++++ .../{nodeAttributes.js => node-attributes.js} | 0 packages/eslint-plugin-next/package.json | 9 +++++ yarn.lock | 14 ++++++- 12 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 packages/eslint-plugin-next/@types/eslint.d.ts create mode 100644 packages/eslint-plugin-next/README.md create mode 100644 packages/eslint-plugin-next/jsconfig.json create mode 100644 packages/eslint-plugin-next/lib/utils/get-root-dirs.js rename packages/eslint-plugin-next/lib/utils/{nodeAttributes.js => node-attributes.js} (100%) diff --git a/packages/eslint-plugin-next/@types/eslint.d.ts b/packages/eslint-plugin-next/@types/eslint.d.ts new file mode 100644 index 000000000000000..d2b6aec0447ea31 --- /dev/null +++ b/packages/eslint-plugin-next/@types/eslint.d.ts @@ -0,0 +1,9 @@ +import type ESLint from 'eslint' + +declare module 'eslint' { + namespace Rule { + interface RuleContext extends ESLint.Rule.RuleContext { + getCwd(): string + } + } +} diff --git a/packages/eslint-plugin-next/README.md b/packages/eslint-plugin-next/README.md new file mode 100644 index 000000000000000..4e4f393c3a0d45a --- /dev/null +++ b/packages/eslint-plugin-next/README.md @@ -0,0 +1,4 @@ +# `@next/eslint-plugin-next` + +Documentation for `@next/eslint-plugin-next` can be found at: +https://nextjs.org/docs/basic-features/eslint#eslint-plugin diff --git a/packages/eslint-plugin-next/jsconfig.json b/packages/eslint-plugin-next/jsconfig.json new file mode 100644 index 000000000000000..e4468a2aee6de84 --- /dev/null +++ b/packages/eslint-plugin-next/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2019" + }, + "exclude": ["node_modules"] +} diff --git a/packages/eslint-plugin-next/lib/rules/google-font-display.js b/packages/eslint-plugin-next/lib/rules/google-font-display.js index d59dde1021fc463..1dc26c5ed7566ed 100644 --- a/packages/eslint-plugin-next/lib/rules/google-font-display.js +++ b/packages/eslint-plugin-next/lib/rules/google-font-display.js @@ -1,4 +1,4 @@ -const NodeAttributes = require('../utils/nodeAttributes.js') +const NodeAttributes = require('../utils/node-attributes.js') module.exports = { meta: { diff --git a/packages/eslint-plugin-next/lib/rules/google-font-preconnect.js b/packages/eslint-plugin-next/lib/rules/google-font-preconnect.js index 6d5aed8bcc43181..930545ae8994bcd 100644 --- a/packages/eslint-plugin-next/lib/rules/google-font-preconnect.js +++ b/packages/eslint-plugin-next/lib/rules/google-font-preconnect.js @@ -1,4 +1,4 @@ -const NodeAttributes = require('../utils/nodeAttributes.js') +const NodeAttributes = require('../utils/node-attributes.js') module.exports = { meta: { diff --git a/packages/eslint-plugin-next/lib/rules/link-passhref.js b/packages/eslint-plugin-next/lib/rules/link-passhref.js index af0f35596c7dbd4..dc41a44051662cd 100644 --- a/packages/eslint-plugin-next/lib/rules/link-passhref.js +++ b/packages/eslint-plugin-next/lib/rules/link-passhref.js @@ -1,4 +1,4 @@ -const NodeAttributes = require('../utils/nodeAttributes.js') +const NodeAttributes = require('../utils/node-attributes.js') module.exports = { meta: { diff --git a/packages/eslint-plugin-next/lib/rules/no-html-link-for-pages.js b/packages/eslint-plugin-next/lib/rules/no-html-link-for-pages.js index f79d6667abac828..c1a6eda6444812e 100644 --- a/packages/eslint-plugin-next/lib/rules/no-html-link-for-pages.js +++ b/packages/eslint-plugin-next/lib/rules/no-html-link-for-pages.js @@ -1,5 +1,7 @@ +// @ts-check const path = require('path') const fs = require('fs') +const getRootDir = require('../utils/get-root-dirs') const { getUrlFromPagesDirectories, normalizeURL, @@ -20,7 +22,7 @@ const fsExistsSyncCache = {} module.exports = { meta: { docs: { - description: 'Prohibit full page refresh for nextjs pages', + description: 'Prohibit full page refresh for Next.js pages', category: 'HTML', recommended: true, }, @@ -43,14 +45,27 @@ module.exports = { ], }, + /** + * Creates an ESLint rule listener. + * + * @param {import('eslint').Rule.RuleContext} context - ESLint rule context + * @returns {import('eslint').Rule.RuleListener} An ESLint rule listener + */ create: function (context) { - const [customPagesDirectory] = context.options - const pagesDirs = customPagesDirectory - ? [customPagesDirectory].flat() - : [ - path.join(context.getCwd(), 'pages'), - path.join(context.getCwd(), 'src', 'pages'), - ] + /** @type {(string|string[])[]} */ + const ruleOptions = context.options + const [customPagesDirectory] = ruleOptions + + const rootDirs = getRootDir(context) + + const pagesDirs = (customPagesDirectory + ? [customPagesDirectory] + : rootDirs.map((dir) => [ + path.join(dir, 'pages'), + path.join(dir, 'src', 'pages'), + ]) + ).flat() + const foundPagesDirs = pagesDirs.filter((dir) => { if (fsExistsSyncCache[dir] === undefined) { fsExistsSyncCache[dir] = fs.existsSync(dir) diff --git a/packages/eslint-plugin-next/lib/rules/no-page-custom-font.js b/packages/eslint-plugin-next/lib/rules/no-page-custom-font.js index ec7971693d3ac93..94ecaba8ee1b3a1 100644 --- a/packages/eslint-plugin-next/lib/rules/no-page-custom-font.js +++ b/packages/eslint-plugin-next/lib/rules/no-page-custom-font.js @@ -1,4 +1,4 @@ -const NodeAttributes = require('../utils/nodeAttributes.js') +const NodeAttributes = require('../utils/node-attributes.js') module.exports = { meta: { diff --git a/packages/eslint-plugin-next/lib/utils/get-root-dirs.js b/packages/eslint-plugin-next/lib/utils/get-root-dirs.js new file mode 100644 index 000000000000000..9dfa6e352f89012 --- /dev/null +++ b/packages/eslint-plugin-next/lib/utils/get-root-dirs.js @@ -0,0 +1,40 @@ +// @ts-check +const glob = require('glob') + +/** + * Process a Next.js root directory glob. + * + * @param {string} rootDir - A Next.js root directory glob. + * @returns {string[]} - An array of Root directories. + */ +const processRootDir = (rootDir) => { + // Ensures we only match folders. + if (!rootDir.endsWith('/')) rootDir += '/' + return glob.sync(rootDir) +} + +/** + * Gets one or more Root + * + * @param {import('eslint').Rule.RuleContext} context - ESLint rule context + * @returns An array of root directories. + */ +const getRootDir = (context) => { + let rootDirs = [context.getCwd()] + + /** @type {{rootDir?:string|string[]}|undefined} */ + const nextSettings = context.settings.next || {} + let rootDir = nextSettings.rootDir + + if (typeof rootDir === 'string') { + rootDirs = processRootDir(rootDir) + } else if (Array.isArray(rootDir)) { + rootDirs = rootDir + .map((dir) => (typeof dir === 'string' ? processRootDir(dir) : [])) + .flat() + } + + return rootDirs +} + +module.exports = getRootDir diff --git a/packages/eslint-plugin-next/lib/utils/nodeAttributes.js b/packages/eslint-plugin-next/lib/utils/node-attributes.js similarity index 100% rename from packages/eslint-plugin-next/lib/utils/nodeAttributes.js rename to packages/eslint-plugin-next/lib/utils/node-attributes.js diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 6b3dc63120eb412..1cd1d05e6b49492 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -7,5 +7,14 @@ "repository": { "url": "vercel/next.js", "directory": "packages/eslint-plugin-next" + }, + "files": [ + "lib" + ], + "dependencies": { + "glob": "7.1.7" + }, + "peerDependencies": { + "@types/eslint": "7.28.0" } } diff --git a/yarn.lock b/yarn.lock index 3b5433efe116ca8..d447222300bc6ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6325,7 +6325,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001165, caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228: +caniuse-lite@1.0.30001228, caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001165, caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228: version "1.0.30001228" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa" integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A== @@ -9826,6 +9826,18 @@ glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glo once "^1.3.0" path-is-absolute "^1.0.0" +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" From c34d9f26c17b64066130d488701395a2628859b0 Mon Sep 17 00:00:00 2001 From: Brody McKee Date: Tue, 10 Aug 2021 17:08:40 +0300 Subject: [PATCH 2/4] Resolve ESLint types issue --- packages/eslint-plugin-next/@types/eslint.d.ts | 9 --------- packages/eslint-plugin-next/package.json | 2 +- yarn.lock | 8 ++++++++ 3 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 packages/eslint-plugin-next/@types/eslint.d.ts diff --git a/packages/eslint-plugin-next/@types/eslint.d.ts b/packages/eslint-plugin-next/@types/eslint.d.ts deleted file mode 100644 index d2b6aec0447ea31..000000000000000 --- a/packages/eslint-plugin-next/@types/eslint.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type ESLint from 'eslint' - -declare module 'eslint' { - namespace Rule { - interface RuleContext extends ESLint.Rule.RuleContext { - getCwd(): string - } - } -} diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index ddff70585ecc536..eba91f0aae91278 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -14,7 +14,7 @@ "dependencies": { "glob": "7.1.7" }, - "peerDependencies": { + "devDependencies": { "@types/eslint": "7.28.0" } } diff --git a/yarn.lock b/yarn.lock index d447222300bc6ba..45bcd5ee4014f9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4150,6 +4150,14 @@ "@types/estree" "*" "@types/json-schema" "*" +"@types/eslint@7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" + integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + "@types/estree@*": version "0.0.45" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" From d4b250b49be1d832d828d3c3e044e86bd537f393 Mon Sep 17 00:00:00 2001 From: Brody McKee Date: Tue, 10 Aug 2021 17:10:54 +0300 Subject: [PATCH 3/4] Rename export --- packages/eslint-plugin-next/lib/utils/get-root-dirs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin-next/lib/utils/get-root-dirs.js b/packages/eslint-plugin-next/lib/utils/get-root-dirs.js index 9dfa6e352f89012..9a65aa99201b8c4 100644 --- a/packages/eslint-plugin-next/lib/utils/get-root-dirs.js +++ b/packages/eslint-plugin-next/lib/utils/get-root-dirs.js @@ -19,7 +19,7 @@ const processRootDir = (rootDir) => { * @param {import('eslint').Rule.RuleContext} context - ESLint rule context * @returns An array of root directories. */ -const getRootDir = (context) => { +const getRootDirs = (context) => { let rootDirs = [context.getCwd()] /** @type {{rootDir?:string|string[]}|undefined} */ @@ -37,4 +37,4 @@ const getRootDir = (context) => { return rootDirs } -module.exports = getRootDir +module.exports = getRootDirs From c675a6702eca951b278e20fcb553af3a8cf3bf24 Mon Sep 17 00:00:00 2001 From: Brody McKee Date: Tue, 10 Aug 2021 17:28:23 +0300 Subject: [PATCH 4/4] Solve duplicate eslint types --- yarn.lock | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 45bcd5ee4014f9a..32c3b2ae84ab1b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4142,15 +4142,7 @@ "@types/eslint" "*" "@types/estree" "*" -"@types/eslint@*": - version "7.2.8" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.8.tgz#45cd802380fcc352e5680e1781d43c50916f12ee" - integrity sha512-RTKvBsfz0T8CKOGZMfuluDNyMFHnu5lvNr4hWEsQeHXH6FcmIDIozOyWMh36nLGMwVd5UFNXC2xztA8lln22MQ== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/eslint@7.28.0": +"@types/eslint@*", "@types/eslint@7.28.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==