detective-vue2 review
detective-vue2 3.0.1 takes the source text of one Vue single-file component and lists import specifiers found in its script blocks and in Sass, SCSS, Less, or Stylus styles. It parses the container with @vue/compiler-sfc, then hands each block to a language-specific detective. The output is a flat array such as ['vue', './UserCard.vue', './tokens.scss']; it does no path resolution or recursive graph building. The name is historical because the README covers Vue 2 and Vue 3 syntax. Release 3.0.1 fixed calls that omit the options object, while the 3.x line also requires modern Node and ships as ESM.
detective-vue2 3.0.1 took 5.3 seconds and 39 MB across 49 installed packages in our sandbox, then failed a browser-targeted bundle, so it fits Node-based SFC analysis inside a larger graph pipeline. Install it only if script and preprocessor imports cover your dependency model; templates, plain CSS, external blocks, and path resolution remain your work.
We installed it
| Install | ✓ · 5.3s | 49 packages on disk · 39 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 | — | no TypeScript types found |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does detective-vue2 install cleanly?
Yes. In a fresh container with an empty cache, npm install detective-vue2 finished in 5 seconds, leaving 49 packages and 39 MB on disk. npm audit reported no known vulnerabilities.
Can detective-vue2 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 detective-vue2 work with both ESM and CommonJS?
Yes. Both import 'detective-vue2' and require('detective-vue2') worked in Node 22 in our run. The package is published as ESM with an exports map.
Does detective-vue2 include TypeScript types?
No type declarations were found in our install, so TypeScript users need their own declarations.
detective-vue2 or @vue/compiler-sfc: which should you use?
@vue/compiler-sfc: Choose it when you need to inspect templates, external src blocks, or custom SFC metadata yourself. detective-vue2 3.0.1 took 5.3 seconds and 39 MB across 49 installed packages in our sandbox, then failed a browser-targeted bundle, so it fits Node-based SFC analysis inside a larger graph pipeline.
When should you not use detective-vue2?
You need a ready-to-query project graph; the return value contains unresolved strings from one component and has no recursion or cycle detection
Use it if
- You are adding Vue SFC awareness to precinct, dependency-tree, or your own multi-language dependency indexer
- You need imports from both script and script setup, including blocks marked lang="ts"
- Your scan must include Sass, SCSS, Less, or Stylus imports embedded in Vue components
- A list of module strings is the right intermediate result because a later stage already handles aliases and file resolution
- You need a ready-to-query project graph; the return value contains unresolved strings from one component and has no recursion or cycle detection
- Template component usage, plain CSS imports, and src attributes on external SFC blocks matter to your analysis; index.js never scans those locations
- Your runtime is older than Node 20.19 or falls between Node 22.0 and 22.11; version 3.0.1 excludes those versions in its engines field
- Your TypeScript project requires declarations for every dependency; our installed package contained no TypeScript types
- You need browser-side analysis; our esbuild browser target failed to bundle the package, which is consistent with its Node-oriented design
Setup reality
Our fresh install of detective-vue2 3.0.1 finished in 5.3 seconds on Node 22. It left 49 packages and 39 MB on disk, with seven direct dependencies plus one TypeScript peer. npm audit reported 0 known vulnerabilities. The package itself was 20 KB unpacked under the MIT license, so most of the installed footprint comes from @vue/compiler-sfc and the delegated scanners.
There are no credentials, config files, postinstall scripts, or native build steps to prepare. Pass the complete component text to the default export. Version 3.0.1 accepts a missing options object, which was the change after 3.0.0. Its engine declaration requires Node 20.19+ or 22.12+, and TypeScript must satisfy the declared peer range when npm installs the package.
The package declares ESM and has an exports map. Both require() and ESM import worked in our sandbox, although CommonJS receives the default export through the package's ESM shape. No declaration files were present. A browser-targeted esbuild bundle failed, so plan to run the scanner in a Node process rather than shipping it to a client bundle.
Parsing is synchronous and accepts only a string. Results keep block order and may contain duplicates when the same path appears in script and script setup. Options flow into the JavaScript, TypeScript, Sass, SCSS, and Less detectives; Stylus receives none. A malformed component or delegated parser error interrupts the call, so batch scanners should catch errors per file and attach the component path.
Patterns
Extract imports from one Vue file scan-component
import {readFile} from 'node:fs/promises';
import detective from 'detective-vue2';
const source = await readFile('src/App.vue', 'utf8');
const imports = detective(source);
console.log(imports);The argument is source text, not a filename. Version 3.0.1 throws when the value is missing or is not a string.
Use it from CommonJS require-package
const {default: detective} = require('detective-vue2');
const imports = detective('<script>import x from \'./x.js\'</script>');The package is ESM, but require() worked in our Node 22 sandbox. The callable function is on the default property.
Read imports from script setup scan-script-setup
const imports = detective(`
<script setup lang="ts">
import {ref} from 'vue';
import Card from './Card.vue';
</script>
`);
// ['vue', './Card.vue']Both script and script setup are scanned. A component tag that appears only in the template produces no result.
Combine normal and setup script imports scan-two-scripts
const imports = detective(`
<script>import config from './config.js'</script>
<script setup>import view from './view.js'</script>
`);
// ['./config.js', './view.js']Normal script results come before script setup results because index.js visits the blocks in that order.
Deduplicate repeated specifiers remove-duplicates
const imports = detective(`
<script>import x from './shared.js'</script>
<script setup>import y from './shared.js'</script>
`);
const unique = [...new Set(imports)];The package appends scanner output without deduplication, so the same path can occur more than once.
Exclude TypeScript type imports skip-type-imports
const imports = detective(`
<script setup lang="ts">
import type {Account} from './types';
import {load} from './api';
</script>
`, {skipTypeImports: true});
// ['./api']skipTypeImports is forwarded to the TypeScript detective. detective-vue2 does not document the delegated option itself.
Find require calls in TypeScript include-require-calls
const imports = detective(`
<script lang="ts">
import api from './api';
const oldClient = require('./old-client');
</script>
`, {mixedImports: true});mixedImports belongs to detective-typescript and applies when the SFC block has the exact lang="ts" attribute.
Ignore lazy imports exclude-dynamic-imports
const imports = detective(`
<script setup>
import eager from './eager.js';
const load = () => import('./lazy.js');
</script>
`, {skipAsyncImports: true});
// ['./eager.js']Set skipAsyncImports when your graph should represent eager imports only. Dynamic import paths are otherwise eligible for extraction.
Collect style dependencies scan-preprocessor-styles
const imports = detective(`
<style lang="scss">@import './tokens';</style>
<style lang="less">@import './theme.less';</style>
<style lang="stylus">@require './mixins.styl'</style>
`);Only Sass, SCSS, Less, and Stylus blocks have scanners. An unlabelled CSS block is skipped.
Include URLs from SCSS include-style-assets
const imports = detective(`
<style lang="scss">
@import './tokens';
.hero { background: url('./hero.webp'); }
</style>
`, {url: true});The url option reaches the Sass, SCSS, and Less detectives. Stylus is called without the options object.
Index Vue files under a source directory scan-directory
import {readFile, readdir} from 'node:fs/promises';
import {join} from 'node:path';
const graph = {};
for (const name of await readdir('src', {recursive: true})) {
if (!name.endsWith('.vue')) continue;
const file = join('src', name);
graph[file] = detective(await readFile(file, 'utf8'));
}This maps files to unresolved strings. Alias rules, extension lookup, traversal, and cycle detection need a separate graph layer.
Keep one bad component from stopping a scan isolate-parser-errors
async function inspect(file) {
try {
return detective(await readFile(file, 'utf8'));
} catch (error) {
return {file, error: error.message};
}
}Parsing and extraction are synchronous after the file read. Catch at file scope so a malformed SFC retains its filename in the report.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @vue/compiler-sfc | npm | Choose it when you need to inspect templates, external src blocks, or custom SFC metadata yourself |
| dependency-tree | npm | Choose it when the deliverable is a recursively resolved file graph instead of raw specifiers |
| precinct | npm | Choose it when one dispatcher must recognize dependencies across several source languages |
| madge | npm | Choose it for project graphs, visual output, and circular dependency reports |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · 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.

