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, or Vim.
There are ImportJS plugins for the following editors:
- Atom
- Emacs (Thanks to @kevinkehl!)
- Sublime (Thanks to @janpaul123)
- Vim
- (your editor here?)
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.
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.
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 React, ImportJS will automatically import React
for you, but
only if you have eslint-plugin-react installed.
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).
- Only files ending in
.js\*
are considered when importing - As part of resolving imports, all imports will be sorted and placed into
groups. Grouping can be disabled, see the
groupImports
configuration option. - You can speed up importing by installing (Watchman)[https://facebook.github.io/watchman/]. See Speeding it up! for more information.
ImportJS is configured through a JavaScript file (.importjs.js
). Save the
configuration file in the root folder of your project.
The following configuration options can be used.
excludes
aliases
environments
namedExports
declarationKeyword
groupImports
importDevDependencies
importFunction
stripFileExtensions
ignorePackagePrefixes
minimumVersion
maxLineLength
moduleNameFormatter
tab
logLevel
Define a list of glob patterns that match files and directories that you don't want to include for importing.
excludes: [
'react-components/**/test/**',
]
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',
}
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';
This list of environments controls what core modules are available when importing. The supported values right now are
["meteor"]
- automatically make the core modules for Meteor available for ImportJs["node"]
- automatically make all the core modules for Node available for ImportJS
environments: ['meteor', 'node']
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',
],
'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.
The default value for this property is import
, making your import statements
use the ES2015 modules syntax:
import Foo from 'foo';
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"
By default, ImportJS will put imports into groups:
- Core modules
- Package dependencies
- 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
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
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'
An array that controls what file extensions are stripped out from the resulting
import statement. The default configuration strips out [".js", ".jsx"]
. Set to
an empty array []
to avoid stripping out extensions.
stripFileExtensions: ['.web.js', '.js']
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
.
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'
Defaults to 80
. This setting controls when import statements are broken into
multiple lines.
maxLineLength: 70
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;
},
Defaults to two spaces (" "
). This setting controls how indentation is
constructed when import statements are broken into multiple lines.
tab: '\t'
One of ["debug", "info", "warn", "error"]
. This controls what ends up in the
logfile (mostly used when ImportJS is run as a daemon
process. 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
importjsd logpath
.
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';
},
}
In order to use functions, you need to use the JavaScript configuration file
(.importjs.js
).
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>
fix [options] <pathToFile>
rewrite [options] <pathToFile>
add [options] <imports> <pathToFile>
goto <word> <pathToFile>
Options:
-h, --help output usage information
-V, --version output the version number
Examples:
$ importjs word 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
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.
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 importjsd
. 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).
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).
See the CONTRIBUTING.md document for tips on how to run, test and develop ImportJS locally.
Happy hacking!