【问题标题】:Browserify's custom dependency name is not workingBrowserify 的自定义依赖名不起作用
【发布时间】:2016-09-08 18:00:47
【问题描述】:

我正在尝试让 browserify 的自定义依赖项名称与内存流一起使用。我使用内存流的原因是因为此代码注定要在 AWS Lambda 内运行,该 AWS Lambda 将接收多个“文件”作为输入,并且无法通过文件系统对 lambda 可用。

根据https://github.com/substack/node-browserify 的文档,这似乎应该是可能的:

b.require(file, opts)

file 也可以是一个流,但你也应该使用 opts.basedir 以便相关需求是可解析的。

使用 opts 的 expose 属性来指定自定义依赖名。 require('./vendor/angular/angular.js', {expose: 'angular'}) 启用 require('angular')

代码:

const browserify = require('browserify')
const path = require('path')
const File = require('vinyl')

const jsIndex = new File({
    file: path.resolve('index.js'),
    contents: new Buffer('var two = require("./two")')})
const jsTwo  = new File({
    file: path.resolve('two.js'),
    contents: new Buffer('console.log("Hello from two.js")')})

browserify({
        entries: [ jsIndex ],
        require: [ jsTwo ],
        basedir: path.resolve('.')
    })
    .bundle((err, res) => {
        if (err) console.log(err.message)
    })
    .pipe(process.stdout)

错误:

Cannot find module './two' from '...'

编辑:

另一个人在 node-browserify github 页面上发布了类似的问题:https://github.com/substack/node-browserify/issues/1622

【问题讨论】:

    标签: node.js browserify


    【解决方案1】:

    Browserify 大量使用文件系统,因为它实现了自己的模块解析,该解析使用与 Node 的 require 相同的算法。要让 Browserify 使用内存中的源文件进行捆绑,一种解决方案是对文件系统进行猴子补丁 - 以类似于对测试有用的 mock-fs 模块的方式。

    但是,您实际上只想对源文件执行此操作。 Browserify 将其模块解析用于捆绑中包含的其他文件,并且必须将这些文件添加到内存文件系统中会很乏味。修补的文件系统函数应该检查涉及内存源文件的调用,相应地处理它们并将其他调用转发到原始实现。 (mock-fs 做了类似的事情,因为它检测到 require 中发生的文件系统调用并将这些调用转发到原始实现。)

    'use strict';
    
    const fs = require('fs');
    const path = require('path');
    const toStream = require('string-to-stream');
    
    // Create an object hash that contains the source file contents as strings,
    // keyed using the resolved file names. The patched fs methods will look in
    // this map for source files before falling back to the original
    // implementations.
    
    const files = {};
    files[path.resolve('one.js')] =
        'console.log("Hello from one.js");' +
        'var two = require("./two");' +
        'exports.one = 1;';
    files[path.resolve('two.js')] =
        'console.log("Hello from two.js");' +
        'exports.two = 2;';
    
    // The three fs methods that need to be patched take a file name as the
    // first parameter - so the patch mechanism is the same for all three.
    
    function patch(method, replacement) {
    
        var original = fs[method];
        fs[method] = function (...args) {
    
            var name = path.resolve(args[0]);
            if (files[name]) {
                args[0] = name;
                return replacement.apply(null, args);
            } else {
                return original.apply(fs, args);
            }
        };
    }
    
    patch('createReadStream', function (name) {
    
        return toStream(files[name]);
    });
    
    patch('lstat', function (...args) {
    
        args[args.length - 1](null, {
            isDirectory: function () { return false; },
            isFile: function () { return true; },
            isSymbolicLink: function () { return false; }
        });
    });
    
    patch('stat', function (...args) {
    
        args[args.length - 1](null, {
            isDirectory: function () { return false; },
            isFile: function () { return true; }
        });
    });
    
    // With the fs module patched, browserify can be required and the bundle
    // can be built.
    
    const browserify = require('browserify');
    
    browserify()
        .require(path.resolve('one.js'), { entry: true, expose: 'one' })
        .require(path.resolve('two.js'), { expose: 'two' })
        .bundle()
        .pipe(process.stdout);
    

    requireexpose

    在您的问题中,您提到了exposeone.jstwo.js 模块是使用 require 捆绑的,因此可以在浏览器中使用 expose 选项中指定的名称来要求它们。如果这不是您想要的,您可以改用add,它们将成为捆绑包内部的模块。

    <!doctype html>
    <html>
    <head>
        <title>so-39397429</title>
    </head>
    <body>
        <script src="./bundle.js"></script>
        <script>
            // At this point, the bundle will have loaded and the entry
            // point(s) will have been executed. Exposed modules can be
            // required in scripts, like this:
            console.log(require('one'));
            console.log(require('two'));
        </script>
    </body>
    </html>
    

    在调查 this tsify question 时,我花了一些时间研究 Browserify 的 requireexpose 选项。这些选项有助于从包外部对模块的要求,但我完全不确定它们对包内模块之间的要求有任何影响(这是你所需要的)。此外,它们的实现有点令人困惑——尤其是this part,其中公开的名称有时会在前面加上一个斜杠。

    乙烯基流

    在您的问题中,您使用了vinyl。但是,我无法将vinyl 用于读取流。尽管它确实实现了pipe,但它不是一个事件发射器,并且似乎没有实现基于on 的pre-Streams 2 API - 这是Browserify 对createReadStream 结果的期望。

    【讨论】:

    • 当我在寻找一个非 hacky 的解决方案时,你的猴子补丁解决方案在技术上解决了这个问题。所以我已经给你积分了,但我会为其他解决方案保留答案。
    • 我还注意到,如果我使用你的猴子补丁技术,我不需要 createReadStream 方法,猴子补丁 lstat 和 stat 足以让 browserify 工作。
    • createReadStream 的补丁必要的。如果它在没有它的情况下为您工作,它正在从磁盘读取文件。我已经在require/expose 的答案中添加了一些注释,以及为什么我认为猴子补丁是唯一可行的选择。但是,看看是否有可能的替代解决方案将会很有趣,因为这并不漂亮。
    • 我创建了一个要点,我将您的猴子补丁(省略 createReadStream)与我的代码(包括乙烯基流)合并。此代码产生正确的输出。这些文件在磁盘上不存在。 gist.github.com/joelnet/801172fa765ef4a8f11f94d044604dd2
    • 是的,我明白你的意思了。这是一个更整洁的解决方案。我猜 Browserify 将 pipe 用于通过选项接收的流,将 on 用于通过createReadStream 调用接收的流。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-09
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    相关资源
    最近更新 更多