Giter VIP home page Giter VIP logo

import-js's Introduction

npm Version License Build Status Test Coverage

ImportJS is a tool to automatically import dependencies in your JavaScript project. Use it along with one of our editor integrations for Atom, Emacs, Sublime, Vim, or VS Code.

Demo of ImportJS in action

Editor support

There are ImportJS plugins for the following editors:

Detailed instructions on how to install ImportJS can be found in the editor links above.

Want to add another editor to the list? See how to contribute.

Dependency on Babel 7

ImportJS uses Babel 7 from version 3.1.0. In most cases, Babel 7 is backwards-compatible with Babel 6, but if you run into issues (such as this one about decorators), consider installing a previous version of ImportJS (e.g. 3.0.0) or updating your project to be Babel 7 compatible.

Importing: Example

Let's say that you have a JavaScript project with the following structure:

.
|-- index.html
|-- components
|     |-- button.js
|     |-- icon.js
|-- vendor
|     |--
|-- pages
|     |-- index.js

Now, imagine that you're editing pages/index.js which contains:

document.createElement(new Button({ text: 'Save' }).toDOMElement());

At this point, Button is undefined, so we need to import it. If you are used to doing this manually, this involves figuring out the path to the JavaScript module that defines Button. With ImportJS you instead place your cursor on the word "Button", then hit <leader>j (Vim), (M-x) import-js-import (Emacs), or choose "ImportJS: import word under cursor" (Sublime). The file buffer will now change to the following:

import Button from '../components/button';

document.createElement(new Button({ text: 'Save' }).toDOMElement());

That's basically it. ImportJS will help you find modules and automatically add import statements. But, keep reading for more neat features.

Fix imports

ImportJS can be used to automatically fix all imports in the current file. By hitting <leader>i (Vim), (M-x) import-js-fix (Emacs), or choose ImportJS: fix all imports (Sublime), all your undefined variables will be resolved, and all your unused imports will be removed.

If you're using JSX, ImportJS will no longer automatically import React for you. If this is something you need, please consider using ImportJS version 5.1.0 or earlier. The need for React imports to use JSX was removed in React 17. Read more here

Go to module

Since ImportJS is pretty good at finding JS modules, it makes sense that there's an option to open/go to a file rather than import it. This is similar to Vim's built in "Open file under cursor". Use it by placing the cursor on a variable and hit <leader>g (Vim), (M-x) import-js-goto (Emacs), or choose "ImportJS: goto module" (Sublime).

Things to note

  • Only files ending in .js\* and .ts* are considered when importing
  • As part of resolving imports, all imports will be sorted and placed into groups. Grouping and sorting can be disabled, see the groupImports and sortImports configuration options. Comments and whitespace will be preserved if these are both disabled.
  • You can speed up ImportJS by installing Watchman. See Speeding it up! for more information.

Configuration

ImportJS is configured through a JavaScript file (.importjs.js).

The file needs to export a single object containing you configuration settings, like the example below.

module.exports = {
  excludes: ['./react-components/**/test/**'],
  // continue with the rest of your settings...
};

Save this file in the root folder of your project (e.g. where the package.json file is found). You can also save it to the user home directory if you want to share a global config between different projects.

The following configuration options are supported.

aliases

Some variable names might not easily map to a file in the filesystem. For those, you can add them to the aliases configuration.

aliases: {
  $: 'third-party-libs/jquery',
  _: 'third-party-libs/underscore',
}

danglingCommas

By default, ImportJS will add trailing commas when constructing import statements with multiple named imports.

You can turn off this behavior by setting danglingCommas to false.

danglingCommas: false;

declarationKeyword

The default value for this property is import, making your import statements use the ES2015 modules syntax:

import Foo from 'foo';

Aliases can be made dynamic by using the {filename} string. This part of the alias will be replaced by the name of the file you are currently editing.

e.g.

aliases: {
  styles: './{filename}.scss',
}

will for a file foo/bar.js result in

import styles from './bar.scss';

emptyLineBetweenGroups

By default, ImportJS will insert empty line between import groups.

You can turn off this behavior by setting emptyLineBetweenGroups to false.

emptyLineBetweenGroups: false;

environments

This list of environments controls what core modules are available when importing, and what variables are considered global by default. The supported values right now are

environments: ['meteor', 'node'];

excludes

Define a list of glob patterns that match files and directories that you don't want to include for importing.

