Git Product home page Git Product logo

node-filing-cabinet's Introduction

filing-cabinet

CI npm version npm downloads

Get the file associated with a dependency/partial's path

npm install filing-cabinet

Usage

const cabinet = require('filing-cabinet');

const result = cabinet({
  partial: 'somePartialPath',
  directory: 'path/to/all/files',
  filename: 'path/to/parent/file',
  ast: {}, // an optional AST representation of `filename`
  // Only for JavaScript files
  config: 'path/to/requirejs/config',
  webpackConfig: 'path/to/webpack/config',
  nodeModulesConfig: {
    entry: 'module'
  },
  tsConfig: 'path/to/tsconfig.json', // or an object
  tsConfigPath: 'path/to/tsconfig.json'
});

console.log(result); // /absolute/path/to/somePartialPath
  • partial: the dependency path
    • This could be in any of the registered languages
  • directory: the path to all files
  • filename: the path to the file containing the partial
  • ast: (optional) the parsed AST for filename.
    • Useful optimization for avoiding a parse of filename
  • config: (optional) requirejs config for resolving aliased JavaScript modules
  • webpackConfig: (optional) Webpack config for resolving aliased JavaScript modules. If exporting multiple configurations, the first configuration is used.
  • nodeModulesConfig: (optional) config for resolving entry file for node_modules. This value overrides the main attribute in the package.json file; used in conjunction with the packageFilter of the resolve package.
  • tsConfig: (optional) path to a TypeScript configuration. Could also be an object representing a pre-parsed TypeScript config.
  • tsConfigPath: (optional) A (virtual) path to TypeScript config file when tsConfig option is given as an object, not a string. Needed to calculate Path Mapping. If not given when tsConfig is an object, Path Mapping is ignored. This is not need when tsConfig is given as string (path to the tsconfig file).
  • noTypeDefinitions: (optional) For TypeScript files, whether to prefer *.js over *.d.ts.

Registered languages

By default, filing-cabinet provides support for the following languages:

  • JavaScript: CommonJS, AMD, ES6
  • TypeScript
  • CSS Preprocessors: Sass (.scss and .sass), Stylus (.styl), and Less (.less)
  • Vue

You can register resolvers for new languages via cabinet.register(extension, resolver).

  • extension: the extension of the file that should use the custom resolver (ex: '.py', '.php')
  • resolver: a function that accepts the following (ordered) arguments that were given to cabinet:
    • partial
    • filename
    • directory
    • config

For examples of resolver implementations, take a look at the default language resolvers:

If a given extension does not have a registered resolver, cabinet will use a generic file resolver which is basically require('path').join with a bit of extension defaulting logic.

CLI

Requires a global install with npm install -g filing-cabinet

filing-cabinet [options] <dependencyPath>

See filing-cabinet --help for details on the options.

License

MIT

node-filing-cabinet's People

Contributors

bpscott avatar dazinator avatar dependabot[bot] avatar digitalkaoz avatar h7kayama avatar havunen avatar igmdvc avatar igrayson avatar jjangga0214 avatar jkemp-spotify avatar jstevans avatar mrjoelkemp avatar nthalk avatar pahen avatar pastak avatar pcardune avatar re-fort avatar realityking avatar simenb avatar symind avatar vansosnin avatar vbraun avatar xhmikosr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

node-filing-cabinet's Issues

Tests assertions fail on windows under vscode due to / versus \ in paths.

For example this test fails:


 it('adds the directory to the require resolution paths', function() {
        const directory = 'js/commonjs/';
        cabinet({
          partial: 'foobar',
          filename: 'js/commonjs/foo.js',
          directory: directory
        });

        assert.ok(require.main.paths.some(function(p) {
          return p.indexOf(directory) !== -1;
        }));
      });

because on windows, require.main.paths looks like this:

image

So the assertion fails becuase the slash direction in the paths doesn't match.

Not all imports in TypeScript are detected.

