-
-
Notifications
You must be signed in to change notification settings - Fork 224
Add support for resolveFromAST in plugins #1005
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
Conversation
commit: |
packages/knip/src/WorkspaceWorker.ts
Outdated
if (plugin.resolveEntryPaths) { | ||
if (!loadedConfig) loadedConfig = await loadConfigForPlugin(configFilePath, plugin, opts, pluginName); | ||
if (loadedConfig) { | ||
const inputs = await plugin.resolveEntryPaths(loadedConfig, opts); | ||
for (const input of inputs) addInput(input, configFilePath); | ||
} | ||
} | ||
|
||
if (plugin.resolveConfig) { | ||
if (!loadedConfig) loadedConfig = await loadConfigForPlugin(configFilePath, plugin, opts, pluginName); | ||
if (loadedConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing that both plugin.resolveEntryPaths
and plugin.resolveConfig
attempt to define loadedConfig
makes me think they are mutually exclusive properties (don't make sense when specified together in plugin
). Is that true? If so, maybe it could be helpful to model the type of plugin
to eliminate the invalid state where both can be passed on the same time. I'm just spitballing here really and could be completely off, also this could not matter at all :) I kind of lack the bigger picture. I'll comment on everything where I have some thought. Feel free to disregard any of my comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolveEntryPaths
and resolveConfig
can both be implemented as they server different purposes. They might get merged one day now that we have all the "inputs" (onEntry
etc), but that'd be for another day. So what I was trying to do here is "load if need, and skip if not".
|
||
// Unbound sourceFile.imports isn't available, maybe create helper | ||
for (const statement of sourceFile.statements) { | ||
if (ts.isImportDeclaration(statement)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we want to care about require
call expressions here?
import('foo')
seems to be a ImportKeyword
in TS AST as well. ts.isImportDeclaration
probably doesn't catch those
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is specific to SST, so probably not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well SST can be used with require
or with import
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One does not generally use CommonJS syntax in TS files (sst.config.ts
). Or is e.g. sst.config.cjs
also supported?
Would be easy to add, though :)
if ( | ||
ts.isCallExpression(node) && | ||
ts.isPropertyAccessExpression(node.expression) && | ||
node.expression.name.text === 'stack' | ||
) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's another dep which is not great but I wonder if you want something like https://github.com/phenomnomnominal/tsquery to make the AST branching logic a bit more terse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When building queries https://astexplorer.net/ and https://tsquery-playground.firebaseapp.com/
are very useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've actually looked at tsquery, but what we need is a few matchers, so at this point I'm thinking to create those as we go (indeed, without pulling in another dep).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for sharing those, I often use https://ts-ast-viewer.com
Deploying knip with
|
Latest commit: |
079263b
|
Status: | ✅ Deploy successful! |
Preview URL: | https://2ee6f1f4.knip.pages.dev |
Branch Preview URL: | https://feat-plugin-ast.knip.pages.dev |
d654708
to
6b79f5a
Compare
Confident to go in this direction, hence the merge to main. |
🚀 This pull request is included in v5.48.0. See Release 5.48.0 for release notes. Using Knip in a commercial project? Please consider becoming a sponsor. |
# Conflicts: # packages/knip/src/graph/build.ts # packages/knip/src/plugins/ava/index.ts # packages/knip/src/plugins/jest/index.ts # packages/knip/src/plugins/storybook/index.ts # packages/knip/src/util/plugin.ts
Add support for
resolveFromAST
in plugins so we can use the source file AST directly (i.e. without loading the file and receiving the resolvedlocalConfig
).Updated plugin guide: https://feat-plugin-ast.knip.pages.dev/guides/writing-a-plugin#example-4-use-the-ast-directly
New plugins:
@tanstack/react-router
plugin #856)Extended plugins:
Astro(new plugin: Starlight)For the existing
resolveEntryPaths
andresolveFromConfig
functions that plugins can implement, Knip loads the tool's configuration file and passes the resulting object to this plugin function. This works well for most cases, but has some drawbacks:An example
vite.config.ts
:We want to know the values of
routesDirectory
andgeneratedRouteTree
, but if weimport('vite.config.ts')
then this information is missing from the default-exported value that we pass on toresolveEntryPaths
andresolveFromConfig
.The
vite.config.ts
is already part of the module graph and it'simport
statements need to be parsed anyway (so we knowvite
and@tanstack/router-plugin/vite
are dependencies that should be listed inpackage.json
), so why not make this available to plugins as well?This way, plugins can create a custom function to extract the information, in this case the (new) TanStack Router plugin can return non-default entry paths (
./routes/**/*.{ts,tsx}
) and a non-default path forgeneratedRouteTree
so users don't need to configure suchentry
patterns manually inknip.json
. This was previously impossible!A downside is that the "DSL" here is the TS API, which is powerful but also tedious. Another downside is that the plugins can start using
typescript
dependency which has a large API surface and reduces their compactness and simplicity, which in turn requires more governance.Here's example code to do what we just described:
Also see the other
resolveFromAST.ts
files in this PR for more examples.There are no utilities for this type of querying yet, but I can imagine we'll often want to do things like "get the string values of certain literal object properties" so that could be a next step once we figure out our needs better.
Edit: added a few helpers already 😇 so also the example above can be implemented more concise now
Edit 2: also see #1009 that has the same plugins using tsquery