precinct
precinct is a Node source-analysis dispatcher that extracts dependency specifiers from one file or syntax tree. It detects a JavaScript module style and hands the input to the matching detective, or you can force parsers for ESM, CommonJS, AMD, TypeScript, TSX, CSS, Sass, SCSS, Less, Stylus, and Vue. The main function analyzes supplied content; paperwork reads one path synchronously and infers its type from the extension. Results are plain strings, not resolved files or a dependency graph.
precinct is a practical format switchboard for Node tooling, and version 13 is actively maintained. Do not mistake its string array for resolution or graph analysis, and avoid paying for all detectives when your inputs are only ESM.
Use it if
- You need one API to scan dependency strings across JavaScript, TypeScript, Vue, CSS, and several preprocessors
- You are building a Node CLI, audit, codemod prepass, or graph tool and already handle file discovery and module resolution elsewhere
- You want to pass an existing JavaScript AST or swap in a parser through node-source-walk options
- You need simple per-file dependency arrays and can force a detective when automatic JavaScript detection is ambiguous
- You need browser-side analysis or a lean install: version 13 imports Node fs, module, path, and util APIs and has 15 runtime dependencies, including TypeScript, PostCSS, Commander, and many detectives
- Your runtime is older than Node 20.19 or Node 22.12: those are the exact minimum branches in the current package engines field
- You need resolved filenames, package exports handling, tsconfig path aliases, recursion, cycles, or a complete graph: the documented return value is only an array of dependency strings from one input
- You need rich metadata such as import kind, local binding, source location, or type-only status: the public API flattens each detective's findings to strings
- You analyze Vue 3 single-file components and require explicit support: version 13 still routes vue inputs to detective-vue2, as its dependency list and supported-types table state
Setup reality
Version 13.0.1 is Node-only and requires Node >=20.19.0 or >=22.12.0. The package is an ES module with a default export and a named paperwork export. On supported recent Node versions, the README also shows CommonJS loading as const {default: precinct} = require('precinct'); older CommonJS runtimes are outside the engine range. TypeScript declarations ship with the package. Installation is heavier than the tiny public API suggests because all format detectives are installed up front: AMD, CommonJS, ESM, TypeScript and TSX, PostCSS, Sass, SCSS, Less, Stylus, Vue 2, module-definition, node-source-walk, plus TypeScript and Commander. The main precinct(content) path parses JavaScript once with node-source-walk, sniffs the module format, and returns [] if parsing fails; the side-channel precinct.ast becomes null in that case. For non-JavaScript text, pass type explicitly because it must not go through the JavaScript parser. paperwork(filename) uses synchronous readFileSync, infers most types from the extension, and sniffs .js and .jsx. It scans one file only. You still need globs or a crawler, and a resolver if raw strings must become files. Mixed ESM and CommonJS collection is off by default, so set es6.mixedImports when both forms matter. CSS url() references are also off by default. includeCore applies only to paperwork and defaults to true. The precinct.ast property is overwritten by every call, which makes it useful for debugging but unsafe as per-call state in concurrent work; use the AST you supplied or parse separately when identity matters. Unknown types and unsupported syntax return an empty list rather than a detailed diagnostic. A custom parser can be passed under walker.parser for JavaScript, but per-detective options live under the matching type key. Finally, Vue support is routed through detective-vue2, so validate modern SFCs before treating an empty or partial result as authoritative.
Patterns
Extract dependencies from JavaScript sourcescan-javascript-source
import precinct from 'precinct'
const dependencies = precinct(`
import express from 'express'
import {readFile} from 'node:fs/promises'
`)
console.log(dependencies)Without type, precinct parses and sniffs JavaScript. A parse failure returns an empty array and sets precinct.ast to null.
Read and scan one filescan-file-path
import {paperwork} from 'precinct'
const dependencies = paperwork('src/server.ts')
console.log(dependencies)paperwork uses readFileSync and analyzes one file. It does not recurse or resolve returned specifiers.
Exclude Node core modulesexclude-node-builtins
import {paperwork} from 'precinct'
const externalOnly = paperwork('src/server.js', {includeCore: false})includeCore is a paperwork option and defaults to true. Both fs-style and node:fs-style built-ins are filtered.
Force ESM detectionforce-esm-parser
import precinct from 'precinct'
const dependencies = precinct(source, {type: 'esm'})Accepted aliases include es6, esm, and mjs. Forcing a type avoids module-definition sniffing when the input is known.
Collect ESM and CommonJS in one filescan-mixed-modules
const dependencies = precinct(source, {
type: 'esm',
es6: {mixedImports: true}
})mixedImports defaults to false. The option lives under es6 even when the forced type uses the esm alias.
Scan TypeScript or TSXscan-typescript
const tsDependencies = precinct(tsSource, {type: 'ts'})
const tsxDependencies = precinct(tsxSource, {type: 'tsx'})paperwork infers these from .ts and .tsx. TypeScript is installed as a runtime dependency in precinct 13.
Extract SCSS dependenciesscan-scss
const dependencies = precinct(`
@use 'theme/colors';
@import 'components/button';
`, {type: 'scss'})Pass the type for non-JavaScript content so precinct does not send it through the JavaScript parser.
Include CSS url referencesinclude-css-assets
const dependencies = precinct(cssSource, {
type: 'css',
css: {url: true}
})url() references such as fonts and images are excluded by default. @import dependencies are still handled by the CSS detective.
Ignore lazy AMD require callsscan-amd-eager-only
const dependencies = precinct(amdSource, {
type: 'amd',
amd: {skipLazyLoaded: true}
})The option only affects inner lazy require calls in AMD. It does not change CommonJS require detection.
Analyze an existing JavaScript ASTscan-existing-ast
import precinct from 'precinct'
const ast = parser.parse(source, {sourceType: 'module'})
const dependencies = precinct(ast, {type: 'esm'})The AST must match what the selected detective and node-source-walk expect. Passing it avoids reparsing source.
Use a custom JavaScript parsersupply-custom-parser
import * as babelParser from '@babel/parser'
import precinct from 'precinct'
const dependencies = precinct(source, {
walker: {
parser: {
parse(code, options) {
return babelParser.parse(code, {...options, sourceType: 'unambiguous'})
}
}
}
})The custom parser must expose parse(source, options). This walker option applies to the automatic JavaScript parse path, not stylesheet detectives.
Scan a file from the command linerun-precinct-cli
npx precinct --type ts src/index.ts
# Mixed ESM and CommonJS
npx precinct --es6-mixed-imports src/index.jsThe CLI prints dependencies for one filename. It does not crawl directories or resolve a project graph.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| madge | npm | You want a ready-made dependency graph, circular-dependency reporting, and visual output rather than a one-file scanner |
| dependency-tree | npm | You want recursive dependency resolution from an entry file and a nested graph object |
| es-module-lexer | npm | You only analyze JavaScript ESM and want a focused lexer with import ranges instead of every stylesheet detective |
| @babel/parser | npm | You need a complete JavaScript or TypeScript AST and will write dependency extraction for the exact syntax you support |