excludes: ['./react-components/**/test/**'];

globals

Provide a list of global identifiers used in the code. ImportJS will ignore these when trying to import all undefined variables.

Note: If you use the environments configuration option correctly, you might not need to specify globals.

groupImports

By default, ImportJS will put imports into groups:

  1. Core modules
  2. Package dependencies
  3. One or more groups with internal imports

You can turn off this behavior by setting groupImports to false. When disabled, imports are listed alphabetically in one list.

groupImports: false;

ignorePackagePrefixes

If you have package dependencies specified in package.json that are prefixed with e.g. an organization name but want to be able to import these without the package prefix, you can set the ignorePackagePrefixes configuration option.

ignorePackagePrefixes: ['my-company-'];

When package dependencies are matched, these prefixes will be ignored. As an example, a variable named validator would match a package named my-company-validator.

importDevDependencies

ImportJS will look for package dependencies listed in package.json when importing. By default, only modules listed under dependencies and peerDependencies will be used. By setting importDevDependencies to true, devDependencies will also be taken into account.

importDevDependencies: true;

importFunction

Note: this only applies if you are using var or const as declarationKeyword.

The default value for this configuration option is "require", which is the standard CommonJS function name used for importing.

importFunction: 'myCustomRequireFunction';

importStatementFormatter

Use a function here to control how the resulting import statement will look like. This is useful if you for instance want to strip out trailing semicolons (that ImportJS adds by default).

Note: this method should only be used in rare cases. There's a chance that ImportJS won't be able to recognize the resulting import statement next time it is about to import something.

importStatementFormatter({ importStatement }) {
  return importStatement.replace(/;$/, '');
},

logLevel

One of ["debug", "info", "warn", "error"]. This controls what ends up in the logfile. The default is info.

logLevel: 'debug';

The logfile is written to "importjs.log" in your operating system's default directory for temporary files. You can get the path to the log file by running importjs logpath.

maxLineLength

Defaults to 80. This setting controls when import statements are broken into multiple lines.

maxLineLength: 70;

mergableOptions

A dictionary of Options that be merged with defaults and values provided by an environment. This can be used to overwrite options provided by environments. Defaults to:

mergableOptions: {
  aliases: true,
  coreModules: true,
  namedExports: true,
  globals: true,
}

Note: the mergableOptions option will always be merged and will be ignored if included in a user config.

To disable merging a particular option or set of options, set the key to false:

mergableOptions: {
  globals: false;
}

For example, if you are using the meteor environment but want to explicitly import modules which are provided as globals, you can use this setting to overwrite the environment globals.

const globals = require('globals');
module.exports = {
  environments: ['meteor', 'node'],
  mergableOptions: {
    globals: false, // Overwrite globals
  },
  globals: [
    // Add the globals you want back in
    ...Object.keys(globals.builtin), // include javascript builtins
    ...Object.keys(globals.node), // include node globals
    'Package',
    'Npm', // Include meteor globals for `package.js` files
  ],
};

minimumVersion

Setting minimumVersion will warn people who are running a version of ImportJS that is older than what your .importjs.js configuration file requires. If your plugin version is older than this value, you will be shown a warning that encourages you to upgrade your plugin.

minimumVersion: '1.0.0';

moduleNameFormatter

Use a function here to control how the resulting module name string will look like. It's useful if you for instance want to add a custom prefix to certain imports. Apart from the standard pathToCurrentFile and pathToImportedModule values passed in to all configuration functions, this method is also passed a moduleName value, which in general is what you want to manipulate.

moduleNameFormatter({ moduleName, pathToCurrentFile }) {
 if (/-test/.test(pathToCurrentFile)) {
   // Import a mocked version in test files
   return `mocks/${moduleName}`;
 }

 if (moduleName.startsWith('foo')) {
   // Add a leading slash to foo imports
   return `/${moduleName}`;
 }

 // Fall back to the original specifier. It's important that this function
 // always returns a string.
 return moduleName;
},

namedExports

*Note: Since 2.1.0 ImportJS finds your named exports automatically. Most likely you don't need this option. If you end up having to use this configuration anyway, there might be a bug in the exports-finding parts of ImportJS. File an issue and tell us about it!

If you have an ES6/ES2015 module that exports multiple things (named exports), or a CommonJS module that exports an object with properties on it that you want to destructure when importing, you can add those to a namedExports configuration option.

