Skip to content

Commit 57db227

Browse files
committed
feat(fmt): add ignore patterns matcher
1 parent 06d4704 commit 57db227

6 files changed

Lines changed: 118 additions & 0 deletions

File tree

packages/rstack/THIRD_PARTY_NOTICES.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
This package includes software developed by third parties.
44

5+
## fast-ignore
6+
7+
This package includes bundled code from [fast-ignore](https://github.com/fabiospampinato/fast-ignore).
8+
9+
License: MIT
10+
11+
The MIT License (MIT)
12+
13+
Copyright (c) 2023-present Fabio Spampinato
14+
15+
Permission is hereby granted, free of charge, to any person obtaining a
16+
copy of this software and associated documentation files (the "Software"),
17+
to deal in the Software without restriction, including without limitation
18+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
19+
and/or sell copies of the Software, and to permit persons to whom the
20+
Software is furnished to do so, subject to the following conditions:
21+
22+
The above copyright notice and this permission notice shall be included in
23+
all copies or substantial portions of the Software.
24+
25+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31+
SOFTWARE.
32+
533
## fresh-import
634

735
This package includes bundled code from [fresh-import](https://github.com/sapphi-red/fresh-import).

packages/rstack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"@rstest/adapter-rslib": "catalog:",
6868
"@types/micromatch": "catalog:",
6969
"@types/node": "catalog:",
70+
"fast-ignore": "catalog:",
7071
"lint-staged": "catalog:",
7172
"micromatch": "catalog:",
7273
"rslog": "catalog:",

packages/rstack/src/fmt/ignore.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { relative } from 'node:path';
2+
import fastIgnore from 'fast-ignore';
3+
import type { ResolvedFmtConfig } from './types.ts';
4+
5+
/** Creates a reusable matcher for config-level ignore patterns. */
6+
const createFmtIgnoreMatcher = (config: ResolvedFmtConfig): ((filePath: string) => boolean) => {
7+
const matches = fastIgnore(config.ignorePatterns.join('\n'));
8+
9+
return (filePath) => matches(relative(config.rootPath, filePath));
10+
};
11+
12+
export { createFmtIgnoreMatcher };
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import path from 'node:path';
2+
import { expect, test } from 'rstack/test';
3+
import { normalizeFmtConfig } from '../../src/fmt/config.ts';
4+
import { createFmtIgnoreMatcher } from '../../src/fmt/ignore.ts';
5+
6+
const rootPath = path.join(import.meta.dirname, 'project');
7+
8+
const createMatcher = (ignorePatterns: string[]) =>
9+
createFmtIgnoreMatcher(normalizeFmtConfig({ ignorePatterns }, rootPath));
10+
11+
test('matches gitignore patterns relative to the config root', () => {
12+
const isIgnored = createMatcher(['dist/', '*.snap', '/root.js', '# comment', '\\#generated.js']);
13+
14+
expect(isIgnored(path.join(rootPath, 'dist/index.js'))).toBe(true);
15+
expect(isIgnored(path.join(rootPath, 'src/data.snap'))).toBe(true);
16+
expect(isIgnored(path.join(rootPath, 'root.js'))).toBe(true);
17+
expect(isIgnored(path.join(rootPath, 'nested/root.js'))).toBe(false);
18+
expect(isIgnored(path.join(rootPath, '#generated.js'))).toBe(true);
19+
expect(isIgnored(path.join(rootPath, 'src/index.js'))).toBe(false);
20+
});
21+
22+
test('applies negated patterns in declaration order', () => {
23+
const isIgnored = createMatcher(['*.js', '!src/keep.js']);
24+
const isIgnoredAgain = createMatcher(['*.js', '!src/keep.js', 'src/keep.js']);
25+
const isReincluded = createMatcher(['dist', '!dist']);
26+
const filePath = path.join(rootPath, 'src/keep.js');
27+
28+
expect(isIgnored(filePath)).toBe(false);
29+
expect(isIgnored(path.join(rootPath, 'src/drop.js'))).toBe(true);
30+
expect(isIgnoredAgain(filePath)).toBe(true);
31+
expect(isReincluded(path.join(rootPath, 'dist'))).toBe(false);
32+
});
33+
34+
test('does not let explicit files bypass ignore patterns', () => {
35+
const isIgnored = createMatcher(['generated/']);
36+
const explicitFilePath = path.join(rootPath, 'generated/output.js');
37+
38+
expect(isIgnored(explicitFilePath)).toBe(true);
39+
});
40+
41+
test('matches parent directory patterns without validation', () => {
42+
const isIgnored = createMatcher(['../shared/*.js']);
43+
44+
expect(isIgnored(path.join(rootPath, '../shared/index.js'))).toBe(true);
45+
expect(isIgnored(path.join(rootPath, 'shared/index.js'))).toBe(false);
46+
});
47+
48+
test('does not ignore files when no patterns are configured', () => {
49+
const isIgnored = createMatcher([]);
50+
51+
expect(isIgnored(path.join(rootPath, 'src/index.js'))).toBe(false);
52+
});

pnpm-lock.yaml

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ catalog:
3434
'@types/react-dom': '^19.2.3'
3535
'@shikijs/transformers': '^4.3.1'
3636
'cspell-ban-words': '^0.0.4'
37+
'fast-ignore': '2.0.0'
3738
'happy-dom': '^20.11.1'
3839
'heading-case': '^1.1.4'
3940
'lint-staged': '^17.2.0'

0 commit comments

Comments
 (0)