Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions app/utils/package-content-hints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ const POSSIBLY_UNNECESSARY_FILES: ReadonlySet<string> = new Set([
'.editorconfig',
'.prettierignore',
'.eslintignore',
'.jshintignore',
'.npmignore',
'.gitignore',
'.gitattributes',
'.travis.yml',
'.verb.md',
'Makefile',
'tsconfig.json',
'jsconfig.json',
'.node-version',
'.nvmrc',
'mise.toml',
Expand All @@ -27,6 +33,18 @@ const POSSIBLY_UNNECESSARY_FILES: ReadonlySet<string> = new Set([
'.env.production.local',
'.nycrc',
'nyc.json',
'.DS_Store',
'AUTHORS',
'test.js',
'test.ts',
'tests.js',
'tests.ts',
'bench.js',
Comment on lines +37 to +42

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot say I've ever heard of these file types 👀

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is some I've found when browsing packages

'benchmark.js',
'yarn.lock',
'bun.lock',
'bun.lockb',
'package-lock.json',
])

const POSSIBLY_UNNECESSARY_DIRECTORIES: ReadonlySet<string> = new Set([
Expand All @@ -35,11 +53,16 @@ const POSSIBLY_UNNECESSARY_DIRECTORIES: ReadonlySet<string> = new Set([
'.github',
'.idea',
'.zed',
'.yarn',
'.husky',
'.changeset',
'test',
'tests',
'__tests__',
'spec',
'specs',
'example',
'examples',
'benchmark',
])

const POSSIBLY_UNNECESSARY_DIRECTORY_PATTERNS: readonly RegExp[] = [/^__.+__$/]
Expand All @@ -53,11 +76,18 @@ const POSSIBLY_UNNECESSARY_PATTERNS: readonly RegExp[] = [
/^\.oxlintrc(?:\.(?:json|js|cjs|yml|yaml))?$/,
/^oxfmt\.config\.(?:js|cjs|mjs|ts|mts|cts)$/,
/^\.oxfmtrc(?:\.(?:json|js|cjs|yml|yaml))?$/,
/^jest\.config\.(?:js|cjs|mjs|ts|mts|cts)$/,
// Match common dot-prefixed config files without flagging all dotfiles;
// files like .npmrc, .npmignore, and .gitkeep can be intentional artifacts.
// files like .npmrc can be intentional artifacts.
/^\.(?!npmrc$)[a-z][a-z0-9_-]*rc$/,
/^\.(?!npmrc\.)[a-z][a-z0-9_-]*rc\.(?:json|js|cjs|mjs|yml|yaml|toml)$/,
/^\.[a-z][a-z0-9_-]*\.config\.(?:js|cjs|mjs|ts|mts|cts)$/,
// Match files ending in .test.js, .test.ts, .spec.js, .spec.ts, etc.
/\.(?:test|spec)\.(?:j|t)s$/,
// Match CHANGELOG.md, etc
/^(?:changelog|releasenotes|release-notes|history|contributing|contribute|news|collaborators)\.(?:md|markdown|txt)$/i,
// Match example.mjs, examples.js, stc
/^examples?\.(?:js|cjs|mjs|ts|mts|cts)$/,
]

export function isPossiblyUnnecessaryContent(name: string, type: 'file' | 'directory'): boolean {
Expand Down
191 changes: 142 additions & 49 deletions test/unit/app/utils/package-content-hints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ describe('isPossiblyUnnecessaryContent', () => {
expect(isPossiblyUnnecessaryContent('.gitattributes', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.prettierignore', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.eslintignore', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.jshintignore', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.npmignore', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('tsconfig.json', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('jsconfig.json', 'file')).toBe(true)
})

it('flags local environment files', () => {
Expand All @@ -30,61 +33,125 @@ describe('isPossiblyUnnecessaryContent', () => {
expect(isPossiblyUnnecessaryContent('nyc.json', 'file')).toBe(true)
})

it('matches ESLint configuration patterns', () => {
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`eslint.config.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.eslintrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'yml', 'yaml']) {
expect(isPossiblyUnnecessaryContent(`.eslintrc.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('eslint.config.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.eslintrc.txt', 'file')).toBe(false)
it('flags editor and CI files', () => {
expect(isPossiblyUnnecessaryContent('.travis.yml', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.verb.md', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('Makefile', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.DS_Store', 'file')).toBe(true)
})

it('matches Prettier configuration patterns', () => {
expect(isPossiblyUnnecessaryContent('.prettierrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'yml', 'yaml', 'toml']) {
expect(isPossiblyUnnecessaryContent(`.prettierrc.${extension}`, 'file')).toBe(true)
}
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`prettier.config.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.prettierrc.txt', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('prettier.config.json', 'file')).toBe(false)
it('flags package manager lockfiles', () => {
expect(isPossiblyUnnecessaryContent('yarn.lock', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('bun.lock', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('bun.lockb', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('package-lock.json', 'file')).toBe(true)
})

it('matches oxlint and oxfmt configuration patterns', () => {
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`oxlint.config.${extension}`, 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent(`oxfmt.config.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.oxlintrc', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.oxfmtrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'yml', 'yaml']) {
expect(isPossiblyUnnecessaryContent(`.oxlintrc.${extension}`, 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent(`.oxfmtrc.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('oxlint.config.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('oxfmt.config.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.oxlintrc.txt', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.oxfmtrc.txt', 'file')).toBe(false)
it('flags example and development files', () => {
expect(isPossiblyUnnecessaryContent('test.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('tests.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('test.ts', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('index.test.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('index.test.ts', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('index.spec.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('index.spec.ts', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('bench.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('benchmark.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('example.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('examples.js', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('example.mjs', 'file')).toBe(true)
})

it('matches common dot-prefixed configuration patterns without over-flagging', () => {
expect(isPossiblyUnnecessaryContent('.babelrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'mjs', 'yml', 'yaml', 'toml']) {
expect(isPossiblyUnnecessaryContent(`.stylelintrc.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.browserslistrc', 'file')).toBe(true)
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`.tailwind.config.${extension}`, 'file')).toBe(true)
it('flags unnecessay metadata and documentation files', () => {
expect(isPossiblyUnnecessaryContent('AUTHORS', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('collaborators.md', 'file')).toBe(true)
})

describe('changelog, release notes, and contributing files', () => {
const files = [
'CHANGELOG.md',
'Changelog.md',
'changelog.md',
'CHANGELOG.markdown',
'changelog.markdown',
'RELEASENOTES.md',
'release-notes.md',
'CONTRIBUTING.md',
'HISTORY.md',
'History.md',
'history.txt',
'NEWS.md',
]
for (const name of files) {
it(`flags ${name}`, () => {
expect(isPossiblyUnnecessaryContent(name, 'file')).toBe(true)
})
}
// .npmrc is sometimes an intentional shipped artifact; do not flag it.
expect(isPossiblyUnnecessaryContent('.npmrc', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.npmrc.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.stylelintrc.ts', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.tailwind.config.json', 'file')).toBe(false)
})

describe('configuration patterns', () => {
it('matches ESLint configuration patterns', () => {
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`eslint.config.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.eslintrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'yml', 'yaml']) {
expect(isPossiblyUnnecessaryContent(`.eslintrc.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('eslint.config.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.eslintrc.txt', 'file')).toBe(false)
})

it('matches Prettier configuration patterns', () => {
expect(isPossiblyUnnecessaryContent('.prettierrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'yml', 'yaml', 'toml']) {
expect(isPossiblyUnnecessaryContent(`.prettierrc.${extension}`, 'file')).toBe(true)
}
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`prettier.config.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.prettierrc.txt', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('prettier.config.json', 'file')).toBe(false)
})

it('matches oxlint and oxfmt configuration patterns', () => {
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`oxlint.config.${extension}`, 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent(`oxfmt.config.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.oxlintrc', 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent('.oxfmtrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'yml', 'yaml']) {
expect(isPossiblyUnnecessaryContent(`.oxlintrc.${extension}`, 'file')).toBe(true)
expect(isPossiblyUnnecessaryContent(`.oxfmtrc.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('oxlint.config.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('oxfmt.config.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.oxlintrc.txt', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.oxfmtrc.txt', 'file')).toBe(false)
})

it('matches jest configuration patterns', () => {
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`jest.config.${extension}`, 'file')).toBe(true)
}
})

it('matches common dot-prefixed configuration patterns without over-flagging', () => {
expect(isPossiblyUnnecessaryContent('.babelrc', 'file')).toBe(true)
for (const extension of ['json', 'js', 'cjs', 'mjs', 'yml', 'yaml', 'toml']) {
expect(isPossiblyUnnecessaryContent(`.stylelintrc.${extension}`, 'file')).toBe(true)
}
expect(isPossiblyUnnecessaryContent('.browserslistrc', 'file')).toBe(true)
for (const extension of ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']) {
expect(isPossiblyUnnecessaryContent(`.tailwind.config.${extension}`, 'file')).toBe(true)
}
// .npmrc is sometimes an intentional shipped artifact; do not flag it.
expect(isPossiblyUnnecessaryContent('.npmrc', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.npmrc.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.stylelintrc.ts', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('.tailwind.config.json', 'file')).toBe(false)
})
})

it('flags editor and CI directories', () => {
Expand All @@ -93,6 +160,8 @@ describe('isPossiblyUnnecessaryContent', () => {
expect(isPossiblyUnnecessaryContent('.github', 'directory')).toBe(true)
expect(isPossiblyUnnecessaryContent('.idea', 'directory')).toBe(true)
expect(isPossiblyUnnecessaryContent('.zed', 'directory')).toBe(true)
expect(isPossiblyUnnecessaryContent('.yarn', 'directory')).toBe(true)
expect(isPossiblyUnnecessaryContent('.husky', 'directory')).toBe(true)
})

it('flags test directories', () => {
Expand All @@ -105,17 +174,41 @@ describe('isPossiblyUnnecessaryContent', () => {
expect(isPossiblyUnnecessaryContent('specs', 'directory')).toBe(true)
})

it('flags development directories', () => {
expect(isPossiblyUnnecessaryContent('benchmark', 'directory')).toBe(true)
})

it('flags example directories', () => {
expect(isPossiblyUnnecessaryContent('example', 'directory')).toBe(true)
expect(isPossiblyUnnecessaryContent('examples', 'directory')).toBe(true)
})

it('does not flag ordinary source files or directories', () => {
expect(isPossiblyUnnecessaryContent('index.js', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('latest.js', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('package.json', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('README.md', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('LICENSE', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('readme.md', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('main.ts', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('src', 'directory')).toBe(false)
expect(isPossiblyUnnecessaryContent('lib', 'directory')).toBe(false)
expect(isPossiblyUnnecessaryContent('dist', 'directory')).toBe(false)
})

it('does not flag legal related files', () => {
expect(isPossiblyUnnecessaryContent('LICENSE', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('LICENCE', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('license', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('licence', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('LICENSE.md', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('LICENCE.md', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('LICENSE-MIT', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('LICENSE-MIT.txt', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('NOTICE', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('NOTICE.md', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('UNLICENSE', 'file')).toBe(false)
})

it('does not confuse a directory name passed as a file with the directory match', () => {
expect(isPossiblyUnnecessaryContent('.vscode', 'file')).toBe(false)
expect(isPossiblyUnnecessaryContent('test', 'file')).toBe(false)
Expand Down
Loading