Skip to content

feat: add Docusaurus plugin #1055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
May 10, 2025
Merged

Conversation

cylewaitforit
Copy link
Contributor

@cylewaitforit cylewaitforit commented Apr 26, 2025

This adds a plugin for Docusaurus.

Issue: #1056

Support for

  • Plugins, Themes, and Presets
    • Module Shorthand
  • Default entry for homepage, docs, blog
  • Sidebar files
  • Special scope import aliases

Copy link

pkg-pr-new bot commented Apr 26, 2025

Open in StackBlitz

npm i https://pkg.pr.new/knip@1055

commit: 296bca4

@cylewaitforit cylewaitforit marked this pull request as ready for review April 26, 2025 21:41
Copy link
Member

@webpro webpro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! It's a great start, but I do have a remark.

@cylewaitforit cylewaitforit marked this pull request as draft April 28, 2025 11:48
@cylewaitforit cylewaitforit marked this pull request as ready for review April 29, 2025 00:03
@cylewaitforit cylewaitforit requested a review from webpro April 29, 2025 00:09
@cylewaitforit cylewaitforit marked this pull request as draft April 29, 2025 11:39
@cylewaitforit cylewaitforit marked this pull request as ready for review April 29, 2025 14:15
@webpro
Copy link
Member

webpro commented Apr 30, 2025

LGTM! Just to be 100% sure, I think this one's finished, right?

@cylewaitforit
Copy link
Contributor Author

cylewaitforit commented May 1, 2025

LGTM! Just to be 100% sure, I think this one's finished, right?

@webpro Good question, I used create-docusaurus@latest to spin up a repo when working on the default entry files to make sure the fixtures added would hit what most docusaurus sites would have.

Pulling on that thread lead to a few other pieces that I'm looking into.

Running knip on the repo with the default entry changes currently returns

Unused files (2)
sidebars.js                               
src/components/HomepageFeatures/index.js
Unused devDependencies (1)
@docusaurus/module-type-aliases  package.json:26:6
Unlisted dependencies (5)
@docusaurus/Link                       src/pages/index.js
@docusaurus/useDocusaurusContext       src/pages/index.js
@theme/Layout                          src/pages/index.js
@site/src/components/HomepageFeatures  src/pages/index.js
@theme/Heading                         src/pages/index.js

From what I can find @docusaurus/module-type-aliases is actually unused. ✂️🎉

The sidebars.js is in the config for the '@docusaurus/plugin-content-docs' as the sidebarPath.

  • Could just set a default for ./sidebars.{js,ts} in the entries (I think this is probably not prefered as anyone using a different path would need to override the knip config)
  • Could get the sidebarPath when resolving the config and add it. (This is the path I think I'll go down.)

As shown in the unlisted dependencies, docusaurus is using certain scopes( @docusaurus, @theme, and @site) for some things that aren't packages. Finding these in their docs hasn't been the most straightforward so it may require me spelunking the code more.

  • @docusaurus appears to be used for some components in @docusuarus/core. https://docusaurus.io/docs/docusaurus-core#link
    • Can't just blanket show anything with the @docusaurus scope as used because that would lead to other packages in the @docusaurus scope as used when they might be unused. Maybe to start this again is matching against a set of what comes from @docusaurus/core.
  • @site from the outset seems like it is used for site components that are defined in the project. So it might be the most straightforward of the three.
    • Figuring out this one would resolve the other unused file src/components/HomepageFeatures/index.js
  • @theme appears to be used for components in the default theme but I think could also be overridden by customized project components if they are "swizzled". https://docusaurus.io/docs/swizzling

I'll move the PR back to draft and add a checklist to the top description while working through these. Certainly let me know if not delivering on all of those doesn't seem like the deal breaker I currently think it is.

@cylewaitforit cylewaitforit marked this pull request as draft May 1, 2025 13:11
@webpro
Copy link
Member

webpro commented May 2, 2025

Thanks a bunch for running this against the docusaurus starter/app.

The custom scoped package specifiers seem indeed blocking, otherwise we'd get the false positives you've listed which are kinda annoying to have to add to ignoreDependencies (even though users can do ignoreDependencies: [/@(docusaurus|site|theme)\/.*/])

There's no generic way to handle this in Knip, other than adding those to the global IGNORED_DEPENDENCIES, which would be inaccurate because, as you mention, some are regular dependencies.

An idea I had was to go down the AST route (a bit like the SST plugin is doing). But realized we'd have to deal with such imports in MDX files well, which gets ugly quickly.

So at this point I'm thinking a reasonable way to do this is to (1) figure out the internal/custom/swizzled "dependencies" we need to ignore/skip (which is already cumbersome enough it seems) and then (2) have a way for the plugin to tell Knip/core about this list (which I could add).

Maybe (2) could be done in core by adding a new property to existing inputs/functions like toDeferResolve and toDependency or a new input such as toIgnoreDependency, that later down the road can be handed over to the DependencyDeputy to be ignored (essentially the same result as users manually doing ignoreDependencies).

But before I start implementing (2) we should be confident we can make (1) happen. So... WDYT? :)

