使用 Mocha 时 Catch-22 递归节点模块爆炸

Catch-22 recursive Node modules blowing up when using Mocha

我一直在从事一个使用一些自定义 Node.js 模块的项目。我创建了一个 'helpers' 模块来帮助加载一些辅助方法:

/helpers/index.js:

var mutability = require('./mutability'),
    cb = require('./cb'),
    build = require('./build'),
    userAgent = require('./userAgent'),
    is = require('./is'),
    query = require('./query'),
    config = require('./config'),
    _ = require('underscore')

module.exports = _.extend({
    cb: cb,
    build: build,
    userAgent: userAgent,
    is: is,
    query: query,
    config: config
}, mutability)

为了好玩,mutability.js 是:

'use strict'

module.exports = {
    setReadOnly: function(obj, key) {
        // whatever
        return obj
    },
    setWritable: function(obj, key) {
        // whatever
        return obj
    }
}

我的一个模块 build 需要 class 来进行一些类型检查:

/helpers/build.js

'use strict'

var urljoin = require('url-join'),
    config = require('./config'),
    cb = require('./cb'),
    Entity = require('../lib/entity'),
    _ = require('underscore')

module.exports = {
    url: function(options) {
        return urljoin(
            config.baseUrl,
            options.client.orgId,
            options.client.appId,
            options.type, (typeof options.uuidOrName === 'string') ? options.uuidOrName : ""
        )
    },
    GET: function(options) {
        options.type = options.type || args[0] instanceof Entity ? args[0]._type : args[0]
        options.query = options.query || args[0] instanceof Entity ? args[0] : undefined
        return options
    }
}

然后 Entity 需要 helpers:

/lib/entity.js

'use strict'

var helpers = require('../helpers'),
    ok = require('objectkit'),
    _ = require('underscore')

var Entity = function(object) {
    var self = this

    _.extend(self, object)

    helpers.setReadOnly(self, ['uuid'])

    return self
}

module.exports = Entity

无论出于何种原因,当我使用 Mocha 运行 时,我 helpers{} 注销并且 Mocha 抛出:

Uncaught TypeError: helpers.setReadOnly is not a function

当我 运行 /lib/entity.js 直接使用 node 时,它会打印正确的模块。是什么赋予了?为什么摩卡会火起来?

你是对的,问题是 index.jsentity.js 之间的循环依赖。

您的依赖关系图看起来像这样(带有规范化路径),其中每个箭头都是 require 语句:

/helpers/index.js -> /helpers/build.js -> /lib/entity.js -> /helpers/index.js

当节点 required 中的模块 module.exports 被初始化为一个空对象。

当你有循环依赖时,这个默认对象可能会返回到另一个模块之前你的代码运行实际上设置了module.exports = ...;(因为 JavaScript 是同步的)。

这就是您的情况:/lib/entity.js 在 index.js 定义为 module.exports = _.extend(...) 之前从 /helpers/index.js 接收默认导出对象。

要修复它,您需要确保扩展已返回到 /lib/entity.js 的同一对象,而不是用新实例替换它:

// Extend `module.exports` instead of replacing it with a new object.
module.exports = _.extend(
    module.exports,
    {
        cb: cb,
        build: build,
        userAgent: userAgent,
        is: is,
        query: query,
        config: config
    },
    mutability
);

但是,通常最好尽可能避免循环依赖。