Как включить скрипты, расположенные в папке node_modules?

275

У меня есть вопрос относительно наилучшей практики для включения node_modulesв HTML-сайт.

Представьте, что в моей node_modulesпапке есть Bootstrap . Теперь для рабочей версии веб-сайта, как мне включить скрипт Bootstrap и CSS-файлы, расположенные внутри node_modulesпапки? Есть ли смысл оставлять Bootstrap внутри этой папки и делать что-то вроде следующего?

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

Или мне нужно добавить правила в мой файл gulp, которые затем скопируют эти файлы в мою папку dist? Или лучше было бы позволить gulp каким-то образом полностью удалить локальный загрузчик из моего HTML-файла и заменить его версией CDN?

Крис
источник
2
Соответствующая тема stackoverflow.com/questions/24397379/… . Так вот, квестино @Palak Bhansali Если предположить, что требуется только одна, оправдывает ли это реализацию bower для этой единственной цели, скажем, например, приложение gulp Express устанавливает загрузку прямо из npm, нуждаясь в доступе к файлам теперь в gulp, которые находятся в node_modules. Оправдывает ли это единственный случай использования сейчас беседки для этой единственной цели? Это проблема, с которой я сталкиваюсь. Я уже использую composer, npm, gulp, grunt, лично не хочу bower, и тоже не хочу grunt в этом приложении.
Буб
Отличный вопрос, который требует интуитивного решения!
Sydwell

Ответы:

297

Обычно вы не хотите раскрывать свои внутренние пути для того, как ваш сервер структурирован с внешним миром. Что вы можете сделать, так это создать /scriptsстатический маршрут на вашем сервере, который выбирает его файлы из любого каталога, в котором они находятся. Так что, если ваши файлы находятся в "./node_modules/bootstrap/dist/". Затем тег script на ваших страницах выглядит так:

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

Если вы использовали экспресс с nodejs, статический маршрут так же прост:

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

Затем любые запросы браузера /scripts/xxx.jsавтоматически будут получены из вашего distкаталога по адресу __dirname + /node_modules/bootstrap/dist/xxx.js.

Примечание: более новые версии NPM помещают больше вещей на верхний уровень, не вкладываясь так глубоко, поэтому, если вы используете более новую версию NPM, имена путей будут отличаться от указанных в вопросе OP и в текущем ответе. Но концепция все та же. Вы узнаете, где файлы физически расположены на диске сервера и вы делаете app.use()с , express.static()чтобы сделать псевдо-путь к этим файлам , так что вы не подвергая фактический файл - сервер организации системы к клиенту.


Если вы не хотите создавать статический маршрут, подобный этому, то вам, вероятно, лучше просто скопировать общедоступные сценарии в путь, который обрабатывает ваш веб-сервер, /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');
});
jfriend00
источник
2
Нужно ли копировать их вручную или есть Grunt способ сделать это? Я думаю, что лучше всего было бы полностью скопировать папку начальной загрузки, /scriptsпотому что таким образом будут сохраняться любые зависимости внутри модуля.
Крис
@phpheini - возможно, это поможет: stackoverflow.com/questions/18966485/… и это npmjs.com/package/grunt-copy
jfriend00
1
@RobertOschler - Нет, он не проверяет наличие дуплекса. express.static()отправляет первое найденное совпадение. Поскольку каждый express.static()оператор создает только один возможный совпадающий путь, не должно быть дубликатов из одного express.static()оператора. Если у вас есть несколько express.static()операторов, каждое из которых может иметь совпадение, то первое в вашем коде - это то, чье совпадение будет использовано. Кроме того, у вас не должно быть нескольких файлов с одинаковыми именами и относительным путем, доступных для express.static()операторов. Лучшая практика - не делать этого.
jfriend00
1
@RobertOschler - Вы должны быть очень, очень осторожны с тем, к чему вы предоставляете нерегулируемый доступ. Как правило, вы должны указывать только express.static()на каталог, содержащий ТОЛЬКО публичные файлы (включая подкаталоги). Так что я не могу представить проект, в котором было distопубликовано много каталогов. Это кажется мне очень необычным. Возможно, вы хотите сделать определенный маршрут к конкретному файлу или 2-3 файлам. В этом случае вы несете ответственность за устранение неоднозначности этих файлов. В script.jsлюбом случае это не принесет вам пользы .
jfriend00
1
@RobertOschler - если вы не хотите, чтобы перед ними требовался уникальный путь, для какого-либо кода просто не было бы способа узнать, какой из них использовать, когда /script.jsзапрашивает браузер. Итак, абстрактный ответ заключается в том, что вы не должны допускать существования ситуации с дублирующимися именами файлов, потому что это не решаемая проблема, если вам не требуется уникальный префикс для каждого каталога файлов. Тогда повторяющиеся корневые имена не являются проблемой в любом случае. Чтобы получить более конкретный ответ с подробной рекомендацией, вам необходимо опубликовать свой вопрос с точными деталями.
jfriend00
18

Я хотел бы использовать модуль path 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.
Буб
2
«path.join - это просто дополнительная мера, но все же то же решение», а не то же самое решение. Строго говоря, он использует специфичное для системы объединение (имейте в виду / и разделители в Windows и Unix)
Александр Егоров,
4
Хорошо, я понимаю вашу точку зрения, да, всегда хорошо иметь системную независимость, я не знал, что это было. Спасибо что подметил это. Было бы разумно добавить это к предложению в вашем ответе, вместо того, чтобы просто предоставить код :-), например, объяснить свой код.
буб
10

Как упомянуто jfriend00, вы не должны раскрывать структуру вашего сервера. Вы можете скопировать файлы зависимостей вашего проекта в нечто подобное public/scripts. Вы можете сделать это очень легко с помощью dep-linker :

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done
Марсело Лазарони
источник
1
Сценарии srcs будут тогда чем-то вроде /scripts/[package-name]/dist/file.min.js.
Джейкоб Форд
7

Если вы хотите быстрое и простое решение (и у вас установлено 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']);

Недостатком этого является то, что он не автоматизирован. Однако, если все, что вам нужно, это скопировать несколько сценариев и стилей (и сохранить их в списке), это должно сработать.

Джесси
источник
Можно добавить это в их package.json в сценариях 'scripts': {'install':'yarn gulp copy-modules'}, предполагая, что yarn является менеджером пакетов, а gulp установлен в пакет.
Тодд
6

Каталог 'node_modules' может отсутствовать в текущем каталоге, поэтому вы должны определить путь динамически.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
Джейк
источник
+1, хотя я не думаю, что это будет работать во всех случаях - связанный модуль egan npm, которого нет в подпапке node_modules
Alasdair McLeay
3

Я хочу обновить этот вопрос с более простым решением. Создайте символическую ссылку на 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

или как это на окнах:

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')));
curtwphillips
источник
1
Но затем вы переместили свое приложение по другому пути и символическая ссылка недействительна. Или вы хотите запустить свое приложение на другом компьютере (test / prod), вам нужно будет создать его заново. Любой участник вашего приложения должен сделать то же самое. Это добавляет некоторый maintanace по вышеупомянутым решениям.
mati.o 19.09.16
@ mati.o А как насчет относительных символических ссылок? Мне нравится это решение, но только с относительными символическими ссылками.
Лукаш Беднаржик
3

Я не нашел чистых решений (я не хочу раскрывать источник всех моих узлов node_modules), поэтому я просто написал скрипт Powershell для их копирования:

$deps = "leaflet", "leaflet-search", "material-components-web"

foreach ($dep in $deps) {
    Copy-Item "node_modules/$dep/dist" "static/$dep" -Recurse
}
Timmmm
источник
1

Я сделал следующие изменения, чтобы автоматически включить файлы в индекс 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>
<% } %>
SuperNova
источник
1

Это то, что я настроил на моем 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>

Удачи...

Акаша
источник