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

問題出現場景

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

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

編寫完ts後運行tsc命令將相應的ts文件編譯爲對應的js文件,以下:html

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

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

可是在啓動node服務時卻報錯,錯誤信息以下: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以後發現雖然提問的人不少,但幾乎沒人回答正確的解決方案,搜索無果後只能本身查爲何會出現這個問題。web

緣由分析

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

經過斷點後發現,問題的緣由在於核心模塊的module.js文件裏面的343行:json

// 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文件,以下圖:
api

因此問題找到了,可是由於module.js是核心模塊,不能修改其代碼,因此只能去更上層的Express的代碼去改。框架

解決方案

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

/**
 * 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怎麼辦,等等。
可是目前現階段而言有沒有什麼其餘更好的辦法,因此只能先這樣了,若是有哪位博客園友知道能好的解決方案,還望不吝賜教。

相關文章
相關標籤/搜索