项目原始demo,不改动
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
Repozitorijs ir arhivēts. Tam var aplūkot failus un to var klonēt, bet nevar iesūtīt jaunas izmaiņas, kā arī atvērt jaunas problēmas/izmaiņu pieprasījumus.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /**
  2. * @fileoverview Main CLI object.
  3. * @author Nicholas C. Zakas
  4. */
  5. "use strict";
  6. /*
  7. * The CLI object should *not* call process.exit() directly. It should only return
  8. * exit codes. This allows other programs to use the CLI object and still control
  9. * when the program exits.
  10. */
  11. //------------------------------------------------------------------------------
  12. // Requirements
  13. //------------------------------------------------------------------------------
  14. const fs = require("fs"),
  15. path = require("path"),
  16. defaultOptions = require("../conf/default-cli-options"),
  17. Linter = require("./linter"),
  18. IgnoredPaths = require("./ignored-paths"),
  19. Config = require("./config"),
  20. fileEntryCache = require("file-entry-cache"),
  21. globUtil = require("./util/glob-util"),
  22. validator = require("./config/config-validator"),
  23. stringify = require("json-stable-stringify-without-jsonify"),
  24. hash = require("./util/hash"),
  25. ModuleResolver = require("./util/module-resolver"),
  26. naming = require("./util/naming"),
  27. pkg = require("../package.json");
  28. const debug = require("debug")("eslint:cli-engine");
  29. const resolver = new ModuleResolver();
  30. //------------------------------------------------------------------------------
  31. // Typedefs
  32. //------------------------------------------------------------------------------
  33. /**
  34. * The options to configure a CLI engine with.
  35. * @typedef {Object} CLIEngineOptions
  36. * @property {boolean} allowInlineConfig Enable or disable inline configuration comments.
  37. * @property {boolean|Object} baseConfig Base config object. True enables recommend rules and environments.
  38. * @property {boolean} cache Enable result caching.
  39. * @property {string} cacheLocation The cache file to use instead of .eslintcache.
  40. * @property {string} configFile The configuration file to use.
  41. * @property {string} cwd The value to use for the current working directory.
  42. * @property {string[]} envs An array of environments to load.
  43. * @property {string[]} extensions An array of file extensions to check.
  44. * @property {boolean|Function} fix Execute in autofix mode. If a function, should return a boolean.
  45. * @property {string[]} globals An array of global variables to declare.
  46. * @property {boolean} ignore False disables use of .eslintignore.
  47. * @property {string} ignorePath The ignore file to use instead of .eslintignore.
  48. * @property {string} ignorePattern A glob pattern of files to ignore.
  49. * @property {boolean} useEslintrc False disables looking for .eslintrc
  50. * @property {string} parser The name of the parser to use.
  51. * @property {Object} parserOptions An object of parserOption settings to use.
  52. * @property {string[]} plugins An array of plugins to load.
  53. * @property {Object<string,*>} rules An object of rules to use.
  54. * @property {string[]} rulePaths An array of directories to load custom rules from.
  55. * @property {boolean} reportUnusedDisableDirectives `true` adds reports for unused eslint-disable directives
  56. */
  57. /**
  58. * A linting warning or error.
  59. * @typedef {Object} LintMessage
  60. * @property {string} message The message to display to the user.
  61. */
  62. /**
  63. * A linting result.
  64. * @typedef {Object} LintResult
  65. * @property {string} filePath The path to the file that was linted.
  66. * @property {LintMessage[]} messages All of the messages for the result.
  67. * @property {number} errorCount Number of errors for the result.
  68. * @property {number} warningCount Number of warnings for the result.
  69. * @property {number} fixableErrorCount Number of fixable errors for the result.
  70. * @property {number} fixableWarningCount Number of fixable warnings for the result.
  71. * @property {string=} [source] The source code of the file that was linted.
  72. * @property {string=} [output] The source code of the file that was linted, with as many fixes applied as possible.
  73. */
  74. //------------------------------------------------------------------------------
  75. // Helpers
  76. //------------------------------------------------------------------------------
  77. /**
  78. * It will calculate the error and warning count for collection of messages per file
  79. * @param {Object[]} messages - Collection of messages
  80. * @returns {Object} Contains the stats
  81. * @private
  82. */
  83. function calculateStatsPerFile(messages) {
  84. return messages.reduce((stat, message) => {
  85. if (message.fatal || message.severity === 2) {
  86. stat.errorCount++;
  87. if (message.fix) {
  88. stat.fixableErrorCount++;
  89. }
  90. } else {
  91. stat.warningCount++;
  92. if (message.fix) {
  93. stat.fixableWarningCount++;
  94. }
  95. }
  96. return stat;
  97. }, {
  98. errorCount: 0,
  99. warningCount: 0,
  100. fixableErrorCount: 0,
  101. fixableWarningCount: 0
  102. });
  103. }
  104. /**
  105. * It will calculate the error and warning count for collection of results from all files
  106. * @param {Object[]} results - Collection of messages from all the files
  107. * @returns {Object} Contains the stats
  108. * @private
  109. */
  110. function calculateStatsPerRun(results) {
  111. return results.reduce((stat, result) => {
  112. stat.errorCount += result.errorCount;
  113. stat.warningCount += result.warningCount;
  114. stat.fixableErrorCount += result.fixableErrorCount;
  115. stat.fixableWarningCount += result.fixableWarningCount;
  116. return stat;
  117. }, {
  118. errorCount: 0,
  119. warningCount: 0,
  120. fixableErrorCount: 0,
  121. fixableWarningCount: 0
  122. });
  123. }
  124. /**
  125. * Processes an source code using ESLint.
  126. * @param {string} text The source code to check.
  127. * @param {Object} configHelper The configuration options for ESLint.
  128. * @param {string} filename An optional string representing the texts filename.
  129. * @param {boolean|Function} fix Indicates if fixes should be processed.
  130. * @param {boolean} allowInlineConfig Allow/ignore comments that change config.
  131. * @param {boolean} reportUnusedDisableDirectives Allow/ignore comments that change config.
  132. * @param {Linter} linter Linter context
  133. * @returns {LintResult} The results for linting on this text.
  134. * @private
  135. */
  136. function processText(text, configHelper, filename, fix, allowInlineConfig, reportUnusedDisableDirectives, linter) {
  137. let filePath,
  138. fileExtension,
  139. processor;
  140. if (filename) {
  141. filePath = path.resolve(filename);
  142. fileExtension = path.extname(filename);
  143. }
  144. const effectiveFilename = filename || "<text>";
  145. debug(`Linting ${effectiveFilename}`);
  146. const config = configHelper.getConfig(filePath);
  147. if (config.plugins) {
  148. configHelper.plugins.loadAll(config.plugins);
  149. }
  150. const loadedPlugins = configHelper.plugins.getAll();
  151. for (const plugin in loadedPlugins) {
  152. if (loadedPlugins[plugin].processors && Object.keys(loadedPlugins[plugin].processors).indexOf(fileExtension) >= 0) {
  153. processor = loadedPlugins[plugin].processors[fileExtension];
  154. break;
  155. }
  156. }
  157. const autofixingEnabled = typeof fix !== "undefined" && (!processor || processor.supportsAutofix);
  158. const fixedResult = linter.verifyAndFix(text, config, {
  159. filename: effectiveFilename,
  160. allowInlineConfig,
  161. reportUnusedDisableDirectives,
  162. fix: !!autofixingEnabled && fix,
  163. preprocess: processor && (rawText => processor.preprocess(rawText, effectiveFilename)),
  164. postprocess: processor && (problemLists => processor.postprocess(problemLists, effectiveFilename))
  165. });
  166. const stats = calculateStatsPerFile(fixedResult.messages);
  167. const result = {
  168. filePath: effectiveFilename,
  169. messages: fixedResult.messages,
  170. errorCount: stats.errorCount,
  171. warningCount: stats.warningCount,
  172. fixableErrorCount: stats.fixableErrorCount,
  173. fixableWarningCount: stats.fixableWarningCount
  174. };
  175. if (fixedResult.fixed) {
  176. result.output = fixedResult.output;
  177. }
  178. if (result.errorCount + result.warningCount > 0 && typeof result.output === "undefined") {
  179. result.source = text;
  180. }
  181. return result;
  182. }
  183. /**
  184. * Processes an individual file using ESLint. Files used here are known to
  185. * exist, so no need to check that here.
  186. * @param {string} filename The filename of the file being checked.
  187. * @param {Object} configHelper The configuration options for ESLint.
  188. * @param {Object} options The CLIEngine options object.
  189. * @param {Linter} linter Linter context
  190. * @returns {LintResult} The results for linting on this file.
  191. * @private
  192. */
  193. function processFile(filename, configHelper, options, linter) {
  194. const text = fs.readFileSync(path.resolve(filename), "utf8"),
  195. result = processText(
  196. text,
  197. configHelper,
  198. filename,
  199. options.fix,
  200. options.allowInlineConfig,
  201. options.reportUnusedDisableDirectives,
  202. linter
  203. );
  204. return result;
  205. }
  206. /**
  207. * Returns result with warning by ignore settings
  208. * @param {string} filePath - File path of checked code
  209. * @param {string} baseDir - Absolute path of base directory
  210. * @returns {LintResult} Result with single warning
  211. * @private
  212. */
  213. function createIgnoreResult(filePath, baseDir) {
  214. let message;
  215. const isHidden = /^\./.test(path.basename(filePath));
  216. const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
  217. const isInBowerComponents = baseDir && path.relative(baseDir, filePath).startsWith("bower_components");
  218. if (isHidden) {
  219. message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
  220. } else if (isInNodeModules) {
  221. message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
  222. } else if (isInBowerComponents) {
  223. message = "File ignored by default. Use \"--ignore-pattern '!bower_components/*'\" to override.";
  224. } else {
  225. message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
  226. }
  227. return {
  228. filePath: path.resolve(filePath),
  229. messages: [
  230. {
  231. fatal: false,
  232. severity: 1,
  233. message
  234. }
  235. ],
  236. errorCount: 0,
  237. warningCount: 1,
  238. fixableErrorCount: 0,
  239. fixableWarningCount: 0
  240. };
  241. }
  242. /**
  243. * Checks if the given message is an error message.
  244. * @param {Object} message The message to check.
  245. * @returns {boolean} Whether or not the message is an error message.
  246. * @private
  247. */
  248. function isErrorMessage(message) {
  249. return message.severity === 2;
  250. }
  251. /**
  252. * return the cacheFile to be used by eslint, based on whether the provided parameter is
  253. * a directory or looks like a directory (ends in `path.sep`), in which case the file
  254. * name will be the `cacheFile/.cache_hashOfCWD`
  255. *
  256. * if cacheFile points to a file or looks like a file then in will just use that file
  257. *
  258. * @param {string} cacheFile The name of file to be used to store the cache
  259. * @param {string} cwd Current working directory
  260. * @returns {string} the resolved path to the cache file
  261. */
  262. function getCacheFile(cacheFile, cwd) {
  263. /*
  264. * make sure the path separators are normalized for the environment/os
  265. * keeping the trailing path separator if present
  266. */
  267. const normalizedCacheFile = path.normalize(cacheFile);
  268. const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
  269. const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
  270. /**
  271. * return the name for the cache file in case the provided parameter is a directory
  272. * @returns {string} the resolved path to the cacheFile
  273. */
  274. function getCacheFileForDirectory() {
  275. return path.join(resolvedCacheFile, `.cache_${hash(cwd)}`);
  276. }
  277. let fileStats;
  278. try {
  279. fileStats = fs.lstatSync(resolvedCacheFile);
  280. } catch (ex) {
  281. fileStats = null;
  282. }
  283. /*
  284. * in case the file exists we need to verify if the provided path
  285. * is a directory or a file. If it is a directory we want to create a file
  286. * inside that directory
  287. */
  288. if (fileStats) {
  289. /*
  290. * is a directory or is a file, but the original file the user provided
  291. * looks like a directory but `path.resolve` removed the `last path.sep`
  292. * so we need to still treat this like a directory
  293. */
  294. if (fileStats.isDirectory() || looksLikeADirectory) {
  295. return getCacheFileForDirectory();
  296. }
  297. // is file so just use that file
  298. return resolvedCacheFile;
  299. }
  300. /*
  301. * here we known the file or directory doesn't exist,
  302. * so we will try to infer if its a directory if it looks like a directory
  303. * for the current operating system.
  304. */
  305. // if the last character passed is a path separator we assume is a directory
  306. if (looksLikeADirectory) {
  307. return getCacheFileForDirectory();
  308. }
  309. return resolvedCacheFile;
  310. }
  311. const configHashCache = new WeakMap();
  312. //------------------------------------------------------------------------------
  313. // Public Interface
  314. //------------------------------------------------------------------------------
  315. class CLIEngine {
  316. /**
  317. * Creates a new instance of the core CLI engine.
  318. * @param {CLIEngineOptions} providedOptions The options for this instance.
  319. * @constructor
  320. */
  321. constructor(providedOptions) {
  322. const options = Object.assign(
  323. Object.create(null),
  324. defaultOptions,
  325. { cwd: process.cwd() },
  326. providedOptions
  327. );
  328. /**
  329. * Stored options for this instance
  330. * @type {Object}
  331. */
  332. this.options = options;
  333. this.linter = new Linter();
  334. if (options.cache) {
  335. const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
  336. /**
  337. * Cache used to avoid operating on files that haven't changed since the
  338. * last successful execution (e.g., file passed linting with no errors and
  339. * no warnings).
  340. * @type {Object}
  341. */
  342. this._fileCache = fileEntryCache.create(cacheFile);
  343. }
  344. // load in additional rules
  345. if (this.options.rulePaths) {
  346. const cwd = this.options.cwd;
  347. this.options.rulePaths.forEach(rulesdir => {
  348. debug(`Loading rules from ${rulesdir}`);
  349. this.linter.rules.load(rulesdir, cwd);
  350. });
  351. }
  352. if (this.options.rules && Object.keys(this.options.rules).length) {
  353. const loadedRules = this.linter.getRules();
  354. Object.keys(this.options.rules).forEach(name => {
  355. validator.validateRuleOptions(loadedRules.get(name), name, this.options.rules[name], "CLI");
  356. });
  357. }
  358. this.config = new Config(this.options, this.linter);
  359. }
  360. getRules() {
  361. return this.linter.getRules();
  362. }
  363. /**
  364. * Returns results that only contains errors.
  365. * @param {LintResult[]} results The results to filter.
  366. * @returns {LintResult[]} The filtered results.
  367. */
  368. static getErrorResults(results) {
  369. const filtered = [];
  370. results.forEach(result => {
  371. const filteredMessages = result.messages.filter(isErrorMessage);
  372. if (filteredMessages.length > 0) {
  373. filtered.push(
  374. Object.assign(result, {
  375. messages: filteredMessages,
  376. errorCount: filteredMessages.length,
  377. warningCount: 0,
  378. fixableErrorCount: result.fixableErrorCount,
  379. fixableWarningCount: 0
  380. })
  381. );
  382. }
  383. });
  384. return filtered;
  385. }
  386. /**
  387. * Outputs fixes from the given results to files.
  388. * @param {Object} report The report object created by CLIEngine.
  389. * @returns {void}
  390. */
  391. static outputFixes(report) {
  392. report.results.filter(result => result.hasOwnProperty("output")).forEach(result => {
  393. fs.writeFileSync(result.filePath, result.output);
  394. });
  395. }
  396. /**
  397. * Add a plugin by passing its configuration
  398. * @param {string} name Name of the plugin.
  399. * @param {Object} pluginobject Plugin configuration object.
  400. * @returns {void}
  401. */
  402. addPlugin(name, pluginobject) {
  403. this.config.plugins.define(name, pluginobject);
  404. }
  405. /**
  406. * Resolves the patterns passed into executeOnFiles() into glob-based patterns
  407. * for easier handling.
  408. * @param {string[]} patterns The file patterns passed on the command line.
  409. * @returns {string[]} The equivalent glob patterns.
  410. */
  411. resolveFileGlobPatterns(patterns) {
  412. return globUtil.resolveFileGlobPatterns(patterns, this.options);
  413. }
  414. /**
  415. * Executes the current configuration on an array of file and directory names.
  416. * @param {string[]} patterns An array of file and directory names.
  417. * @returns {Object} The results for all files that were linted.
  418. */
  419. executeOnFiles(patterns) {
  420. const options = this.options,
  421. fileCache = this._fileCache,
  422. configHelper = this.config;
  423. const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
  424. if (!options.cache && fs.existsSync(cacheFile)) {
  425. fs.unlinkSync(cacheFile);
  426. }
  427. /**
  428. * Calculates the hash of the config file used to validate a given file
  429. * @param {string} filename The path of the file to retrieve a config object for to calculate the hash
  430. * @returns {string} the hash of the config
  431. */
  432. function hashOfConfigFor(filename) {
  433. const config = configHelper.getConfig(filename);
  434. if (!configHashCache.has(config)) {
  435. configHashCache.set(config, hash(`${pkg.version}_${stringify(config)}`));
  436. }
  437. return configHashCache.get(config);
  438. }
  439. const startTime = Date.now();
  440. const fileList = globUtil.listFilesToProcess(this.resolveFileGlobPatterns(patterns), options);
  441. const results = fileList.map(fileInfo => {
  442. if (fileInfo.ignored) {
  443. return createIgnoreResult(fileInfo.filename, options.cwd);
  444. }
  445. if (options.cache) {
  446. /*
  447. * get the descriptor for this file
  448. * with the metadata and the flag that determines if
  449. * the file has changed
  450. */
  451. const descriptor = fileCache.getFileDescriptor(fileInfo.filename);
  452. const hashOfConfig = hashOfConfigFor(fileInfo.filename);
  453. const changed = descriptor.changed || descriptor.meta.hashOfConfig !== hashOfConfig;
  454. if (!changed) {
  455. debug(`Skipping file since hasn't changed: ${fileInfo.filename}`);
  456. /*
  457. * Add the the cached results (always will be 0 error and
  458. * 0 warnings). We should not cache results for files that
  459. * failed, in order to guarantee that next execution will
  460. * process those files as well.
  461. */
  462. return descriptor.meta.results;
  463. }
  464. }
  465. debug(`Processing ${fileInfo.filename}`);
  466. return processFile(fileInfo.filename, configHelper, options, this.linter);
  467. });
  468. if (options.cache) {
  469. results.forEach(result => {
  470. if (result.messages.length) {
  471. /*
  472. * if a file contains errors or warnings we don't want to
  473. * store the file in the cache so we can guarantee that
  474. * next execution will also operate on this file
  475. */
  476. fileCache.removeEntry(result.filePath);
  477. } else {
  478. /*
  479. * since the file passed we store the result here
  480. * TODO: it might not be necessary to store the results list in the cache,
  481. * since it should always be 0 errors/warnings
  482. */
  483. const descriptor = fileCache.getFileDescriptor(result.filePath);
  484. descriptor.meta.hashOfConfig = hashOfConfigFor(result.filePath);
  485. descriptor.meta.results = result;
  486. }
  487. });
  488. // persist the cache to disk
  489. fileCache.reconcile();
  490. }
  491. const stats = calculateStatsPerRun(results);
  492. debug(`Linting complete in: ${Date.now() - startTime}ms`);
  493. return {
  494. results,
  495. errorCount: stats.errorCount,
  496. warningCount: stats.warningCount,
  497. fixableErrorCount: stats.fixableErrorCount,
  498. fixableWarningCount: stats.fixableWarningCount
  499. };
  500. }
  501. /**
  502. * Executes the current configuration on text.
  503. * @param {string} text A string of JavaScript code to lint.
  504. * @param {string} filename An optional string representing the texts filename.
  505. * @param {boolean} warnIgnored Always warn when a file is ignored
  506. * @returns {Object} The results for the linting.
  507. */
  508. executeOnText(text, filename, warnIgnored) {
  509. const results = [],
  510. options = this.options,
  511. configHelper = this.config,
  512. ignoredPaths = new IgnoredPaths(options);
  513. // resolve filename based on options.cwd (for reporting, ignoredPaths also resolves)
  514. const resolvedFilename = filename && !path.isAbsolute(filename)
  515. ? path.resolve(options.cwd, filename)
  516. : filename;
  517. if (resolvedFilename && ignoredPaths.contains(resolvedFilename)) {
  518. if (warnIgnored) {
  519. results.push(createIgnoreResult(resolvedFilename, options.cwd));
  520. }
  521. } else {
  522. results.push(
  523. processText(
  524. text,
  525. configHelper,
  526. resolvedFilename,
  527. options.fix,
  528. options.allowInlineConfig,
  529. options.reportUnusedDisableDirectives,
  530. this.linter
  531. )
  532. );
  533. }
  534. const stats = calculateStatsPerRun(results);
  535. return {
  536. results,
  537. errorCount: stats.errorCount,
  538. warningCount: stats.warningCount,
  539. fixableErrorCount: stats.fixableErrorCount,
  540. fixableWarningCount: stats.fixableWarningCount
  541. };
  542. }
  543. /**
  544. * Returns a configuration object for the given file based on the CLI options.
  545. * This is the same logic used by the ESLint CLI executable to determine
  546. * configuration for each file it processes.
  547. * @param {string} filePath The path of the file to retrieve a config object for.
  548. * @returns {Object} A configuration object for the file.
  549. */
  550. getConfigForFile(filePath) {
  551. const configHelper = this.config;
  552. return configHelper.getConfig(filePath);
  553. }
  554. /**
  555. * Checks if a given path is ignored by ESLint.
  556. * @param {string} filePath The path of the file to check.
  557. * @returns {boolean} Whether or not the given path is ignored.
  558. */
  559. isPathIgnored(filePath) {
  560. const resolvedPath = path.resolve(this.options.cwd, filePath);
  561. const ignoredPaths = new IgnoredPaths(this.options);
  562. return ignoredPaths.contains(resolvedPath);
  563. }
  564. /**
  565. * Returns the formatter representing the given format or null if no formatter
  566. * with the given name can be found.
  567. * @param {string} [format] The name of the format to load or the path to a
  568. * custom formatter.
  569. * @returns {Function} The formatter function or null if not found.
  570. */
  571. getFormatter(format) {
  572. // default is stylish
  573. const resolvedFormatName = format || "stylish";
  574. // only strings are valid formatters
  575. if (typeof resolvedFormatName === "string") {
  576. // replace \ with / for Windows compatibility
  577. const normalizedFormatName = resolvedFormatName.replace(/\\/g, "/");
  578. const cwd = this.options ? this.options.cwd : process.cwd();
  579. const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
  580. let formatterPath;
  581. // if there's a slash, then it's a file
  582. if (!namespace && normalizedFormatName.indexOf("/") > -1) {
  583. formatterPath = path.resolve(cwd, normalizedFormatName);
  584. } else {
  585. try {
  586. const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
  587. formatterPath = resolver.resolve(npmFormat, `${cwd}/node_modules`);
  588. } catch (e) {
  589. formatterPath = `./formatters/${normalizedFormatName}`;
  590. }
  591. }
  592. try {
  593. return require(formatterPath);
  594. } catch (ex) {
  595. ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
  596. throw ex;
  597. }
  598. } else {
  599. return null;
  600. }
  601. }
  602. }
  603. CLIEngine.version = pkg.version;
  604. CLIEngine.getFormatter = CLIEngine.prototype.getFormatter;
  605. module.exports = CLIEngine;