在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怎么办,等等。
可是目前现阶段而言有没有什么其余更好的办法,因此只能先这样了,若是有哪位博客园友知道能好的解决方案,还望不吝赐教。

相关文章
相关标签/搜索