From ff0536b58b214549cf11c91fe034e0629872766d Mon Sep 17 00:00:00 2001
From: Alejandro <alejandroramirezjimenez1999@gmail.com>
Date: Sat, 1 Feb 2025 00:12:23 +0100
Subject: [PATCH 1/4] feat(plugin-knip-e2e): adds e2e base project setup

---
 e2e/plugin-knip-e2e/.eslintrc.json     | 12 ++++++++++++
 e2e/plugin-knip-e2e/project.json       | 23 +++++++++++++++++++++++
 e2e/plugin-knip-e2e/tsconfig.json      | 20 ++++++++++++++++++++
 e2e/plugin-knip-e2e/tsconfig.test.json | 13 +++++++++++++
 e2e/plugin-knip-e2e/vite.config.e2e.ts | 20 ++++++++++++++++++++
 5 files changed, 88 insertions(+)
 create mode 100644 e2e/plugin-knip-e2e/.eslintrc.json
 create mode 100644 e2e/plugin-knip-e2e/project.json
 create mode 100644 e2e/plugin-knip-e2e/tsconfig.json
 create mode 100644 e2e/plugin-knip-e2e/tsconfig.test.json
 create mode 100644 e2e/plugin-knip-e2e/vite.config.e2e.ts

diff --git a/e2e/plugin-knip-e2e/.eslintrc.json b/e2e/plugin-knip-e2e/.eslintrc.json
new file mode 100644
index 0000000..93485ec
--- /dev/null
+++ b/e2e/plugin-knip-e2e/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+  "extends": ["../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*", "code-pushup.config*.ts"],
+  "overrides": [
+    {
+      "files": ["*.ts", "*.tsx"],
+      "parserOptions": {
+        "project": ["e2e/plugin-knip-e2e/tsconfig.*?.json"]
+      }
+    }
+  ]
+}
diff --git a/e2e/plugin-knip-e2e/project.json b/e2e/plugin-knip-e2e/project.json
new file mode 100644
index 0000000..4a994f1
--- /dev/null
+++ b/e2e/plugin-knip-e2e/project.json
@@ -0,0 +1,23 @@
+{
+  "name": "plugin-knip-e2e",
+  "$schema": "../../node_modules/nx/schemas/project-schema.json",
+  "sourceRoot": "examples/plugin-knip-e2e/src",
+  "projectType": "application",
+  "targets": {
+    "lint": {
+      "executor": "@nx/linter:eslint",
+      "outputs": ["{options.outputFile}"],
+      "options": {
+        "lintFilePatterns": ["e2e/plugin-knip-e2e/**/*.ts"]
+      }
+    },
+    "e2e": {
+      "executor": "@nx/vite:test",
+      "options": {
+        "config": "e2e/plugin-knip-e2e/vite.config.e2e.ts"
+      }
+    }
+  },
+  "implicitDependencies": ["plugin-knip"],
+  "tags": ["scope:tooling", "type:e2e"]
+}
diff --git a/e2e/plugin-knip-e2e/tsconfig.json b/e2e/plugin-knip-e2e/tsconfig.json
new file mode 100644
index 0000000..f5a2f89
--- /dev/null
+++ b/e2e/plugin-knip-e2e/tsconfig.json
@@ -0,0 +1,20 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "module": "ESNext",
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true,
+    "types": ["vitest"]
+  },
+  "files": [],
+  "include": [],
+  "references": [
+    {
+      "path": "./tsconfig.test.json"
+    }
+  ]
+}
diff --git a/e2e/plugin-knip-e2e/tsconfig.test.json b/e2e/plugin-knip-e2e/tsconfig.test.json
new file mode 100644
index 0000000..cc6383c
--- /dev/null
+++ b/e2e/plugin-knip-e2e/tsconfig.test.json
@@ -0,0 +1,13 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../dist/out-tsc",
+    "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"]
+  },
+  "include": [
+    "vite.config.e2e.ts",
+    "tests/**/*.e2e.test.ts",
+    "tests/**/*.d.ts",
+    "mocks/**/*.ts"
+  ]
+}
diff --git a/e2e/plugin-knip-e2e/vite.config.e2e.ts b/e2e/plugin-knip-e2e/vite.config.e2e.ts
new file mode 100644
index 0000000..771687a
--- /dev/null
+++ b/e2e/plugin-knip-e2e/vite.config.e2e.ts
@@ -0,0 +1,20 @@
+/// <reference types="vitest" />
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+  cacheDir: '../../node_modules/.vite/plugin-knip-e2e',
+  test: {
+    reporters: ['basic'],
+    testTimeout: 60_000,
+    globals: true,
+    pool: 'threads',
+    poolOptions: { threads: { singleThread: true } },
+    cache: {
+      dir: '../../node_modules/.vitest',
+    },
+    environment: 'node',
+    include: ['tests/**/*.e2e.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+    globalSetup: ['../../global-setup.ts'],
+    setupFiles: ['../../testing/test-setup/src/lib/reset.mocks.ts'],
+  },
+});

