Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. "use strict";
  2. var assign = require("object-assign");
  3. var loaderUtils = require("loader-utils");
  4. var objectHash = require("object-hash");
  5. var createCache = require("loader-fs-cache");
  6. var pkg = require("./package.json");
  7. var cache = createCache("eslint-loader");
  8. var engines = {};
  9. /**
  10. * Class representing an ESLintError.
  11. * @extends Error
  12. */
  13. class ESLintError extends Error {
  14. /**
  15. * Create an ESLintError.
  16. * @param {string} messages - Formatted eslint errors.
  17. */
  18. constructor(messages) {
  19. super();
  20. this.name = "ESLintError";
  21. this.message = messages;
  22. this.stack = "";
  23. }
  24. /**
  25. * Returns a stringified representation of our error. This method is called
  26. * when an error is consumed by console methods
  27. * ex: console.error(new ESLintError(formattedMessage))
  28. * @return {string} error - A stringified representation of the error.
  29. */
  30. inspect() {
  31. return this.message;
  32. }
  33. }
  34. /**
  35. * printLinterOutput
  36. *
  37. * @param {Object} eslint.executeOnText return value
  38. * @param {Object} config eslint configuration
  39. * @param {Object} webpack webpack instance
  40. * @return {void}
  41. */
  42. function printLinterOutput(res, config, webpack) {
  43. // skip ignored file warning
  44. if (
  45. !(
  46. res.warningCount === 1 &&
  47. res.results[0].messages[0] &&
  48. res.results[0].messages[0].message &&
  49. res.results[0].messages[0].message.indexOf("ignore") > 1
  50. )
  51. ) {
  52. // quiet filter done now
  53. // eslint allow rules to be specified in the input between comments
  54. // so we can found warnings defined in the input itself
  55. if (res.warningCount && config.quiet) {
  56. res.warningCount = 0;
  57. res.results[0].warningCount = 0;
  58. res.results[0].messages = res.results[0].messages.filter(function(
  59. message
  60. ) {
  61. return message.severity !== 1;
  62. });
  63. }
  64. // if enabled, use eslint auto-fixing where possible
  65. if (config.fix && (res.results[0].fixableErrorCount > 0 || res.results[0].fixableWarningCount)) {
  66. var eslint = require(config.eslintPath);
  67. eslint.CLIEngine.outputFixes(res);
  68. }
  69. if (res.errorCount || res.warningCount) {
  70. // add filename for each results so formatter can have relevant filename
  71. res.results.forEach(function(r) {
  72. r.filePath = webpack.resourcePath;
  73. });
  74. var messages = config.formatter(res.results);
  75. if (config.outputReport && config.outputReport.filePath) {
  76. var reportOutput;
  77. // if a different formatter is passed in as an option use that
  78. if (config.outputReport.formatter) {
  79. reportOutput = config.outputReport.formatter(res.results);
  80. } else {
  81. reportOutput = messages;
  82. }
  83. var filePath = loaderUtils.interpolateName(
  84. webpack,
  85. config.outputReport.filePath,
  86. {
  87. content: res.results
  88. .map(function(r) {
  89. return r.source;
  90. })
  91. .join("\n")
  92. }
  93. );
  94. webpack.emitFile(filePath, reportOutput);
  95. }
  96. // default behavior: emit error only if we have errors
  97. var emitter = res.errorCount ? webpack.emitError : webpack.emitWarning;
  98. // force emitError or emitWarning if user want this
  99. if (config.emitError) {
  100. emitter = webpack.emitError;
  101. } else if (config.emitWarning) {
  102. emitter = webpack.emitWarning;
  103. }
  104. if (emitter) {
  105. if (config.failOnError && res.errorCount) {
  106. throw new ESLintError(
  107. "Module failed because of a eslint error.\n" + messages
  108. );
  109. } else if (config.failOnWarning && res.warningCount) {
  110. throw new ESLintError(
  111. "Module failed because of a eslint warning.\n" + messages
  112. );
  113. }
  114. emitter(new ESLintError(messages));
  115. } else {
  116. throw new Error(
  117. "Your module system doesn't support emitWarning. " +
  118. "Update available? \n" +
  119. messages
  120. );
  121. }
  122. }
  123. }
  124. }
  125. /**
  126. * webpack loader
  127. *
  128. * @param {String|Buffer} input JavaScript string
  129. * @param {Object} map input source map
  130. * @return {void}
  131. */
  132. module.exports = function(input, map) {
  133. var webpack = this;
  134. var userOptions = assign(
  135. // user defaults
  136. (webpack.options && webpack.options.eslint) || webpack.query || {},
  137. // loader query string
  138. loaderUtils.getOptions(webpack)
  139. );
  140. var userEslintPath = userOptions.eslintPath;
  141. var config = assign(
  142. // loader defaults
  143. {
  144. cacheIdentifier: JSON.stringify({
  145. "eslint-loader": pkg.version,
  146. eslint: require(userEslintPath || "eslint").version
  147. }),
  148. eslintPath: "eslint"
  149. },
  150. userOptions
  151. );
  152. if (typeof config.formatter === "string") {
  153. try {
  154. config.formatter = require(config.formatter);
  155. if (
  156. config.formatter &&
  157. typeof config.formatter !== "function" &&
  158. typeof config.formatter.default === "function"
  159. ) {
  160. config.formatter = config.formatter.default;
  161. }
  162. } catch (_) {
  163. // ignored
  164. }
  165. }
  166. if (config.formatter == null || typeof config.formatter !== "function") {
  167. if (userEslintPath) {
  168. try {
  169. config.formatter = require(userEslintPath + "/lib/formatters/stylish");
  170. } catch (e) {
  171. config.formatter = require("eslint/lib/formatters/stylish");
  172. }
  173. } else {
  174. config.formatter = require("eslint/lib/formatters/stylish");
  175. }
  176. }
  177. var cacheDirectory = config.cache;
  178. var cacheIdentifier = config.cacheIdentifier;
  179. delete config.cacheIdentifier;
  180. // Create the engine only once per config
  181. var configHash = objectHash(config);
  182. if (!engines[configHash]) {
  183. var eslint = require(config.eslintPath);
  184. engines[configHash] = new eslint.CLIEngine(config);
  185. }
  186. webpack.cacheable();
  187. var resourcePath = webpack.resourcePath;
  188. var cwd = process.cwd();
  189. // remove cwd from resource path in case webpack has been started from project
  190. // root, to allow having relative paths in .eslintignore
  191. if (resourcePath.indexOf(cwd) === 0) {
  192. resourcePath = resourcePath.substr(cwd.length + 1);
  193. }
  194. var engine = engines[configHash];
  195. // return early if cached
  196. if (config.cache) {
  197. var callback = webpack.async();
  198. return cache(
  199. {
  200. directory: cacheDirectory,
  201. identifier: cacheIdentifier,
  202. options: config,
  203. source: input,
  204. transform: function() {
  205. return lint(engine, input, resourcePath);
  206. }
  207. },
  208. function(err, res) {
  209. if (err) {
  210. return callback(err);
  211. }
  212. try {
  213. printLinterOutput(res || {}, config, webpack);
  214. } catch (e) {
  215. err = e;
  216. }
  217. return callback(err, input, map);
  218. }
  219. );
  220. }
  221. printLinterOutput(lint(engine, input, resourcePath), config, webpack);
  222. webpack.callback(null, input, map);
  223. };
  224. function lint(engine, input, resourcePath) {
  225. return engine.executeOnText(input, resourcePath, true);
  226. }