namedExports: {
  underscore: [
    'omit',
    'debounce',
    'memoize'
  ],
  'lib/utils': [
    'escape',
    'hasKey',
  ],
}

Imports that use the import declaration keyword then use named imports syntax. e.g.

import { memoize } from 'underscore';

memoize(() => {
  foo();
});

and imports that use const or var use [ES2015 Destructuring Assigment][destructing assignment], e.g.

const { memoize } = require('underscore');

memoize(() => {
  foo();
});

The key used to describe the named exports should be a valid import path. This can be e.g. the name of a package found under node_modules, a path to a module you created yourself, or a relative import path.

Consider the example as a valid use case for the namedExports property. Let's say we have a file:

import { Provider } from 'react-redux';
import React from 'react';
import store from './redux/redux-store';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>,
  document.getElementById('root'),
);

And we're going to import BrowserRouter but instead of the desired result we get the No JS module to import for BrowserRouter message. In order to fix the problem, populate namedExports in your config file as follows:

namedExports: {
  'react-router-dom': ['BrowserRouter', 'Route', 'Redirect']
}

After that we are able to import BrowserRouter correctly. The resulting import statement will look like this:

import { BrowserRouter } from 'react-router-dom'

If you aren't ready for ES2015 yet, you have the option to use var or const instead.

declarationKeyword: 'const';

In such case, your import statements will look something like this:

var Foo = require('foo'); // "declarationKeyword": "var"
const Foo = require('foo'); // "declarationKeyword": "const"

parserPlugins

ImportJS defaults to a reasonable compromise for what syntax to support but can be overridden (replaced) in configuration. The latest defaults can be found here

Available plugins are over at Babel: Plugins List

Example: Remove all preconfigured defaults

parserPlugins: []

Example: Add pipeline operator (hack proposal)

When parserPlugins is specified you need to re-add the defaults.

parserPlugins: [
  'jsx',
  'doExpressions',
  'objectRestSpread',
  'decorators-legacy',
  'classProperties',
  'classPrivateProperties',
  'classPrivateMethods',
  'exportExtensions',
  'asyncGenerators',
  'functionBind',
  'functionSent',
  'dynamicImport',
  'numericSeparator',
  'optionalChaining',
  'importMeta',
  'bigInt',
  'optionalCatchBinding',
  'throwExpressions',
  'nullishCoalescingOperator',
  'exportNamespaceFrom',
  'exportDefaultFrom',
  [
    'pipelineOperator',
    {
      proposal: 'hack',
    },
  ],
];

sortImports

By default, ImportJS will sort imports by the name or path of the imported module.

You can turn off this behavior by setting sortImports to false. When disabled, existing imports are not rearranged, and new imports are always added above existing imports.

sortImports: false;

stripFileExtensions

An array that controls what file extensions are stripped out from the resulting import statement. The default configuration strips out [".js", ".jsx", ".ts", ".tsx"]. Set to an empty array [] to avoid stripping out extensions.

stripFileExtensions: ['.web.js', '.js'];

tab

Defaults to two spaces (" "). This setting controls how indentation is constructed when import statements are broken into multiple lines.

tab: '\t';

useRelativePaths

This option is enabled by default. When enabled, imports will be resolved relative to the current file being edited.

import Foo from './foo';
import Bar from '../baz/bar';

You can disable this by setting it to false:

useRelativePaths: false;

Package dependencies (located in node_modules) will not be imported relatively.

Dynamic configuration

Different sections of your application may have special importing needs. For instance, your tests might need the 'const' declaration keyword, but the rest of your application can use 'import'. To be able to target these special cases, you can turn your configuration option into a function. When ImportJS resolves a configuration option, it will check to see if a function is used. In such case, the function is invoked with the following arguments:

  • pathToCurrentFile: (always available) A path to the file you are editing.
  • pathToImportedModule (not available for some options) A path to the file/module you are importing.

Here's an example of how to dynamically control the declarationKeyword configuration option based on the file you are importing:

// .importjs.js
function isTestFile(path) {
  return path.endsWith('-test.js');
}

module.exports = {
  declarationKeyword({ pathToImportedModule }) {
    if (isTestFile(pathToImportedModule)) {
      return 'const';
    }
    return 'import';
  },
};

Here's a more elaborate example taking both pathToImportedModule and pathToCurrentFile into account:

module.exports = {
  useRelativePaths({ pathToImportedModule, pathToCurrentFile }) {
    if (pathToCurrentFile.endsWith('-mock.js')) {
      return false;
    }
    if (pathToImportedModule.endsWith('-test.js')) {
      return false;
    }
    return true;
  },
};

In order to use functions, you need to use the JavaScript configuration file (.importjs.js).

Command-line tool

ImportJS comes with a handy command-line tool that can help you perform importing outside of an editor. Under the hood, this is what most of the editor integrations use.

โจ  importjs --help

  Usage: importjs [options] [command]


  Commands:

    word [options] <word> <pathToFile>
    search [options] <word> <pathToFile>
    fix [options] <pathToFile>
    rewrite [options] <pathToFile>
    add [options] <imports> <pathToFile>
    goto <word> <pathToFile>
    start [options]                       start a daemon
    cachepath                             show path to cache file
    logpath                               show path to log file

  Options:

    -h, --help     output usage information
    -V, --version  output the version number

  Examples:

    $ importjs word someModule path/to/file.js
    $ importjs search someModule* path/to/file.js
    $ importjs fix path/to/file.js
    $ importjs rewrite --overwrite path/to/file.js
    $ importjs add '{ "foo": "path/to/foo", "bar": "path/to/bar" }' path/to/file.js
    $ importjs goto someModule path/to/file.js
    $ importjs cachepath
    $ importjs logpath
    $ importjs start --parent-pid=12345

Batch-rewriting

If you want to change how imports are constructed in an existing project, you can use the command-line tool in combination with find to batch-update a set of files. E.g.

find ./app -name "**.js*" -exec importjs rewrite --overwrite {} \;

Since the --overwrite flag makes ImportJS destructive (files are overwritten), it's a good thing to double-check that the find command returns the right files before adding the -exec part.

Specifying alternate package directory

ImportJS looks for the package.json file in the closest ancestor directory for the file you're editing to find node modules to import. However, sometimes it might pull dependencies from a directory further up the chain. For example, your directory structure might look like this:

.
|-- package.json
|-- components
|     |-- button.js
|     |-- icon.js
|-- node_modules
|     |-- react
|-- subpackage
|     |-- package.json
|     |-- components
|           |-- bulletin.js

If you were to use ImportJS on subpackage/components/bulletin.js which imports React, ImportJS would not know that react is a valid dependency.

To tell ImportJS to skip a directory and keep searching upwards to find the root package directory, specify "importjs": { "isRoot": false } in the package.json of the directory to ignore. In this case, you would want something like this:

{
  "name": "subpackage",
  ...
  "importjs": {
    "isRoot": false
  }
}

Running as a daemon

Note: This section is intended mostly for developers of editor plugins. If you are using one of the standard editor plugins, you are most likely using the daemon under the hood already.

You can run ImportJS in a background process and communicate with it using stdin and stdout. This will make importing faster because we're not spinning up a node environment on every invocation.

The daemon is started by running running importjs. It accepts commands sent via stdin. Each command is a (oneline) JSON string ending with a newline. The command structure is basically the same as for the command-line tool, but wrapped in JSON instead of expressed on the command line. Here are a few examples:

Run fix imports:

{
  "command": "fix",
  "fileContent": "const foo = bar();\n",
  "pathToFile": "foo.js"
}

Import a single word:

{
  "command": "word",
  "commandArg": "bar",
  "fileContent": "const foo = bar();\n",
  "pathToFile": "foo.js"
}

Goto:

{
  "command": "goto",
  "commandArg": "bar",
  "fileContent": "const foo = bar();\n",
  "pathToFile": "foo.js"
}

Results are printed to stdout in JSON format. The response will look the same as what the command-line tool produces. If an error occurs, it will also end up in stdout as JSON (an object with an error key).

On startup, the daemon will print a path to a logfile. If you want to find out what's going on behind the scenes, you can inspect this file. If you don't have access to the console log of the daemon, you'll find the logfile in os.tmpdir() + '/importjs.log (which will resolve to something like var/folders/1l/_t6tm7195nd53936tsvh2pcr0000gn/T/importjs.log on a Mac).

Speeding it up!

If you have a large application, traversing the file system to find modules can be slow. That's why ImportJS has built-in integration with Watchman, a fast and robust file watching service developed by Facebook. All you have to do to get a performance boost is to install watchman locally, and make sure to use an up-to-date editor plugin (Watchman is only used when ImportJS is run as a daemon).

Contributing