From 98ab1cf712e9f5a644bd8307429e71178cecbf50 Mon Sep 17 00:00:00 2001
From: Alejandro <alejandroramirezjimenez1999@gmail.com>
Date: Sat, 1 Feb 2025 01:10:38 +0100
Subject: [PATCH 2/4] wip

---
 .../unused-dependency/code-pushup.config.ts   | 10 +++
 .../fixtures/unused-dependency/knip.config.ts | 69 +++++++++++++++++++
 .../fixtures/unused-dependency/package.json   |  6 ++
 e2e/plugin-knip-e2e/project.json              |  8 +--
 e2e/plugin-knip-e2e/tests/collect.e2e.test.ts | 65 +++++++++++++++++
 e2e/plugin-knip-e2e/tsconfig.test.json        |  4 +-
 e2e/plugin-knip-e2e/vite.config.e2e.ts        |  5 +-
 nx.json                                       | 12 +++-
 package-lock.json                             | 57 ++++++++++++++-
 package.json                                  |  4 +-
 tools/vitest-tsconfig-path-aliases.ts         | 20 ++++++
 tsconfig.base.json                            |  4 +-
 vitest.workspace.ts                           |  2 +-
 13 files changed, 253 insertions(+), 13 deletions(-)
 create mode 100644 e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts
 create mode 100644 e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts
 create mode 100644 e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json
 create mode 100644 e2e/plugin-knip-e2e/tests/collect.e2e.test.ts
 create mode 100644 tools/vitest-tsconfig-path-aliases.ts

diff --git a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts
new file mode 100644
index 0000000..aa7552e
--- /dev/null
+++ b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts
@@ -0,0 +1,10 @@
+import path from 'node:path';
+import { knipPlugin } from '@code-pushup/plugin-knip';
+import type { CoreConfig } from '@code-pushup/models';
+export default {
+  plugins: [
+    knipPlugin({
+      workspace: path.join(__dirname, '..', '..', '..'),
+    }),
+  ]
+} satisfies CoreConfig;
diff --git a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts
new file mode 100644
index 0000000..b791b52
--- /dev/null
+++ b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts
@@ -0,0 +1,69 @@
+import {
+  KnipConfigPlugin,
+  combineNxKnipPlugins,
+  withEsbuildApps,
+  withEsbuildPublishableLibs,
+  withEslint,
+  withLibraryMapper,
+  withLocalNxPlugins,
+  withNxTsPaths,
+  withVitest,
+} from '@beaussan/nx-knip';
+
+
+const withNxStandards = (): KnipConfigPlugin => () => {
+  return {
+    project: ['**/*.{ts,js,tsx,jsx}'],
+    ignore: ['tmp/**', 'node_modules/**'],
+    commitlint: {
+      config: ['commitlint.config.js'],
+    },
+    entry: [
+      // unknown why this is needed, it should be picked up by knip from the vitest setup files
+      'testing/test-utils/src/index.ts',
+      'testing/test-utils/src/lib/fixtures/configs/*.ts',
+      'testing/test-setup/src/index.ts',
+      'testing/test-setup/src/lib/**/*.{js,mjs,ts,cjs,mts,cts}',
+      'global-setup.ts',
+      'global-setup.e2e.ts',
+      'examples/react-todos-app/code-pushup.config.js',
+      'examples/plugins/code-pushup.config.ts',
+      'testing/test-utils/src/lib/fixtures/configs/code-pushup.config.js',
+      'testing/test-utils/src/lib/fixtures/configs/code-pushup.empty.config.js',
+      'examples/plugins/src/package-json/src/index.ts',
+      // missing knip plugin for now, so this is in the root entry
+      'packages/models/zod2md.config.ts',
+      //'code-pushup.config.ts',
+      'esbuild.config.js',
+      'tools/**/*.{js,mjs,ts,cjs,mts,cts}',
+    ],
+    ignoreDependencies: [
+      'prettier',
+      '@swc/helpers',
+      '@swc/cli',
+      '@nx/plugin',
+      '@nx/workspace',
+      // Same issue as the other vitest related, it should be picked up by knip from the vitest setup files
+      // 'global-setup.ts',
+      // 'global-setup.e2e.ts',
+
+      // Knip should be able to pick up this
+      'tsx',
+      // Not a npm library, and resolved in a different typescript path than the global import one
+      '@example/custom-plugin',
+
+      // Prettier magic resolve is not picked up by knip
+      '@trivago/prettier-plugin-sort-imports',
+    ],
+  };
+};
+
+export default combineNxKnipPlugins(
+  withNxTsPaths(),
+  withLocalNxPlugins({ pluginNames: ['nx-plugin'] }),
+  withEsbuildApps(),
+  withEsbuildPublishableLibs(),
+  withVitest(),
+  withEslint(),
+  withNxStandards(),
+);
diff --git a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json
new file mode 100644
index 0000000..972d88b
--- /dev/null
+++ b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json
@@ -0,0 +1,6 @@
+{
+  "name": "any-package",
+  "dependencies": {
+    "zod": "^3.23.6"
+  }
+}
diff --git a/e2e/plugin-knip-e2e/project.json b/e2e/plugin-knip-e2e/project.json
index 4a994f1..cbd0b81 100644
--- a/e2e/plugin-knip-e2e/project.json
+++ b/e2e/plugin-knip-e2e/project.json
@@ -1,8 +1,10 @@
 {
   "name": "plugin-knip-e2e",
   "$schema": "../../node_modules/nx/schemas/project-schema.json",
-  "sourceRoot": "examples/plugin-knip-e2e/src",
+  "sourceRoot": "e2e/plugin-knip-e2e/src",
   "projectType": "application",
+  "implicitDependencies": ["plugin-knip"],
+  "tags": ["scope:tooling", "type:e2e"],
   "targets": {
     "lint": {
       "executor": "@nx/linter:eslint",
@@ -17,7 +19,5 @@
         "config": "e2e/plugin-knip-e2e/vite.config.e2e.ts"
       }
     }
-  },
-  "implicitDependencies": ["plugin-knip"],
-  "tags": ["scope:tooling", "type:e2e"]
+  }
 }
