1/** 2 * @fileoverview Tools for obtaining SourceCode objects. 3 * @author Ian VanSchooten 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Requirements 10//------------------------------------------------------------------------------ 11 12const { CLIEngine } = require("../cli-engine"); 13 14/* 15 * This is used for: 16 * 17 * 1. Enumerate target file because we have not expose such a API on `CLIEngine` 18 * (https://github.com/eslint/eslint/issues/11222). 19 * 2. Create `SourceCode` instances. Because we don't have any function which 20 * instantiate `SourceCode` so it needs to take the created `SourceCode` 21 * instance out after linting. 22 * 23 * TODO1: Expose the API that enumerates target files. 24 * TODO2: Extract the creation logic of `SourceCode` from `Linter` class. 25 */ 26const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require 27 28const debug = require("debug")("eslint:source-code-utils"); 29 30//------------------------------------------------------------------------------ 31// Helpers 32//------------------------------------------------------------------------------ 33 34/** 35 * Get the SourceCode object for a single file 36 * @param {string} filename The fully resolved filename to get SourceCode from. 37 * @param {Object} engine A CLIEngine. 38 * @returns {Array} Array of the SourceCode object representing the file 39 * and fatal error message. 40 */ 41function getSourceCodeOfFile(filename, engine) { 42 debug("getting sourceCode of", filename); 43 const results = engine.executeOnFiles([filename]); 44 45 if (results && results.results[0] && results.results[0].messages[0] && results.results[0].messages[0].fatal) { 46 const msg = results.results[0].messages[0]; 47 48 throw new Error(`(${filename}:${msg.line}:${msg.column}) ${msg.message}`); 49 } 50 51 // TODO: extract the logic that creates source code objects to `SourceCode#parse(text, options)` or something like. 52 const { linter } = getCLIEngineInternalSlots(engine); 53 const sourceCode = linter.getSourceCode(); 54 55 return sourceCode; 56} 57 58//------------------------------------------------------------------------------ 59// Public Interface 60//------------------------------------------------------------------------------ 61 62 63/** 64 * This callback is used to measure execution status in a progress bar 65 * @callback progressCallback 66 * @param {number} The total number of times the callback will be called. 67 */ 68 69/** 70 * Gets the SourceCode of a single file, or set of files. 71 * @param {string[]|string} patterns A filename, directory name, or glob, or an array of them 72 * @param {Object} options A CLIEngine options object. If not provided, the default cli options will be used. 73 * @param {progressCallback} callback Callback for reporting execution status 74 * @returns {Object} The SourceCode of all processed files. 75 */ 76function getSourceCodeOfFiles(patterns, options, callback) { 77 const sourceCodes = {}; 78 const globPatternsList = typeof patterns === "string" ? [patterns] : patterns; 79 const engine = new CLIEngine({ ...options, rules: {} }); 80 81 // TODO: make file iteration as a public API and use it. 82 const { fileEnumerator } = getCLIEngineInternalSlots(engine); 83 const filenames = 84 Array.from(fileEnumerator.iterateFiles(globPatternsList)) 85 .filter(entry => !entry.ignored) 86 .map(entry => entry.filePath); 87 88 if (filenames.length === 0) { 89 debug(`Did not find any files matching pattern(s): ${globPatternsList}`); 90 } 91 92 filenames.forEach(filename => { 93 const sourceCode = getSourceCodeOfFile(filename, engine); 94 95 if (sourceCode) { 96 debug("got sourceCode of", filename); 97 sourceCodes[filename] = sourceCode; 98 } 99 if (callback) { 100 callback(filenames.length); // eslint-disable-line node/callback-return 101 } 102 }); 103 104 return sourceCodes; 105} 106 107module.exports = { 108 getSourceCodeOfFiles 109}; 110