From b2f099870148fd1eb8a74147a80ade2750b3441d Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 5 Aug 2026 11:59:14 +0800 Subject: [PATCH 1/3] perf(fmt): optimize default lock file matching --- packages/rstack/src/fmt/ignore.ts | 27 +++++++++++++++++++----- packages/rstack/tests/fmt/ignore.test.ts | 4 ++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/rstack/src/fmt/ignore.ts b/packages/rstack/src/fmt/ignore.ts index bc814a5..99571a0 100644 --- a/packages/rstack/src/fmt/ignore.ts +++ b/packages/rstack/src/fmt/ignore.ts @@ -9,7 +9,7 @@ import type { ResolvedFmtConfig } from './types.ts'; * Prettier already skips other generated lock files when it cannot infer a parser, so this list * contains only the additional defaults owned by `rs fmt`. */ -const defaultIgnorePatterns = ['package-lock.json', 'pnpm-lock.yaml']; +const defaultIgnoreNames = new Set(['package-lock.json', 'pnpm-lock.yaml']); type IgnoreMatcher = (filePath: string, isDirectory?: boolean) => boolean; @@ -20,6 +20,21 @@ interface CreateIgnoreMatcherOptions { ignorePaths?: string[]; } +const createDefaultIgnoreMatcher = (rootPath: string): IgnoreMatcher => { + const rootPrefix = rootPath.endsWith(path.sep) ? rootPath : `${rootPath}${path.sep}`; + + return (filePath) => { + const relativePath = filePath.startsWith(rootPrefix) + ? filePath.slice(rootPrefix.length) + : path.relative(rootPath, filePath); + + return ( + relativePath !== '' && + relativePath.split(path.sep).some((segment) => defaultIgnoreNames.has(segment)) + ); + }; +}; + const createPatternMatcher = (rootPath: string, patterns: string): IgnoreMatcher => { const matcher = createIgnore({ allowRelativePaths: true }).add(patterns); const rootPrefix = rootPath.endsWith(path.sep) ? rootPath : `${rootPath}${path.sep}`; @@ -58,10 +73,12 @@ const createIgnoreMatcher = async ({ cwd, ignorePaths = [], }: CreateIgnoreMatcherOptions): Promise => { - const configMatcher = createPatternMatcher( - config.rootPath, - [...defaultIgnorePatterns, ...config.ignorePatterns].join('\n'), - ); + const configMatcher = config.ignorePatterns.length + ? createPatternMatcher( + config.rootPath, + [...defaultIgnoreNames, ...config.ignorePatterns].join('\n'), + ) + : createDefaultIgnoreMatcher(config.rootPath); if (ignorePaths.length === 0) { return configMatcher; } diff --git a/packages/rstack/tests/fmt/ignore.test.ts b/packages/rstack/tests/fmt/ignore.test.ts index 5826767..adf5bbf 100644 --- a/packages/rstack/tests/fmt/ignore.test.ts +++ b/packages/rstack/tests/fmt/ignore.test.ts @@ -64,6 +64,10 @@ test('ignores common lock files by default and allows explicit negation', async expect(isIgnored(path.join(rootPath, 'package-lock.json'))).toBe(true); expect(isIgnored(path.join(rootPath, 'packages/app/pnpm-lock.yaml'))).toBe(true); + expect(isIgnored(path.join(rootPath, 'packages/app/PNPM-LOCK.YAML'))).toBe(false); + expect(isIgnored(path.join(rootPath, 'package-lock.json/index.js'))).toBe(true); + expect(isIgnored(path.join(rootPath, '../shared/pnpm-lock.yaml'))).toBe(true); + expect(isIgnored(path.join(rootPath, 'pnpm-lock.yaml.backup'))).toBe(false); expect(isIgnoredAfterReinclude(path.join(rootPath, 'pnpm-lock.yaml'))).toBe(false); }); From d610b1b3fd09c9ab4778c056839d5f1a46156fa0 Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 5 Aug 2026 13:17:30 +0800 Subject: [PATCH 2/3] refactor(fmt): simplify default lock matcher --- packages/rstack/src/fmt/ignore.ts | 21 +++++---------------- packages/rstack/tests/fmt/ignore.test.ts | 1 - 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/packages/rstack/src/fmt/ignore.ts b/packages/rstack/src/fmt/ignore.ts index 99571a0..bb24ccb 100644 --- a/packages/rstack/src/fmt/ignore.ts +++ b/packages/rstack/src/fmt/ignore.ts @@ -9,7 +9,8 @@ import type { ResolvedFmtConfig } from './types.ts'; * Prettier already skips other generated lock files when it cannot infer a parser, so this list * contains only the additional defaults owned by `rs fmt`. */ -const defaultIgnoreNames = new Set(['package-lock.json', 'pnpm-lock.yaml']); +const defaultIgnoreNames = ['package-lock.json', 'pnpm-lock.yaml']; +const defaultIgnoreSuffixes = defaultIgnoreNames.map((name) => `${path.sep}${name}`); type IgnoreMatcher = (filePath: string, isDirectory?: boolean) => boolean; @@ -20,20 +21,8 @@ interface CreateIgnoreMatcherOptions { ignorePaths?: string[]; } -const createDefaultIgnoreMatcher = (rootPath: string): IgnoreMatcher => { - const rootPrefix = rootPath.endsWith(path.sep) ? rootPath : `${rootPath}${path.sep}`; - - return (filePath) => { - const relativePath = filePath.startsWith(rootPrefix) - ? filePath.slice(rootPrefix.length) - : path.relative(rootPath, filePath); - - return ( - relativePath !== '' && - relativePath.split(path.sep).some((segment) => defaultIgnoreNames.has(segment)) - ); - }; -}; +const createDefaultIgnoreMatcher = (): IgnoreMatcher => (filePath) => + defaultIgnoreSuffixes.some((suffix) => filePath.endsWith(suffix)); const createPatternMatcher = (rootPath: string, patterns: string): IgnoreMatcher => { const matcher = createIgnore({ allowRelativePaths: true }).add(patterns); @@ -78,7 +67,7 @@ const createIgnoreMatcher = async ({ config.rootPath, [...defaultIgnoreNames, ...config.ignorePatterns].join('\n'), ) - : createDefaultIgnoreMatcher(config.rootPath); + : createDefaultIgnoreMatcher(); if (ignorePaths.length === 0) { return configMatcher; } diff --git a/packages/rstack/tests/fmt/ignore.test.ts b/packages/rstack/tests/fmt/ignore.test.ts index adf5bbf..1d03361 100644 --- a/packages/rstack/tests/fmt/ignore.test.ts +++ b/packages/rstack/tests/fmt/ignore.test.ts @@ -65,7 +65,6 @@ test('ignores common lock files by default and allows explicit negation', async expect(isIgnored(path.join(rootPath, 'package-lock.json'))).toBe(true); expect(isIgnored(path.join(rootPath, 'packages/app/pnpm-lock.yaml'))).toBe(true); expect(isIgnored(path.join(rootPath, 'packages/app/PNPM-LOCK.YAML'))).toBe(false); - expect(isIgnored(path.join(rootPath, 'package-lock.json/index.js'))).toBe(true); expect(isIgnored(path.join(rootPath, '../shared/pnpm-lock.yaml'))).toBe(true); expect(isIgnored(path.join(rootPath, 'pnpm-lock.yaml.backup'))).toBe(false); expect(isIgnoredAfterReinclude(path.join(rootPath, 'pnpm-lock.yaml'))).toBe(false); From c0f75d25c04b4b43633055ae909a81232bef9b5a Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 5 Aug 2026 13:23:00 +0800 Subject: [PATCH 3/3] refactor(fmt): lazily create lock suffixes --- packages/rstack/src/fmt/ignore.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/rstack/src/fmt/ignore.ts b/packages/rstack/src/fmt/ignore.ts index bb24ccb..1405de2 100644 --- a/packages/rstack/src/fmt/ignore.ts +++ b/packages/rstack/src/fmt/ignore.ts @@ -10,7 +10,6 @@ import type { ResolvedFmtConfig } from './types.ts'; * contains only the additional defaults owned by `rs fmt`. */ const defaultIgnoreNames = ['package-lock.json', 'pnpm-lock.yaml']; -const defaultIgnoreSuffixes = defaultIgnoreNames.map((name) => `${path.sep}${name}`); type IgnoreMatcher = (filePath: string, isDirectory?: boolean) => boolean; @@ -21,8 +20,11 @@ interface CreateIgnoreMatcherOptions { ignorePaths?: string[]; } -const createDefaultIgnoreMatcher = (): IgnoreMatcher => (filePath) => - defaultIgnoreSuffixes.some((suffix) => filePath.endsWith(suffix)); +const createDefaultIgnoreMatcher = (): IgnoreMatcher => { + const suffixes = defaultIgnoreNames.map((name) => `${path.sep}${name}`); + + return (filePath) => suffixes.some((suffix) => filePath.endsWith(suffix)); +}; const createPatternMatcher = (rootPath: string, patterns: string): IgnoreMatcher => { const matcher = createIgnore({ allowRelativePaths: true }).add(patterns);