I'm using dependency-tree to get a tree of my TypeScript project, but it does not show all imported files. When an import statement directs to a folder with an index file, it will not detect it. When I step through the call stack the problem is probably in the tsLookup() function in filing-cabinet.

The line: const namedModule = ts.resolveModuleName(dependency, filename, compilerOptions, host); does not resolve in a module. am I doing something wrong, or is this a bug?

When I change the files to .js everything works as expected.

Example

  • File Structure

    ├── main.ts
    ├── lib
        ├── index.ts
    
  • main.ts

    import { func } from './lib';
  • index.ts

    export function func() {
      
    }
  • Output

    { 'main.ts': {} }
  • Desired output

    { 'main.ts': { 'main.ts/lib/index.ts': {} } }

Config dependency-tree

const tree = dependencyTree({
  filename: 'src/index.ts',
  directory: 'src',
  nodeModulesConfig: {
    entry: 'module'
  },
});

`failedLookupLocations` no longer exists in Typescript

I needed to get newer versions of ts-parsers etc to support using the using keyword. Got some issues with filing-cabinet then which is a sub-sub dependency of madge.

The stacktrace:

TypeError: Cannot read properties of undefined (reading 'filter')
 ❯ tsLookup node_modules/filing-cabinet/index.js:262:8
 ❯ module.exports node_modules/filing-cabinet/index.js:73:18
 ❯ Function.module.exports._getDependencies node_modules/dependency-tree/index.js:100:20
 ❯ traverse node_modules/dependency-tree/index.js:146:37
 ❯ traverse node_modules/dependency-tree/index.js:170:29
 ❯ traverse node_modules/dependency-tree/index.js:170:29
 ❯ module.exports node_modules/dependency-tree/index.js:37:19
 ❯ node_modules/madge/lib/tree.js:121:27
 ❯ Tree.generateTree node_modules/madge/lib/tree.js:116:9

I've done a local yarn patch filing-cabinet which looks like this to fix it:

diff --git a/index.js b/index.js
index d5e792434fb0b3ad5ef060cc9b924b845df5605d..4e257fd48b898b08275abd79db66c1a8c4546f9f 100644
--- a/index.js
+++ b/index.js
@@ -259,10 +259,10 @@ function tsLookup({ dependency, filename, directory, webpackConfig, tsConfig, ts
   } else {
     const suffix = '.d.ts';
     const lookUpLocations = namedModule.failedLookupLocations
-      .filter(string => string.endsWith(suffix))
+      ?.filter(string => string.endsWith(suffix))
       .map(string => string.substr(0, string.length - suffix.length));

-    result = lookUpLocations.find(location => ts.sys.fileExists(location)) || '';
+    result = lookUpLocations?.find(location => ts.sys.fileExists(location)) || '';
   }

   if (!result && tsConfigPath && compilerOptions.baseUrl && compilerOptions.paths) {

Which works well and as expected. Not sure about the impact of effectively just ignoring that code though, but thought I'd give a heads up. Also if anyone else is (or comes into) the same situation.

I can provide the above patch as a PR of course, but I'm quite sure that it's probably not a great route. So someone with more insight should probably look at it :)

tsConfig argument conflates two different objects

The documentation says

Could also be an object representing a pre-parsed typescript config.

but that could be two different things:

1) raw json

Meaning that you read the tsconfig.json file and apply JSON.parse()

In this case, the compiler options are tsconfig.compilerOptions. The module setting tsconfig.compilerOptions.module is a string, for example 'CommonJs'. And if you want to be able to resolve index.ts files then it better be that value.

filing-cabinet still needs to convert the raw json to typescript's own format before using it.

2) parsed using typescript's api to parse the config file

That is, using typescript.parseJsonSourceFileConfigFileContent

In this case, the compiler options are tsconfig.options. The module setting tsconfig.options.module is a number, for example the number-based enum value typescript.ModuleKind.CommonJS. And if you want to be able to resolve index.ts files then it better be that value.

This is the preferred usage if you care about resolution speed for multiple files as typescript doesn't have to re-parse its configuration.

