knip icon indicating copy to clipboard operation
knip copied to clipboard

💡 Respect `pageExtensions` option in Next.js config

Open kachkaev opened this issue 10 months ago • 5 comments

Suggest an idea for Knip

👋 @webpro! We use knip in a Next.js project and I’ve noticed one false-negative which is to do with how our app is setup. When pageExtensions option is set in next.config.ts, standard globs from the plugin are no longer valid:

https://github.com/webpro-nl/knip/blob/c3664373828751945ebad38a617135df6047dced/packages/knip/src/plugins/next/index.ts#L14-L23

We’ve got this in next.config.ts:

pageExtensions: ["page.tsx", "handler.ts"],

With this setting, the following files would be entry points:

pages/index.page.tsx
pages/about.page.tsx
pages/product/[slug].page.tsx
pages/ping.handler.ts

However, these files would not be entry points and should be reported, if unused:

pages/shared/logo.tsx
pages/about.page/map.tsx
pages/product/[slug].page/interactive-3d-model.tsx
pages/ping.handler/helpers.ts

At the moment, knip is unaware of pageExtensions so marks all files inside the pages folder as used.

I understand that pageExtensions may be a tricky thing to support, because essentially knip needs to run next.config.ts to get the right value. Perhaps, at least we could disable standard globs is pageExtensions is found anywhere in file. In the meantime, I’ll see if I can manually turn off the Next.js plugin to define custom globs. Open to other ideas too!

kachkaev avatar Feb 25 '25 17:02 kachkaev

Knip could run next.config.ts (it does this for many plugins).

Feel free to take a stab at it, there are a few plugins that do something similar already:

  • https://github.com/webpro-nl/knip/blob/main/packages/knip/src/plugins/nuxt/index.ts
  • https://github.com/webpro-nl/knip/blob/main/packages/knip/src/plugins/eleventy/index.ts

So the idea is to implement resolveEntryPaths and return toProductionEntry inputs.

I'm not sure about potential issues with loading next.config.ts but in general this doesn't give issues (apart from users often not having environment variables set).

webpro avatar Feb 25 '25 18:02 webpro

I see, interesting! I don’t think I’ll have capacity to submit a PR in the next couple of weeks, so it’s up for grabs. In the meantime, I’ve added a local pnpm patch (patches/knip.patch) and it seems to be working!

diff --git a/dist/plugins/next/index.js b/dist/plugins/next/index.js
index 268b0d10de39192d2dc4ef08fc09c4d668edd45d..60a1a569432bcc11ee4f0bf31bcc616c900bba4f 100644
--- a/dist/plugins/next/index.js
+++ b/dist/plugins/next/index.js
@@ -11,7 +11,10 @@ const productionEntryFilePatternsWithoutSrc = [
     'app/{manifest,sitemap,robots}.{js,ts}',
     'app/**/{icon,apple-icon}.{js,jsx,ts,tsx}',
     'app/**/{opengraph,twitter}-image.{js,jsx,ts,tsx}',
-    'pages/**/*.{js,jsx,ts,tsx}',
+    // https://github.com/webpro-nl/knip/issues/960
+    "instrumentation.handler.ts",
+    "pages/**/*.handler.ts",
+    "pages/**/*.page.tsx",
 ];
 const production = [
     ...productionEntryFilePatternsWithoutSrc,

kachkaev avatar Feb 25 '25 23:02 kachkaev

Why not override next.entry in your Knip config instead of patching the package, if I may ask?

webpro avatar Feb 26 '25 17:02 webpro

Oh I did not know about this option! Indeed, setting this in knip.config.ts for a workspace works like a charm!

      next: {
        entry: [
          // https://github.com/webpro-nl/knip/issues/960
          "src/instrumentation.handler.ts",
          "src/pages/**/*.handler.ts",
          "src/pages/**/*.page.tsx",
        ],

I believe that the issue is still relevant because automatic pageExtensions would save users from a footgun. But the workaround is much neater than I initially thought, thank you!

kachkaev avatar Feb 27 '25 09:02 kachkaev

Great! And indeed the issue is still relevant, but it would require some work and testing as it requires to actually load (not just statically parse) next.config.ts, which might come with its own set of issues (I mean, you know about eslint.config.js...)

webpro avatar Feb 28 '25 04:02 webpro

This is resolved by merging #1005.

webpro avatar Apr 08 '25 07:04 webpro

Just found a remaining edge case: "instrumentation.handler.ts" is not being picked up as an entry point:

Unused files (1)
apps/frontend/src/instrumentation.handler.ts  

Relevant source:

https://github.com/webpro-nl/knip/blob/b1ee77635954df6bd5328b65ad456c9ea1d95906/packages/knip/src/plugins/next/index.ts#L18-L34

I believe that '{instrumentation,instrumentation-client,middleware,proxy}.{js,ts}' should be returned from getEntryFilePatterns, WDYT?

kachkaev avatar Nov 03 '25 14:11 kachkaev