Skip to content

Commit

Permalink
implement the rest of the eslint-like behavior requirements, use gene…
Browse files Browse the repository at this point in the history
…rators to avoid problems with big arrays
  • Loading branch information
thorn0 committed Feb 23, 2020
1 parent 0ad7003 commit 93755e0
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 193 deletions.
213 changes: 213 additions & 0 deletions src/cli/expand-patterns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
"use strict";

const path = require("path");
const fs = require("fs");
const globby = require("globby");
const { flattenArray } = require("./util");

/** @typedef {import('./util').Context} Context */

/**
* @param {Context} context
*/
function* expandPatterns(context) {
const cwd = process.cwd();
const seen = Object.create(null);
let noResults = true;
for (const pathOrError of expandPatternsInternal(context)) {
noResults = false;
if (typeof pathOrError !== "string") {
yield pathOrError;
continue;
}
// filter out duplicates
if (pathOrError in seen) {
continue;
}
seen[pathOrError] = true;
yield path.relative(cwd, pathOrError);
}
if (noResults) {
// If there was no files and no other errors, let's yield a general error.
yield {
error: `No matching files. Patterns: ${context.filePatterns.join(" ")}`
};
}
}

const isWindows = path.sep === "\\";
// TODO: use `fast-glob` directly, without `globby`
const baseGlobbyOptions = {
dot: true,
expandDirectories: false,
absolute: true
};

/**
* @param {Context} context
*/
function* expandPatternsInternal(context) {
// Ignores files in version control systems directories and `node_modules`
const ignoredDirectories = {
".git": true,
".svn": true,
".hg": true,
node_modules: context.argv["with-node-modules"] !== true
};

const globbyOptions = {
...baseGlobbyOptions,
ignore: Object.keys(ignoredDirectories)
.filter(dir => ignoredDirectories[dir])
.map(dir => "**/" + dir)
};

const cwd = process.cwd();

/**
* @type {Array<{
* type: 'file' | 'dir' | 'glob';
* path?: string;
* glob?: string;
* input: string;
* }>}
*/
const entries = [];

for (const pattern of context.filePatterns) {
const absolutePath = path.resolve(cwd, pattern);

if (containsIgnoredPathSegment(absolutePath, cwd, ignoredDirectories)) {
continue;
}

const stat = statSafeSync(absolutePath);
if (stat) {
if (stat.isFile()) {
entries.push({ type: "file", path: absolutePath, input: pattern });
} else if (stat.isDirectory()) {
entries.push({
type: "dir",
path: absolutePath,
glob: getSupportedFilesGlob(),
input: pattern
});
}
} else if (pattern[0] === "!") {
// convert negative patterns to `ignore` entries
globbyOptions.ignore.push(pattern.slice(1));
} else {
entries.push({
type: "glob",
// Using backslashes in globs is probably not okay, but not accepting
// backslashes as path separators on Windows is even more not okay.
// https://github.com/prettier/prettier/pull/6776#discussion_r380723717
// https://github.com/mrmlnc/fast-glob#how-to-write-patterns-on-windows
glob: isWindows ? pattern.replace(/\\/g, "/") : pattern,
input: pattern
});
}
}

for (const { type, path, glob, input } of entries) {
switch (type) {
case "file":
yield path;
continue;

case "dir": {
let result;
try {
result = globby.sync(glob, { ...globbyOptions, cwd: path });
} catch ({ message }) {
yield { error: `Unable to expand directory: ${input}\n${message}` };
continue;
}
if (result.length === 0) {
yield {
error: `No supported files were found in the directory "${input}".`
};
} else {
yield* sortIfNotBig(result);
}
continue;
}

case "glob": {
let result;
try {
result = globby.sync(glob, globbyOptions);
} catch ({ message }) {
yield {
error: `Unable to expand glob pattern: ${input}\n${message}`
};
continue;
}
if (result.length === 0) {
yield {
error: `No files matching the pattern "${input}" were found.`
};
} else {
yield* sortIfNotBig(result);
}
continue;
}
}
}

let supportedFilesGlob;

function getSupportedFilesGlob() {
if (!supportedFilesGlob) {
const extensions = flattenArray(
context.languages.map(lang => lang.extensions || [])
);
const filenames = flattenArray(
context.languages.map(lang => lang.filenames || [])
);
supportedFilesGlob = `**/{${extensions
.map(ext => "*" + (ext[0] === "." ? ext : "." + ext))
.concat(filenames)}}`;
}
return supportedFilesGlob;
}
}

/**
* @param {string} absolutePath
* @param {string} cwd
* @param {Record<string, boolean>} ignoredDirectories
*/
function containsIgnoredPathSegment(absolutePath, cwd, ignoredDirectories) {
return path
.relative(cwd, absolutePath)
.split(path.sep)
.some(dir => ignoredDirectories[dir]);
}

/**
* @param {string[]} paths
*/
function sortIfNotBig(paths) {
return paths.length > 10000
? paths
: paths.sort((a, b) => a.localeCompare(b));
}

/**
* Get stats of a given path.
* @param {string} filePath The path to target file.
* @returns {fs.Stats | undefined} The stats.
*/
function statSafeSync(filePath) {
try {
return fs.statSync(filePath);
} catch (error) {
/* istanbul ignore next */
if (error.code !== "ENOENT") {
throw error;
}
}
}

module.exports = expandPatterns;
Loading

0 comments on commit 93755e0

Please sign in to comment.