Skip to content
22 changes: 20 additions & 2 deletions app/utils/package-content-hints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ const POSSIBLY_UNNECESSARY_FILES: ReadonlySet<string> = new Set([
'.editorconfig',
'.prettierignore',
'.eslintignore',
'.jshintignore',
'.npmignore',
'.gitignore',
'.gitattributes',
'.travis.yml',
'.verb.md',
'Makefile',
'tsconfig.json',
'.node-version',
'.nvmrc',
Expand All @@ -27,6 +32,11 @@ const POSSIBLY_UNNECESSARY_FILES: ReadonlySet<string> = new Set([
'.env.production.local',
'.nycrc',
'nyc.json',
'.DS_Store',
'AUTHORS',
'test.js',
'test.ts',
'bench.js',
])

const POSSIBLY_UNNECESSARY_DIRECTORIES: ReadonlySet<string> = new Set([
Expand All @@ -37,9 +47,11 @@ const POSSIBLY_UNNECESSARY_DIRECTORIES: ReadonlySet<string> = new Set([
'.zed',
'test',
'tests',
'__tests__',
'spec',
'specs',
'example',
'examples',
'benchmark',
])

const POSSIBLY_UNNECESSARY_DIRECTORY_PATTERNS: readonly RegExp[] = [/^__.+__$/]
Expand All @@ -54,10 +66,16 @@ const POSSIBLY_UNNECESSARY_PATTERNS: readonly RegExp[] = [
/^oxfmt\.config\.(?:js|cjs|mjs|ts|mts|cts)$/,
/^\.oxfmtrc(?:\.(?:json|js|cjs|yml|yaml))?$/,
// 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)\.(?: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
66 changes: 65 additions & 1 deletion test/unit/app/utils/package-content-hints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ 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)
})

Expand All @@ -30,6 +32,45 @@ describe('isPossiblyUnnecessaryContent', () => {
expect(isPossiblyUnnecessaryContent('nyc.json', 'file')).toBe(true)
})

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('flags example and development files', () => {
expect(isPossiblyUnnecessaryContent('bench.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('flags unnecessay metadata and documentation files', () => {
expect(isPossiblyUnnecessaryContent('AUTHORS', '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',
]
for (const name of files) {
it(`flags ${name}`, () => {
expect(isPossiblyUnnecessaryContent(name, '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)
Expand Down Expand Up @@ -105,17 +146,40 @@ 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('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