mrkeyoor.com_
Sat 08 Aug 22:01 UTC
npmUtilsupdated 08 Aug 2026

detective-vue2

detective-vue2 reads the text of one Vue single-file component and returns module specifiers found in its script, script setup, and supported preprocessor style blocks. It uses @vue/compiler-sfc to split the file, detective-es6 for JavaScript, detective-typescript for lang="ts", and separate scanners for SCSS, indented Sass, Less, and Stylus. Despite the name, the README says it handles both Vue 2 and Vue 3 syntax. It reports strings such as vue, ./Widget.vue, and ./variables.scss; it does not resolve those strings to files or build a dependency graph.

Verdict

A useful adapter inside a larger dependency-analysis stack, especially for script setup plus preprocessor imports. It is too incomplete for a standalone Vue graph tool, and the ignored template, CSS, and external-block cases must be acceptable.

API stability4/5The callable contract is deliberately small: a source string plus an optional options object returns an array of dependency strings, with invalid input throwing synchronously. Version 3 changed packaging and runtime expectations enough that consumers must honor ESM-only loading and the modern Node engine range, but the extraction model remains straightforward. The main stability risk comes from delegated detectives and @vue/compiler-sfc, whose parsers and option behavior are part of the effective API even though this repository does not document or wrap them.
Docs2/5The README gives accurate ESM and CommonJS loading snippets, identifies Vue 2 and Vue 3 support, and tells users to install TypeScript. It omits every accepted option, supported style language, Node engine requirement, duplicate behavior, unresolved-output semantics, and ignored SFC region. It also says callers may pass content or an AST, while index.js rejects every non-string value. The tests are concise and useful for script, script setup, TypeScript, SCSS, Sass, Less, Stylus, and plain-CSS behavior, but users must read source and each delegated detective's README to understand the real feature set.
Maintenance5/5Version 3.0.1 was published on June 6, 2026 and the repository was pushed on August 4, 2026. Its current CI matrix runs coverage on Ubuntu and Windows across Node 20, 22, and 24; separate workflows run linting and CodeQL. Dependencies track current major releases of @vue/compiler-sfc and the language detectives, and GitHub reports no open issues or pull requests. The repository is tiny and has one star, so bus-factor caution still applies, but the release, dependency, security, and cross-platform test signals are all current.
Ecosystem3/5detective-vue2 records 3,611,526 downloads for the measured week, largely because it participates in the Dependents family used by precinct and dependency-tree style tooling. That family supplies specialized JavaScript, TypeScript, Sass, SCSS, Less, and Stylus scanners with compatible flat-string output. Direct community presence is minimal at one GitHub star, there are no plugins or TypeScript declarations, and Vue-specific resolution remains someone else's job. This is a useful ecosystem component, not a platform around which to design a tool.

Use it if

  • You are extending dependency-tree, precinct, or a similar Node analysis pipeline with Vue single-file component support
  • You need direct imports from both normal script and script setup blocks in Vue 2 or Vue 3 files
  • Your components embed SCSS, Sass, Less, or Stylus imports that should appear beside JavaScript and TypeScript dependencies
  • A flat list of raw module specifiers is enough and another layer will resolve aliases, extensions, packages, and recursion
Skip it if

Setup reality

Use Node 20.19+, Node 22.12+, or a later supported major, then install detective-vue2 and typescript as the README instructs. Version 3 is ESM-only with a single default export. Native ESM can import detective directly; CommonJS must require the package and read its default property. There is no CLI, browser build, config file, native compilation, or credential. Pass the complete .vue file as a UTF-8 string, not a path and not an AST. @vue/compiler-sfc parses the container, then exact lang values choose a detective: lang="ts" uses the TypeScript scanner; JavaScript and unlabelled script blocks use the ES module scanner; scss, sass, less, and stylus style blocks use their respective scanners. lang="tsx" is not special-cased. Options are passed through rather than documented by this package, so skipTypeImports and skipAsyncImports affect script scanners, mixedImports enables TypeScript require calls, and url adds asset URLs for Sass, SCSS, and Less. Stylus does not receive the options object. Results preserve discovery order and are not deduplicated: the same import in script and script setup appears twice. Plain CSS is deliberately ignored, template component tags are ignored, and src attributes on external SFC blocks return nothing. Parsing or downstream detective errors are thrown synchronously, so a repository scanner should catch them per file and report the filename. Finally, these are unresolved specifiers. You still need Vue alias rules, extension lookup, package resolution, recursion, and cycle handling if the output is meant to become a real graph.

Patterns

Read and scan one Vue componentscan-vue-file

import {readFile} from 'node:fs/promises';
import detective from 'detective-vue2';

const source = await readFile('src/App.vue', 'utf8');
const specifiers = detective(source);
console.log(specifiers);

