diff --git a/.changeset/cool-pants-boil.md b/.changeset/cool-pants-boil.md new file mode 100644 index 0000000000..792fb57195 --- /dev/null +++ b/.changeset/cool-pants-boil.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Improve knip reports by using a single workspace-based config diff --git a/package.json b/package.json index 41b8529c70..0595e00b68 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,6 @@ "eslint-plugin-testing-library": "^6.0.0", "fs-extra": "^11.2.0", "husky": "^9.0.0", - "knip": "^5.0.0", "lint-staged": "^15.0.0", "minimist": "^1.2.5", "node-gyp": "^10.0.0", diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index f5c6a0c060..4ccad33712 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -74,6 +74,7 @@ "just-diff": "^6.0.2", "knex": "^3.0.0", "knex-pglite": "^0.11.0", + "knip": "^5.42.0", "lodash": "^4.17.21", "minimatch": "^9.0.0", "p-limit": "^3.0.2", diff --git a/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts b/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts index 50c429f986..645d084017 100644 --- a/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts +++ b/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts @@ -18,6 +18,7 @@ import pLimit from 'p-limit'; import os from 'os'; import { relative as relativePath, resolve as resolvePath } from 'path'; import fs from 'fs-extra'; +import type { KnipConfig } from 'knip'; import { createBinRunner } from '../util'; // Ignore this due to Knip error: Error: ENAMETOOLONG: name too long, scandir @@ -29,7 +30,7 @@ interface KnipExtractionOptions { } interface KnipConfigOptions { - packageDir: string; + knipConfigPath: string; } interface KnipPackageOptions { @@ -55,15 +56,20 @@ function logKnipReportInstructions() { console.log(''); } -async function generateKnipConfig({ packageDir }: KnipConfigOptions) { - const knipConfig = { - entry: [ - 'dev/index.{ts,tsx}', - 'src/index.{ts,tsx}', - 'src/alpha.{ts,tsx}', - 'src/routes.ts', - 'src/run.ts', - ], +async function generateKnipConfig({ knipConfigPath }: KnipConfigOptions) { + const knipConfig: KnipConfig = { + workspaces: { + '.': {}, + '{packages,plugins}/*': { + entry: [ + 'dev/index.{ts,tsx}', + 'src/index.{ts,tsx}', + 'src/alpha.{ts,tsx}', + 'src/routes.ts', + 'src/run.ts', + ], + }, + }, jest: { entry: ['src/setupTests.ts', '**/*.test.{ts,tsx}'], }, @@ -81,15 +87,12 @@ async function generateKnipConfig({ packageDir }: KnipConfigOptions) { '@backstage/theme', // this uses `declare module` in .d.ts so is implicitly used whenever extensions are needed ], }; - await fs.writeFile( - `${packageDir}/knip.json`, - JSON.stringify(knipConfig, null, 2), - ); + await fs.writeFile(knipConfigPath, JSON.stringify(knipConfig, null, 2)); } -function cleanKnipConfig({ packageDir }: KnipConfigOptions) { - if (fs.existsSync(`${packageDir}/knip.json`)) { - fs.rmSync(`${packageDir}/knip.json`); +function cleanKnipConfig({ knipConfigPath }: KnipConfigOptions) { + if (fs.existsSync(knipConfigPath)) { + fs.rmSync(knipConfigPath); } } @@ -105,13 +108,11 @@ async function handlePackage({ } const fullDir = cliPaths.resolveTargetRoot(packageDir); const reportPath = resolvePath(fullDir, 'knip-report.md'); - const run = createBinRunner(fullDir, ''); + const run = createBinRunner(cliPaths.targetRoot, ''); - await generateKnipConfig({ packageDir: fullDir }); - - const report = await run( + let report = await run( `${knipDir}/knip.js`, - `--directory ${fullDir}`, // Run in the package directory + `-W ${packageDir}`, // Run the desired workspace '--config knip.json', '--no-exit-code', // Removing this will end the process in case there are findings by knip '--no-progress', // Remove unnecessary debugging from output @@ -120,7 +121,17 @@ async function handlePackage({ '--reporter markdown', ); - cleanKnipConfig({ packageDir: fullDir }); + // Adjust report paths to be relative to workspace + report = report.replaceAll(`| ${packageDir}/`, '| '); + // Adjust table separators + report = report.replaceAll( + new RegExp(`(\\| :-+ \\| :)-{${packageDir.length + 1}}`, 'g'), + (_, p1) => p1, + ); + report = report.replaceAll( + new RegExp(` \\| Location {1,${packageDir.length + 2}}`, 'g'), + ' | Location ', + ); const existingReport = await fs.readFile(reportPath, 'utf8').catch(error => { if (error.code === 'ENOENT') { @@ -160,8 +171,10 @@ export async function runKnipReports({ isLocalBuild, }: KnipExtractionOptions) { const knipDir = cliPaths.resolveTargetRoot('./node_modules/knip/bin/'); + const knipConfigPath = cliPaths.resolveTargetRoot('./knip.json'); const limiter = pLimit(os.cpus().length); + await generateKnipConfig({ knipConfigPath }); try { await Promise.all( packageDirs.map(packageDir => @@ -170,15 +183,9 @@ export async function runKnipReports({ ), ), ); + await cleanKnipConfig({ knipConfigPath }); } catch (e) { - console.log( - `Error occurred during knip reporting: ${e}, cleaning knip configs`, - ); - packageDirs.map(packageDir => { - const fullDir = cliPaths.resolveTargetRoot(packageDir); - cleanKnipConfig({ packageDir: fullDir }); - }); - + console.log(`Error occurred during knip reporting: ${e}`); throw e; } } diff --git a/packages/repo-tools/src/commands/util.ts b/packages/repo-tools/src/commands/util.ts index e28aae6267..ff4cdad1b8 100644 --- a/packages/repo-tools/src/commands/util.ts +++ b/packages/repo-tools/src/commands/util.ts @@ -35,11 +35,12 @@ export function createBinRunner(cwd: string, path: string) { { cwd, shell: true, - timeout: 60000, + timeout: 3 * 60 * 1000, maxBuffer: 1024 * 1024, }, (err, stdout, stderr) => { if (err) { + console.log('err', err); reject(new Error(`${err.message}\n${stderr}`)); } else if (stderr) { reject(new Error(`Command printed error output: ${stderr}`)); diff --git a/yarn.lock b/yarn.lock index cf388516b3..3d19a0021b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8329,6 +8329,7 @@ __metadata: just-diff: ^6.0.2 knex: ^3.0.0 knex-pglite: ^0.11.0 + knip: ^5.42.0 lodash: ^4.17.21 minimatch: ^9.0.0 p-limit: ^3.0.2 @@ -34104,7 +34105,7 @@ __metadata: languageName: node linkType: hard -"knip@npm:^5.0.0": +"knip@npm:^5.42.0": version: 5.42.2 resolution: "knip@npm:5.42.2" dependencies: @@ -39801,11 +39802,11 @@ __metadata: linkType: hard "pretty-ms@npm:^9.0.0": - version: 9.0.0 - resolution: "pretty-ms@npm:9.0.0" + version: 9.2.0 + resolution: "pretty-ms@npm:9.2.0" dependencies: parse-ms: ^4.0.0 - checksum: 072b17547e09cb232e8e4c7be0281e256b6d8acd18dfb2fdd715d50330d1689fdaa877f53cf90c62ed419ef842f0f5fb94a2cd8ed1aa6d7608ad48834219435d + checksum: d3a5a5b1c8a3417f64a877dba5ee2bacee404b59bc12083466e5e6dce2745e4bd716e1f9860624c7dceb1b4a532e808e4f2a7a03903a132344b3818951e2d125 languageName: node linkType: hard @@ -42217,7 +42218,6 @@ __metadata: eslint-plugin-testing-library: ^6.0.0 fs-extra: ^11.2.0 husky: ^9.0.0 - knip: ^5.0.0 lint-staged: ^15.0.0 minimist: ^1.2.5 node-gyp: ^10.0.0