@cylewaitforit
Copy link
Contributor Author

@webpro I think that would work for the @docusaurus alias and likely for @theme(and related @theme-internal and @theme-init that I found today). Those default component lists should be fairly static and adding them to the Ignored Dependencies initially seems right.

I could be wrong, but it feels like @site would need to be handled differently since it is an alias for the website's directory. . Knip needs to know that '@site/src/components/HomepageFeatures' is the same as 'src/components/HomepageFeatures/index.js' for the latter to not be reported as an unused file.

@webpro
Copy link
Member

webpro commented May 2, 2025

@webpro I think that would work for the @docusaurus alias and likely for @theme(and related @theme-internal and @theme-init that I found today). Those default component lists should be fairly static and adding them to the Ignored Dependencies initially seems right.

Alright, that's great.

I could be wrong, but it feels like @site would need to be handled differently since it is an alias for the website's directory. . Knip needs to know that '@site/src/components/HomepageFeatures' is the same as 'src/components/HomepageFeatures/index.js' for the latter to not be reported as an unused file.

Sounds like something that belongs locally in tsconfig.json#compilerOptions.paths.

If it isn't we could consider and try supporting that from plugins as well. That might help solve other cases as well:

Maybe something like a new input type so we can do e.g. toCompilerOptionsPath() (just thinking out loud here).

@cylewaitforit
Copy link
Contributor Author

Looks like @docusaurus/module-type-aliases may not be unused and could actually hold the key to some of this https://github.com/facebook/docusaurus/blob/fadb6d2607a73d597875541f4abcc05d9cac265a/packages/docusaurus-module-type-aliases/src/index.d.ts#L170.

@webpro
Copy link
Member

webpro commented May 3, 2025

Not sure if that may in turn require the --include-libs flag on user's end (https://knip.dev/guides/handling-issues#external-libraries)

@webpro webpro force-pushed the main branch 5 times, most recently from 83dfdce to 61d09ab Compare May 6, 2025 10:54
@webpro
Copy link
Member

webpro commented May 6, 2025

Took the liberty to add toAlias and return production entries, one step close to our goal :)

Tried a few things, but still can't think of anything better than adding the option to return toIgnore() patterns from the plugin.

@cylewaitforit
Copy link
Contributor Author

Took the liberty to add toAlias and return production entries, one step close to our goal :)

That's great! The site scope changes seem to be working solid.

I had seen the production entry change in the knip changelogs and figured that was why the tests in this branch started throwing but I hadn't had a chance to dig. That looks much simpler to understand for plugins going forward. I'll take a look at adding back the small ts,jsx,tsx to production entry change now that things are green again.

@webpro
Copy link
Member

webpro commented May 7, 2025

Took the liberty to add toAlias and return production entries, one step close to our goal :)

That's great! The site scope changes seem to be working solid.

Awesome.

I had seen the production entry change in the knip changelogs and figured that was why the tests in this branch started throwing but I hadn't had a chance to dig. That looks much simpler to understand for plugins going forward.

Added a purple box about this: https://knip.dev/guides/writing-a-plugin#7-entry-and-production so hopefully at least it will be a bit more clear to the next developer.

I'll take a look at adding back the small ts,jsx,tsx to production entry change now that things are green again.

Thanks, we will get there!

@webpro webpro force-pushed the main branch 2 times, most recently from ea72d80 to 7787123 Compare May 8, 2025 05:47
@webpro webpro force-pushed the docusaurus-plugin branch from 69c1d88 to 76a2288 Compare May 8, 2025 07:08
@webpro
Copy link
Member

webpro commented May 8, 2025

