Skip to content

Commit

Permalink
fix: Behavior of CLI when no arguments are passed
Browse files Browse the repository at this point in the history
Fixes #14308
  • Loading branch information
nzakas committed Dec 20, 2023
1 parent 5aa9c49 commit ce58a6a
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 25 deletions.
9 changes: 9 additions & 0 deletions docs/src/use/command-line-interface.md
Expand Up @@ -36,6 +36,15 @@ Please note that when passing a glob as a parameter, it is expanded by your shel
npx eslint "lib/**"
```

If you are using a [flat configuration file](./configure/configuration-files-new) (`eslint.config.js`), you can also omit the file arguments and ESLint will use `.`. For instance, these two lines perform the same operation:

```shell
npx eslint .
npx eslint
```

If you are not using a flat configuration file, running ESLint without file arguments results in an error.

**Note:** You can also use alternative package managers such as [Yarn](https://yarnpkg.com/) or [pnpm](https://pnpm.io/) to run ESLint. Please refer to your package manager's documentation for the correct syntax.

## Pass Multiple Values to an Option
Expand Down
16 changes: 8 additions & 8 deletions lib/eslint/eslint-helpers.js
Expand Up @@ -105,20 +105,20 @@ class AllFilesIgnoredError extends Error {

/**
* Check if a given value is a non-empty string or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is a non-empty string.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is a non-empty string.
*/
function isNonEmptyString(x) {
return typeof x === "string" && x.trim() !== "";
function isNonEmptyString(value) {
return typeof value === "string" && value.trim() !== "";
}

/**
* Check if a given value is an array of non-empty strings or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is an array of non-empty strings.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an array of non-empty strings.
*/
function isArrayOfNonEmptyString(x) {
return Array.isArray(x) && x.every(isNonEmptyString);
function isArrayOfNonEmptyString(value) {
return Array.isArray(value) && value.length && value.every(isNonEmptyString);
}

//-----------------------------------------------------------------------------
Expand Down
40 changes: 25 additions & 15 deletions lib/eslint/eslint.js
Expand Up @@ -97,35 +97,45 @@ const privateMembersMap = new WeakMap();

/**
* Check if a given value is a non-empty string or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is a non-empty string.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is a non-empty string.
*/
function isNonEmptyString(x) {
return typeof x === "string" && x.trim() !== "";
function isNonEmptyString(value) {
return typeof value === "string" && value.trim() !== "";
}

/**
* Check if a given value is an array of non-empty strings or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is an array of non-empty strings.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an array of non-empty strings.
*/
function isArrayOfNonEmptyString(x) {
return Array.isArray(x) && x.every(isNonEmptyString);
function isArrayOfNonEmptyString(value) {
return Array.isArray(value) && value.length && value.every(isNonEmptyString);
}

/**
* Check if a given value is an empty array or an array of non-empty strings.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an empty array or an array of non-empty
* strings.
*/
function isEmptyArrayOrArrayOfNonEmptyString(value) {
return Array.isArray(value) && value.every(isNonEmptyString);
}

/**
* Check if a given value is a valid fix type or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is valid fix type.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is valid fix type.
*/
function isFixType(x) {
return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
function isFixType(value) {
return value === "directive" || value === "problem" || value === "suggestion" || value === "layout";
}

/**
* Check if a given value is an array of fix types or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is an array of fix types.
* @returns {boolean} `true` if `value` is an array of fix types.
*/
function isFixTypeArray(x) {
return Array.isArray(x) && x.every(isFixType);
Expand Down Expand Up @@ -225,7 +235,7 @@ function processOptions({
if (typeof errorOnUnmatchedPattern !== "boolean") {
errors.push("'errorOnUnmatchedPattern' must be a boolean.");
}
if (!isArrayOfNonEmptyString(extensions) && extensions !== null) {
if (!isEmptyArrayOrArrayOfNonEmptyString(extensions) && extensions !== null) {
errors.push("'extensions' must be an array of non-empty strings or null.");
}
if (typeof fix !== "boolean" && typeof fix !== "function") {
Expand Down Expand Up @@ -271,7 +281,7 @@ function processOptions({
) {
errors.push("'resolvePluginsRelativeTo' must be a non-empty string or null.");
}
if (!isArrayOfNonEmptyString(rulePaths)) {
if (!isEmptyArrayOrArrayOfNonEmptyString(rulePaths)) {
errors.push("'rulePaths' must be an array of non-empty strings.");
}
if (typeof useEslintrc !== "boolean") {
Expand Down
18 changes: 16 additions & 2 deletions lib/eslint/flat-eslint.js
Expand Up @@ -738,7 +738,21 @@ class FlatESLint {
* @returns {Promise<LintResult[]>} The results of linting the file patterns given.
*/
async lintFiles(patterns) {
if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {

let normalizedPatterns = patterns;

/*
* Special cases:
* 1. `patterns` is an empty string
* 2. `patterns` is an empty array
*
* In both cases, we use the cwd as the directory to lint.
*/
if (patterns === "" || Array.isArray(patterns) && patterns.length === 0) {
normalizedPatterns = ["."];
} else if (typeof patterns === "string") {
normalizedPatterns = [patterns];
} else if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
}

Expand Down Expand Up @@ -779,7 +793,7 @@ class FlatESLint {
}

const filePaths = await findFiles({
patterns: typeof patterns === "string" ? [patterns] : patterns,
patterns: normalizedPatterns,
cwd,
globInputPaths,
configs,
Expand Down
21 changes: 21 additions & 0 deletions tests/lib/eslint/eslint.js
Expand Up @@ -1075,6 +1075,27 @@ describe("ESLint", () => {
await assert.rejects(async () => await eslint.lintFiles(["lib/cli.js"]), /Cannot find module 'test11'/u);
});

describe("Invalid inputs", () => {

[
["an empty string", ""],
["an empty array", []],
["an array with one empty string", [""]],
["an array with two empty strings", ["", ""]]

].forEach(([name, value]) => {

it(`should throw an error when passed ${name}`, async () => {
eslint = new ESLint({
useEslintrc: false
});

await assert.rejects(async () => await eslint.lintFiles(value), /'patterns' must be a non-empty string or an array of non-empty strings/u);
});
});

});

it("should report zero messages when given a directory with a .js2 file", async () => {
eslint = new ESLint({
cwd: path.join(fixtureDir, ".."),
Expand Down
47 changes: 47 additions & 0 deletions tests/lib/eslint/flat-eslint.js
Expand Up @@ -960,6 +960,53 @@ describe("FlatESLint", () => {
await assert.rejects(async () => await eslint.lintFiles(["lib/cli.js"]), /Expected object with parse\(\) or parseForESLint\(\) method/u);
});

describe("Invalid inputs", () => {

[
["an array with one empty string", [""]],
["an array with two empty strings", ["", ""]]

].forEach(([name, value]) => {

it(`should throw an error when passed ${name}`, async () => {
eslint = new FlatESLint({
overrideConfigFile: true
});

await assert.rejects(async () => await eslint.lintFiles(value), /'patterns' must be a non-empty string or an array of non-empty strings/u);
});
});

});

describe("Normalized inputs", () => {

[
["an empty string", ""],
["an empty array", []]

].forEach(([name, value]) => {

it(`should normalize to '.' when ${name} is passed`, async () => {
eslint = new FlatESLint({
ignore: false,
cwd: getFixturePath("files"),
overrideConfig: { files: ["**/*.js"] },
overrideConfigFile: getFixturePath("eslint.config.js")
});
const results = await eslint.lintFiles(value);

assert.strictEqual(results.length, 2);
assert.strictEqual(results[0].filePath, getFixturePath("files/.bar.js"));
assert.strictEqual(results[0].messages.length, 0);
assert.strictEqual(results[1].filePath, getFixturePath("files/foo.js"));
assert.strictEqual(results[1].messages.length, 0);
assert.strictEqual(results[0].suppressedMessages.length, 0);
});
});

});

it("should report zero messages when given a directory with a .js2 file", async () => {
eslint = new FlatESLint({
cwd: path.join(fixtureDir, ".."),
Expand Down

0 comments on commit ce58a6a

Please sign in to comment.