mrkeyoor.com_
Sat 08 Aug 21:01 UTC
npmCLI & Toolingupdated 08 Aug 2026

filing-cabinet

filing-cabinet is a synchronous Node.js resolver for source-code dependency strings. Give it an import such as ./button, the file containing that import, and the project root; it selects a resolver from the containing file's extension and returns the matching absolute file path. Version 6 understands CommonJS, ESM, AMD, webpack resolution, TypeScript and tsconfig paths, Sass, Less, Stylus, Vue, Svelte, Node package #imports, and custom extensions. It is infrastructure for dependency graphs, codemods, and analysis CLIs, not code that belongs in a browser bundle.

Verdict

filing-cabinet earns its install when one analysis tool must understand a genuinely mixed frontend codebase. For ordinary Node or TypeScript resolution, a focused resolver is smaller, easier to type, and less likely to execute project configuration unexpectedly.

API stability3/5The core options-in, absolute-path-or-empty-string contract and register, unregister, getLookup, and supportedFileExtensions members are compact. Version 6 still raises the runtime floor and is packaged as an ES module with CommonJS consumers using .default; resolver behavior also moves with TypeScript, enhanced-resolve, and the other bundled engines, so upgrades can change edge-case results without changing cabinet's top-level signature.
Docs4/5The README now provides ESM and CommonJS imports, a complete option table, nodeModulesConfig object and function examples, custom resolver lifecycle, supported extensions, #imports behavior, CLI usage, and the empty-string miss contract. It lacks TypeScript declarations, a migration guide for version 6, security guidance for executable webpack configs, and end-to-end fixtures for each supported language.
Maintenance4/5Version 6.0.0 was published on May 19, 2026, and the repository was pushed on August 4, 2026. It is not archived and currently reports seven open issues and pull requests, a manageable queue for a project with 87 stars; continued maintenance also depends on keeping several upstream resolution engines compatible under one synchronous API.
Ecosystem4/5The package recorded 3,898,144 downloads last week despite only 87 GitHub stars, a pattern consistent with infrastructure used transitively by dependency-analysis tools. It covers JavaScript, TypeScript, AMD, webpack, Vue, Svelte, Sass, Less, Stylus, and custom resolvers, but its extension API is small and there is no broad third-party plugin catalog documented by the project.

Use it if

  • You are building a dependency graph, unused-file checker, codemod, or editor tool that must resolve imports the way several source ecosystems do
  • One project mixes JavaScript, TypeScript, Vue or Svelte files, and stylesheet imports and you want one synchronous resolver interface
  • You need to honor RequireJS, webpack, tsconfig path aliases, package #imports, or a custom package.json entry field
  • You need to register a resolver for an extra source extension while retaining the built-in resolver table
Skip it if

Setup reality

npm install filing-cabinet brings a pure JavaScript install with no native build or credentials, but it is not a small dependency: version 6 directly includes TypeScript 6, Commander 14, enhanced-resolve, resolve, tsconfig-paths, AMD lookup, Sass and Stylus lookup, module detection, and path helpers. Your Node version must satisfy >=20.19.0 or >=22.12.0. The package is type: module and exports its root; ESM uses a default import, while CommonJS must read require('filing-cabinet').default. Every resolution call is synchronous and needs partial, filename, and directory. filename is the importing source file, not the candidate target, and directory is the project root used to locate node_modules. A miss is '', so check truthiness before passing the result to fs. JavaScript module style is detected by parsing the source unless config, webpackConfig, or a pre-parsed ast selects a path; large graph crawls should reuse ASTs where available. A webpack config path is loaded and may execute code, and an array export uses only its first config. TypeScript aliases require the correct tsConfig; when passing a config object, also provide tsConfigPath so relative baseUrl and paths have a real location. Type resolution can return .d.ts unless noTypeDefinitions is true. nodeModulesConfig can prefer a field such as module or run a packageFilter, which can make results differ from Node runtime resolution. Custom register and unregister calls modify a process-global resolver table, so tests and concurrent analyses need cleanup.

Patterns

Resolve a relative JavaScript importresolve-relative-import

import path from 'node:path';
import cabinet from 'filing-cabinet';

const root = process.cwd();
const resolved = cabinet({
  partial: './button',
  filename: path.join(root, 'src/app.js'),
  directory: root,
});

if (!resolved) throw new Error('dependency not found');

The return value is an absolute path or an empty string. filename must identify the file containing the import so relative resolution has the right base.

Load version 6 from CommonJSrequire-from-commonjs

const { default: cabinet } = require('filing-cabinet');