See the CONTRIBUTING.md document for tips on how to run, test and develop ImportJS locally.

Thank you:

  • @janpaul123 for writing the Sublime plugin.
  • @kevinkehl for getting the parentheses right for the Emacs plugin
  • @rhettlivingston for making import-js work for Meteor, and for driving the development forward by bringing in lots of experience and great ideas.
  • @dabbott for writing the VS Code plugin.

Happy hacking!

import-js's People

Contributors

asfktz avatar coagmano avatar coaxial avatar dabbott avatar dagolinuxoid avatar dependabot[bot] avatar flarnie avatar hontas avatar ianvs avatar ivangeorgiew avatar janpaul123 avatar jeffwillette avatar kevinkehl avatar lencioni avatar mikabytes avatar milahu avatar mutewinter avatar paradoxxxzero avatar philolo1 avatar reohjs avatar rhettlivingston avatar rndmerle avatar syohex avatar trotzig avatar trysound avatar tungv avatar vinpac avatar williamboman avatar wyuenho 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar

import-js's Issues

Make Emacs, Sublime, and vim plugin support same set of features

As I've been working to improve the Sublime plugin, I've enhanced the command-line interface to support more usecases. I've noticed that the Emacs plugin only supports a subset of the commands available:

  • Import single variable
  • Goto

The Sublime and vim plugins both support all of these commands:

  • Import single variable
  • Import all undefined variables
  • Fix imports
  • Goto

They're also able to resolve variables that resolve to multiple js modules by bringing up a selection list to pick from.

I want import-js to be the same across the board, and it would be great if Emacs could catch up. @kevinkehl: would it make sense for the Emacs plugin to also use the command line tool? I know that it uses a buffer to send messages back and forth, but there might be some things there that could be simplified by using the cli tool instead. import-js has since the first release been enhanced in a number of ways:

  • The resulting content is written to stdout, and nothing else
  • stderr is used for messaging - it will contain a json string with messages and ask_for_selections keys.
  • When the user has selected something (because ask_for_selections was in the json), you can rerun the command and pass in the selection using --selections foo:1,bar:3.
  • If --goto is passed in, the stdout will contain a path to the file to open.
  • --goto can also be used together with --selections.

I'm happy to work on this if you don't have time/feel up for it @kevinkehl. But it would be good to hear your thoughts first.

Multi-line destructuring

As lines get longer, it would probably make sense for destructuring to be broken out into one variable per line, such as seen in the following code snippet:

import React from 'react';
import { flatten, unique, compact } from 'underscore';
import cheerio from 'cheerio';
import {
  nodeEqual,
  propFromEvent,
  withSetStateAllowed,
  propsOfNode,
} from './Utils';
import {
  debugNodes,
} from './Debug';
import {
  getTextFromNode,
  hasClassName,
  childrenOfNode,
  parentsOfNode,
  treeFilter,
  buildPredicate,
} from './ShallowTraversal';
import {
  createShallowRenderer,
  renderToStaticMarkup,
} from './react-compat';

Allow packages to define things that can be destructured

It would be nice if packages could add some data that would prevent consumers of the package from having to add a set of destructured properties to their import-js config. For instance, the set of things that could be destructured from underscore are pretty stable and don't really need to be defined for each individual consumer. Maybe this could be added to the package.json file or to a different file that is distributed with the package.

Vim cursor disappears when running fix imports

In a file run the fix imports command. In my Vim, the cursor disappears until I try moving it again. This seems a bit awkward so it might be nice to find a way to prevent it from happening.

I think I've traced it down to this line where we run the eslint command in Importer:

out, err = Open3.capture3(command,
                          stdin_data: @editor.current_file_content)

but I'm not entirely sure why this has this side-effect.

This is a really minor annoyance, but I figured I'd log it here in case someone happens to know of an easy fix.

Dynamic aliases

I am working in an application components are not structured into directories that contain index.jsx, index.scss, etc. In this structure, components are in files like MyComponent.jsx and their styles are in files like MyComponent.scss. In this case, it would be nice to be able to make a dynamic alias for styles that resolves to the current filename plus .scss.

Messes up 'use strict';

When adding 'use strict'; to the top of a file to enable strict mode for the file, the 'use strict'; declaration must be at the top of the file. However, ImportJS puts its imports above this declaration, causing it to not work.

Problems installing import-js with gem for Sublime Text

When i try to install import-js by gem it shows an error.

sudo gem install import-js

