Ohm-Management - Projektarbeit B-ME
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.

glob-utils.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /**
  2. * @fileoverview Utilities for working with globs and the filesystem.
  3. * @author Ian VanSchooten
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const lodash = require("lodash"),
  10. fs = require("fs"),
  11. path = require("path"),
  12. GlobSync = require("./glob"),
  13. pathUtils = require("./path-utils"),
  14. IgnoredPaths = require("../ignored-paths");
  15. const debug = require("debug")("eslint:glob-utils");
  16. //------------------------------------------------------------------------------
  17. // Helpers
  18. //------------------------------------------------------------------------------
  19. /**
  20. * Checks whether a directory exists at the given location
  21. * @param {string} resolvedPath A path from the CWD
  22. * @returns {boolean} `true` if a directory exists
  23. */
  24. function directoryExists(resolvedPath) {
  25. return fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory();
  26. }
  27. /**
  28. * Checks if a provided path is a directory and returns a glob string matching
  29. * all files under that directory if so, the path itself otherwise.
  30. *
  31. * Reason for this is that `glob` needs `/**` to collect all the files under a
  32. * directory where as our previous implementation without `glob` simply walked
  33. * a directory that is passed. So this is to maintain backwards compatibility.
  34. *
  35. * Also makes sure all path separators are POSIX style for `glob` compatibility.
  36. *
  37. * @param {Object} [options] An options object
  38. * @param {string[]} [options.extensions=[".js"]] An array of accepted extensions
  39. * @param {string} [options.cwd=process.cwd()] The cwd to use to resolve relative pathnames
  40. * @returns {Function} A function that takes a pathname and returns a glob that
  41. * matches all files with the provided extensions if
  42. * pathname is a directory.
  43. */
  44. function processPath(options) {
  45. const cwd = (options && options.cwd) || process.cwd();
  46. let extensions = (options && options.extensions) || [".js"];
  47. extensions = extensions.map(ext => ext.replace(/^\./, ""));
  48. let suffix = "/**";
  49. if (extensions.length === 1) {
  50. suffix += `/*.${extensions[0]}`;
  51. } else {
  52. suffix += `/*.{${extensions.join(",")}}`;
  53. }
  54. /**
  55. * A function that converts a directory name to a glob pattern
  56. *
  57. * @param {string} pathname The directory path to be modified
  58. * @returns {string} The glob path or the file path itself
  59. * @private
  60. */
  61. return function(pathname) {
  62. let newPath = pathname;
  63. const resolvedPath = path.resolve(cwd, pathname);
  64. if (directoryExists(resolvedPath)) {
  65. newPath = pathname.replace(/[/\\]$/, "") + suffix;
  66. }
  67. return pathUtils.convertPathToPosix(newPath);
  68. };
  69. }
  70. /**
  71. * The error type when no files match a glob.
  72. */
  73. class NoFilesFoundError extends Error {
  74. /**
  75. * @param {string} pattern - The glob pattern which was not found.
  76. */
  77. constructor(pattern) {
  78. super(`No files matching '${pattern}' were found.`);
  79. this.messageTemplate = "file-not-found";
  80. this.messageData = { pattern };
  81. }
  82. }
  83. /**
  84. * The error type when there are files matched by a glob, but all of them have been ignored.
  85. */
  86. class AllFilesIgnoredError extends Error {
  87. /**
  88. * @param {string} pattern - The glob pattern which was not found.
  89. */
  90. constructor(pattern) {
  91. super(`All files matched by '${pattern}' are ignored.`);
  92. this.messageTemplate = "all-files-ignored";
  93. this.messageData = { pattern };
  94. }
  95. }
  96. const NORMAL_LINT = {};
  97. const SILENTLY_IGNORE = {};
  98. const IGNORE_AND_WARN = {};
  99. /**
  100. * Tests whether a file should be linted or ignored
  101. * @param {string} filename The file to be processed
  102. * @param {{ignore: (boolean|null)}} options If `ignore` is false, updates the behavior to
  103. * not process custom ignore paths, and lint files specified by direct path even if they
  104. * match the default ignore path
  105. * @param {boolean} isDirectPath True if the file was provided as a direct path
  106. * (as opposed to being resolved from a glob)
  107. * @param {IgnoredPaths} ignoredPaths An instance of IgnoredPaths to check whether a given
  108. * file is ignored.
  109. * @returns {(NORMAL_LINT|SILENTLY_IGNORE|IGNORE_AND_WARN)} A directive for how the
  110. * file should be processed (either linted normally, or silently ignored, or ignored
  111. * with a warning that it is being ignored)
  112. */
  113. function testFileAgainstIgnorePatterns(filename, options, isDirectPath, ignoredPaths) {
  114. const shouldProcessCustomIgnores = options.ignore !== false;
  115. const shouldLintIgnoredDirectPaths = options.ignore === false;
  116. const fileMatchesIgnorePatterns = ignoredPaths.contains(filename, "default") ||
  117. (shouldProcessCustomIgnores && ignoredPaths.contains(filename, "custom"));
  118. if (fileMatchesIgnorePatterns && isDirectPath && !shouldLintIgnoredDirectPaths) {
  119. return IGNORE_AND_WARN;
  120. }
  121. if (!fileMatchesIgnorePatterns || (isDirectPath && shouldLintIgnoredDirectPaths)) {
  122. return NORMAL_LINT;
  123. }
  124. return SILENTLY_IGNORE;
  125. }
  126. //------------------------------------------------------------------------------
  127. // Public Interface
  128. //------------------------------------------------------------------------------
  129. /**
  130. * Resolves any directory patterns into glob-based patterns for easier handling.
  131. * @param {string[]} patterns File patterns (such as passed on the command line).
  132. * @param {Object} options An options object.
  133. * @param {string} [options.globInputPaths] False disables glob resolution.
  134. * @returns {string[]} The equivalent glob patterns and filepath strings.
  135. */
  136. function resolveFileGlobPatterns(patterns, options) {
  137. if (options.globInputPaths === false) {
  138. return patterns;
  139. }
  140. const processPathExtensions = processPath(options);
  141. return patterns.map(processPathExtensions);
  142. }
  143. const dotfilesPattern = /(?:(?:^\.)|(?:[/\\]\.))[^/\\.].*/;
  144. /**
  145. * Build a list of absolute filesnames on which ESLint will act.
  146. * Ignored files are excluded from the results, as are duplicates.
  147. *
  148. * @param {string[]} globPatterns Glob patterns.
  149. * @param {Object} [providedOptions] An options object.
  150. * @param {string} [providedOptions.cwd] CWD (considered for relative filenames)
  151. * @param {boolean} [providedOptions.ignore] False disables use of .eslintignore.
  152. * @param {string} [providedOptions.ignorePath] The ignore file to use instead of .eslintignore.
  153. * @param {string} [providedOptions.ignorePattern] A pattern of files to ignore.
  154. * @param {string} [providedOptions.globInputPaths] False disables glob resolution.
  155. * @returns {string[]} Resolved absolute filenames.
  156. */
  157. function listFilesToProcess(globPatterns, providedOptions) {
  158. const options = providedOptions || { ignore: true };
  159. const cwd = options.cwd || process.cwd();
  160. const getIgnorePaths = lodash.memoize(
  161. optionsObj =>
  162. new IgnoredPaths(optionsObj)
  163. );
  164. /*
  165. * The test "should use default options if none are provided" (source-code-utils.js) checks that 'module.exports.resolveFileGlobPatterns' was called.
  166. * So it cannot use the local function "resolveFileGlobPatterns".
  167. */
  168. const resolvedGlobPatterns = module.exports.resolveFileGlobPatterns(globPatterns, options);
  169. debug("Creating list of files to process.");
  170. const resolvedPathsByGlobPattern = resolvedGlobPatterns.map(pattern => {
  171. const file = path.resolve(cwd, pattern);
  172. if (options.globInputPaths === false || (fs.existsSync(file) && fs.statSync(file).isFile())) {
  173. const ignoredPaths = getIgnorePaths(options);
  174. const fullPath = options.globInputPaths === false ? file : fs.realpathSync(file);
  175. return [{
  176. filename: fullPath,
  177. behavior: testFileAgainstIgnorePatterns(fullPath, options, true, ignoredPaths)
  178. }];
  179. }
  180. // regex to find .hidden or /.hidden patterns, but not ./relative or ../relative
  181. const globIncludesDotfiles = dotfilesPattern.test(pattern);
  182. let newOptions = options;
  183. if (!options.dotfiles) {
  184. newOptions = Object.assign({}, options, { dotfiles: globIncludesDotfiles });
  185. }
  186. const ignoredPaths = getIgnorePaths(newOptions);
  187. const shouldIgnore = ignoredPaths.getIgnoredFoldersGlobChecker();
  188. const globOptions = {
  189. nodir: true,
  190. dot: true,
  191. cwd
  192. };
  193. return new GlobSync(pattern, globOptions, shouldIgnore).found.map(globMatch => {
  194. const relativePath = path.resolve(cwd, globMatch);
  195. return {
  196. filename: relativePath,
  197. behavior: testFileAgainstIgnorePatterns(relativePath, options, false, ignoredPaths)
  198. };
  199. });
  200. });
  201. const allPathDescriptors = resolvedPathsByGlobPattern.reduce((pathsForAllGlobs, pathsForCurrentGlob, index) => {
  202. if (pathsForCurrentGlob.every(pathDescriptor => pathDescriptor.behavior === SILENTLY_IGNORE)) {
  203. throw new (pathsForCurrentGlob.length ? AllFilesIgnoredError : NoFilesFoundError)(globPatterns[index]);
  204. }
  205. pathsForCurrentGlob.forEach(pathDescriptor => {
  206. switch (pathDescriptor.behavior) {
  207. case NORMAL_LINT:
  208. pathsForAllGlobs.push({ filename: pathDescriptor.filename, ignored: false });
  209. break;
  210. case IGNORE_AND_WARN:
  211. pathsForAllGlobs.push({ filename: pathDescriptor.filename, ignored: true });
  212. break;
  213. case SILENTLY_IGNORE:
  214. // do nothing
  215. break;
  216. default:
  217. throw new Error(`Unexpected file behavior for ${pathDescriptor.filename}`);
  218. }
  219. });
  220. return pathsForAllGlobs;
  221. }, []);
  222. return lodash.uniqBy(allPathDescriptors, pathDescriptor => pathDescriptor.filename);
  223. }
  224. module.exports = {
  225. resolveFileGlobPatterns,
  226. listFilesToProcess
  227. };