完善 VSCode 的 Node 自動補全

bestswifter發表於2018-04-23

這是一篇水文,講講踩坑的經歷。本文的 Demo 已經上傳到我的 GitHub

背景

起因是在寫 Node 時,受夠了 require ('../../../../helper.js') 這種相對路徑。不夠直觀不談,如果將來在別的地方用,都不能直接 copy 過來,還得重新計算相對路徑,因此希望用絕對路徑(換句話說就是永遠相對根路徑)來表示。

一種比較簡單的方案是封裝 require 函式:

global.rootRequire = function(name) {
    return require(__dirname + '/' + name);
}
複製程式碼

在我們的 rootRequire 函式中,所有的路徑都會被加上 __dirname 的字首,也就實現了絕對路徑。

這麼做功能上沒有問題,然而似乎 VSCode 對這種寫法支援得不夠好(有了解的大佬還望指教),表現為以下兩個問題:

  1. 雖然我們把 rootRequire 定義為全域性的,但在別的檔案中輸入這個單詞時,並沒有自動補全
  2. 通過這種方式引入的模組,不能跳轉到模組的實現,也看不到模組的內部結構,如果用 require 引入則沒有問題。

經過更進一步的測試,甚至於這種寫法也是不行的:

let r = require
let a = r('../test/a')
複製程式碼

在 VSCode 中會發現 a 的型別為 any 並且丟失了很多資訊。

解決方案

換句話說,封裝 require 的路是行不通了,只能用原生的 require 函式,那麼只能看看有什麼辦法可以影響到模組查詢的流程了。

內建的那套流程和順序肯定是改不了,看起來只能從 NODE_PATH 這個全域性變數下手了。我們知道 require 函式會去 NODE_PATH 的目錄裡查詢模組,所以只要把它設定為工程的根路徑,就可以實現絕對路徑載入了。試驗一下,專案目錄如下所示:

project
    |-----main
           |------index.js
    |-----util
           |------utils.js
複製程式碼

很簡單的定義一下 utils.js,就匯出一個物件:

// utils.js
module.exports = {
    key:' value'
}
複製程式碼

index.js 中這麼寫:

let utils = require('utils/utils')
console.log(utils.key)
複製程式碼

然後執行 node main/index.js,肯定會編譯失敗。

但如果指定了 NODE_PATH 就不一樣了,此時可以正常執行:

export NODE_PATH=$PWD && node main/index.js
複製程式碼

優化

直接在命令列中指定 NODE_PATH 有兩個問題:

  1. 改變了專案的啟動方式,別的開發者也會受到影響,不過這一點問題不大,因為一般都是通過命令來啟動的。
  2. 如果在不同的路徑下啟動 node,那麼 $PWD 是會變的,這種方式不夠安全。

所以比較好的做法是,在入口檔案中指定 NODE_PATH,因為這個檔案的路徑一般不會改變。所以 index.js 可以改造成這樣:

let path = require('path')
process.env.NODE_PATH = path.resolve(__dirname, '../') ;
require('module').Module._initPaths();

let utils = require('utils/utils')

console.log(utils.key)
複製程式碼

這種寫法的好處在於,無論我們在哪裡執行 node path/to/index.js 都會得到正確的結果。

最後還需要修正一下寫 require 函式時,路徑補全的問題,只要在根目錄裡面加上一個 jsconfig.json 檔案並新增如下內容即可:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "baseUrl": "./",
  },
  "exclude": [
    "node_modules"
  ]
}
複製程式碼

核心在於 "baseUrl": "./" 這一行。這樣當我們寫 utils 這個單詞的時候,就可以享受到自動補全了。

至此,無論是 Node 的執行,還是路徑補全,抑或是定義跳轉功能,都正常工作了。

相關文章