gem install import-js
ERROR:  Could not find a valid gem 'import-js' (>= 0) in any repository
ERROR:  Possible alternatives: import_js

So I tried sudo gem install import_js

ERROR:  Error installing import_js:
    slop requires Ruby version >= 2.0.0.

How can I solve this? I really want to use this :)

New destructured wrapping breaks on subsequent runs

If you have code and configuration that will result in fixing imports generating this:

import Blah, {
  Bar,
  Baz,
  Bazz,
  Buzz,
  Fizz,
  Foo,
  Ooze,
  Poo,
} from 'something';

running fix imports again incorrectly changes it to:

import Blah, {
  Bar,
  Baz,
  Bazz,
  Buzz,
  Fizz,
  Foo,
  Ooze,
  Poo,
} from
  'something';

Extract core functionality into standalone program

We have people on our team who use Vim, Emacs, and Sublime Text. It seems like each of these editors could benefit from this functionality, so it might be nice to extract the core of this plugin into a standalone program that individual plugins for each editor could then depend on.

Match dashed and camel cased filenames

It seems like import-js doesn't find files if they use dashed or camel-cased filenames. My first thought is that by default it should look for all matching filenames that are either snake-cased, dashed, or camel cased, and users can configure it to only use one if they prefer. That way the default works for everyone, no configuration required.

The alternative would be to pick one as the default and allow users to configure it to use one or more of the other patterns.

Does not understand line breaks

If I have a section of required modules at the top that looks like this:

const MyModule = require('path/to/my_module');
const MyModuleThatHasALongName =
  require('path/to/my_module_that_has_a_long_name');
const SomeModule = require('path/to/some_module');

import-js gets confused when adding a new module to the list:

const AnotherModule = require('path/to/another_module');
const MyModule = require('path/to/my_module');

const MyModuleThatHasALongName =
  require('path/to/my_module_that_has_a_long_name');
const SomeModule = require('path/to/some_module');

where I would expect:

const AnotherModule = require('path/to/another_module');
const MyModule = require('path/to/my_module');
const MyModuleThatHasALongName =
  require('path/to/my_module_that_has_a_long_name');
const SomeModule = require('path/to/some_module');

It would be nice if import-js worked better with this format.

Bonus points would be specifying a character limit (defaulted to 80) that it would try to stay within when importing new dependencies.

Prefer local eslint

In #114 I improved some error reporting that happens when packages that are expected by the local project's .eslintrc file are not installed globally. It would be nice, however, if import-js preferred the locally installed eslint over the globally installed one, so it could just rely on these packages being installed locally.

Normally, to execute local node_modules binaries, I do something like $(npm bin)/executable since npm bin will output the path to the local node_modules/.bin directory.

Fixing imports does not remove things from destructured imports

If you have code and configuration that will result in fixing imports generating this:

import Blah, {
  Bar,
  Baz,
  Bazz,
  Buzz,
  Fizz,
  Foo,
  Ooze,
  Poo,
} from 'something';

and then removing some or all of the references to variables that are imported and run fix imports again, all of them stay. This does not seem to be a problem with imports that are not destructured.

Add sublime text plugin

