runLintCheck.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.runLintCheck = runLintCheck;
  6. var _fs = require("fs");
  7. var _chalk = _interopRequireDefault(require("next/dist/compiled/chalk"));
  8. var _path = _interopRequireDefault(require("path"));
  9. var _findUp = _interopRequireDefault(require("next/dist/compiled/find-up"));
  10. var _semver = _interopRequireDefault(require("next/dist/compiled/semver"));
  11. var CommentJson = _interopRequireWildcard(require("next/dist/compiled/comment-json"));
  12. var _customFormatter = require("./customFormatter");
  13. var _writeDefaultConfig = require("./writeDefaultConfig");
  14. var _hasEslintConfiguration = require("./hasEslintConfiguration");
  15. var _writeOutputFile = require("./writeOutputFile");
  16. var _constants = require("../constants");
  17. var _findPagesDir = require("../find-pages-dir");
  18. var _installDependencies = require("../install-dependencies");
  19. var _hasNecessaryDependencies = require("../has-necessary-dependencies");
  20. var Log = _interopRequireWildcard(require("../../build/output/log"));
  21. var _isError = _interopRequireWildcard(require("../is-error"));
  22. var _getPkgManager = require("../helpers/get-pkg-manager");
  23. function _interopRequireDefault(obj) {
  24. return obj && obj.__esModule ? obj : {
  25. default: obj
  26. };
  27. }
  28. function _getRequireWildcardCache() {
  29. if (typeof WeakMap !== "function") return null;
  30. var cache = new WeakMap();
  31. _getRequireWildcardCache = function() {
  32. return cache;
  33. };
  34. return cache;
  35. }
  36. function _interopRequireWildcard(obj) {
  37. if (obj && obj.__esModule) {
  38. return obj;
  39. }
  40. if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
  41. return {
  42. default: obj
  43. };
  44. }
  45. var cache = _getRequireWildcardCache();
  46. if (cache && cache.has(obj)) {
  47. return cache.get(obj);
  48. }
  49. var newObj = {};
  50. var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
  51. for(var key in obj){
  52. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  53. var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
  54. if (desc && (desc.get || desc.set)) {
  55. Object.defineProperty(newObj, key, desc);
  56. } else {
  57. newObj[key] = obj[key];
  58. }
  59. }
  60. }
  61. newObj.default = obj;
  62. if (cache) {
  63. cache.set(obj, newObj);
  64. }
  65. return newObj;
  66. }
  67. // 0 is off, 1 is warn, 2 is error. See https://eslint.org/docs/user-guide/configuring/rules#configuring-rules
  68. const VALID_SEVERITY = [
  69. "off",
  70. "warn",
  71. "error"
  72. ];
  73. function isValidSeverity(severity) {
  74. return VALID_SEVERITY.includes(severity);
  75. }
  76. const requiredPackages = [
  77. {
  78. file: "eslint",
  79. pkg: "eslint",
  80. exportsRestrict: false
  81. },
  82. {
  83. file: "eslint-config-next",
  84. pkg: "eslint-config-next",
  85. exportsRestrict: false
  86. },
  87. ];
  88. async function cliPrompt() {
  89. console.log(_chalk.default.bold(`${_chalk.default.cyan("?")} How would you like to configure ESLint? https://nextjs.org/docs/basic-features/eslint`));
  90. try {
  91. const cliSelect = (await Promise.resolve(require("next/dist/compiled/cli-select"))).default;
  92. const { value } = await cliSelect({
  93. values: _constants.ESLINT_PROMPT_VALUES,
  94. valueRenderer: ({ title , recommended }, selected)=>{
  95. const name = selected ? _chalk.default.bold.underline.cyan(title) : title;
  96. return name + (recommended ? _chalk.default.bold.yellow(" (recommended)") : "");
  97. },
  98. selected: _chalk.default.cyan("\u276F "),
  99. unselected: " "
  100. });
  101. return {
  102. config: value == null ? void 0 : value.config
  103. };
  104. } catch {
  105. return {
  106. config: null
  107. };
  108. }
  109. }
  110. async function lint(baseDir, lintDirs, eslintrcFile, pkgJsonPath, hasAppDir, { lintDuringBuild =false , eslintOptions =null , reportErrorsOnly =false , maxWarnings =-1 , formatter =null , outputFile =null }) {
  111. try {
  112. var ref, ref1;
  113. // Load ESLint after we're sure it exists:
  114. const deps = await (0, _hasNecessaryDependencies).hasNecessaryDependencies(baseDir, requiredPackages);
  115. const packageManager = (0, _getPkgManager).getPkgManager(baseDir);
  116. if (deps.missing.some((dep)=>dep.pkg === "eslint")) {
  117. Log.error(`ESLint must be installed${lintDuringBuild ? " in order to run during builds:" : ":"} ${_chalk.default.bold.cyan((packageManager === "yarn" ? "yarn add --dev" : packageManager === "pnpm" ? "pnpm install --save-dev" : "npm install --save-dev") + " eslint")}`);
  118. return null;
  119. }
  120. const mod = await Promise.resolve(require(deps.resolved.get("eslint")));
  121. const { ESLint } = mod;
  122. var ref2;
  123. let eslintVersion = (ref2 = ESLint == null ? void 0 : ESLint.version) != null ? ref2 : mod == null ? void 0 : (ref = mod.CLIEngine) == null ? void 0 : ref.version;
  124. if (!eslintVersion || _semver.default.lt(eslintVersion, "7.0.0")) {
  125. return `${_chalk.default.red("error")} - Your project has an older version of ESLint installed${eslintVersion ? " (" + eslintVersion + ")" : ""}. Please upgrade to ESLint version 7 or above`;
  126. }
  127. let options = {
  128. useEslintrc: true,
  129. baseConfig: {},
  130. errorOnUnmatchedPattern: false,
  131. extensions: [
  132. ".js",
  133. ".jsx",
  134. ".ts",
  135. ".tsx"
  136. ],
  137. cache: true,
  138. ...eslintOptions
  139. };
  140. let eslint = new ESLint(options);
  141. let nextEslintPluginIsEnabled = false;
  142. const nextRulesEnabled = new Map();
  143. const pagesDirRules = [
  144. "@next/next/no-html-link-for-pages"
  145. ];
  146. for (const configFile of [
  147. eslintrcFile,
  148. pkgJsonPath
  149. ]){
  150. var ref3;
  151. if (!configFile) continue;
  152. const completeConfig = await eslint.calculateConfigForFile(configFile);
  153. if ((ref3 = completeConfig.plugins) == null ? void 0 : ref3.includes("@next/next")) {
  154. nextEslintPluginIsEnabled = true;
  155. for (const [name, [severity]] of Object.entries(completeConfig.rules)){
  156. if (!name.startsWith("@next/next/")) {
  157. continue;
  158. }
  159. if (typeof severity === "number" && severity >= 0 && severity < VALID_SEVERITY.length) {
  160. nextRulesEnabled.set(name, VALID_SEVERITY[severity]);
  161. } else if (typeof severity === "string" && isValidSeverity(severity)) {
  162. nextRulesEnabled.set(name, severity);
  163. }
  164. }
  165. break;
  166. }
  167. }
  168. const pagesDir = (0, _findPagesDir).findPagesDir(baseDir, hasAppDir).pages;
  169. if (nextEslintPluginIsEnabled) {
  170. let updatedPagesDir = false;
  171. for (const rule of pagesDirRules){
  172. var ref4, ref5;
  173. if (!((ref4 = options.baseConfig.rules) == null ? void 0 : ref4[rule]) && !((ref5 = options.baseConfig.rules) == null ? void 0 : ref5[rule.replace("@next/next", "@next/babel-plugin-next")])) {
  174. if (!options.baseConfig.rules) {
  175. options.baseConfig.rules = {};
  176. }
  177. options.baseConfig.rules[rule] = [
  178. 1,
  179. pagesDir
  180. ];
  181. updatedPagesDir = true;
  182. }
  183. }
  184. if (updatedPagesDir) {
  185. eslint = new ESLint(options);
  186. }
  187. } else {
  188. Log.warn("The Next.js plugin was not detected in your ESLint configuration. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config");
  189. }
  190. const lintStart = process.hrtime();
  191. let results = await eslint.lintFiles(lintDirs);
  192. let selectedFormatter = null;
  193. if (options.fix) await ESLint.outputFixes(results);
  194. if (reportErrorsOnly) results = await ESLint.getErrorResults(results) // Only return errors if --quiet flag is used
  195. ;
  196. if (formatter) selectedFormatter = await eslint.loadFormatter(formatter);
  197. const formattedResult = (0, _customFormatter).formatResults(baseDir, results, selectedFormatter == null ? void 0 : selectedFormatter.format);
  198. const lintEnd = process.hrtime(lintStart);
  199. const totalWarnings = results.reduce((sum, file)=>sum + file.warningCount, 0);
  200. if (outputFile) await (0, _writeOutputFile).writeOutputFile(outputFile, formattedResult.output);
  201. return {
  202. output: formattedResult.outputWithMessages,
  203. isError: ((ref1 = ESLint.getErrorResults(results)) == null ? void 0 : ref1.length) > 0 || maxWarnings >= 0 && totalWarnings > maxWarnings,
  204. eventInfo: {
  205. durationInSeconds: lintEnd[0],
  206. eslintVersion: eslintVersion,
  207. lintedFilesCount: results.length,
  208. lintFix: !!options.fix,
  209. nextEslintPluginVersion: nextEslintPluginIsEnabled && deps.resolved.has("eslint-config-next") ? require(_path.default.join(_path.default.dirname(deps.resolved.get("eslint-config-next")), "package.json")).version : null,
  210. nextEslintPluginErrorsCount: formattedResult.totalNextPluginErrorCount,
  211. nextEslintPluginWarningsCount: formattedResult.totalNextPluginWarningCount,
  212. nextRulesEnabled: Object.fromEntries(nextRulesEnabled)
  213. }
  214. };
  215. } catch (err) {
  216. if (lintDuringBuild) {
  217. Log.error(`ESLint: ${(0, _isError).default(err) && err.message ? err.message.replace(/\n/g, " ") : err}`);
  218. return null;
  219. } else {
  220. throw (0, _isError).getProperError(err);
  221. }
  222. }
  223. }
  224. async function runLintCheck(baseDir, lintDirs, opts) {
  225. const { lintDuringBuild =false , eslintOptions =null , reportErrorsOnly =false , maxWarnings =-1 , formatter =null , outputFile =null , strict =false , hasAppDir , } = opts;
  226. try {
  227. var ref;
  228. // Find user's .eslintrc file
  229. // See: https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-file-formats
  230. const eslintrcFile = (ref = await (0, _findUp).default([
  231. ".eslintrc.js",
  232. ".eslintrc.cjs",
  233. ".eslintrc.yaml",
  234. ".eslintrc.yml",
  235. ".eslintrc.json",
  236. ".eslintrc",
  237. ], {
  238. cwd: baseDir
  239. })) != null ? ref : null;
  240. var ref6;
  241. const pkgJsonPath = (ref6 = await (0, _findUp).default("package.json", {
  242. cwd: baseDir
  243. })) != null ? ref6 : null;
  244. let packageJsonConfig = null;
  245. if (pkgJsonPath) {
  246. const pkgJsonContent = await _fs.promises.readFile(pkgJsonPath, {
  247. encoding: "utf8"
  248. });
  249. packageJsonConfig = CommentJson.parse(pkgJsonContent);
  250. }
  251. const config = await (0, _hasEslintConfiguration).hasEslintConfiguration(eslintrcFile, packageJsonConfig);
  252. let deps;
  253. if (config.exists) {
  254. // Run if ESLint config exists
  255. return await lint(baseDir, lintDirs, eslintrcFile, pkgJsonPath, hasAppDir, {
  256. lintDuringBuild,
  257. eslintOptions,
  258. reportErrorsOnly,
  259. maxWarnings,
  260. formatter,
  261. outputFile
  262. });
  263. } else {
  264. // Display warning if no ESLint configuration is present inside
  265. // config file during "next build", no warning is shown when
  266. // no eslintrc file is present
  267. if (lintDuringBuild) {
  268. if (config.emptyPkgJsonConfig || config.emptyEslintrc) {
  269. Log.warn(`No ESLint configuration detected. Run ${_chalk.default.bold.cyan("next lint")} to begin setup`);
  270. }
  271. return null;
  272. } else {
  273. // Ask user what config they would like to start with for first time "next lint" setup
  274. const { config: selectedConfig } = strict ? _constants.ESLINT_PROMPT_VALUES.find((opt)=>opt.title === "Strict") : await cliPrompt();
  275. if (selectedConfig == null) {
  276. // Show a warning if no option is selected in prompt
  277. Log.warn("If you set up ESLint yourself, we recommend adding the Next.js ESLint plugin. See https://nextjs.org/docs/basic-features/eslint#migrating-existing-config");
  278. return null;
  279. } else {
  280. // Check if necessary deps installed, and install any that are missing
  281. deps = await (0, _hasNecessaryDependencies).hasNecessaryDependencies(baseDir, requiredPackages);
  282. if (deps.missing.length > 0) await (0, _installDependencies).installDependencies(baseDir, deps.missing, true);
  283. // Write default ESLint config.
  284. // Check for /pages and src/pages is to make sure this happens in Next.js folder
  285. if ((0, _findPagesDir).existsSync(_path.default.join(baseDir, "pages")) || (0, _findPagesDir).existsSync(_path.default.join(baseDir, "src/pages"))) {
  286. await (0, _writeDefaultConfig).writeDefaultConfig(baseDir, config, selectedConfig, eslintrcFile, pkgJsonPath, packageJsonConfig);
  287. }
  288. }
  289. Log.ready(`ESLint has successfully been configured. Run ${_chalk.default.bold.cyan("next lint")} again to view warnings and errors.`);
  290. return null;
  291. }
  292. }
  293. } catch (err) {
  294. throw err;
  295. }
  296. }
  297. //# sourceMappingURL=runLintCheck.js.map