ImportJS 是一个在 JavaScript 项目中自动导入依赖项的工具。将其与我们针对 Atom、Emacs、Sublime、Vim 或 VS Code 的编辑器集成之一结合使用。
有适用于以下编辑器的 ImportJS 插件:
有关如何安装 ImportJS 的详细说明可以在上面的编辑器链接中找到。
想要添加另一个编辑器到列表中吗?了解如何做出贡献。
ImportJS 使用 Babel 7 3.1.0 版本。在大多数情况下,Babel 7 向后兼容 Babel 6,但如果您遇到问题(例如关于装饰器的问题),请考虑安装先前版本的 ImportJS(例如 3.0.0)或将项目更新为 Babel 7兼容的。
假设您有一个具有以下结构的 JavaScript 项目:
.
|-- index.html
|-- components
| |-- button.js
| |-- icon.js
|-- vendor
| |--
|-- pages
| |-- index.js
现在,假设您正在编辑pages/index.js
,其中包含:
document . createElement ( new Button ( { text : 'Save' } ) . toDOMElement ( ) ) ;
此时, Button
尚未定义,因此我们需要导入它。如果您习惯于手动执行此操作,则这涉及到确定定义Button
JavaScript 模块的路径。使用 ImportJS,您可以将光标放在单词“Button”上,然后点击<leader>j
(Vim)、 (Mx) import-js-import
(Emacs),或选择“ImportJS:导入光标下的单词”(Sublime)。文件缓冲区现在将更改为以下内容:
import Button from '../components/button' ;
document . createElement ( new Button ( { text : 'Save' } ) . toDOMElement ( ) ) ;
基本上就是这样。 ImportJS 将帮助您查找模块并自动添加import
语句。但是,请继续阅读以获得更多简洁的功能。
ImportJS 可用于自动修复当前文件中的所有导入。通过点击<leader>i
(Vim)、 (Mx) import-js-fix
(Emacs) 或选择ImportJS: fix all imports
(Sublime),所有未定义的变量都将被解析,并且所有未使用的导入都将被删除。
如果您使用 JSX,ImportJS 将不再自动为您导入React
。如果您需要此功能,请考虑使用 ImportJS 版本 5.1.0 或更早版本。 React 17 中不再需要使用 React 导入来使用 JSX。在此处阅读更多信息
由于 ImportJS 非常擅长查找 JS 模块,因此有一个打开/转到文件而不是导入文件的选项是有意义的。这类似于 Vim 内置的“打开光标下的文件”。通过将光标放在变量上并点击<leader>g
(Vim)、 (Mx) import-js-goto
(Emacs) 或选择“ImportJS: goto module”(Sublime) 来使用它。
.js*
和.ts*
结尾的文件groupImports
和sortImports
配置选项。如果注释和空格都被禁用,则将保留它们。ImportJS 通过 JavaScript 文件 ( .importjs.js
) 配置。
该文件需要导出包含配置设置的单个对象,如下例所示。
module . exports = {
excludes : [ './react-components/**/test/**' ] ,
// continue with the rest of your settings...
} ;
将此文件保存在项目的根文件夹中(例如,package.json 文件所在的位置)。如果您想在不同项目之间共享全局配置,您也可以将其保存到用户主目录。
支持以下配置选项。
aliases
danglingCommas
declarationKeyword
emptyLineBetweenGroups
environments
excludes
globals
groupImports
ignorePackagePrefixes
importDevDependencies
importFunction
importStatementFormatter
logLevel
maxLineLength
mergableOptions
minimumVersion
moduleNameFormatter
namedExports
parserPlugins
sortImports
stripFileExtensions
tab
useRelativePaths
aliases
某些变量名称可能无法轻松映射到文件系统中的文件。对于这些,您可以将它们添加到aliases
配置中。
aliases: {
$ : 'third-party-libs/jquery' ,
_ : 'third-party-libs/underscore' ,
}
danglingCommas
默认情况下,ImportJS 在构建具有多个命名导入的导入语句时将添加尾随逗号。
您可以通过将danglingCommas
设置为false
来关闭此行为。
danglingCommas: false ;
declarationKeyword
该属性的默认值为import
,使您的 import 语句使用 ES2015 模块语法:
import Foo from 'foo' ;
可以使用{filename}
字符串使别名动态化。这部分别名将替换为您当前正在编辑的文件的名称。
例如
aliases: {
styles : './{filename}.scss' ,
}
对于文件foo/bar.js
将导致
import styles from './bar.scss' ;
emptyLineBetweenGroups
默认情况下,ImportJS 将在导入组之间插入空行。
您可以通过将emptyLineBetweenGroups
设置为false
来关闭此行为。
emptyLineBetweenGroups: false ;
environments
此环境列表控制导入时可用的核心模块以及默认情况下哪些变量被视为全局变量。目前支持的值为
['meteor']
- 使 Meteor 的核心模块可用,并添加一堆流星全局变量['node']
- 使 Node 的所有核心模块可用,并添加一堆节点全局变量['browser']
- 添加一堆浏览器全局变量['jasmine']
- 添加一堆 jasmine 全局变量['jest']
- 添加一堆笑话全局变量environments: [ 'meteor' , 'node' ] ;
excludes
定义与您不想包含在导入中的文件和目录相匹配的全局模式列表。
excludes: [ './react-components/**/test/**' ] ;
globals
提供代码中使用的全局标识符的列表。当尝试导入所有未定义的变量时,ImportJS 将忽略这些。
注意:如果正确使用environments
配置选项,则可能不需要指定 globals 。
groupImports
默认情况下,ImportJS 会将导入分组:
您可以通过将groupImports
设置为false
来关闭此行为。禁用后,导入会按字母顺序列在一个列表中。
groupImports: false ;
ignorePackagePrefixes
如果您在package.json
中指定了以组织名称为前缀的包依赖项,但希望能够在没有包前缀的情况下导入这些依赖项,则可以设置ignorePackagePrefixes
配置选项。
ignorePackagePrefixes: [ 'my-company-' ] ;
当包依赖项匹配时,这些前缀将被忽略。例如,名为validator
的变量将与名为my-company-validator
包匹配。
importDevDependencies
ImportJS 在导入时会查找package.json
中列出的包依赖项。默认情况下,仅使用在dependencies
和peerDependencies
下列出的模块。通过将importDevDependencies
设置为true
, devDependencies
也将被考虑在内。
importDevDependencies: true ;
importFunction
注意:这仅适用于您使用var
或const
作为declarationKeyword
情况。
此配置选项的默认值为"require"
,这是用于导入的标准 CommonJS 函数名称。
importFunction: 'myCustomRequireFunction' ;
importStatementFormatter
此处使用函数来控制生成的导入语句的外观。例如,如果您想要删除尾随分号(ImportJS 默认添加),这非常有用。
注意:此方法只应在极少数情况下使用。当下次要导入某些内容时,ImportJS 有可能无法识别生成的导入语句。
importStatementFormatter ( { importStatement } ) {
return importStatement . replace ( / ;$ / , '' ) ;
} ,
logLevel
["debug", "info", "warn", "error"]
之一。这控制日志文件中最终的内容。默认为info
。
logLevel: 'debug' ;
日志文件写入操作系统默认临时文件目录中的“importjs.log”。您可以通过运行importjs logpath
来获取日志文件的路径。
maxLineLength
默认为80
。此设置控制导入语句何时分成多行。
maxLineLength: 70 ;
mergableOptions
与environment
提供的默认值和值合并的选项字典。这可用于覆盖环境提供的选项。默认为:
mergableOptions: {
aliases : true ,
coreModules : true ,
namedExports : true ,
globals : true ,
}
注意: mergableOptions
选项将始终被合并,并且如果包含在用户配置中将被忽略。
要禁用合并特定选项或选项集,请将键设置为false
:
mergableOptions: {
globals: false ;
}
例如,如果您正在使用meteor
环境,但想要显式导入作为全局变量提供的模块,则可以使用此设置来覆盖环境全局变量。
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
设置minimumVersion
将警告正在运行比.importjs.js
配置文件要求的版本更旧的ImportJS版本的人。如果您的插件版本低于此值,您将看到一条警告,鼓励您升级插件。
minimumVersion: '1.0.0' ;
moduleNameFormatter
此处使用函数来控制生成的模块名称字符串的外观。例如,如果您想向某些导入添加自定义前缀,那么它会很有用。除了传递给所有配置函数的标准pathToCurrentFile
和pathToImportedModule
值之外,此方法还传递一个moduleName
值,这通常是您想要操作的值。
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
*注意:从 2.1.0 开始,ImportJS 会自动查找您的命名导出。您很可能不需要此选项。如果您最终不得不使用此配置,则 ImportJS 的导出查找部分可能存在错误。提出问题并告诉我们!
如果您有一个导出多个内容(命名导出)的 ES6/ES2015 模块,或者导出一个带有您想要在导入时解构的属性的对象的 CommonJS 模块,您可以将它们添加到namedExports
配置选项中。
namedExports: {
underscore : [
'omit' ,
'debounce' ,
'memoize'
] ,
'lib/utils' : [
'escape' ,
'hasKey' ,
] ,
}
使用import
声明关键字的导入然后使用命名导入语法。例如
import { memoize } from 'underscore' ;
memoize ( ( ) => {
foo ( ) ;
} ) ;
使用const
或var
导入使用 [ES2015 解构赋值][解构赋值],例如
const { memoize } = require ( 'underscore' ) ;
memoize ( ( ) => {
foo ( ) ;
} ) ;
用于描述命名导出的键应该是有效的导入路径。例如,这可以是在node_modules
下找到的包的名称、您自己创建的模块的路径或相对导入路径。
将该示例视为namedExports
属性的有效用例。假设我们有一个文件:
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' ) ,
) ;
我们将导入BrowserRouter
但我们得到的不是所需的结果,而是No JS module to import for BrowserRouter
消息。为了解决该问题,请在配置文件中填充namedExports
,如下所示:
namedExports: {
'react-router-dom' : [ 'BrowserRouter' , 'Route' , 'Redirect' ]
}
之后我们就可以正确导入BrowserRouter
了。生成的导入语句将如下所示:
import { BrowserRouter } from 'react-router-dom'
如果您还没有准备好迎接 ES2015,您可以选择使用var
或const
。
declarationKeyword: 'const' ;
在这种情况下,您的导入语句将如下所示:
var Foo = require ( 'foo' ) ; // "declarationKeyword": "var"
const Foo = require ( 'foo' ) ; // "declarationKeyword": "const"
parserPlugins
ImportJS 默认对支持的语法进行合理的折衷,但可以在配置中覆盖(替换)。最新的默认值可以在这里找到
可用插件位于 Babel:插件列表
parserPlugins: [ ]
hack
建议)当指定parserPlugins
时,您需要重新添加默认值。
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
默认情况下,ImportJS 将按导入模块的名称或路径对导入进行排序。
您可以通过将sortImports
设置为false
来关闭此行为。禁用后,现有导入不会重新排列,并且新导入始终添加到现有导入之上。
sortImports: false ;
stripFileExtensions
一个数组,用于控制从生成的导入语句中删除哪些文件扩展名。默认配置会删除[".js", ".jsx", ".ts", ".tsx"]
。设置为空数组[]
以避免剥离扩展名。
stripFileExtensions: [ '.web.js' , '.js' ] ;
tab
默认为两个空格 ( " "
)。此设置控制当导入语句分为多行时如何构造缩进。
tab: 't' ;
useRelativePaths
默认情况下启用此选项。启用后,导入将相对于当前正在编辑的文件进行解析。
import Foo from './foo' ;
import Bar from '../baz/bar' ;
您可以通过将其设置为 false 来禁用它:
useRelativePaths: false ;
包依赖项(位于node_modules
)不会相对导入。
您的应用程序的不同部分可能有特殊的导入需求。例如,您的测试可能需要'const'
声明关键字,但应用程序的其余部分可以使用'import'
。为了能够针对这些特殊情况,您可以将配置选项转变为函数。当 ImportJS 解析配置选项时,它将检查是否使用了某个函数。在这种情况下,将使用以下参数调用该函数:
pathToCurrentFile
:(始终可用)您正在编辑的文件的路径。pathToImportedModule
(对于某些选项不可用)正在导入的文件/模块的路径。以下是如何根据要导入的文件动态控制declarationKeyword
配置选项的示例:
// .importjs.js
function isTestFile ( path ) {
return path . endsWith ( '-test.js' ) ;
}
module . exports = {
declarationKeyword ( { pathToImportedModule } ) {
if ( isTestFile ( pathToImportedModule ) ) {
return 'const' ;
}
return 'import' ;
} ,
} ;
这是一个更详细的示例,同时考虑了pathToImportedModule
和pathToCurrentFile
:
module . exports = {
useRelativePaths ( { pathToImportedModule , pathToCurrentFile } ) {
if ( pathToCurrentFile . endsWith ( '-mock.js' ) ) {
return false ;
}
if ( pathToImportedModule . endsWith ( '-test.js' ) ) {
return false ;
}
return true ;
} ,
} ;
为了使用函数,您需要使用 JavaScript 配置文件 ( .importjs.js
)。
ImportJS 附带了一个方便的命令行工具,可以帮助您在编辑器之外执行导入。在幕后,这是大多数编辑器集成所使用的。
⨠ 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
如果要更改现有项目中构建导入的方式,可以将命令行工具与find
结合使用来批量更新一组文件。例如
find ./app -name " **.js* " -exec importjs rewrite --overwrite {} ;
由于--overwrite
标志会使 ImportJS 具有破坏性(文件被覆盖),因此在添加-exec
部分之前仔细检查find
命令是否返回正确的文件是一件好事。
ImportJS 在最近的祖先目录中查找您正在编辑的文件的package.json
文件,以查找要导入的节点模块。但是,有时它可能会从链上更上游的目录中提取依赖项。例如,您的目录结构可能如下所示:
.
|-- package.json
|-- components
| |-- button.js
| |-- icon.js
|-- node_modules
| |-- react
|-- subpackage
| |-- package.json
| |-- components
| |-- bulletin.js
如果您要在导入 React 的subpackage/components/bulletin.js
上使用 ImportJS,ImportJS 将不知道react
是有效的依赖项。
要告诉 ImportJS 跳过某个目录并继续向上搜索以查找根包目录,请在要忽略的目录的package.json
中指定"importjs": { "isRoot": false }
。在这种情况下,你会想要这样的东西:
{
"name" : " subpackage " ,
...
"importjs" : {
"isRoot" : false
}
}
注意:本节主要面向编辑器插件的开发人员。如果您正在使用标准编辑器插件之一,那么您很可能已经在幕后使用了守护进程。
您可以在后台进程中运行 ImportJS 并使用stdin
和stdout
与其进行通信。这将使导入速度更快,因为我们不会在每次调用时都启动节点环境。
该守护进程通过运行importjs
来启动。它接受通过stdin
发送的命令。每个命令都是一个以换行符结尾的(单行)JSON 字符串。命令结构与命令行工具基本相同,但用 JSON 封装,而不是在命令行上表达。以下是一些示例:
运行fix imports
:
{
"command" : " fix " ,
"fileContent" : " const foo = bar(); n " ,
"pathToFile" : " foo.js "
}
导入单个单词:
{
"command" : " word " ,
"commandArg" : " bar " ,
"fileContent" : " const foo = bar(); n " ,
"pathToFile" : " foo.js "
}
转到:
{
"command" : " goto " ,
"commandArg" : " bar " ,
"fileContent" : " const foo = bar(); n " ,
"pathToFile" : " foo.js "
}
结果以 JSON 格式打印到stdout
。响应看起来与命令行工具生成的响应相同。如果发生错误,它也会以 JSON(带有error
键的对象)的形式出现在stdout
中。
启动时,守护进程将打印日志文件的路径。如果您想了解幕后发生的事情,可以检查此文件。如果您无权访问守护程序的控制台日志,您将在os.tmpdir() + '/importjs.log
中找到日志文件(它将解析为类似var/folders/1l/_t6tm7195nd53936tsvh2pcr0000gn/T/importjs.log
在 Mac 上var/folders/1l/_t6tm7195nd53936tsvh2pcr0000gn/T/importjs.log
)。
如果您有大型应用程序,则遍历文件系统来查找模块可能会很慢。这就是 ImportJS 与 Watchman 内置集成的原因,Watchman 是 Facebook 开发的快速而强大的文件监视服务。要获得性能提升,您所要做的就是在本地安装 watchman,并确保使用最新的编辑器插件(Watchman 仅在 ImportJS 作为守护进程运行时使用)。
有关如何在本地运行、测试和开发 ImportJS 的提示,请参阅 CONTRIBUTING.md 文档。
快乐黑客!