const resolved = cabinet({
  partial: './config',
  filename: '/workspace/src/index.js',
  directory: '/workspace',
});

Version 6 is an ES module. require returns a namespace object, so the documented CommonJS form destructures default.

Resolve a package through its module fieldprefer-package-module-field

const resolved = cabinet({
  partial: 'some-package',
  filename: '/workspace/src/app.js',
  directory: '/workspace',
  nodeModulesConfig: { entry: 'module' },
});

This deliberately differs from normal Node main resolution. Use it when analyzing bundler inputs, not when predicting what require will load at runtime.

Select package entries with packageFilterfilter-package-entry

const resolved = cabinet({
  partial: 'some-package',
  filename: '/workspace/src/app.js',
  directory: '/workspace',
  nodeModulesConfig(pkg) {
    pkg.main = pkg.browser ?? pkg.module ?? pkg.main;
    return pkg;
  },
});

The callback can mutate package metadata before resolution. Its policy must match the runtime or bundler whose dependency graph you are modeling.

Resolve with a tsconfig fileresolve-typescript-import

const resolved = cabinet({
  partial: '@app/models/user',
  filename: '/workspace/src/service.ts',
  directory: '/workspace',
  tsConfig: '/workspace/tsconfig.json',
});

A TypeScript resolution may return a .d.ts file. Set noTypeDefinitions when the graph should follow executable JavaScript instead of declarations.

Prefer JavaScript over type declarationsresolve-runtime-file

const resolved = cabinet({
  partial: 'some-library',
  filename: '/workspace/src/app.ts',
  directory: '/workspace',
  tsConfig: '/workspace/tsconfig.json',
  noTypeDefinitions: true,
});

noTypeDefinitions only affects TypeScript lookup. If TypeScript cannot map a declaration back to executable code, the result may still be empty.

Pass an in-memory TypeScript configuse-parsed-tsconfig

const resolved = cabinet({
  partial: '@lib/math',
  filename: '/virtual/src/app.ts',
  directory: '/virtual',
  tsConfig: {
    compilerOptions: { baseUrl: '.', paths: { '@lib/*': ['src/lib/*'] } },
  },
  tsConfigPath: '/virtual/tsconfig.json',
  fileSystem: virtualFs,
});

tsConfigPath supplies the location used to interpret relative baseUrl and paths. Without it, path mapping from an object config is ignored.

Resolve through a webpack configresolve-webpack-alias

const resolved = cabinet({
  partial: '@ui/Button',
  filename: '/workspace/src/app.js',
  directory: '/workspace',
  webpackConfig: '/workspace/webpack.config.cjs',
});

The config is loaded as code, function exports are invoked, and only the first entry of an array export is used. Never do this in-process for an untrusted repository.

Resolve a package #imports aliasresolve-package-imports

const resolved = cabinet({
  partial: '#internal/logger',
  filename: '/workspace/src/app.js',
  directory: '/workspace',
});

Version 6 automatically handles package.json imports entries beginning with # for both JavaScript and TypeScript containing files.

Resolve an import found in a Vue fileresolve-vue-style-import

const resolved = cabinet({
  partial: './styles/theme.scss',
  filename: '/workspace/src/App.vue',
  directory: '/workspace',
});

The Vue and Svelte resolver dispatches by dependency suffix; scss, sass, and less use sass-lookup, while styl uses stylus-lookup.

Add and remove a custom resolverregister-custom-extension

cabinet.register('.py', ({ partial, filename }) => {
  return resolvePythonImport(partial, filename) || '';
});

try {
  const resolved = cabinet({
    partial: 'my_package.tools',
    filename: '/workspace/app.py',
    directory: '/workspace',
  });
} finally {
  cabinet.unregister('.py');
}

Registration changes a process-global table. Always unregister in tests or temporary analyses so later calls do not inherit the custom behavior.

Check whether an extension has a resolverinspect-supported-extensions

if (cabinet.supportedFileExtensions.includes('.tsx')) {
  const resolver = cabinet.getLookup('.tsx');
  console.log(typeof resolver === 'function');
}

The array updates when register or unregister is called. Unknown containing-file extensions fall back to a generic dependency-path resolver.

Alternatives

PackageRegistryPick it when
resolvenpmYou only need the classic Node require.resolve algorithm with packageFilter control and a much narrower dependency set
enhanced-resolvenpmYou specifically need webpack-style aliases, extensions, condition names, and plugin-driven resolution
tsconfig-pathsnpmYour only nonstandard requirement is resolving TypeScript baseUrl and paths mappings