Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple --ignore-path flags #6345

Merged
merged 10 commits into from Sep 14, 2022
5 changes: 5 additions & 0 deletions .changeset/real-items-wait.md
@@ -0,0 +1,5 @@
---
"stylelint": minor
---

Added: support for multiple `--ignore-path` flags
2 changes: 1 addition & 1 deletion docs/user-guide/usage/cli.md
Expand Up @@ -58,7 +58,7 @@ Ignore `stylelint-disable` (e.g. `/* stylelint-disable block-no-empty */`) comme

### `--ignore-path, -i`

A path to a file containing patterns describing files to ignore. The path can be absolute or relative to `process.cwd()`. By default, Stylelint looks for `.stylelintignore` in `process.cwd()`. [More info](options.md#ignorePath).
Path to a file containing patterns that describe files to ignore. The path can be absolute or relative to `process.cwd()`. You can repeat the option to provide multiple paths. By default, Stylelint looks for `.stylelintignore` in `process.cwd()`. [More info](options.md#ignorePath).

### `--ignore-pattern, --ip`

Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/usage/options.md
Expand Up @@ -133,7 +133,7 @@ Disable the default ignores. Stylelint will not automatically ignore the content

CLI flags: `--ignore-path, -i`

A path to a file containing patterns describing files to ignore. The path can be absolute or relative to `process.cwd()`. By default, Stylelint looks for `.stylelintignore` in `process.cwd()`.
Path to a file containing patterns that describe files to ignore. The path can be absolute or relative to `process.cwd()`. You can repeat the option to provide multiple paths. By default, Stylelint looks for `.stylelintignore` in `process.cwd()`.

## `ignoreDisables`

Expand Down
5 changes: 3 additions & 2 deletions lib/__tests__/__snapshots__/cli.test.js.snap
Expand Up @@ -40,8 +40,9 @@ exports[`CLI --help 1`] = `
--ignore-path, -i

Path to a file containing patterns that describe files to ignore. The
path can be absolute or relative to process.cwd(). By default, stylelint
looks for .stylelintignore in process.cwd().
path can be absolute or relative to process.cwd(). You can repeat the
option to provide multiple paths. By default, Stylelint looks for
.stylelintignore in process.cwd().

--ignore-pattern, --ip

Expand Down
12 changes: 10 additions & 2 deletions lib/__tests__/cli.test.js
Expand Up @@ -25,6 +25,7 @@ describe('buildCLI', () => {
formatter: 'string',
help: false,
ignoreDisables: false,
ignorePath: [],
ignorePattern: [],
printConfig: false,
quiet: false,
Expand Down Expand Up @@ -95,8 +96,15 @@ describe('buildCLI', () => {
});

it('flags.ignorePath', () => {
expect(buildCLI(['--ignore-path=/path/to/file']).flags.ignorePath).toBe('/path/to/file');
expect(buildCLI(['-i', '/path/to/file']).flags.ignorePath).toBe('/path/to/file');
expect(buildCLI(['--ignore-path=/path/to/file']).flags.ignorePath).toEqual(['/path/to/file']);
expect(buildCLI(['-i', '/path/to/file']).flags.ignorePath).toEqual(['/path/to/file']);
expect(
buildCLI(['--ignore-path=/path/to/file1', '--ignore-path=/path/to/file2']).flags.ignorePath,
).toEqual(['/path/to/file1', '/path/to/file2']);
expect(buildCLI(['-i', '/path/to/file1', '-i', '/path/to/file2']).flags.ignorePath).toEqual([
'/path/to/file1',
'/path/to/file2',
]);
});

it('flags.ignorePattern', () => {
Expand Down
3 changes: 3 additions & 0 deletions lib/__tests__/fixtures/ignore-2.txt
@@ -0,0 +1,3 @@
# Ignore file patterns are always relative to process.cwd()

fixtures/empty-block-with-disables.css
27 changes: 24 additions & 3 deletions lib/__tests__/ignore.test.js
Expand Up @@ -175,7 +175,28 @@ test('specified `ignorePath` file ignoring one file', async () => {
'block-no-empty': true,
},
},
ignorePath: fixtures('ignore.txt'),
ignorePath: [fixtures('ignore.txt')],
}),
).rejects.toThrow(AllFilesIgnoredError); // all files ignore

process.chdir(actualCwd);
});

test('specified multiple `ignorePath` to ignore files', async () => {
const files = [fixtures('empty-block.css'), fixtures('empty-block-with-disables.css')];
const actualCwd = process.cwd();

process.chdir(__dirname);

await expect(
standalone({
files,
config: {
rules: {
'block-no-empty': true,
},
},
ignorePath: [fixtures('ignore.txt'), fixtures('ignore-2.txt')],
}),
).rejects.toThrow(AllFilesIgnoredError); // all files ignore

Expand All @@ -196,7 +217,7 @@ test('specified `ignorePath` file ignoring one file using options.cwd', async ()
'block-no-empty': true,
},
},
ignorePath: fixtures('ignore.txt'),
ignorePath: [fixtures('ignore.txt')],
cwd: __dirname,
}),
).rejects.toThrow(allFilesIgnoredErrorMessage); // all files ignore
Expand All @@ -209,7 +230,7 @@ test('specified `ignorePath` directory, not a file', async () => {
standalone({
files: [],
config: {},
ignorePath: '__snapshots__',
ignorePath: ['__snapshots__'],
cwd: __dirname,
}),
).rejects.toThrow(/EISDIR/);
Expand Down
2 changes: 1 addition & 1 deletion lib/__tests__/invalidScopeDisables.test.js
Expand Up @@ -157,7 +157,7 @@ it('invalidScopeDisables ignored case', async () => {
config,
files: [fixture('disabled-ranges-1.css'), fixture('ignored-file.css')],
ignoreDisables: true,
ignorePath: fixture('.stylelintignore'),
ignorePath: [fixture('.stylelintignore')],
reportInvalidScopeDisables: true,
});

Expand Down
2 changes: 1 addition & 1 deletion lib/__tests__/needlessDisables.test.js
Expand Up @@ -223,7 +223,7 @@ it('needlessDisables ignored case', async () => {
config,
files: [fixture('disabled-ranges-1.css'), fixture('ignored-file.css')],
reportNeedlessDisables: true,
ignorePath: fixture('.stylelintignore'),
ignorePath: [fixture('.stylelintignore')],
});

expect(results).toHaveLength(1);
Expand Down
31 changes: 31 additions & 0 deletions lib/__tests__/postcssPlugin.test.js
Expand Up @@ -134,6 +134,21 @@ describe('stylelintignore', () => {
postcss([postcssPlugin(options)]).process('a {}', { from: 'foo.css' }),
).resolves.toHaveProperty('stylelint.ignored', true);
});

it('postcssPlugin with ignorePath array and file is ignored', () => {
const options = {
config: {
rules: {
'block-no-empty': true,
},
},
ignorePath: [path.join(__dirname, './stylelintignore-test/.postcssPluginignore')],
};

return expect(
postcss([postcssPlugin(options)]).process('a {}', { from: 'foo.css' }),
).resolves.toHaveProperty('stylelint.ignored', true);
});
});

describe('stylelintignore with options.cwd', () => {
Expand Down Expand Up @@ -169,4 +184,20 @@ describe('stylelintignore with options.cwd', () => {
postcss([postcssPlugin(options)]).process('a {}', { from: path.join(__dirname, 'foo.css') }),
).resolves.toHaveProperty('stylelint.ignored', true);
});

it('postcssPlugin with ignorePath array and file is ignored', () => {
const options = {
config: {
rules: {
'block-no-empty': true,
},
},
cwd: __dirname,
ignorePath: [path.join(__dirname, './stylelintignore-test/.postcssPluginignore')],
};

return expect(
postcss([postcssPlugin(options)]).process('a {}', { from: path.join(__dirname, 'foo.css') }),
).resolves.toHaveProperty('stylelint.ignored', true);
});
});
16 changes: 8 additions & 8 deletions lib/__tests__/stylelintignore-test/stylelintignore.test.js
Expand Up @@ -58,7 +58,7 @@ describe('stylelintignore', () => {
const data = await standalone({
code: '.bar {}',
codeFilename: `${fixturesPath}/empty-block.css`,
ignorePath: path.join(__dirname, '.stylelintignore'),
ignorePath: [path.join(__dirname, '.stylelintignore')],
config: {
extends: `${fixturesPath}/config-block-no-empty`,
},
Expand All @@ -78,7 +78,7 @@ describe('stylelintignore', () => {
const data = await standalone({
code: '.bar {}',
codeFilename: `${fixturesPath}/empty-block.css`,
ignorePath: path.join(__dirname, '.stylelintignore-empty'),
ignorePath: [path.join(__dirname, '.stylelintignore-empty')],
config: {
extends: `${fixturesPath}/config-block-no-empty`,
},
Expand All @@ -95,7 +95,7 @@ describe('stylelintignore', () => {
const data = await standalone({
code: 'var a = {',
codeFilename: `test.js`,
ignorePath: path.join(__dirname, '.stylelintignore2'),
ignorePath: [path.join(__dirname, '.stylelintignore2')],
config: {
extends: `${fixturesPath}/config-block-no-empty`,
},
Expand All @@ -115,7 +115,7 @@ describe('stylelintignore', () => {
const data = await standalone({
code: 'var a = {',
codeFilename: `test.js`,
ignorePath: path.join(__dirname, '.stylelintignore2'),
ignorePath: [path.join(__dirname, '.stylelintignore2')],
});

expect(data).toEqual({
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('stylelintignore with options.cwd', () => {
const data = await standalone({
code: '.bar {}',
codeFilename: `${fixturesPath}/empty-block.css`,
ignorePath: path.join(__dirname, '.stylelintignore'),
ignorePath: [path.join(__dirname, '.stylelintignore')],
config: {
extends: `${fixturesPath}/config-block-no-empty`,
},
Expand All @@ -196,7 +196,7 @@ describe('stylelintignore with options.cwd', () => {
const data = await standalone({
code: '.bar {}',
codeFilename: `${fixturesPath}/empty-block.css`,
ignorePath: path.join(__dirname, '.stylelintignore-empty'),
ignorePath: [path.join(__dirname, '.stylelintignore-empty')],
config: {
extends: `${fixturesPath}/config-block-no-empty`,
},
Expand All @@ -214,7 +214,7 @@ describe('stylelintignore with options.cwd', () => {
const data = await standalone({
code: 'var a = {',
codeFilename: `test.js`,
ignorePath: path.join(__dirname, '.stylelintignore2'),
ignorePath: [path.join(__dirname, '.stylelintignore2')],
config: {
extends: `${fixturesPath}/config-block-no-empty`,
},
Expand All @@ -235,7 +235,7 @@ describe('stylelintignore with options.cwd', () => {
const data = await standalone({
code: 'var a = {',
codeFilename: `test.js`,
ignorePath: path.join(__dirname, '.stylelintignore2'),
ignorePath: [path.join(__dirname, '.stylelintignore2')],
cwd: __dirname,
});

Expand Down
8 changes: 5 additions & 3 deletions lib/cli.js
Expand Up @@ -32,7 +32,7 @@ const EXIT_CODE_ERROR = 2;
* @property {string} [formatter="string"]
* @property {string} [help]
* @property {boolean} [ignoreDisables]
* @property {string} [ignorePath]
* @property {string[]} [ignorePath]
* @property {string[]} [ignorePattern]
* @property {string} [noColor]
* @property {string} [outputFile]
Expand Down Expand Up @@ -125,8 +125,9 @@ const meowOptions = {
--ignore-path, -i

Path to a file containing patterns that describe files to ignore. The
path can be absolute or relative to process.cwd(). By default, stylelint
looks for .stylelintignore in process.cwd().
path can be absolute or relative to process.cwd(). You can repeat the
option to provide multiple paths. By default, Stylelint looks for
.stylelintignore in process.cwd().

--ignore-pattern, --ip

Expand Down Expand Up @@ -273,6 +274,7 @@ const meowOptions = {
ignorePath: {
alias: 'i',
type: 'string',
isMultiple: true,
},
ignorePattern: {
alias: 'ip',
Expand Down
39 changes: 24 additions & 15 deletions lib/utils/getFileIgnorer.js
Expand Up @@ -10,25 +10,34 @@ const isPathNotFoundError = require('./isPathNotFoundError');
const DEFAULT_IGNORE_FILENAME = '.stylelintignore';

/**
* @param {{ cwd: string, ignorePath?: string, ignorePattern?: string[] }} options
* @param {{ cwd: string, ignorePath?: string | string[], ignorePattern?: string[] }} options
* @return {import('ignore').Ignore}
*/
module.exports = function getFileIgnorer(options) {
const ignoreFilePath = options.ignorePath || DEFAULT_IGNORE_FILENAME;
const absoluteIgnoreFilePath = path.isAbsolute(ignoreFilePath)
? ignoreFilePath
: path.resolve(options.cwd, ignoreFilePath);
let ignoreText = '';

try {
ignoreText = fs.readFileSync(absoluteIgnoreFilePath, 'utf8');
} catch (readError) {
if (!isPathNotFoundError(readError)) {
throw readError;
const ignorer = ignore();
const ignorePaths = [options.ignorePath || []].flat();

if (ignorePaths.length === 0) {
ignorePaths.push(DEFAULT_IGNORE_FILENAME);
}

for (const ignoreFilePath of ignorePaths) {
const absoluteIgnoreFilePath = path.isAbsolute(ignoreFilePath)
? ignoreFilePath
: path.resolve(options.cwd, ignoreFilePath);

try {
const ignoreText = fs.readFileSync(absoluteIgnoreFilePath, 'utf8');

ignorer.add(ignoreText);
} catch (readError) {
if (!isPathNotFoundError(readError)) {
throw readError;
}
}
}

return ignore()
.add(ignoreText)
.add(options.ignorePattern || []);
ignorer.add(options.ignorePattern || []);

return ignorer;
};
2 changes: 1 addition & 1 deletion types/stylelint/index.d.ts
Expand Up @@ -223,7 +223,7 @@ declare module 'stylelint' {
*/
cwd?: string;
ignoreDisables?: boolean;
ignorePath?: string;
ignorePath?: string | string[];
ignorePattern?: string[];
reportDescriptionlessDisables?: boolean;
reportNeedlessDisables?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion types/stylelint/type-test.ts
Expand Up @@ -32,7 +32,7 @@ const options: Partial<LinterOptions> = {
reportDescriptionlessDisables: true,
reportInvalidScopeDisables: true,
reportNeedlessDisables: true,
ignorePath: 'foo',
ignorePath: ['foo'],
customSyntax: 'postcss-scss',
syntax: 'scss', // Removed but still accepted in type definition
config: {
Expand Down