precinct review
Precinct 13.0.1 reads one source file, source string, or compatible AST and returns the dependency specifiers found by a format-specific detective. It covers AMD, CommonJS, ESM, TypeScript, TSX, CSS, Sass, SCSS, Less, Stylus, and Vue 2. `paperwork` adds synchronous file reading and extension inference. The result is still a flat string array, with no module resolution or recursive graph. Version 13.0.1 repairs the published TypeScript surface by adding `precinct.ast`, the named `paperwork` export, and the correct options type. Our browser build failed, which matches its direct use of Node filesystem and module APIs.
Precinct 13.0.1 installed 65 packages and used 41 MB in our sandbox, while its browser build failed and its output remained a flat `string[]`. Use that cost for multi-format Node analysis; a single-format scanner or graph tool is a better install when the input and desired output are narrower.
We installed it
| Install | ✓ · 7.6s | 65 packages on disk · 41 MB |
| Import | ✓ | ESM import works · require() works · ESM package with exports map |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does precinct install cleanly?
Yes. In a fresh container with an empty cache, npm install precinct finished in 8 seconds, leaving 65 packages and 41 MB on disk. npm audit reported no known vulnerabilities.
Can precinct run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does precinct work with both ESM and CommonJS?
Yes. Both import 'precinct' and require('precinct') worked in Node 22 in our run. The package is published as ESM with an exports map.
Does precinct include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
precinct or dependency-tree: which should you use?
dependency-tree: Choose it when an entry file should become a recursively resolved dependency tree. Precinct 13.0.1 installed 65 packages and used 41 MB in our sandbox, while its browser build failed and its output remained a flat string[].
When should you not use precinct?
You need a dependency graph with cycles and resolved filenames. Precinct scans one input and returns raw strings; Madge or dependency-tree owns more of that job.
Use it if
- A Node audit or CLI needs one call for dependency strings across scripts, stylesheets, TypeScript, and Vue 2 files.
- File crawling and specifier resolution already exist elsewhere, leaving only per-file import extraction to solve.
- You want to pass a prebuilt JavaScript AST or replace the parser through `walker.parser`.
- Mixed ESM and CommonJS imports or CSS `url()` references must be opt-in rather than collected automatically.
- You need a dependency graph with cycles and resolved filenames. Precinct scans one input and returns raw strings; Madge or dependency-tree owns more of that job.
- Your process runs below Node 20.19.0 or the 22 line below 22.12.0. Those exact minimums are declared by 13.0.1.
- The input is Vue 3 and accuracy must be established. The current package explicitly routes `vue` through `detective-vue2`.
- A browser must perform the scan. Our esbuild browser attempt failed, and the implementation imports `node:fs`, `node:module`, `node:path`, and `node:util`.
- You need import locations, binding names, type-only markers, or dynamic/static classification. The public return type is only `string[]`.
- Your project scans only one syntax. Installing 65 packages and 41 MB for every detective is wasteful when `detective-es6` or another focused parser covers the known format.
Setup reality
We installed precinct 13.0.1 in 7.6 seconds, leaving 65 packages and 41 MB on disk. Precinct itself is 40 KB unpacked and declares 15 direct dependencies with no peers. npm audit found zero known vulnerabilities. The package is ESM with an exports map and bundled declarations. Both require() and ESM import worked under our supported Node 22 runtime. Its engines require Node >=20.19.0 || >=22.12.0.
There are no credentials or config files. The weight comes from installing every detective, plus TypeScript, PostCSS, Commander, module detection, and the AST walker. precinct(source) assumes JavaScript when no type is given. A parse error is caught, precinct.ast becomes null, and the function returns an empty array. Unknown types also return an empty array. Treat an empty result as ambiguous unless parsing was independently known to succeed.
paperwork(filename) reads UTF-8 synchronously and handles one path. .ts, .tsx, .scss, and most extensions map directly to a detective; .js and .jsx are sniffed. Core-module filtering is available only there through includeCore: false. You must supply directory traversal, ignore rules, symlink policy, resolver behavior, and caching. The mutable precinct.ast property holds the most recent parse, so concurrent calls cannot safely use it as request-local state.
Mixed ESM and CommonJS extraction is disabled unless es6.mixedImports is true. CSS assets inside url() are disabled unless css.url is true. Non-JavaScript source should always receive an explicit type. Our browser bundle failed in esbuild, consistent with Node-only file access and built-in module filtering. For one known grammar, install its detective alone; the 65-package footprint only pays off when the shared dispatcher replaces several parsers.
Patterns
Extract imports from JavaScript text scan-javascript
import precinct from 'precinct'
const imports = precinct(`
import fastify from 'fastify'
import { readFile } from 'node:fs/promises'
`)
console.log(imports)With no `type`, 13.0.1 parses and sniffs JavaScript; a parse failure returns `[]` and sets `precinct.ast` to null.
Read one source path scan-file
import { paperwork } from 'precinct'
const imports = paperwork('src/worker.ts')`paperwork` uses synchronous UTF-8 reading and does not recurse beyond this 1 file.
Filter Node core specifiers drop-node-builtins
import { paperwork } from 'precinct'
const packages = paperwork('src/worker.js', {
includeCore: false,
})`includeCore` belongs to `paperwork` and defaults to true; it recognizes both `fs` and `node:fs` forms.
Bypass JavaScript module sniffing force-module-type
import precinct from 'precinct'
const imports = precinct(source, { type: 'esm' })Version 13.0.1 accepts `esm`, `es6`, and `mjs` as aliases for the ESM detective.
Read ESM and require calls together collect-mixed-modules
const imports = precinct(source, {
type: 'esm',
es6: { mixedImports: true },
})`mixedImports` is false by default and lives under `es6` even when `type` uses the `esm` alias.
Select the TypeScript detective scan-typescript
const tsImports = precinct(tsSource, { type: 'ts' })
const tsxImports = precinct(componentSource, { type: 'tsx' })The TSX branch calls the detective's separate `tsx` function; `paperwork` infers both types from filename extensions.
Extract Sass module references scan-stylesheet
const imports = precinct(`
@use 'tokens/colors';
@forward 'layout/grid';
`, { type: 'scss' })Pass `type: 'scss'` for source strings, or the default JavaScript parser will see invalid syntax and return an empty result.
Collect CSS asset URLs include-css-urls
const references = precinct(css, {
type: 'css',
css: { url: true },
})The PostCSS detective omits `url()` assets by default; setting `css.url` includes fonts and images alongside imports.
Ignore lazy AMD dependencies skip-lazy-amd
const eager = precinct(amdSource, {
type: 'amd',
amd: { skipLazyLoaded: true },
})This switch affects nested AMD `require` calls and does not control CommonJS detection.
Analyze an AST you already parsed reuse-ast
const ast = parser.parse(source, { sourceType: 'module' })
const imports = precinct(ast, { type: 'esm' })The AST shape must suit `node-source-walk` and the chosen detective; supplying it avoids Precinct's parse step.
Provide a parser through the walker replace-parser
const imports = precinct(source, {
walker: {
parser: {
parse(text) {
return customParser.parse(text, { sourceType: 'unambiguous' })
},
},
},
})The replacement needs a `.parse(source, options)` method and only affects the automatic JavaScript path.
Inspect a file from the shell run-cli
npx precinct --type ts src/index.ts
npx precinct --es6-mixed-imports src/legacy.jsThe 13.0.1 CLI accepts one filename; directory crawling and graph construction remain separate work.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| dependency-tree | npm | Choose it when an entry file should become a recursively resolved dependency tree. |
| madge | npm | Choose it for graph output, circular dependency checks, and project-level CLI reports. |
| detective | npm | Choose the focused CommonJS dependency extractor when other source formats never enter the pipeline. |
| dependency-cruiser | npm | Choose it when dependency rules, forbidden relationships, and architecture reports are part of the requirement. |
More cli & tooling guides
chalk · commander · typescript · esbuild · yargs · click · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