diff --git a/e2e/plugin-knip-e2e/tests/collect.e2e.test.ts b/e2e/plugin-knip-e2e/tests/collect.e2e.test.ts
new file mode 100644
index 0000000..4e41d9e
--- /dev/null
+++ b/e2e/plugin-knip-e2e/tests/collect.e2e.test.ts
@@ -0,0 +1,65 @@
+import { cp } from 'node:fs/promises';
+import path from 'node:path';
+import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
+import { type Report, reportSchema } from '@code-pushup/models';
+import { nxTargetProject } from '@code-pushup/test-nx-utils';
+import { teardownTestFolder } from '@code-pushup/test-setup';
+import {
+  E2E_ENVIRONMENTS_DIR,
+  TEST_OUTPUT_DIR,
+  removeColorCodes,
+} from '@code-pushup/test-utils';
+import { executeProcess, readJsonFile } from '@code-pushup/utils';
+
+describe('PLUGIN collect report with knip-plugin NPM package', () => {
+  const fixturesDir = path.join(
+    'e2e',
+    'plugin-knip-e2e',
+    'mocks',
+    'fixtures',
+  );
+  const fixtureUnusedDependency = path.join(fixturesDir, 'unused-dependency');
+
+  const envRoot = path.join(
+    E2E_ENVIRONMENTS_DIR,
+    nxTargetProject(),
+    TEST_OUTPUT_DIR,
+  );
+  const unusedDependencyDir = path.join(envRoot, 'unused-dependency');
+  const unusedDependencyOutputDir = path.join(unusedDependencyDir, '.code-pushup');
+
+  beforeAll(async () => {
+    await cp(fixtureUnusedDependency, unusedDependencyDir, { recursive: true });
+  });
+
+  afterAll(async () => {
+    await teardownTestFolder(unusedDependencyDir);
+  });
+
+  afterEach(async () => {
+    await teardownTestFolder(unusedDependencyOutputDir);
+  });
+
+  it('should run Knip plugin for Unused Dependency example dir and create report.json', async () => {
+    const { code, stdout } = await executeProcess({
+      command: 'npx',
+      args: ['@code-pushup/cli', 'collect', '--no-progress'],
+      cwd: unusedDependencyDir,
+    });
+
+    expect(code).toBe(0);
+
+    expect(removeColorCodes(stdout)).toMatchFileSnapshot(
+      '__snapshots__/report.txt',
+    );
+
+    const report = await readJsonFile(
+      path.join(unusedDependencyOutputDir, 'report.json'),
+    );
+
+    expect(() => reportSchema.parse(report)).not.toThrow();
+    expect(
+      JSON.stringify(report as Report, null, 2),
+    ).toMatchFileSnapshot('__snapshots__/report.json');
+  });
+});
diff --git a/e2e/plugin-knip-e2e/tsconfig.test.json b/e2e/plugin-knip-e2e/tsconfig.test.json
index cc6383c..34f35e3 100644
--- a/e2e/plugin-knip-e2e/tsconfig.test.json
+++ b/e2e/plugin-knip-e2e/tsconfig.test.json
@@ -2,8 +2,10 @@
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "../../dist/out-tsc",
-    "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"]
+    "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"],
+    "target": "ES2020"
   },