I've added the toIgnore input type and using this in the plugin seems to work well.

Rebased this PR on top of that (which in turn was (re)based on an updated main already).

The version of knip that features toIgnore can also be installed separately using e.g.:

https://pkg.pr.new/knip@fd6ec64

There's currently one caveat to toIgnore though: if we ignore e.g. @theme/(Heading|Layout) and nothing of the unlisted dependencies matches with that (i.e. the user does not use any of those) then a configuration hint about the unused option will appear.

Happy to hear your thoughts about it. Feel free to hack on this further 😇

@cylewaitforit
Copy link
Contributor Author

cylewaitforit commented May 8, 2025

There's currently one caveat to toIgnore though: if we ignore e.g. @theme/(Heading|Layout) and nothing of the unlisted dependencies matches with that (i.e. the user does not use any of those) then a configuration hint about the unused option will appear.

Does it make a difference to the configuration hint if the toIgnore were to be paired with a toAlias?

e.g.

toAlias('@theme/Heading', '@docusaurus/theme-classic/lib/theme/Heading'),
toIgnore('@theme/Heading', 'dependencies'),

@cylewaitforit
Copy link
Contributor Author

cylewaitforit commented May 8, 2025

Or maybe the @theme toIgnore should only be enabled if @docusaurus/theme-classic(or @docusaurus/preset-classic) was an enabler. That kind of feels like it starts getting into the Astro/Starlight two plugins realm which I don't know if it feels right for this.

@webpro
Copy link
Member

webpro commented May 8, 2025

At first sight, feels better to keep logic like this within the plugin.

In resolveConfig, we do have access to options.manifest (the package.json object), so we could e.g. check if something is listed in dependencies or devDependencies (or if there's something specific in scripts, etc).

@cylewaitforit
Copy link
Contributor Author

@webpro I think this plugin might be ready to see sunlight. Besides the toIgnore changes merging into main I can't think of any blockers.

@cylewaitforit cylewaitforit marked this pull request as ready for review May 8, 2025 14:13
@cylewaitforit cylewaitforit requested a review from webpro May 8, 2025 14:13
Copy link
Member

@webpro webpro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one nit, but overall this is pretty great. Glad we ended up with something that has improvements to core and feels manageable even though the underlying tool is heavy on virtual modules (a concept that might require a better solution but we can keep exploring).

...(hasClassicTheme ? [toIgnore('(@theme|@theme-init|@theme-original)/*', 'dependencies')] : []),
// Ignore aliases for @docusaurus/core/lib/client/exports/ https://docusaurus.io/docs/docusaurus-core
toIgnore(
'@docusaurus/(BrowserOnly|ComponentCreator|constants|ExecutionEnvironment|Head|Interpolate|isInternalUrl|Link|Noop|renderRoutes|router|Translate|useBaseUrl|useBrokenLinks|useDocusaurusContext|useGlobalData|useIsBrowser|useIsomorphicLayoutEffect|useRouteContext)',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could create an array somewhere inside this resolveConfig scope to make this a but more readable/maintainable, such as ["BrowserOnly", ...].join('|')) so that it won't be such a long line.

Or maybe based on an array similar to FIRST_PARTY_MODULES

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit to add it here: 2e48a79

@webpro
Copy link
Member

webpro commented May 9, 2025

Ran the plugin against two repos:

This turned out to require a few adaptions, and support for configureWebpack turned out to require a refactoring as findWebpackDependenciesFromConfig is an async function.

Hope you don't mind! WDYT, does it still work well on your own project(s)?

@cylewaitforit
Copy link
Contributor Author

Hope you don't mind! WDYT, does it still work well on your own project(s)?

Don't mind at all! Glad it will work in even more uses. It still works just as well with the projects I've tested it on.

@webpro webpro merged commit ccb9a5f into webpro-nl:main May 10, 2025
29 checks passed
@webpro
Copy link
Member

webpro commented May 10, 2025

Thank you so much, @cylewaitforit! Great job 🚀

@webpro
Copy link
Member

webpro commented May 14, 2025

🚀 This pull request is included in v5.56.0. See Release 5.56.0 for release notes.

Using Knip in a commercial project? Please consider becoming a sponsor.

clickCA pushed a commit to clickCA/knip that referenced this pull request May 24, 2025
@cylewaitforit cylewaitforit deleted the docusaurus-plugin branch July 2, 2025 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants