web 3d图形渲染器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
7.8 KiB

  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = linter;
  6. var _path = require("path");
  7. var _ESLintError = _interopRequireDefault(require("./ESLintError"));
  8. var _getESLint = _interopRequireDefault(require("./getESLint"));
  9. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  10. /** @typedef {import('eslint').ESLint} ESLint */
  11. /** @typedef {import('eslint').ESLint.Formatter} Formatter */
  12. /** @typedef {import('eslint').ESLint.LintResult} LintResult */
  13. /** @typedef {import('webpack').Compiler} Compiler */
  14. /** @typedef {import('webpack').Compilation} Compilation */
  15. /** @typedef {import('webpack-sources').Source} Source */
  16. /** @typedef {import('./options').Options} Options */
  17. /** @typedef {import('./options').FormatterFunction} FormatterFunction */
  18. /** @typedef {(compilation: Compilation) => Promise<void>} GenerateReport */
  19. /** @typedef {{errors?: ESLintError, warnings?: ESLintError, generateReportAsset?: GenerateReport}} Report */
  20. /** @typedef {() => Promise<Report>} Reporter */
  21. /** @typedef {(files: string|string[]) => void} Linter */
  22. /** @typedef {{[files: string]: LintResult}} LintResultMap */
  23. /** @type {WeakMap<Compiler, LintResultMap>} */
  24. const resultStorage = new WeakMap();
  25. /**
  26. * @param {string|undefined} key
  27. * @param {Options} options
  28. * @param {Compilation} compilation
  29. * @returns {{lint: Linter, report: Reporter}}
  30. */
  31. function linter(key, options, compilation) {
  32. /** @type {ESLint} */
  33. let eslint;
  34. /** @type {(files: string|string[]) => Promise<LintResult[]>} */
  35. let lintFiles;
  36. /** @type {() => Promise<void>} */
  37. let cleanup;
  38. /** @type {Promise<LintResult[]>[]} */
  39. const rawResults = [];
  40. const crossRunResultStorage = getResultStorage(compilation);
  41. try {
  42. ({
  43. eslint,
  44. lintFiles,
  45. cleanup
  46. } = (0, _getESLint.default)(key, options));
  47. } catch (e) {
  48. throw new _ESLintError.default(e.message);
  49. }
  50. return {
  51. lint,
  52. report
  53. };
  54. /**
  55. * @param {string | string[]} files
  56. */
  57. function lint(files) {
  58. for (const file of asList(files)) {
  59. delete crossRunResultStorage[file];
  60. }
  61. rawResults.push(lintFiles(files).catch(e => {
  62. compilation.errors.push(e);
  63. return [];
  64. }));
  65. }
  66. async function report() {
  67. // Filter out ignored files.
  68. let results = await removeIgnoredWarnings(eslint, // Get the current results, resetting the rawResults to empty
  69. await flatten(rawResults.splice(0, rawResults.length)));
  70. await cleanup();
  71. for (const result of results) {
  72. crossRunResultStorage[result.filePath] = result;
  73. }
  74. results = Object.values(crossRunResultStorage); // do not analyze if there are no results or eslint config
  75. if (!results || results.length < 1) {
  76. return {};
  77. }
  78. const formatter = await loadFormatter(eslint, options.formatter);
  79. const {
  80. errors,
  81. warnings
  82. } = formatResults(formatter, parseResults(options, results));
  83. return {
  84. errors,
  85. warnings,
  86. generateReportAsset
  87. };
  88. /**
  89. * @param {Compilation} compilation
  90. * @returns {Promise<void>}
  91. */
  92. async function generateReportAsset({
  93. compiler
  94. }) {
  95. const {
  96. outputReport
  97. } = options;
  98. /**
  99. * @param {string} name
  100. * @param {string | Buffer} content
  101. */
  102. const save = (name, content) =>
  103. /** @type {Promise<void>} */
  104. new Promise((finish, bail) => {
  105. const {
  106. mkdir,
  107. writeFile
  108. } = compiler.outputFileSystem; // ensure directory exists
  109. // @ts-ignore - the types for `outputFileSystem` are missing the 3 arg overload
  110. mkdir((0, _path.dirname)(name), {
  111. recursive: true
  112. }, err => {
  113. /* istanbul ignore if */
  114. if (err) bail(err);else writeFile(name, content, err2 => {
  115. /* istanbul ignore if */
  116. if (err2) bail(err2);else finish();
  117. });
  118. });
  119. });
  120. if (!outputReport || !outputReport.filePath) {
  121. return;
  122. }
  123. const content = outputReport.formatter ? (await loadFormatter(eslint, outputReport.formatter)).format(results) : formatter.format(results);
  124. let {
  125. filePath
  126. } = outputReport;
  127. if (!(0, _path.isAbsolute)(filePath)) {
  128. filePath = (0, _path.join)(compiler.outputPath, filePath);
  129. }
  130. await save(filePath, content);
  131. }
  132. }
  133. }
  134. /**
  135. * @param {Formatter} formatter
  136. * @param {{ errors: LintResult[]; warnings: LintResult[]; }} results
  137. * @returns {{errors?: ESLintError, warnings?: ESLintError}}
  138. */
  139. function formatResults(formatter, results) {
  140. let errors;
  141. let warnings;
  142. if (results.warnings.length > 0) {
  143. warnings = new _ESLintError.default(formatter.format(results.warnings));
  144. }
  145. if (results.errors.length > 0) {
  146. errors = new _ESLintError.default(formatter.format(results.errors));
  147. }
  148. return {
  149. errors,
  150. warnings
  151. };
  152. }
  153. /**
  154. * @param {Options} options
  155. * @param {LintResult[]} results
  156. * @returns {{errors: LintResult[], warnings: LintResult[]}}
  157. */
  158. function parseResults(options, results) {
  159. /** @type {LintResult[]} */
  160. const errors = [];
  161. /** @type {LintResult[]} */
  162. const warnings = [];
  163. results.forEach(file => {
  164. if (fileHasErrors(file)) {
  165. const messages = file.messages.filter(message => options.emitError && message.severity === 2);
  166. if (messages.length > 0) {
  167. errors.push({ ...file,
  168. messages
  169. });
  170. }
  171. }
  172. if (fileHasWarnings(file)) {
  173. const messages = file.messages.filter(message => options.emitWarning && message.severity === 1);
  174. if (messages.length > 0) {
  175. warnings.push({ ...file,
  176. messages
  177. });
  178. }
  179. }
  180. });
  181. return {
  182. errors,
  183. warnings
  184. };
  185. }
  186. /**
  187. * @param {LintResult} file
  188. * @returns {boolean}
  189. */
  190. function fileHasErrors(file) {
  191. return file.errorCount > 0;
  192. }
  193. /**
  194. * @param {LintResult} file
  195. * @returns {boolean}
  196. */
  197. function fileHasWarnings(file) {
  198. return file.warningCount > 0;
  199. }
  200. /**
  201. * @param {ESLint} eslint
  202. * @param {string|FormatterFunction=} formatter
  203. * @returns {Promise<Formatter>}
  204. */
  205. async function loadFormatter(eslint, formatter) {
  206. if (typeof formatter === 'function') {
  207. return {
  208. format: formatter
  209. };
  210. }
  211. if (typeof formatter === 'string') {
  212. try {
  213. return eslint.loadFormatter(formatter);
  214. } catch (_) {// Load the default formatter.
  215. }
  216. }
  217. return eslint.loadFormatter();
  218. }
  219. /**
  220. * @param {ESLint} eslint
  221. * @param {LintResult[]} results
  222. * @returns {Promise<LintResult[]>}
  223. */
  224. async function removeIgnoredWarnings(eslint, results) {
  225. const filterPromises = results.map(async result => {
  226. // Short circuit the call to isPathIgnored.
  227. // fatal is false for ignored file warnings.
  228. // ruleId is unset for internal ESLint errors.
  229. // line is unset for warnings not involving file contents.
  230. const ignored = result.messages.length === 0 || result.warningCount === 1 && result.errorCount === 0 && !result.messages[0].fatal && !result.messages[0].ruleId && !result.messages[0].line && (await eslint.isPathIgnored(result.filePath));
  231. return ignored ? false : result;
  232. }); // @ts-ignore
  233. return (await Promise.all(filterPromises)).filter(result => !!result);
  234. }
  235. /**
  236. * @param {Promise<LintResult[]>[]} results
  237. * @returns {Promise<LintResult[]>}
  238. */
  239. async function flatten(results) {
  240. /**
  241. * @param {LintResult[]} acc
  242. * @param {LintResult[]} list
  243. */
  244. const flat = (acc, list) => [...acc, ...list];
  245. return (await Promise.all(results)).reduce(flat, []);
  246. }
  247. /**
  248. * @param {Compilation} compilation
  249. * @returns {LintResultMap}
  250. */
  251. function getResultStorage({
  252. compiler
  253. }) {
  254. let storage = resultStorage.get(compiler);
  255. if (!storage) {
  256. resultStorage.set(compiler, storage = {});
  257. }
  258. return storage;
  259. }
  260. /**
  261. * @param {string | string[]} x
  262. */
  263. function asList(x) {
  264. /* istanbul ignore next */
  265. return Array.isArray(x) ? x : [x];
  266. }