-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(examples-plugins): refactor knip reporter
- Loading branch information
Showing
15 changed files
with
739 additions
and
1,407 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
164 changes: 164 additions & 0 deletions
164
examples/plugins/src/knip/src/reporter/__snapshots__/reporter.unit.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html | ||
|
||
exports[`knipReporter > should produce valid audit outputsass 1`] = ` | ||
[ | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Unused file code-pushup.json", | ||
"severity": "info", | ||
"source": { | ||
"file": "../../../code-pushup.json", | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "unused-files", | ||
"value": 1, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Unused dependency cli-table3", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/package.json", | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "unused-dependencies", | ||
"value": 1, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Unused devDependency @trivago/prettier-plugin-sort-imports", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/package.json", | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "unused-devdependencies", | ||
"value": 1, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Referenced optional peerDependency ts-node", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/package.json", | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "referenced-optional-peerdependencies", | ||
"value": 1, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Unlisted dependency jsonc-eslint-parser", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/packages/plugin-lighthouse/.eslintrc.json", | ||
}, | ||
}, | ||
{ | ||
"message": "Unlisted dependency jsonc-eslint-parser", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/.eslintrc.json", | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "unlisted-dependencies", | ||
"value": 2, | ||
}, | ||
{ | ||
"score": 1, | ||
"slug": "unlisted-binaries", | ||
"value": 0, | ||
}, | ||
{ | ||
"score": 1, | ||
"slug": "unresolved-imports", | ||
"value": 0, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Unused export duplicateErrorMsg", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/packages/models/src/lib/category-config.ts", | ||
"position": { | ||
"startColumn": 17, | ||
"startLine": 54, | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "unused-exports", | ||
"value": 1, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Unused exported type GroupMeta", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/packages/models/src/lib/group.ts", | ||
"position": { | ||
"startColumn": 13, | ||
"startLine": 26, | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "unused-exported-types", | ||
"value": 1, | ||
}, | ||
{ | ||
"score": 1, | ||
"slug": "unused-exported-enum-members", | ||
"value": 0, | ||
}, | ||
{ | ||
"details": { | ||
"issues": [ | ||
{ | ||
"message": "Duplicate export initGenerator|default", | ||
"severity": "error", | ||
"source": { | ||
"file": "/Users/username/Projects/code-pushup/packages/nx-plugin/src/generators/init/generator.ts", | ||
}, | ||
}, | ||
], | ||
}, | ||
"score": 0, | ||
"slug": "duplicate-exports", | ||
"value": 1, | ||
}, | ||
] | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { IssueType } from 'knip/dist/types/issues'; | ||
|
||
export const ISSUE_TYPES = [ | ||
'files', | ||
'dependencies', | ||
'devDependencies', | ||
'optionalPeerDependencies', | ||
'unlisted', | ||
'binaries', | ||
'unresolved', | ||
'exports', | ||
'nsExports', | ||
'types', | ||
'nsTypes', | ||
'enumMembers', | ||
'classMembers', | ||
'duplicates', | ||
] as const; | ||
|
||
export const ISSUE_TYPE_TITLE: Record<IssueType | '_files', string> = { | ||
files: 'Unused files', | ||
_files: 'Unused files', | ||
dependencies: 'Unused dependencies', | ||
devDependencies: 'Unused devDependencies', | ||
optionalPeerDependencies: 'Referenced optional peerDependencies', | ||
unlisted: 'Unlisted dependencies', | ||
binaries: 'Unlisted binaries', | ||
unresolved: 'Unresolved imports', | ||
exports: 'Unused exports', | ||
nsExports: 'Exports in used namespace', | ||
types: 'Unused exported types', | ||
nsTypes: 'Exported types in used namespace', | ||
enumMembers: 'Unused exported enum members', | ||
classMembers: 'Unused exported class members', | ||
duplicates: 'Duplicate exports', | ||
} as const; | ||
|
||
export const ISSUE_TYPE_MESSAGE: Record< | ||
IssueType | '_files', | ||
(arg: string) => string | ||
> = { | ||
files: (file: string) => `Unused file ${file}`, | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
_files: (file: string) => `Unused file ${file}`, | ||
dependencies: (dep: string) => `Unused dependency ${dep}`, | ||
devDependencies: (dep: string) => `Unused devDependency ${dep}`, | ||
optionalPeerDependencies: (dep: string) => | ||
`Referenced optional peerDependency ${dep}`, | ||
unlisted: (dep: string) => `Unlisted dependency ${dep}`, | ||
binaries: (binary: string) => `Unlisted binary ${binary}`, | ||
unresolved: (importName: string) => `Unresolved import ${importName}`, | ||
exports: (exportName: string) => `Unused export ${exportName}`, | ||
nsExports: (namespace: string) => `Exports in used namespace ${namespace}`, | ||
types: (type: string) => `Unused exported type ${type}`, | ||
nsTypes: (namespace: string) => | ||
`Exported types in used namespace ${namespace}`, | ||
enumMembers: (enumMember: string) => | ||
`Unused exported enum member ${enumMember}`, | ||
classMembers: (classMember: string) => | ||
`Unused exported class member ${classMember}`, | ||
duplicates: (duplicate: string) => `Duplicate export ${duplicate}`, | ||
} as const; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,5 @@ | ||
import type { ReporterOptions } from 'knip'; | ||
import { writeFile } from 'node:fs/promises'; | ||
import { dirname } from 'node:path'; | ||
import { ensureDirectoryExists } from '@code-pushup/utils'; | ||
import { knipToCpReport } from './utils'; | ||
|
||
/** | ||
* @example | ||
* | ||
* npx knip --reporter ./code-pushup.reporter.ts --reporter-options '{"outputFile":"tmp"}' | ||
* | ||
*/ | ||
export type CustomReporterOptions = { | ||
outputFile?: string; | ||
rawOutputFile?: string; | ||
}; | ||
|
||
function parseCustomReporterOptions( | ||
optionsString?: string, | ||
): Record<string, unknown> { | ||
return typeof optionsString === 'string' && optionsString !== '' | ||
? (JSON.parse(optionsString) as Record<string, unknown>) | ||
: {}; | ||
} | ||
|
||
export const knipReporter = async ({ | ||
report, | ||
issues, | ||
options, | ||
}: ReporterOptions) => { | ||
const reporterOptions = parseCustomReporterOptions( | ||
options, | ||
) as CustomReporterOptions; | ||
const { outputFile = `knip-report.json`, rawOutputFile } = reporterOptions; | ||
if (rawOutputFile) { | ||
await ensureDirectoryExists(dirname(rawOutputFile)); | ||
await writeFile( | ||
rawOutputFile, | ||
JSON.stringify({ report, issues, options: reporterOptions }, null, 2), | ||
); | ||
} | ||
const result = knipToCpReport({ issues }); | ||
|
||
await ensureDirectoryExists(dirname(outputFile)); | ||
await writeFile(outputFile, JSON.stringify(result, null, 2)); | ||
}; | ||
import { knipReporter } from './reporter'; | ||
|
||
export default knipReporter; | ||
export { knipReporter } from './reporter'; | ||
export { CustomReporterOptions } from './model'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import chalk from 'chalk'; | ||
import { z } from 'zod'; | ||
import { filePathSchema } from '@code-pushup/models'; | ||
|
||
export const customReporterOptionsSchema = z.object({ | ||
outputFile: filePathSchema.optional(), | ||
rawOutputFile: filePathSchema.optional(), | ||
}); | ||
|
||
export type CustomReporterOptions = z.infer<typeof customReporterOptionsSchema>; | ||
|
||
export function parseCustomReporterOptions( | ||
optionsString?: string, | ||
): CustomReporterOptions { | ||
// eslint-disable-next-line functional/no-let | ||
let rawJson; | ||
try { | ||
rawJson = | ||
typeof optionsString === 'string' && optionsString !== '' | ||
? (JSON.parse(optionsString) as Record<string, unknown>) | ||
: {}; | ||
} catch (error) { | ||
throw new Error(`The passed knip reporter options have to be a JSON parseable string. E.g. --reporter-options='{\\"prop\\":42}' | ||
Option string: ${chalk.bold(optionsString)} | ||
Error: ${(error as Error).message}`); | ||
} | ||
|
||
try { | ||
return customReporterOptionsSchema.parse(rawJson); | ||
} catch (error) { | ||
throw new Error(`The reporter options options have to follow the schema.' | ||
Error: ${(error as Error).message}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import chalk from 'chalk'; | ||
import { describe, expect, it } from 'vitest'; | ||
import { CustomReporterOptions, parseCustomReporterOptions } from './model'; | ||
|
||
describe('parseCustomReporterOptions', () => { | ||
it('should return empty object if no reporter options are given', () => { | ||
expect(parseCustomReporterOptions()).toStrictEqual({}); | ||
}); | ||
|
||
it('should return valid report options', () => { | ||
expect( | ||
parseCustomReporterOptions( | ||
JSON.stringify({ | ||
outputFile: 'my-knip-report.json', | ||
rawOutputFile: 'my-knip-raw-report.json', | ||
} satisfies CustomReporterOptions), | ||
), | ||
).toStrictEqual({ | ||
outputFile: 'my-knip-report.json', | ||
rawOutputFile: 'my-knip-raw-report.json', | ||
}); | ||
}); | ||
|
||
it('should throw for invalid reporter-options argument', () => { | ||
expect(() => parseCustomReporterOptions('{asd')).toThrow( | ||
`The passed knip reporter options have to be a JSON parseable string. E.g. --reporter-options='{\\"prop\\":42}'`, | ||
); | ||
expect(() => parseCustomReporterOptions('{asd')).toThrow( | ||
`Option string: ${chalk.bold('{asd')}`, | ||
); | ||
expect(() => parseCustomReporterOptions('{asd')).toThrow( | ||
`Error: Unexpected token a in JSON at position 1`, | ||
); | ||
}); | ||
|
||
it('should throw for invalid options', () => { | ||
const opt = JSON.stringify({ | ||
outputFile: '', | ||
} satisfies CustomReporterOptions); | ||
expect(() => parseCustomReporterOptions(opt)).toThrow( | ||
'The reporter options options have to follow the schema.', | ||
); | ||
expect(() => parseCustomReporterOptions(opt)).toThrow('path is invalid'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import type { ReporterOptions } from 'knip'; | ||
import { writeFile } from 'node:fs/promises'; | ||
import { dirname } from 'node:path'; | ||
import { ensureDirectoryExists, ui } from '@code-pushup/utils'; | ||
import { KNIP_REPORT_NAME } from '../constants'; | ||
import { parseCustomReporterOptions } from './model'; | ||
import { DeepPartial } from './types'; | ||
import { knipToCpReport } from './utils'; | ||
|
||
/** | ||
* @example | ||
* | ||
* npx knip --reporter ./code-pushup.reporter.ts --reporter-options='{\"outputFile\":\"my-knip-report.json\"}' | ||
* | ||
*/ | ||
export const knipReporter = async ({ | ||
report, | ||
issues, | ||
options, | ||
}: DeepPartial<ReporterOptions>) => { | ||
const reporterOptions = parseCustomReporterOptions(options); | ||
const { outputFile = KNIP_REPORT_NAME, rawOutputFile } = reporterOptions; | ||
|
||
if (rawOutputFile) { | ||
await ensureDirectoryExists(dirname(rawOutputFile)); | ||
await writeFile( | ||
rawOutputFile, | ||
JSON.stringify({ report, issues, options: reporterOptions }, null, 2), | ||
); | ||
ui().logger.info(`Saved raw report to ${rawOutputFile}`); | ||
} | ||
|
||
const result = await knipToCpReport({ issues, report }); | ||
|
||
await ensureDirectoryExists(dirname(outputFile)); | ||
await writeFile(outputFile, JSON.stringify(result, null, 2)); | ||
ui().logger.info(`Saved report to ${outputFile}`); | ||
}; |
Oops, something went wrong.