getESLint.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. "use strict";
  2. const {
  3. cpus
  4. } = require('os');
  5. const {
  6. Worker: JestWorker
  7. } = require('jest-worker');
  8. const {
  9. getESLintOptions
  10. } = require('./options');
  11. const {
  12. jsonStringifyReplacerSortKeys
  13. } = require('./utils');
  14. /** @type {{[key: string]: any}} */
  15. const cache = {};
  16. /** @typedef {import('eslint').ESLint} ESLint */
  17. /** @typedef {import('eslint').ESLint.LintResult} LintResult */
  18. /** @typedef {import('./options').Options} Options */
  19. /** @typedef {() => Promise<void>} AsyncTask */
  20. /** @typedef {(files: string|string[]) => Promise<LintResult[]>} LintTask */
  21. /** @typedef {{threads: number, ESLint: ESLint, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask}} Linter */
  22. /** @typedef {JestWorker & {lintFiles: LintTask}} Worker */
  23. /**
  24. * @param {Options} options
  25. * @returns {Linter}
  26. */
  27. function loadESLint(options) {
  28. const {
  29. eslintPath
  30. } = options;
  31. const {
  32. ESLint
  33. } = require(eslintPath || 'eslint'); // Filter out loader options before passing the options to ESLint.
  34. const eslint = new ESLint(getESLintOptions(options));
  35. return {
  36. threads: 1,
  37. ESLint,
  38. eslint,
  39. lintFiles: async files => {
  40. const results = await eslint.lintFiles(files); // istanbul ignore else
  41. if (options.fix) {
  42. await ESLint.outputFixes(results);
  43. }
  44. return results;
  45. },
  46. // no-op for non-threaded
  47. cleanup: async () => {}
  48. };
  49. }
  50. /**
  51. * @param {string|undefined} key
  52. * @param {number} poolSize
  53. * @param {Options} options
  54. * @returns {Linter}
  55. */
  56. function loadESLintThreaded(key, poolSize, options) {
  57. const cacheKey = getCacheKey(key, options);
  58. const {
  59. eslintPath = 'eslint'
  60. } = options;
  61. const source = require.resolve('./worker');
  62. const workerOptions = {
  63. enableWorkerThreads: true,
  64. numWorkers: poolSize,
  65. setupArgs: [{
  66. eslintPath,
  67. eslintOptions: getESLintOptions(options)
  68. }]
  69. };
  70. const local = loadESLint(options);
  71. let worker =
  72. /** @type {Worker?} */
  73. new JestWorker(source, workerOptions);
  74. /** @type {Linter} */
  75. const context = { ...local,
  76. threads: poolSize,
  77. lintFiles: async files => worker && (await worker.lintFiles(files)) ||
  78. /* istanbul ignore next */
  79. [],
  80. cleanup: async () => {
  81. cache[cacheKey] = local;
  82. context.lintFiles = files => local.lintFiles(files);
  83. if (worker) {
  84. worker.end();
  85. worker = null;
  86. }
  87. }
  88. };
  89. return context;
  90. }
  91. /**
  92. * @param {string|undefined} key
  93. * @param {Options} options
  94. * @returns {Linter}
  95. */
  96. function getESLint(key, {
  97. threads,
  98. ...options
  99. }) {
  100. const max = typeof threads !== 'number' ? threads ? cpus().length - 1 : 1 :
  101. /* istanbul ignore next */
  102. threads;
  103. const cacheKey = getCacheKey(key, {
  104. threads,
  105. ...options
  106. });
  107. if (!cache[cacheKey]) {
  108. cache[cacheKey] = max > 1 ? loadESLintThreaded(key, max, options) : loadESLint(options);
  109. }
  110. return cache[cacheKey];
  111. }
  112. /**
  113. * @param {string|undefined} key
  114. * @param {Options} options
  115. * @returns {string}
  116. */
  117. function getCacheKey(key, options) {
  118. return JSON.stringify({
  119. key,
  120. options
  121. }, jsonStringifyReplacerSortKeys);
  122. }
  123. module.exports = {
  124. loadESLint,
  125. loadESLintThreaded,
  126. getESLint
  127. };