+  "exclude": ["__test-env__/**"],
   "include": [
     "vite.config.e2e.ts",
     "tests/**/*.e2e.test.ts",
diff --git a/e2e/plugin-knip-e2e/vite.config.e2e.ts b/e2e/plugin-knip-e2e/vite.config.e2e.ts
index 771687a..d87fd35 100644
--- a/e2e/plugin-knip-e2e/vite.config.e2e.ts
+++ b/e2e/plugin-knip-e2e/vite.config.e2e.ts
@@ -1,12 +1,14 @@
 /// <reference types="vitest" />
 import { defineConfig } from 'vite';
+import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js';
 
 export default defineConfig({
   cacheDir: '../../node_modules/.vite/plugin-knip-e2e',
   test: {
     reporters: ['basic'],
-    testTimeout: 60_000,
+    testTimeout: 120_000,
     globals: true,
+    alias: tsconfigPathAliases(),
     pool: 'threads',
     poolOptions: { threads: { singleThread: true } },
     cache: {
@@ -14,7 +16,6 @@ export default defineConfig({
     },
     environment: 'node',
     include: ['tests/**/*.e2e.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
-    globalSetup: ['../../global-setup.ts'],
     setupFiles: ['../../testing/test-setup/src/lib/reset.mocks.ts'],
   },
 });
diff --git a/nx.json b/nx.json
index 2fe0a29..ba7c28a 100644
--- a/nx.json
+++ b/nx.json
@@ -26,5 +26,15 @@
       "inputs": ["default", "^default"]
     }
   },
-  "plugins": []
+  "plugins": [
+    {
+      "plugin": "@push-based/nx-verdaccio",
+      "options": {
+        "environments": {
+          "environmentsDir": "tmp/environments",
+          "targetNames": ["e2e"]
+        }
+      }
+    }
+  ]
 }
diff --git a/package-lock.json b/package-lock.json
index 1cef6ad..cc1e3b9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,18 +12,20 @@
         "packages/*"
       ],
       "dependencies": {
-        "@code-pushup/cli": "^0.57.0",
         "@code-pushup/core": "^0.57.0",
         "@code-pushup/models": "^0.57.0",
         "@code-pushup/utils": "^0.57.0",
         "@nx/devkit": "19.4.3",
         "@poppinss/cliui": "^6.4.1",
+        "@push-based/nx-verdaccio": "^0.0.0-alpha.26",
         "ansis": "^3.8.1",
         "knip": "^5.42.0",
         "memfs": "^4.17.0",
         "zod": "^3.24.1"
       },
       "devDependencies": {
+        "@beaussan/nx-knip": "^0.0.5-15",
+        "@code-pushup/cli": "^0.57.0",
         "@nx/esbuild": "19.4.3",
         "@nx/eslint": "19.4.3",
         "@nx/eslint-plugin": "19.4.3",
@@ -1846,10 +1848,25 @@
       "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
       "dev": true
     },
+    "node_modules/@beaussan/nx-knip": {
+      "version": "0.0.5-15",
+      "resolved": "https://registry.npmjs.org/@beaussan/nx-knip/-/nx-knip-0.0.5-15.tgz",
+      "integrity": "sha512-YonAc6JmCjvr5sg+83NXQOzvBB1hnLcPcoMBTD8mnStnxX/65ZZ5SDdUuvDmpIYguOACpR7T3l1S9fbKhC1xNg==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.21",
+        "tslib": "^2.3.0"
+      },
+      "peerDependencies": {
+        "@nx/devkit": "18 || 17",
+        "knip": "5"
+      }
+    },
     "node_modules/@code-pushup/cli": {
       "version": "0.57.0",
       "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.57.0.tgz",
       "integrity": "sha512-qZqUqN2BIrON6grGrU5bheYZ306KH8bM5gGBTTVoWzWdyHQh4FnJWPAoYh5gGN7wTpUvYZloPR5dcYtJPVQucw==",
+      "dev": true,
       "dependencies": {
         "@code-pushup/core": "0.57.0",
         "@code-pushup/models": "0.57.0",
@@ -4929,6 +4946,44 @@
         "node": ">=18.16.0"
       }
     },
