Script Parser
Knip parses shell commands and scripts to find additional dependencies, entry files and configuration files in various places:
- In
package.json - In CLI arguments
- In scripts
- In source code
Shell scripts can be read and statically analyzed, but they’re not executed.
package.json
Section titled “package.json”The main, bin, exports and scripts fields may contain entry files. Let’s
take a look at this example:
{ "name": "my-package", "main": "index.js", "exports": { "./lib": { "import": "./dist/index.mjs", "require": "./dist/index.cjs" } }, "bin": { "program": "bin/cli.js" }, "scripts": { "build": "rollup src/entry.ts", "start": "node --loader tsx server.ts" }}From this example, Knip automatically adds the following files as entry files:
index.js./dist/index.mjs./dist/index.cjsbin/cli.jssrc/entry.tsserver.ts
Excluded files
Section titled “Excluded files”Knip would not add the exports if the dist folder is matching a pattern in a
relevant .gitignore file or ignore option.
Knip does not add scripts without a standard extension. For instance, the
bin/tool file might be a valid executable for Node.js, but wouldn’t be added
or parsed by Knip.
CLI Arguments
Section titled “CLI Arguments”When parsing the scripts of package.json and other files, Knip detects
various types of inputs. Some examples:
- The first positional argument is usually an entry file
- Configuration files are often in the
-cor--configargument - The
--require,--loaderor--importarguments are often dependencies
{ "name": "my-lib", "scripts": { "start": "node --import tsx/esm run.ts", "bundle": "tsup -c tsup.lib.config.ts", "type-check": "tsc -p tsconfig.app.json" }}The "start" script will have tsx marked as a referenced dependency, and adds
run.ts as an entry file.
Additionally, the following files are detected as configuration files:
tsup.lib.config.ts- to be handled by the tsup plugintsconfig.app.json- to be handled by the TypeScript plugin
Such executables and their arguments are all defined in plugins separately for fine-grained results.
Scripts
Section titled “Scripts”Plugins may also use the script parser to extract entry files and dependencies from commands. A few examples:
- GitHub Actions: workflow files may contain
runcommands (e.g..github/workflows/ci.yml) - Husky & Lefthook: Git hooks such as
.git/hooks/pre-pushcontain scripts; alsolefthook.ymlhasruncommands - Lint Staged: configuration values are all commands
- Nx: task executors and
nx:run-commandsexecutors inproject.jsoncontains scripts - Release It:
hookscontain commands
Plugins can also return configuration files. Some examples:
- The Angular plugin detects
options.tsConfigas a TypeScript config file - The GitHub Actions plugin parses
runcommands which may contain configuration file paths
Source Code
Section titled “Source Code”When Knip is walking the abstract syntax trees (ASTs) of JavaScript and TypeScript source code files, it looks for imports and exports. But there’s a few more (rather obscure) things that Knip detects in the process. Below are examples of additional scripts Knip parses to find entry files and dependencies.
If the bun dependency is imported in source code, Knip considers the contents
of $ template tags to be scripts:
import { $ } from 'bun';await $`bun boxen I ❤ unicorns`;await $`boxen I ❤ unicorns`;Parsing the script results in the boxen binary (the boxen-cli dependency) as
referenced (twice).
If the execa dependency is imported in source code, Knip considers the
contents of $ template tags to be scripts:
await $({ stdio: 'inherit' })`c8 node hydrate.js`;Parsing the script results in hydrate.js added as an entry file and the c8
binary/dependency as referenced.
If the zx dependency is imported in source code, Knip considers the contents
of $ template tags to be scripts:
await $`node scripts/parse.js`;This will add scripts/parse.js as an entry file.
ISC License © 2024 Lars Kappert