There is no api to go from 2) back to 1), only from 1) to 2).

Problem

Passing 2) into filing-cabinet means that the module is typescript configuration is accidentally undefined. Filing-cabinet then defaults to typescript.ModuleKind.AMD which does not include index.ts files.

madge tries to hack around that by trying to convert 2) back to 1), but unsuccessfully so: pahen/madge#271, pahen/madge#322

Instead, filing-cabinet should accept both kinds of tsConfig objects and handle them according to wether tsConfig.compilerOptions or tsConfig.options is present.

Webpack: Support Ignore Plugin

I've recently been using pahen/madge to show dependency graphs (and explain circular dependencies), for my webpack codebase. pahen/madge depends on this module. In tracing why dependencies are showing on the graph that shouldn't be there, it has come to light that node-filing-cabinet only checks the resolve field in webpackConfig despite resolution issues being affected by plugin fields.

In my case I use the canonical IgnorePlugin to exclude certain files from certain builds.

I doubt it is feasible to process every plugin, but it would be very useful to support the built in webpack plugin "IgnorePlugin".

configure NODE_PATH?

Hey,

we're using something similar to https://gist.github.com/branneman/8048520#52-only-while-executing-node to prevent having to use long, relative paths when importing/requiring modules.

Is there a way to make filing cabinet aware of such a situation? I'm trying to build a dependency graph for a project that uses NODE_PATH=src and would like to be able to resolve something like import Foo from 'components/Foo' to /absolute/path/to/project/src/components/Foo.js

When `noTypeDefinitions` is enabled and the extension `.d.ts` is found, a crash can occur.

There are likely two bugs here:

Given that I am performing a TypeScript import, with noTypeDefinitions as a parameter, if I have a package like @types/ramda installed, resolveJSModule will not resolve to ramda.

Instead of gracefully ignoring it, what will instead occur is it will check for the .js file in the wrong directory, and resolveJSModule will throw an exception, rather than nothing (which the || result implies the code expects)

if (namedModule.resolvedModule) {
    result = namedModule.resolvedModule.resolvedFileName;
    if (namedModule.resolvedModule.extension === '.d.ts' && noTypeDefinitions) {
      const resolvedFileNameWithoutExtension = result.replace(namedModule.resolvedModule.extension, '');
      result = ts.resolveJSModule(resolvedFileNameWithoutExtension, path.dirname(filename), host) || result;
    }
  }

For now, I will work around it by enabling type definitions, but I recommend wrapping the logic with a try catch instead of using the or statement, and potentially reworking the logic to check another directory.

Upgrade Typescript

Typescript 3.0.0 has been release with no breaking changes
Upgrading typescript to ^2.4.2 || ^3.0.1 should work best

Enhacement: support for package.json module property

Hi,

First of all, thanks for the great job on this module and also the dependency-tree one which I am currently using.

I am trying to generate dependencies and some of the packages in node_modules that declare the module property as entry point, and it would be nice that, through configuration, we can resolve the filename of the import by using module property instead of the main one.

I have been looking at the source code and I think it would be straightforward by using the option packageFilter from resolve npm module. Something like this inside commonJSLookup resolve.sync:

result = resolve.sync(partial, { basedir: directory, packageFilter: function(obj) { obj.main = obj.module ? obj.module : obj.main; return obj; }, // Add fileDir to resolve index.js files in that dir moduleDirectory: ['node_modules', directory] });

webpack config appears to be re-executed once for every resolve attempt

This greatly increases execution times for non-trivial webpack.config.js files. It would be great if the config would only be required and instantiated once, using memoization if necessary.

function resolveWebpackPath({dependency, filename, directory, webpackConfig}) {
if (!webpackResolve) {
webpackResolve = require('enhanced-resolve');
}
webpackConfig = path.resolve(webpackConfig);
let loadedConfig;
try {
loadedConfig = require(webpackConfig);
if (typeof loadedConfig === 'function') {
loadedConfig = loadedConfig();
}
} catch (e) {
debug('error loading the webpack config at ' + webpackConfig);
debug(e.message);
debug(e.stack);
return '';
}
const resolveConfig = Object.assign({}, loadedConfig.resolve);
if (!resolveConfig.modules && (resolveConfig.root || resolveConfig.modulesDirectories)) {
resolveConfig.modules = [];
if (resolveConfig.root) {
resolveConfig.modules = resolveConfig.modules.concat(resolveConfig.root);
}
if (resolveConfig.modulesDirectories) {
resolveConfig.modules = resolveConfig.modules.concat(resolveConfig.modulesDirectories);
}
}
try {
const resolver = webpackResolve.create.sync(resolveConfig);
// We don't care about what the loader resolves the dependency to
// we only wnat the path of the resolved file
dependency = stripLoader(dependency);
const lookupPath = isRelative(dependency) ? path.dirname(filename) : directory;
return resolver(lookupPath, dependency);
} catch (e) {
debug('error when resolving ' + dependency);
debug(e.message);
debug(e.stack);
return '';
}
}

returns empty path for require'd json file w/ no extension

So, given foo.json, apparently this is valid:

require('./foo')

(And they say you shouldn't require JSON files...)

At any rate, when filing-cabinet receives a partial of ./foo, it returns an empty string instead of /path/to/foo.json.

tsLookup only resolves using AMD module system

var options = {
module: ts.ModuleKind.AMD
};
var host = ts.createCompilerHost({});
debug('with options: ', options);
var resolvedModule = ts.resolveModuleName(partial, filename, options, host).resolvedModule;

Currently the tsLookup function only resolves using the AMD module system. Any specific reason for that? or can we find a way to make it configurable?

Thanks for the great lib! 🍻

allow non-singleton usage

the register() method keeps whatever was registered in memory, but if two libs were using filing-cabinet at the same time, they may have differing requirements for custom resolvers. maybe a resolver for .foo is different from one to the other, or perhaps one of them should not, under any circumstances, resolve a .foo file.

a way to address this would be to provide a factory function, e.g.:

const filingCabinet = require('filing-cabinet');
const cabinet = filingCabinet.create();
cabinet.register('.foo', myFooResolver);
cabinet({partial: 'bar', filename: 'quux.js', directory: '/some/path'}) // works with .foo files
filingCabinet({partial: 'bar', filename: 'quux.js', directory: '/some/path'}) // does not work with .foo files

I can send a PR to that effect, or if you have a different idea...

Issue in tsConfig options resolving

Hello!

Noticed that method that is being used to resolve ts config compiler options not working properly if passed config has extends field.

const {options} = ts.convertCompilerOptionsFromJson(tsConfig.compilerOptions);

Maybe try using parseJsonConfigFileContent from TS Compiler API? It resolves CompilerOptions well, though a bit more complicated in use: you need to create an instance of ParseConfigHost. It can be done like here.

Resolving symlink real paths for js resolver

The problem I have is that syminks are not replaced with their real paths. I found two problems:

  1. https://github.com/browserify/resolve/pull/195/files
  2. filing-cabinet does not allow to set preserveSymlinks: false anyway.

So, I have two questions:

  1. Could you replace browserify/resolve with build-in node resolve? (because the first one have bugs)
  2. Could you set preserveSymlinks: false by default for js resolver? TS resolver has that option by default.

PS: I use yarn workspaces, and all dependencies symlinked through node_modules. But I want to resolve all dependencies through their real paths.

CommonJS needs to resolve in directory

Currently trying to resolve the import in this library's directory.

Need to somehow specify a context for resolution. If I want to continue using require.resolve (which does everything I need but in the wrong directory), then I may need to call static private methods of require('module') to work around the limitation.

Allow multiple default lookups for projects with JS and TS usage

Our use case is that we have a monorepo with several projects, some of them are in Typescript and some in plain JS.

When we pass tsConfig options (via madge) it fails to resolve modules written in JS because on the default lookups array:

const defaultLookups = {

Only seems to have one lookup for each file extension, where as tsConfig can also resolve JS module via allowJs option.

This will make it significantly easier for project which are in the process of migration to Typescript.

Resolves to wrong version

Considering the following package.json:

{
  "dependencies": {
    "uuid": "8.3.2",
    "request": "2.88.2"
  }
}

this will install the following (among others):

|- node_modules
   |- uuid // version 8.3.2
   |- request
      |- node_modules
         |- uuid // version 3.4.0

filing-cabinet -d ./ -f ./node_modules/request/lib/auth.js uuid
yields

/node_modules/uuid/dist/index.js

instead of

/node_modules/request/node_modules/uuid/index.js

1.14.4 contains a breaking change

Hi,

version 1.14.4 starts to use language features such as const which is not a bug fix more a breaking change. Was this intended? I also see that a version 2.y.z now exists which might be better suited to contain such changes.

I'm not a direct user of filing-cabinet, instead madge -> dependency-tree -> filing-cabinet and dependency-tree sets this dependency filing-cabinet@^1.9.0

Consider supporting Webpack configurations that export a Promise

Webpack -- I believe since version 2 -- has been able to export a promise as it's configuration object. At the time of writing, filing-cabinet does check if the config object is a function, but this if branch will be spuriously matched in the case of an exported Promise, and will (eventually) result in result in an UnhandledPromiseRejectionWarning being thrown.

I'm not sure if there's an easy fix for this missing feature, though, so I would understand if handling promises in this manner isn't in scope for the project.

Fails with an error when resolving import of the js file that has the same-named ".d.ts" type-definition file

Fails with an error when resolving import of the js file that has the same-named ".d.ts" type-definition file
with 'noTypeDefinitions' set to true

Reproduce:
File structure:

src
 |- bar.ts // ts file with "import something from '@test/foo';"
 |- foo.js
 |- foo.d.ts // type-definition file with the same name

The script that resolves imports:

import cabinet from 'filing-cabinet';

const pathToDirectory = './';
const result = cabinet({ 
  partial: '@test/foo',
  `${pathToDirectory}/bar.ts`, // fileName
  pathToDirectory, // directory
  tsConfig: {
    compilerOptions: {
      allowJs: true,
      moduleResolution: 'node',
      baseUrl: pathToDirectory,
      paths: {
        '@test/*': ['*'],
      }
    }
  },
  noTypeDefinitions: true,
});

console.log('result: ', result);

Expected result:

result: <path_to_directory>/foo.js

Actual result:
Fails with the next error:

Error: Could not resolve JS module '@test/foo'

Typescript module resolution does not resolve arbitrary file extensions

JS module resolution supports arbitrary import names. For instance given two files in a directory "index.js" and "style.css", where "index.js" contains `import style from './style.css';

Running npx filing-cabinet -d . -f index.js './style.css' will return <path_current_folder>/style.css.

Thats' all good.

However if you rename index.ts and perform the same command (changing index.js to index.ts) then no value is returned. I would expect <path_current_folder>/style.css to be returned.

It also does not work if you add a tsconfig containing { "compilerOptions": { "moduleResolution": "node" } } to force using node-style module resolution.

I'm not sure how the classic module resolution should work but that should work using the node-style one. falling back to the commonJs style lookup would kinda work in the common case but would break if people have custom paths specified in their tsconfig.json

Filename not given

I get an error "Filename not given" thrown from resolve-dependency-path. I'm using [email protected].

Here's a relevant piece of code:

caller:

resolveDependencyPath = require('resolve-dependency-path');
}
resolver = resolveDependencyPath;
}
debug(`found a resolver for ${ext}`);
options.dependency = partial;
const result = resolver(options);

callee:

https://github.com/dependents/node-resolve-dependency-path/blob/6bad609f2cd9f5e740a78b8c2c05007903ed6ca8/index.js#L10-L12

Could you explain how it supposed to work if you supply only one argument to the function that throws when there are less than 3 arguments?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.