Pass file contents, not the path. Results are unresolved module specifiers and can include package names, relative paths, and style imports.

Load the ESM package from CommonJSload-from-commonjs

async function scanVue(source) {
  const {default: detective} = await import('detective-vue2');
  return detective(source);
}

Version 3 is ESM-only. The README also shows require('detective-vue2').default where the CommonJS runtime supports requiring this ESM package, but dynamic import is more portable.

Extract imports from script setupscan-script-setup

const source = `
<script setup lang="ts">
import {ref} from 'vue';
import UserCard from './UserCard.vue';
</script>
<template><UserCard /></template>`;

detective(source); // ['vue', './UserCard.vue']

The imports come from the script setup block. The UserCard tag in the template is not analyzed independently.

Scan normal and setup scripts togethercombine-script-blocks

const deps = detective(`
<script>import shared from './shared.js'</script>
<script setup>import sharedAgain from './shared.js'</script>
`);

const uniqueDeps = [...new Set(deps)];

The scanner appends results from the normal script and then script setup. It does not deduplicate, so this source returns the same specifier twice.

Omit TypeScript type-only importsskip-type-imports

const deps = detective(`
<script setup lang="ts">
import type {User} from './types';
import {loadUser} from './api';
</script>
`, {skipTypeImports: true});

// ['./api']

skipTypeImports is passed to detective-typescript and detective-es6. Without it, type-only specifiers are included in the result.

Include require calls in a TypeScript blockinclude-commonjs-requires

const deps = detective(`
<script lang="ts">
import api from './api';
const legacy = require('./legacy');
</script>
`, {mixedImports: true});

// ['./api', './legacy']

mixedImports is a detective-typescript option. JavaScript blocks are handled by detective-es6, so do not assume the same require behavior there.

Exclude lazy import expressionsskip-dynamic-imports

const deps = detective(`
<script setup>
import eager from './eager.js';
const lazy = () => import('./lazy.js');
</script>
`, {skipAsyncImports: true});

// ['./eager.js']

Dynamic imports are included by default. Set skipAsyncImports only when your graph intentionally represents eager dependencies.

Collect dependencies from preprocessor stylesscan-style-imports

const deps = detective(`
<style lang="scss">@import './tokens';</style>
<style lang="less">@import './theme.less';</style>
<style lang="stylus">@require './mixins.styl'</style>
`);

// ['./tokens', './theme.less', './mixins.styl']

Recognized lang values are scss, sass, less, and stylus. Plain CSS blocks are ignored even when they contain @import.

Include Sass, SCSS, and Less asset URLsinclude-style-urls

const deps = detective(`
<style lang="scss">
@import './tokens';
.hero { background: url('./hero.webp'); }
</style>
`, {url: true});

// ['./tokens', './hero.webp']

The url option is delegated to the Sass, SCSS, and Less scanners. detective-vue2 does not pass options to its Stylus scanner.

Scan every Vue file in a directoryscan-directory

import {readFile, readdir} from 'node:fs/promises';
import {join} from 'node:path';

const entries = await readdir('src', {recursive: true});
const graph = {};
for (const entry of entries.filter((name) => name.endsWith('.vue'))) {
  const file = join('src', entry);
  graph[file] = detective(await readFile(file, 'utf8'));
}

This creates a file-to-specifier index, not a resolved graph. Alias expansion, extension lookup, package resolution, recursion, and cycles still need another layer.

Attach filenames to scanner failuresreport-parse-errors

async function scanFile(file) {
  const source = await readFile(file, 'utf8');
  try {
    return detective(source);
  } catch (error) {
    throw new Error(`Cannot scan ${file}: ${error.message}`, {cause: error});
  }
}

Vue parser and delegated language-parser errors are synchronous. Catch per file so one malformed component does not erase the location from a batch report.

Flag SFC regions this package will not scandetect-coverage-gaps

import {parse} from '@vue/compiler-sfc';

const {descriptor} = parse(source);
const external = [
  descriptor.template?.src,
  descriptor.script?.src,
  descriptor.scriptSetup?.src,
  ...descriptor.styles.map((style) => style.src)
].filter(Boolean);

if (external.length) console.warn('External blocks need separate handling', external);

detective-vue2 ignores external src attributes and template dependencies. Use the already installed Vue SFC compiler when those gaps matter.

Alternatives

PackageRegistryPick it when
@vue/compiler-sfcnpmYou need full control over Vue block parsing, src attributes, templates, and custom dependency extraction
dependency-treenpmYou need resolved recursive file graphs rather than one file's raw import strings
precinctnpmYou want a higher-level dispatcher that selects detectives for many source-file types
madgenpmYour goal is graph visualization and circular dependency detection across a project