在nodeJs的Express框架下用TypeScript編寫router路由出現import關鍵字錯誤的解決方案


問題出現場景

在項目中采用nodejs做中間層,做頁面的首屏渲染,同時采用express作為主web框架,其中express的router頁面路由我采用ts語言來編寫。如下:

//page.ts 文件
import request = require('request');
module.exports = function(router) {
    router.get('/', function(req, resp) {
        resp.render('xxx/page');
    });
};

編寫完ts后運行tsc命令將相應的ts文件編譯為對應的js文件,如下:

//page.js 文件
var request = require('request');
module.exports = function(router) {
    router.get('/', function(req, resp) {
        resp.render('xxx/page');
    });
};

其實這里只是import變成了var而已,但其意義在於在ts代碼中采用import載入的模塊可以享用強類型檢查,以及代碼自動補全,預編譯檢查等。

但是在啟動node服務時卻報錯,錯誤信息如下:

Debugger listening on port 45864
/Users/WebSite/routes/xxx/page.ts:8
import request = require('request');
^^^^^^
SyntaxError: Unexpected reserved word
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:373:25)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    ...

提示import關鍵字非法,是保留字不能使用,在搜了Google以及Stackflow之后發現雖然提問的人很多,但幾乎沒人回答正確的解決方案,搜索無果后只能自己查為什么會出現這個問題。

原因分析

在受到stackoverflow上某一個回答(http://stackoverflow.com/a/23113558/6001468) 的啟發后,發現我的路由代碼的code.ts和編譯后的code.js文件都在express下的router文件夾下,而通過在這兩個文件分別輸出log發現均會輸出,而實際上我希望只有.js文件被識別,ts文件在編譯完成后就不再參與node的執行。
所以問題的原因就在於本不應該被node載入的.ts也被拉去進行執行。

通過斷點后發現,問題的原因在於核心模塊的module.js文件里面的343行:

// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function(filename) {
  debug('load %j for module %j', filename, this.id);

  assert(!this.loaded);
  this.filename = filename;
  this.paths = Module._nodeModulePaths(path.dirname(filename));

  var extension = path.extname(filename) || '.js';
  if (!Module._extensions[extension]) extension = '.js';
  Module._extensions[extension](this, filename);
  this.loaded = true;
};

在倒數第4行,如果某個文件的擴展名沒有在Module._extensions字典內的文件,均被強制識別為.js文件,然后按照js代碼去執行。而_extensions字典內只有js,json和node文件,如下圖:

所以問題找到了,但是因為module.js是核心模塊,不能修改其代碼,所以只能去更上層的Express的代碼去改。

解決方案

node_modules/express-enrouten/lib/directory.js中,有一個isFileModule的函數,用來判斷當前文件是否一個模塊,從而來決定是否要用node去加載它,可以通過修改這個函數來達到目的。


/**
 * Returns true if `require` is able to load the provided file
 * or false if not.
 * http://nodejs.org/api/modules.html#modules_file_modules
 * @param file the file for which to determine module-ness.
 * @returns {boolean}
 */
function isFileModule(file) {
    var ext = path.extname(file);
    
    // 新增:如果文件擴展名是.ts則不進行加載 --xxcanghai@博客園
    if(ext === ".ts") {
        return false;
    }

    // Omit dotfiles
    // NOTE: Temporary fix in lieu of more complete (cross platform)
    // fix using file flags/attributes.
    if (path.basename(file, ext)[0] === '.') {
        return false;
    }

    try {
        // remove the file extension and use require.resolve to resolve known
        // file types eg. CoffeeScript. Will throw if not found/loadable by node.
        file = ext ? file.slice(0, -ext.length) : file;
        require.resolve(file);
        return true;
    } catch (err) {
        return false;
    }
}

如果當前文件的擴展名是.ts則判斷為非模塊,而不去加載。

修改后問題解決了。

后記

雖然問題解決了,但是修改框架代碼這種事其實是有非常多的坑的,而且我本人也極力反感這種行為。
比如以后如何面臨框架升級,還有因為修改了源碼而帶來的其他bug怎么辦,等等。
但是目前現階段而言有沒有什么其他更好的辦法,所以只能先這樣了,如果有哪位博客園友知道能好的解決方案,還望不吝賜教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM