【问题标题】:How to include scripts located inside the node_modules folder?如何包含位于 node_modules 文件夹中的脚本?
【发布时间】:2015-02-12 09:01:05
【问题描述】:

我有一个关于将node_modules 包含到 HTML 网站的最佳做法的问题。

假设我的 node_modules 文件夹中有 Bootstrap。现在对于网站的生产版本,我将如何包含位于 node_modules 文件夹中的引导脚本和 CSS 文件?将 Bootstrap 留在该文件夹中并执行以下操作是否有意义?

<script src="./node_modules/bootstrap/dist/bootstrap.min.js"></script>

或者我是否必须在我的 gulp 文件中添加规则,然后将这些文件复制到我的 dist 文件夹中?还是最好让 gulp 以某种方式从我的 HTML 文件中完全删除本地引导程序并用 CDN 版本替换它?

【问题讨论】:

  • 相关话题stackoverflow.com/questions/24397379/…。所以这里是questino @Palak Bhansali 假设一个人只需要,它是否证明为了这个唯一目的实施凉亭是合理的,例如一个直接从npm安装引导的gulp express应用程序,需要访问现在在gulp中的文件,这些文件位于node_modules中。这是否证明了一个用例现在需要凉亭来实现这个唯一目的?这就是我遇到的问题。我已经在使用 composer、npm、gulp、grunt,个人不想使用 bower,也不想在这个应用上使用 grunt。
  • 需要直观解决方案的优秀问题!
  • 现实地说,这是一个没有触及的重要问题。有很多 JS/CSS 库与流行的框架不兼容。当开发人员总是从 bower 和 JSPM 迁移时,这是一个巨大的差距。

标签: node.js twitter-bootstrap npm node-modules


【解决方案1】:

如果您要链接到许多文件,请创建一个白名单,然后使用 sendFile():

app.get('/npm/:pkg/:file', (req, res) => {
    const ok = ['jquery','bootstrap','interactjs'];
    if (!ok.includes(req.params.pkg)) res.status(503).send("Not Permitted.");
    res.sendFile(__dirname + `/node_modules/${req.params.pkg}/dist/${req.params.file}`);
    });

例如,您可以安全地链接到 /npm/bootstrap/bootsrap.js、/npm/bootstrap/bootsrap.css 等。

顺便说一句,我很想知道是否有办法使用 express.static 将其列入白名单

【讨论】:

    【解决方案2】:

    要在 html 中使用 node_modules 中的多个文件,我发现最好的方法是将它们放到一个数组中,然后循环它们以使它们对 web 客户端可见,例如使用 node_modules 中的 filepond 模块:

    const filePondModules = ['filepond-plugin-file-encode', 'filepond-plugin-image-preview', 'filepond-plugin-image-resize', 'filepond']
    filePondModules.forEach(currentModule => {
        let module_dir = require.resolve(currentModule)
                               .match(/.*\/node_modules\/[^/]+\//)[0];
        app.use('/' + currentModule, express.static(module_dir + 'dist/'));
    })
    

    然后在 html(或布局)文件中,像这样调用它们:

        <link rel="stylesheet" href="/filepond/filepond.css">
        <link rel="stylesheet" href="/filepond-plugin-image-preview/filepond-plugin-image-preview.css">
    ...
        <script src="/filepond-plugin-image-preview/filepond-plugin-image-preview.js" ></script>
        <script src="/filepond-plugin-file-encode/filepond-plugin-file-encode.js"></script>
        <script src="/filepond-plugin-image-resize/filepond-plugin-image-resize.js"></script>
        <script src="/filepond/filepond.js"></script>
    

    【讨论】:

      【解决方案3】:

      我会使用路径 npm 模块,然后执行以下操作:

      var path = require('path');
      app.use('/scripts', express.static(path.join(__dirname, 'node_modules/bootstrap/dist')));
      

      重要提示:我们使用 path.join 以与系统无关的方式连接路径,即在 windows 和 unix 上,我们有不同的路径分隔符(/ 和 )

      【讨论】:

      • 如上标记重复,path.join 只是一个附加措施,但仍然是通过向 node_modules 目录添加静态路径的相同解决方案。
      • "path.join 只是一个附加措施,但仍然是相同的解决方案"不是相同的解决方案。严格来说,它使用系统特定的连接(记住 / 和 Windows 和 unix 中的 \ 分隔符)
      • 好的,我明白你的意思,是的,与系统无关总是好的,我不知道那是什么。感谢您指出了这一点。将其添加到答案中的句子中是明智的,而不仅仅是提供代码:-),例如解释你的代码。
      • 为什么不在node_modules/bootstrap/dist 上也使用path.join 而使用/
      • 你是对的,但我认为框架会处理这种情况:)
      【解决方案4】:

      这是我在express 服务器上设置的:

      // app.js
      const path = require('path');
      const express = require('express');
      const expressApp  = express();
      const nm_dependencies = ['bootstrap', 'jquery', 'popper.js']; // keep adding required node_modules to this array.
      nm_dependencies.forEach(dep => {
        expressApp.use(`/${dep}`, express.static(path.resolve(`node_modules/${dep}`)));
      });
      

      <!-- somewhere inside head tag -->
      <link rel="stylesheet" href="bootstrap/dist/css/bootstrap.css" />
      
      <!-- somewhere near ending body tag -->
      <script src="jquery/dist/jquery.js" charset="utf-8"></script>
      <script src="popper.js/dist/popper.js" charset="utf-8"></script>
      <script src="bootstrap/dist/js/bootstrap.js" charset="utf-8"></script>
      

      祝你好运……

      【讨论】:

      • 这很有帮助!特别是对于经常需要引用其他与框架无关的 JS/CSS 的 Web 应用程序。在许多情况下,最好单独卸载它们。谢谢@Aakash
      • 这是一个很好的解决方案
      【解决方案5】:

      通常,您不希望将任何内部路径暴露给外部世界,以了解您的服务器的结构。你可以在你的服务器中创建一个/scripts 静态路由,从它们碰巧驻留的任何目录中获取它的文件。所以,如果你的文件在"./node_modules/bootstrap/dist/" 中。然后,您页面中的脚本标记将如下所示:

      <script src="/scripts/bootstrap.min.js"></script>
      

      如果你在 nodejs 中使用 express,静态路由就这么简单:

      app.use('/scripts', express.static(__dirname + '/node_modules/bootstrap/dist/'));
      

      然后,来自/scripts/xxx.js 的任何浏览器请求都将自动从位于__dirname + /node_modules/bootstrap/dist/xxx.jsdist 目录中获取。

      注意:较新版本的 NPM 将更多内容放在顶层,没有嵌套那么深,因此如果您使用较新版本的 NPM,那么路径名称将与 OP 问题和当前答案中指示的不同.但是,这个概念还是一样的。您找出文件在服务器驱动器上的物理位置,并使用express.static() 创建一个app.use() 来创建这些文件的伪路径,这样您就不会将实际的服务器文件系统组织暴露给客户端。


      如果您不想创建这样的静态路由,那么您最好将公共脚本复制到您的网络服务器确实视为/scripts 或您想要的任何顶级名称的路径采用。通常,您可以将此复制作为构建/部署过程的一部分。


      如果您只想在一个目录中公开一个特定文件,而不是在该目录中找到所有文件,那么您可以为每个文件手动创建单独的路由,而不是使用express.static(),例如:

      <script src="/bootstrap.min.js"></script>
      

      以及为此创建路由的代码

      app.get('/bootstrap.min.js', function(req, res) {
          res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
      });
      

      或者,如果您仍想使用/scripts 为脚本描绘路线,您可以这样做:

      <script src="/scripts/bootstrap.min.js"></script>
      

      以及为此创建路由的代码

      app.get('/scripts/bootstrap.min.js', function(req, res) {
          res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
      });
      

      【讨论】:

      • 我需要手动复制这些内容还是有 Grunt 方法可以做到这一点?我认为最好的办法是将引导文件夹完全复制到/scripts,因为这样可以维护模块内的任何依赖关系。
      • @phpheini - 也许这会有所帮助:stackoverflow.com/questions/18966485/… 和这个npmjs.com/package/grunt-copy
      • @RobertOschler - 不,它不检查重复数据。 express.static() 发送它找到的第一个匹配项。由于每个express.static() 语句只创建一个可能的匹配路径,因此不应有来自一个express.static() 语句的重复项。如果您有多个 express.static() 语句,每个语句都有一个匹配项,那么代码中的第一个语句就是将使用其匹配项的语句。此外,您真的不应该有多个具有相同名称和相对路径的文件可以通过express.static() 语句访问。最佳做法是不要这样做。
      • @RobertOschler - 您必须非常非常小心地提供不受监管的访问权限。一般来说,您应该只将express.static() 指向一个只包含公共文件(包括子目录)的目录。所以,我真的无法想象一个项目会发布大量dist 目录。这对我来说似乎很不寻常。也许您想为特定文件或 2-3 个文件创建特定路径。在这种情况下,您有责任消除这些文件的歧义。无论如何,拥有四个 script.js 文件对你没有任何好处。
      • @RobertOschler - 如果您没有在它们前面设置唯一路径以进行匹配,那么任何代码都无法知道在请求 /script.js 时要提供哪个路径通过浏览器。因此,抽象的答案是您不应该允许存在重复文件名的情况,因为除非您需要每个文件目录的唯一前缀,否则这不是一个可解决的问题。然后,重复的根名称无论如何都不是问题。如需更具体的答案和详细的建议,您需要发布您自己的问题并提供详细信息。
      【解决方案6】:

      如果您想要一个快速简单的解决方案(并且您已安装 gulp)。

      在我的gulpfile.js 中,我运行一个简单的复制粘贴任务,将我可能需要的任何文件放入./public/modules/ 目录中。

      gulp.task('modules', function() {
          sources = [
            './node_modules/prismjs/prism.js',
            './node_modules/prismjs/themes/prism-dark.css',
          ]
          gulp.src( sources ).pipe(gulp.dest('./public/modules/'));
      });
      
      gulp.task('copy-modules', ['modules']);
      

      这样做的缺点是它不是自动化的。但是,如果您只需要复制一些脚本和样式(并保存在列表中),这应该可以完成工作。

      【讨论】:

      • 可以将其添加到脚本'scripts': {'install':'yarn gulp copy-modules'} 中的 package.json 中,假设 yarn 是包管理器并且 gulp 安装在包中。
      【解决方案7】:

      我没有找到任何干净的解决方案(我不想暴露我所有 node_modules 的来源),所以我只写了一个 Powershell 脚本来复制它们:

      $deps = "leaflet", "leaflet-search", "material-components-web"
      
      foreach ($dep in $deps) {
          Copy-Item "node_modules/$dep/dist" "static/$dep" -Recurse
      }
      

      【讨论】:

        【解决方案8】:

        我想用更简单的解决方案更新这个问题。创建指向 node_modules 的符号链接。

        授予对 node_modules 的公共访问权限的最简单方法是在您的公共目录中创建一个指向您的 node_modules 的符号链接。符号链接将使文件在创建链接的任何位置都存在。

        例如,如果节点服务器有提供静态文件的代码

        app.use(serveStatic(path.join(__dirname, 'dist')));
        

        并且 __dirname 引用 /path/to/app 以便从 /path/to/app/dist 提供静态文件

        node_modules 位于 /path/to/app/node_modules,然后在 mac/linux 上创建一个像这样的符号链接:

        ln -s /path/to/app/node_modules /path/to/app/dist/node_modules
        

        或在 Windows 上这样:

        mklink /path/to/app/node_modules /path/to/app/dist/node_modules
        

        现在是一个获取请求:

        node_modules/some/path 
        

        将在

        处收到包含文件的响应
        /path/to/app/dist/node_modules/some/path 
        

        这是真正的文件

        /path/to/app/node_modules/some/path
        

        如果您位于 /path/to/app/dist 的目录不是安全位置,可能是因为使用 gulp 或 grunt 构建过程的干扰,那么您可以为链接添加一个单独的目录并添加一个新的 serveStatic 调用如:

        ln -s /path/to/app/node_modules /path/to/app/newDirectoryName/node_modules
        

        并在节点中添加:

        app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));
        

        【讨论】:

        • 但是您已经将您的应用程序移动到不同的路径并且符号链接无效。或者您想在不同的机器(测试/产品)上运行您的应用程序,您需要再次创建它。您的应用程序的任何贡献者都必须这样做。它在上述解决方案的基础上增加了一些维护。
        • @mati.o 相对符号链接呢?我喜欢这个解决方案,但只能使用相对符号链接。
        【解决方案9】:

        正如 jfriend00 所提到的,你不应该暴露你的服务器结构。您可以将项目依赖文件复制到public/scripts 之类的位置。您可以像这样使用dep-linker非常轻松地做到这一点:

        var DepLinker = require('dep-linker');
        DepLinker.copyDependenciesTo('./public/scripts')
        // Done
        

        【讨论】:

        • Scripts srcs 将类似于 /scripts/[package-name]/dist/file.min.js
        【解决方案10】:

        我对索引 html 中的文件进行了以下更改以自动包含文件。这样当您在文件夹中添加文件时,它将自动从文件夹中提取,而无需将文件包含在 index.html 中

        //// THIS WORKS FOR ME 
        ///// in app.js or server.js
        
        var app = express();
        
        app.use("/", express.static(__dirname));
        var fs = require("fs"),
        
        function getFiles (dir, files_){
            files_ = files_ || [];
            var files = fs.readdirSync(dir);
            for (var i in files){
                var name = dir + '/' + files[i];
                if (fs.statSync(name).isDirectory()){
                    getFiles(name, files_);
                } else {
                    files_.push(name);
                }
            }
            return files_;
        }
        //// send the files in js folder as variable/array 
        ejs = require('ejs');
        
        res.render('index', {
            'something':'something'...........
            jsfiles: jsfiles,
        });
        
        ///--------------------------------------------------
        
        ///////// in views/index.ejs --- the below code will list the files in index.ejs
        
        <% for(var i=0; i < jsfiles.length; i++) { %>
           <script src="<%= jsfiles[i] %>"></script>
        <% } %>
        

        【讨论】:

          【解决方案11】:

          目录'node_modules'可能不在当前目录中,所以你应该动态解析路径。

          var bootstrap_dir = require.resolve('bootstrap')
                                     .match(/.*\/node_modules\/[^/]+\//)[0];
          app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
          

          【讨论】:

          • +1,虽然我不认为这适用于所有情况 - 例如,不在 node_modules 子文件夹中的 npm 链接模块
          猜你喜欢
          • 1970-01-01
          • 2017-12-15
          • 1970-01-01
          • 2016-08-22
          • 1970-01-01
          • 2014-11-26
          • 1970-01-01
          • 2013-04-09
          • 2020-11-05
          相关资源
          最近更新 更多