+    "node_modules/@push-based/nx-verdaccio": {
+      "version": "0.0.0-alpha.26",
+      "resolved": "https://registry.npmjs.org/@push-based/nx-verdaccio/-/nx-verdaccio-0.0.0-alpha.26.tgz",
+      "integrity": "sha512-Go11Dg+w5Ntl5Ig8YNzVVPbpOG85aVszjyBIK0FvVBX+/QllQY1F4fP8K8fYnMJnO9v5Tao3cryGFY5Zo9i+/g==",
+      "dependencies": {
+        "@nx/devkit": "19.8.0",
+        "ansis": "^3.3.2",
+        "simple-git": "^3.27.0",
+        "tslib": "^2.3.0"
+      }
+    },
+    "node_modules/@push-based/nx-verdaccio/node_modules/@nrwl/devkit": {
+      "version": "19.8.0",
+      "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.8.0.tgz",
+      "integrity": "sha512-LehpQ2D1687+JWaUpW84NPuXsQuPosmts66LShPT4+6KozB4gd0hJGAXNXpjNs9CUfLyNf8rRdEeqNjWnPYEmA==",
+      "dependencies": {
+        "@nx/devkit": "19.8.0"
+      }
+    },
+    "node_modules/@push-based/nx-verdaccio/node_modules/@nx/devkit": {
+      "version": "19.8.0",
+      "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.8.0.tgz",
+      "integrity": "sha512-nPaKHF0m2KONlt8GXjN9EhFo+NOvJnFcK6ujKFFLAyZ4TACY4F1FCjSHFTjYI82j+WukzuyjSmY9wzxYughWIQ==",
+      "dependencies": {
+        "@nrwl/devkit": "19.8.0",
+        "ejs": "^3.1.7",
+        "enquirer": "~2.3.6",
+        "ignore": "^5.0.4",
+        "minimatch": "9.0.3",
+        "semver": "^7.5.3",
+        "tmp": "~0.2.1",
+        "tslib": "^2.3.0",
+        "yargs-parser": "21.1.1"
+      },
+      "peerDependencies": {
+        "nx": ">= 17 <= 20"
+      }
+    },
     "node_modules/@rollup/plugin-babel": {
       "version": "6.0.4",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz",
diff --git a/package.json b/package.json
index 5d720af..0fbbe61 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,8 @@
   "scripts": {},
   "private": true,
   "devDependencies": {
+    "@beaussan/nx-knip": "^0.0.5-15",
+    "@code-pushup/cli": "^0.57.0",
     "@nx/esbuild": "19.4.3",
     "@nx/eslint": "19.4.3",
     "@nx/eslint-plugin": "19.4.3",
@@ -46,12 +48,12 @@
     "packages/*"
   ],
   "dependencies": {
-    "@code-pushup/cli": "^0.57.0",
     "@code-pushup/core": "^0.57.0",
     "@code-pushup/models": "^0.57.0",
     "@code-pushup/utils": "^0.57.0",
     "@nx/devkit": "19.4.3",
     "@poppinss/cliui": "^6.4.1",
+    "@push-based/nx-verdaccio": "^0.0.0-alpha.26",
     "ansis": "^3.8.1",
     "knip": "^5.42.0",
     "memfs": "^4.17.0",
diff --git a/tools/vitest-tsconfig-path-aliases.ts b/tools/vitest-tsconfig-path-aliases.ts
new file mode 100644
index 0000000..ac8be04
--- /dev/null
+++ b/tools/vitest-tsconfig-path-aliases.ts
@@ -0,0 +1,20 @@
+import { loadConfig } from 'tsconfig-paths';
+import type { Alias, AliasOptions } from 'vite';
+
+export function tsconfigPathAliases(): AliasOptions {
+  const result = loadConfig('tsconfig.base.json');
+  if (result.resultType === 'failed') {
+    throw new Error(
+      `Failed to load path aliases from tsconfig for Vitest: ${result.message}`,
+    );
+  }
+  return Object.entries(result.paths)
+    .map(([key, value]) => [key, value[0]])
+    .filter((pair): pair is [string, string] => pair[1] != null)
+    .map(
+      ([importPath, relativePath]): Alias => ({
+        find: importPath,
+        replacement: new URL(`../${relativePath}`, import.meta.url).pathname,
+      }),
+    );
+}
diff --git a/tsconfig.base.json b/tsconfig.base.json
index ac9158f..07ca263 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -19,8 +19,8 @@
       "@code-pushup/test-nx-utils": ["testing/test-nx-utils/src/index.ts"],
       "@code-pushup/test-setup": ["testing/test-setup/src/index.ts"],
       "@code-pushup/test-utils": ["testing/test-utils/src/index.ts"],
-      "knip-plugin": ["packages/plugin-knip/src/index.ts"],
-      "plugin-factory": ["tooling/plugin-factory/src/index.ts"]
+      "@code-pushup/plugin-knip": ["packages/plugin-knip/src/lib/index.ts"],
+      "@code-pushup/plugin-factory": ["tooling/plugin-factory/src/index.ts"]
     }
   },
   "exclude": ["node_modules", "tmp"]
diff --git a/vitest.workspace.ts b/vitest.workspace.ts
index 61c97a5..033b777 100644
--- a/vitest.workspace.ts
+++ b/vitest.workspace.ts
@@ -1,3 +1,3 @@
 import { defineWorkspace } from 'vitest/config';
 
-export default defineWorkspace(['packages/*', 'examples/*-e2e']);
+export default defineWorkspace(['packages/*', 'e2e/*-e2e']);

From 57257f6e2659e111d8adf2e9fa7a2c29356c99b0 Mon Sep 17 00:00:00 2001
From: Alejandro <alejandroramirezjimenez1999@gmail.com>
Date: Sat, 1 Feb 2025 01:19:22 +0100
Subject: [PATCH 3/4] fix: types

---
 .../mocks/fixtures/raw-knip.report.ts         | 37 +++++++++++--------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/packages/plugin-knip/mocks/fixtures/raw-knip.report.ts b/packages/plugin-knip/mocks/fixtures/raw-knip.report.ts
index 4235107..249ee3f 100644
--- a/packages/plugin-knip/mocks/fixtures/raw-knip.report.ts
+++ b/packages/plugin-knip/mocks/fixtures/raw-knip.report.ts
@@ -18,6 +18,7 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
       enumMembers: true,
       classMembers: true,
       duplicates: true,
