通过 SystemJS 为 NodeJS 打字稿

TypeScript for NodeJS via SystemJS

我想在我的 NodeJS 应用程序中使用 TypeScript。为了做到这一点,我认为使用 SystemJS 将我的源文件动态转换为 JavaScript 会很好。我已经设法绕过了大多数与 SystemJS 配置相关的问题,但以下问题除外:

如何使 SystemJS 阻止处理某些导入并使其使用原始 require?

SystemJS 通过它自己的 require 实现将所有内容包装在它自己的模块表示中,不尊重模块的原始类型。它使 express 这样的 NodeJS 模块成为对象而不是函数(问题出在下面的代码中)

'use strict';

console.log('In server.ts');

if ('production' === process.env.NODE_ENV)
    require('newrelic');

let PORT = process.env.PORT || 3333;

import * as fs from 'fs';
import * as os from 'os';
import * as https from 'https';
import * as express from 'express';  // <------------- SystemJS makes express an object    
import * as socketio from 'socket.io';

import { Routes } from './routes/index';
import { DBConfig } from './config/db.conf';
import { RoutesConfig } from './config/routes.conf';
import { SocketServer } from "./reflex/SocketServer";

const app = express(); // <------------ This is object, so no way to initialize

RoutesConfig.init(app);
DBConfig.init();
Routes.init(app, express.Router());

const opts = {
  key: fs.readFileSync(__dirname + '/cert/server.key'),
  cert: fs.readFileSync(__dirname + '/cert/server.crt')
};

let server = https.createServer(opts, <any>app);
let socket = socketio(server, { transports: ['websocket'] });

let socketServer = new SocketServer(100, socket);

server.listen(PORT, () => {
  console.log(`Application is up and running @: ${os.hostname()} on port: ${PORT}`);
  console.log(`enviroment: ${process.env.NODE_ENV}`);
});

另一个问题是,通过替换原来的 require,不仅每个模块都应该用 SystemJS 配置覆盖,而且每个子模块都应该覆盖,这是一个大材小用。

这是我的 SystemJS 配置:

SystemJS.config({
    transpiler: 'typescript',
    defaultJSExtensions: false,
    map: {
        // ------ system modules ------

        "console": "@node/console",
        "buffer": "@node/buffer",
        "querystring": "@node/querystring",
        "events": "@node/events",
        "http": "@node/http",
        "cluster": "@node/cluster",
        "zlib": "@node/zlib",
        "os": "@node/os",
        "https": "@node/https",
        "punycode": "@node/punycode",
        "repl": "@node/repl",
        "readline": "@node/readline",
        "vm": "@node/vm",
        "child_process": "@node/child_process",
        "url": "@node/url",
        "dns": "@node/dns",
        "net": "@node/net",
        "dgram": "@node/dgram",
        "fs": "@node/fs",
        "path": "@node/path",
        "string_decoder": "@node/string_decoder",
        "tls": "@node/tls",
        "crypto": "@node/crypto",
        "stream": "@node/stream",
        "util": "@node/util",
        "assert": "@node/assert",
        "tty": "@node/tty",
        "domain": "@node/domain",
        "constants": "@node/constants",

        // ------ common modules ------
        'helmet': '@node/helmet',
        'morgan': '@node/morgan',
        'express': '@node/express',
        'mongoose': '@node/mongoose',

        'socket.io': '@node/socket.io',
        'socket.io-client': '@node/socket.io-client', // <----- this module is being referenced by 'socket.io'
        'body-parser': '@node/body-parser',

        // ------ SystemJS configuration ------
        json: './node_modules/systemjs-plugin-json/json.js'

    },
    meta: {
        '*.json': {
            loader: 'json'
        }
    },
    paths: {
        'typescript': './node_modules/typescript',
    },
    packages: {
        'typescript': {
            main: 'lib/typescript'
        },
        'server': {
            defaultExtension: 'ts'
        }
    },
    typescriptOptions: tsconfig
});

所以问题如下:如何让 SystemJS 忽略某些导入(通过配置)并将控制绕过本机 NodeJS require 实现?

提前致谢!

如果你看一下 express.js 是如何导出东西的,你会看到

exports = module.exports = createApplication;

然后

exports.application = proto;
exports.request = req;
exports.response = res;

当 SystemJS 将其转换为模块时,您需要的函数 createApplication 变为 default export

有一个 shorthand 语法只导入默认值

import express from 'express';

console.log(typeof express); // function

当然,分配给它的所有属性 - applicationrequest 等 - 仍然可用。

或者,如果您更喜欢用于导入的原始表格,您可以通过 default 属性

访问它
import * as express from 'express';

console.log(typeof express.default); // function

现在回答你的问题

how to make SystemJS ignore certain imports (via configuration) and bypass control to native NodeJS require implementation?

AFAIK 配置中没有这样的选项,但 SystemJS 提供原始节点要求 System._nodeRequire,因此您可以在代码中使用它作为回退:

var express = System._nodeRequire('express');

注意:正如 Lu4 在评论中指出的那样,SystemJS 配置中的 'express': '@node/express' 映射非常重要。除了从 node_modules 加载外,@node 特殊符号告诉 SystemJS 模块内部所需的所有包及其依赖项都将使用节点 require 加载,而不是转换为模块。这有时是必要的,因为 SystemJS 模块解析算法与 nodejs 不同,如 SystemJS 文档中的 CommonJS 模块格式部分所述。