Now that import-js has extracted the core into a standalone gem, it can be integrated more easily with additional editors. We already have emacs support (#61) and we should add sublime text support.

Feature request: add configuration to allow for automatic destructuring

Let's say that you want to use a library that exports a single object that has a bunch of objects on it, but you want to just use those objects directly. It would be nice if import-js were able to be set up to allow for automatic destructuring.

For example, if I am using underscore, but instead of wanting to reference things like _.memoize() and _.debounce() I want to use memoize() and debounce(), it would be nice to be able to use import-js on the memoize() function call to grab the dependency like this:

const { memoize } = require('underscore');

And if I trigger it again on another function call from the same package, it should add to the destructuring:

const { debounce, memoize } = require('underscore');

Aliases with ".js*" in the end are stripped from file extension

I was in a situation where I needed to import shared_config.json (not shared_config). If I add "SharedConfig": "shared_config.json" to the aliases configuration, I still get this when I import:

const SharedConfig = require('shared_config');

In this case the file ending is important (the import won't work withouth it).

Improve performance

Sometimes when I run import-js, it takes a while for it to figure out what to import. I would like to set it up to run whenever I save a .js file so I can stop managing my require statements entirely, but before I do that it would be great if we could make this run faster.

One thing that might help would be to use some caching in the find_files method. It might work well to swap out the Dir calls for command line tools if they are available. Taking it a step further, it might be interesting to be able to optionally tap into other plugins' file listings, such as Command-T's file listing.

Add Atom plugin

Now that import-js has extracted the core into a standalone gem, it can be integrated more easily with additional editors. We already have emacs and Sublime Text support and we should add Atom support.

Allow separate configuration for different directory trees

If I am working on an app that uses JavaScript for the client side and Node.js for the server side, and I want to use ES2015 modules (ESM) import syntax on the client side, I am currently somewhat out of luck with import-js since Node.js doesn't yet support ESM and I don't really see it on their roadmap. For these cases, it would be nice if import-js had a way where I could use const for my server code and import for my client code.

One option would be to allow this to be configured in the .importjs.json file. Another option would be to allow .importjs.json files to be added wherever and then merged into the main configuration. I think this is the approach that ESLint takes, FWIW.

I'm not really sure if the juice is worth the squeeze here, but I figured I'd open this issue while I was thinking about it.

Import all doesn't work well with ESLint and eslint-plugin-react

Since we are adding the ESLint control comment to the top of the file for detecting undefined variables

/* eslint no-unused-vars: [2, { "vars": "all", "args": "none" }] */

this will work well for regular variables. However, if you are using JSX and React, we need an additional option and access to the eslint-plugin-react plugin, since that plugin is responsible for finding reference errors for components. https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md

/* eslint react/jsx-no-undef: 2 */

Rename to vim-import-js?

Many Vim plugins are prefixed with vim-, which I think helps people find the plugins, understand what they are used for, and prevents naming collisions. I think it might be worth considering renaming this to vim-import-js, unless you plan on expanding it to support other editors, like Sublime. What do you think?

Feature request: exclude devDependencies

Your code should typically not depend on devDependencies. It would be nice if import-js didn't match on files that were in node_modules directories that belong to devDependencies.

Match directories that have `main` defined in `package.json`

Similar to #26:

index.js may resolve to another file if defined in the package.json

So if you have a node_module at something like node_modules/my-package/dist/my-package.js and the package.json file in that directory defines its main as dist/my-package.js, it would be really great if import-js would match on my-package and not my-package/dist/my-package.js.

Enhancement: prevent finding nested dependencies

If you are using import-js in a project that uses npm, you might have a folder structure that looks like:

node_modules/package/node_modules/something_else/node_modules/another_thing/utils.js

However, you should never require something that your app doesn't directly depend on, so it would be nice if anything in a nested node_modules directory was simply excluded from import-js.

Perhaps import-js should have a notion of excluded patterns and allow people to specify whatever fits their app? e.g.

"exclude": [
  "node_modules/**/node_modules"
]

node_modules importing doesn't seem to be working as I expected

I am working in a large project where I am trying to get import-js set up for the first time. In this project we have moment in our package.json file and I'd like to import it into a new module. However, when matching modules, import-js presents me with a list like the following:

ImportJS: Pick JS module to import for 'moment':
1: moment (main: ./moment.js)
2: node_modules/some-package/node_modules/convict/node_modules/moment (main: ./moment.js)
3: node_modules/some-package/node_modules/convict/node_modules/moment/moment
4: node_modules/some-package/node_modules/convict/node_modules/moment/package
5: node_modules/casual/node_modules/moment (main: ./moment.js)
6: node_modules/casual/node_modules/moment/moment
7: node_modules/casual/node_modules/moment/package
8: node_modules/moment (main: ./moment.js)
9: node_modules/moment/moment
10: node_modules/moment/package

Perhaps my memory is failing me, but I expected this to resolve to just one module, which happens to be the first on this list (see e237f86).

Delete unused imports on save

Wouldn't it be cool to configure your editor to run an import-cleanup task on save? Something that would find unused imports, and delete the ones not used. And potentially also import anything that's not been imported, but I can see that as a next step.

Support for relative paths

As seen in this code snippet, some projects prefer relative paths for non-node_modules modules:

import cheerio from 'cheerio';
import Sinon from 'sinon';
import ReactWrapper from './ReactWrapper';
import ShallowWrapper from './ShallowWrapper';
import { onPrototype } from './Utils';
import { renderToStaticMarkup } from './react-compat';

It would be nice if ImportJS was able to support this.

Allow configuration to leave file extensions intact

In some projects I prefer to configure webpack to not automagically add the file extension (.js or .jsx) to require statements, and then when using import-js I have to manually add it to the generated require statement.

We could add a configuration setting for 'include_file_extensions' that would allow import-js to leave the file extension as part of the require path.

Running the Sublime plugin in Windows

The ImportJS package in Package Control is only available to OSX users. I choose to make that limitation because I don't have a Linux or Windows environment available to test things in at the moment.

@janpaul123, @shreeya, @kevinkehl, @lencioni: Does anyone have another environment than OSX where they would be willing to test import-js? I'd mainly like to see how the Sublime plugin works, but I guess it would be good to test the Emacs and vim plugins too if possible.

Remove dependency on Ruby

Depending on Ruby makes the plug-in harder to install (trust me, I know...) and the Ruby bit is actually pretty small, so it might be worth porting it to vimscript.

Make import all work with eslint

At Brigade we have recently switched from jshint/jsxhint to eslint, and it would be nice if this plugin worked with the eslint tool in the same way that it works with jshint/jsxhint.

Truncate logs to prevent having to confirm

Sometimes, when you're importing a module with a long name/path to, the Vim buffer width isn't big enough to display the full message. In those cases, you have to hit Enter in order to continue doing what you were doing in the file. We could fix this by looking at the width of the buffer and automatically truncating the log message.

Auto import first match?

I think that most times there are multiple matches, I want the first match. If import-js picked the first match in the priority order as defined in the configuration file, using the plugin would be a lot smoother. It could also stop searching after finding the first match which would make it faster in most cases.

Error from `importjs#ImportJSImport` command

When I try to use 'importJS' I have been seeing this error:

Error detected while processing /Users/paradasia/.vim/bundle/import-js/autoload/importjs.vim:
line 20:
SyntaxError: (eval):1:in require': /Users/paradasia/.vim/bundle/import-js/ruby/import-js/importer.rb:71: syntax error, unexpected '.', expecting kEND Error detected while processing function importjs#ImportJSImport: line 1: NoMethodError: undefined methodimport' for nil:NilClass
Press ENTER or type command to continue

I'm going to look into it a bit and see if I can find the problem. Since this does not happen on my work laptop, I assume it is related to something in how my environment is set up.

Importing default and destructuring unnecessarily creates two separate imports when using import declaration keyword

Currently, with aliases configuration like the following:

"React": {
  "path": "react",
  "destructure": [
    "PropTypes"
  ]
}

importing both React and PropTypes will result in:

import React from 'react';
import { PropTypes } from 'react';

but it would be better if it instead resulted in:

import React, { PropTypes } from 'react';

I'm not sure if there is any equivalent syntax for CommonJS imports, but it would be really nice if ImportJS did this when using the import declaration keyword. I suppose if you were using CommonJS, it would be better to have one require and one assignment after the fact than two requires:

const React = require('react');
const { PropTypes } = React;

Aliases with slashes drop their slashes

I have the following alias:

"aliases": {
  "React": "react/addons"
},

And when I import React, I get the following code:

const React = require('reactaddons');

but I expect to get:

const React = require('react/addons');

Add command to remove unused modules

When working on a module I sometimes end up removing all references to a module that I had previously depended on. It would be nice if import-js could automatically remove the require statements for these for me similar to how it can add all missing modules by running :ImportJSImportAll.

I think importing and removing could be implemented as separate commands, and a third command could be added to do both at the same time--like an auto cleanup imports command. Or, if you don't see the value in keeping them separate, just roll it all into one command.

With this feature, it might work really well to automatically run this when saving .js files with something like:

autocmd BufWritePre *.js :ImportJSAutoFix

Then I could simply stop thinking about managing this bit of code in every file, freeing up my mind to think about more important things.

Auto-break long lines

It would be great if I could specify a character limit (defaulted to 80) that import-js would try to stay within when importing new dependencies.

So instead of import-js generating this:

const MyModule = require('path/to/my_module');
const MyModuleThatHasALongName = require('path/to/my_module_that_has_a_long_name');

it would generate this:

const MyModule = require('path/to/my_module');
const MyModuleThatHasALongName =
  require('path/to/my_module_that_has_a_long_name');

This is similar to #9.

Vim plugin picks up installed gem

My vim wasn't picking up changes I made in the local import-js project. As part of autoloading the vim plugin, require 'import_js' is run, and that will pick up any installed gems before the code in the lib folder.

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.