+      _files: false
     },
     issues: {
       files: new Set([
@@ -30,6 +31,7 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
             filePath: '/Users/username/Projects/code-pushup-cli/package.json',
             symbol: 'cli-table3',
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -40,6 +42,7 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
             filePath: '/Users/username/Projects/code-pushup-cli/package.json',
             symbol: '@trivago/prettier-plugin-sort-imports',
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -50,6 +53,7 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
             filePath: '/Users/username/Projects/code-pushup-cli/package.json',
             symbol: 'ts-node',
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -58,16 +62,16 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
           'jsonc-eslint-parser': {
             type: 'unlisted',
             symbol: 'jsonc-eslint-parser',
-            filePath:
-              '/User/username/code-pushup-cli/packages/utils/package.json',
+            filePath: '/User/username/code-pushup-cli/packages/utils/package.json',
+            workspace: 'code-pushup-cli',
           },
         },
         'examples/plugins/.eslintrc.json': {
           'jsonc-eslint-parser': {
             type: 'unlisted',
             symbol: 'jsonc-eslint-parser',
-            filePath:
-              '/User/username/code-pushup-cli/packages/utils/package.json',
+            filePath: '/User/username/code-pushup-cli/packages/utils/package.json',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -78,10 +82,10 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
           'some-package': {
             type: 'unresolved',
             symbol: 'smo-package',
-            filePath:
-              '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/category-config.ts',
+            filePath: '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/category-config.ts',
             line: 8,
             col: 23,
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -89,13 +93,13 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
         'packages/models/src/lib/category-config.ts': {
           duplicateErrorMsg: {
             type: 'exports',
-            filePath:
-              '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/category-config.ts',
+            filePath: '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/category-config.ts',
             symbol: 'duplicateErrorMsg',
             symbolType: 'function' as SymbolType,
             line: 54,
             col: 17,
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -105,13 +109,13 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
         'packages/models/src/lib/group.ts': {
           GroupMeta: {
             type: 'types',
-            filePath:
-              '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/group.ts',
+            filePath: '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/group.ts',
             symbol: 'GroupMeta',
             symbolType: 'type' as SymbolType,
             line: 26,
             col: 13,
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -121,13 +125,13 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
         'packages/models/src/lib/group.ts': {
           MyEnum: {
             type: 'enumMembers',
-            filePath:
-              '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/group.ts',
+            filePath: '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/group.ts',
             symbol: 'unusedMember',
             symbolType: 'enum' as SymbolType,
             line: 26,
             col: 13,
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -135,13 +139,13 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
         'packages/models/src/lib/group.ts': {
           MyClass: {
             type: 'classMembers',
-            filePath:
-              '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/group.ts',
+            filePath: '/Users/username/Projects/code-pushup-cli/packages/models/src/lib/group.ts',
             symbol: 'unusedKey',
             symbolType: 'enum' as SymbolType,
             line: 40,
             col: 687,
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
@@ -149,8 +153,7 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
         'packages/nx-plugin/src/generators/configuration/generator.ts': {
           'configurationGenerator|default': {
             type: 'duplicates',
-            filePath:
-              '/Users/username/Projects/quality-metrics-cli/packages/nx-plugin/src/generators/configuration/generator.ts',
+            filePath: '/Users/username/Projects/quality-metrics-cli/packages/nx-plugin/src/generators/configuration/generator.ts',
             symbol: 'configurationGenerator|default',
             symbols: [
               {
@@ -165,9 +168,11 @@ export const rawReport: Pick<ReporterOptions, 'report' | 'issues' | 'options'> =
               },
             ],
             severity: 'error',
+            workspace: 'code-pushup-cli',
           },
         },
       },
+      _files: new Set(),
     },
     options: JSON.stringify({
       outputFile: 'knip-report.json',

From ba8da9c06bb880638d7b596cdd556ab908292317 Mon Sep 17 00:00:00 2001
From: Alejandro <alejandroramirezjimenez1999@gmail.com>
Date: Wed, 12 Feb 2025 19:11:26 +0100
Subject: [PATCH 4/4] chore: removes e2e of knip

---
 e2e/plugin-knip-e2e/.eslintrc.json            | 12 ----
 .../unused-dependency/code-pushup.config.ts   | 10 ---
 .../fixtures/unused-dependency/knip.config.ts | 69 -------------------
 .../fixtures/unused-dependency/package.json   |  6 --
 e2e/plugin-knip-e2e/project.json              | 23 -------
 e2e/plugin-knip-e2e/tests/collect.e2e.test.ts | 65 -----------------
 e2e/plugin-knip-e2e/tsconfig.json             | 20 ------
 e2e/plugin-knip-e2e/tsconfig.test.json        | 15 ----
 e2e/plugin-knip-e2e/vite.config.e2e.ts        | 21 ------
 9 files changed, 241 deletions(-)
 delete mode 100644 e2e/plugin-knip-e2e/.eslintrc.json
 delete mode 100644 e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts
 delete mode 100644 e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts
 delete mode 100644 e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json
 delete mode 100644 e2e/plugin-knip-e2e/project.json
 delete mode 100644 e2e/plugin-knip-e2e/tests/collect.e2e.test.ts
 delete mode 100644 e2e/plugin-knip-e2e/tsconfig.json
 delete mode 100644 e2e/plugin-knip-e2e/tsconfig.test.json
 delete mode 100644 e2e/plugin-knip-e2e/vite.config.e2e.ts

diff --git a/e2e/plugin-knip-e2e/.eslintrc.json b/e2e/plugin-knip-e2e/.eslintrc.json
deleted file mode 100644
index 93485ec..0000000
--- a/e2e/plugin-knip-e2e/.eslintrc.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "extends": ["../../.eslintrc.json"],
-  "ignorePatterns": ["!**/*", "code-pushup.config*.ts"],
-  "overrides": [
-    {
-      "files": ["*.ts", "*.tsx"],
-      "parserOptions": {
-        "project": ["e2e/plugin-knip-e2e/tsconfig.*?.json"]
-      }
-    }
-  ]
-}
diff --git a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts
deleted file mode 100644
index aa7552e..0000000
--- a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/code-pushup.config.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import path from 'node:path';
-import { knipPlugin } from '@code-pushup/plugin-knip';
-import type { CoreConfig } from '@code-pushup/models';
-export default {
-  plugins: [
-    knipPlugin({
-      workspace: path.join(__dirname, '..', '..', '..'),
-    }),
-  ]
-} satisfies CoreConfig;
diff --git a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts
deleted file mode 100644
index b791b52..0000000
--- a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/knip.config.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import {
-  KnipConfigPlugin,
-  combineNxKnipPlugins,
-  withEsbuildApps,
-  withEsbuildPublishableLibs,
-  withEslint,
-  withLibraryMapper,
-  withLocalNxPlugins,
-  withNxTsPaths,
-  withVitest,
-} from '@beaussan/nx-knip';
-
-
-const withNxStandards = (): KnipConfigPlugin => () => {
-  return {
-    project: ['**/*.{ts,js,tsx,jsx}'],
-    ignore: ['tmp/**', 'node_modules/**'],
-    commitlint: {
-      config: ['commitlint.config.js'],
-    },
-    entry: [
-      // unknown why this is needed, it should be picked up by knip from the vitest setup files
-      'testing/test-utils/src/index.ts',
-      'testing/test-utils/src/lib/fixtures/configs/*.ts',
-      'testing/test-setup/src/index.ts',
-      'testing/test-setup/src/lib/**/*.{js,mjs,ts,cjs,mts,cts}',
-      'global-setup.ts',
-      'global-setup.e2e.ts',
-      'examples/react-todos-app/code-pushup.config.js',
-      'examples/plugins/code-pushup.config.ts',
-      'testing/test-utils/src/lib/fixtures/configs/code-pushup.config.js',
-      'testing/test-utils/src/lib/fixtures/configs/code-pushup.empty.config.js',
-      'examples/plugins/src/package-json/src/index.ts',
-      // missing knip plugin for now, so this is in the root entry
-      'packages/models/zod2md.config.ts',
-      //'code-pushup.config.ts',
-      'esbuild.config.js',
-      'tools/**/*.{js,mjs,ts,cjs,mts,cts}',
-    ],
-    ignoreDependencies: [
-      'prettier',
-      '@swc/helpers',
-      '@swc/cli',
-      '@nx/plugin',
-      '@nx/workspace',
-      // Same issue as the other vitest related, it should be picked up by knip from the vitest setup files
-      // 'global-setup.ts',
-      // 'global-setup.e2e.ts',
-
-      // Knip should be able to pick up this
-      'tsx',
-      // Not a npm library, and resolved in a different typescript path than the global import one
-      '@example/custom-plugin',
-
-      // Prettier magic resolve is not picked up by knip
-      '@trivago/prettier-plugin-sort-imports',
-    ],
-  };
-};
-
-export default combineNxKnipPlugins(
-  withNxTsPaths(),
-  withLocalNxPlugins({ pluginNames: ['nx-plugin'] }),
-  withEsbuildApps(),
-  withEsbuildPublishableLibs(),
-  withVitest(),
-  withEslint(),
-  withNxStandards(),
-);
diff --git a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json b/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json
deleted file mode 100644
index 972d88b..0000000
--- a/e2e/plugin-knip-e2e/mocks/fixtures/unused-dependency/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "name": "any-package",
-  "dependencies": {
-    "zod": "^3.23.6"
-  }
-}
diff --git a/e2e/plugin-knip-e2e/project.json b/e2e/plugin-knip-e2e/project.json
deleted file mode 100644
index cbd0b81..0000000
--- a/e2e/plugin-knip-e2e/project.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "name": "plugin-knip-e2e",
-  "$schema": "../../node_modules/nx/schemas/project-schema.json",
-  "sourceRoot": "e2e/plugin-knip-e2e/src",
-  "projectType": "application",
-  "implicitDependencies": ["plugin-knip"],
-  "tags": ["scope:tooling", "type:e2e"],
-  "targets": {
-    "lint": {
-      "executor": "@nx/linter:eslint",
-      "outputs": ["{options.outputFile}"],
-      "options": {
-        "lintFilePatterns": ["e2e/plugin-knip-e2e/**/*.ts"]
-      }
-    },
-    "e2e": {
-      "executor": "@nx/vite:test",
-      "options": {
-        "config": "e2e/plugin-knip-e2e/vite.config.e2e.ts"
-      }
-    }
-  }
-}
diff --git a/e2e/plugin-knip-e2e/tests/collect.e2e.test.ts b/e2e/plugin-knip-e2e/tests/collect.e2e.test.ts
deleted file mode 100644
index 4e41d9e..0000000
--- a/e2e/plugin-knip-e2e/tests/collect.e2e.test.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { cp } from 'node:fs/promises';
-import path from 'node:path';
-import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
-import { type Report, reportSchema } from '@code-pushup/models';
-import { nxTargetProject } from '@code-pushup/test-nx-utils';
-import { teardownTestFolder } from '@code-pushup/test-setup';
-import {
-  E2E_ENVIRONMENTS_DIR,
-  TEST_OUTPUT_DIR,
-  removeColorCodes,
-} from '@code-pushup/test-utils';
-import { executeProcess, readJsonFile } from '@code-pushup/utils';
-
-describe('PLUGIN collect report with knip-plugin NPM package', () => {
-  const fixturesDir = path.join(
-    'e2e',
-    'plugin-knip-e2e',
-    'mocks',
-    'fixtures',
-  );
-  const fixtureUnusedDependency = path.join(fixturesDir, 'unused-dependency');
-
-  const envRoot = path.join(
-    E2E_ENVIRONMENTS_DIR,
-    nxTargetProject(),
-    TEST_OUTPUT_DIR,
-  );
-  const unusedDependencyDir = path.join(envRoot, 'unused-dependency');
-  const unusedDependencyOutputDir = path.join(unusedDependencyDir, '.code-pushup');
-
-  beforeAll(async () => {
-    await cp(fixtureUnusedDependency, unusedDependencyDir, { recursive: true });
-  });
-
-  afterAll(async () => {
-    await teardownTestFolder(unusedDependencyDir);
-  });
-
-  afterEach(async () => {
-    await teardownTestFolder(unusedDependencyOutputDir);
-  });
-
-  it('should run Knip plugin for Unused Dependency example dir and create report.json', async () => {
-    const { code, stdout } = await executeProcess({
-      command: 'npx',
-      args: ['@code-pushup/cli', 'collect', '--no-progress'],
-      cwd: unusedDependencyDir,
-    });
-
-    expect(code).toBe(0);
-
-    expect(removeColorCodes(stdout)).toMatchFileSnapshot(
-      '__snapshots__/report.txt',
-    );
-
-    const report = await readJsonFile(
-      path.join(unusedDependencyOutputDir, 'report.json'),
-    );
-
-    expect(() => reportSchema.parse(report)).not.toThrow();
-    expect(
-      JSON.stringify(report as Report, null, 2),
-    ).toMatchFileSnapshot('__snapshots__/report.json');
-  });
-});
diff --git a/e2e/plugin-knip-e2e/tsconfig.json b/e2e/plugin-knip-e2e/tsconfig.json
deleted file mode 100644
index f5a2f89..0000000
--- a/e2e/plugin-knip-e2e/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "extends": "../../tsconfig.base.json",
-  "compilerOptions": {
-    "module": "ESNext",
-    "forceConsistentCasingInFileNames": true,
-    "strict": true,
-    "noImplicitOverride": true,
-    "noPropertyAccessFromIndexSignature": true,
-    "noImplicitReturns": true,
-    "noFallthroughCasesInSwitch": true,
-    "types": ["vitest"]
-  },
-  "files": [],
-  "include": [],
-  "references": [
-    {
-      "path": "./tsconfig.test.json"
-    }
-  ]
-}
diff --git a/e2e/plugin-knip-e2e/tsconfig.test.json b/e2e/plugin-knip-e2e/tsconfig.test.json
deleted file mode 100644
index 34f35e3..0000000
--- a/e2e/plugin-knip-e2e/tsconfig.test.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "extends": "./tsconfig.json",
-  "compilerOptions": {
-    "outDir": "../../dist/out-tsc",
-    "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"],
-    "target": "ES2020"
-  },
-  "exclude": ["__test-env__/**"],
-  "include": [
-    "vite.config.e2e.ts",
-    "tests/**/*.e2e.test.ts",
-    "tests/**/*.d.ts",
-    "mocks/**/*.ts"
-  ]
-}
diff --git a/e2e/plugin-knip-e2e/vite.config.e2e.ts b/e2e/plugin-knip-e2e/vite.config.e2e.ts
deleted file mode 100644
index d87fd35..0000000
--- a/e2e/plugin-knip-e2e/vite.config.e2e.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/// <reference types="vitest" />
-import { defineConfig } from 'vite';
-import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js';
-
-export default defineConfig({
-  cacheDir: '../../node_modules/.vite/plugin-knip-e2e',
-  test: {
-    reporters: ['basic'],
-    testTimeout: 120_000,
-    globals: true,
-    alias: tsconfigPathAliases(),
-    pool: 'threads',
-    poolOptions: { threads: { singleThread: true } },
-    cache: {
-      dir: '../../node_modules/.vitest',
-    },
-    environment: 'node',
-    include: ['tests/**/*.e2e.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
-    setupFiles: ['../../testing/test-setup/src/lib/reset.mocks.ts'],
-  },
-});