Comment inclure des scripts situés dans le dossier node_modules?

275

J'ai une question concernant les meilleures pratiques à inclure node_modulesdans un site Web HTML.

Imaginez que j'ai Bootstrap dans mon node_modulesdossier. Maintenant, pour la version de production du site Web, comment pourrais-je inclure le script Bootstrap et les fichiers CSS situés dans le node_modulesdossier? Est-il judicieux de laisser Bootstrap dans ce dossier et de faire quelque chose comme ceci?

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

Ou devrais-je ajouter des règles à mon fichier gulp qui copient ensuite ces fichiers dans mon dossier dist? Ou serait-il préférable de laisser gulp supprimer complètement le bootstrap local de mon fichier HTML et le remplacer par la version CDN?

Chris
la source
2
Rubrique connexe stackoverflow.com/questions/24397379/… . SO heres the questino @Palak Bhansali En supposant qu'un seul besoin, cela justifie-t-il d'implémenter bower à cette seule fin, disons par exemple qu'une application gulp express n'installe pas bootstrap directement à partir de npm, ayant besoin d'accéder aux fichiers maintenant dans gulp, qui sont dans node_modules. Cela justifie-t-il un cas d'utilisation unique pour avoir besoin de tonnelle maintenant à cette seule fin? C'est le problème que je rencontre. J'utilise déjà le compositeur, npm, gulp, grunt, je ne veux pas bower personnellement, et je ne veux pas non plus grogner sur cette application.
blamb
Excellente question qui nécessite une solution intuitive!
Sydwell

Réponses:

297

Habituellement, vous ne voulez exposer aucun de vos chemins internes pour la structure de votre serveur au monde extérieur. Ce que vous pouvez faire est de créer une /scriptsroute statique dans votre serveur qui récupère ses fichiers à partir de n'importe quel répertoire où ils se trouvent. Donc, si vos fichiers sont dedans "./node_modules/bootstrap/dist/". Ensuite, la balise de script dans vos pages ressemble à ceci:

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

Si vous utilisiez express avec nodejs, une route statique est aussi simple que ceci:

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

Ensuite, toutes les demandes du navigateur /scripts/xxx.jsseront automatiquement récupérées de votre distrépertoire à l'adresse __dirname + /node_modules/bootstrap/dist/xxx.js.

Remarque: les nouvelles versions de NPM placent plus de choses au niveau supérieur, pas imbriquées si profondément, donc si vous utilisez une version plus récente de NPM, les noms de chemin seront différents de ceux indiqués dans la question du PO et dans la réponse actuelle. Mais, le concept est toujours le même. Vous découvrez où les fichiers se trouvent physiquement sur votre lecteur de serveur et vous créez un app.use()avec express.static()pour créer un pseudo-chemin d'accès à ces fichiers afin de ne pas exposer l'organisation réelle du système de fichiers du serveur au client.


Si vous ne souhaitez pas créer une route statique comme celle-ci, il vaut probablement mieux copier les scripts publics vers un chemin que votre serveur Web traite comme /scriptsou quelle que soit la désignation de niveau supérieur que vous souhaitez utiliser. Habituellement, vous pouvez intégrer cette copie à votre processus de génération / déploiement.


Si vous souhaitez rendre public un seul fichier particulier dans un répertoire et que tout ce qui ne s'y trouve pas, vous pouvez créer manuellement des itinéraires individuels pour chaque fichier plutôt que d'utiliser, express.static()par exemple:

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

Et le code pour créer un itinéraire pour cela

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

Ou, si vous souhaitez toujours délimiter les routes pour les scripts avec /scripts, vous pouvez le faire:

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

Et le code pour créer un itinéraire pour cela

app.get('/scripts/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});
jfriend00
la source
2
Dois-je les copier manuellement ou existe-t-il un moyen Grunt de le faire? Je pense que le mieux serait de copier complètement le dossier bootstrap /scriptscar de cette façon, toutes les dépendances à l'intérieur du module seraient conservées.
Chris
@phpheini - peut-être que cela vous aidera: stackoverflow.com/questions/18966485/… et ce npmjs.com/package/grunt-copy
jfriend00
1
@RobertOschler - Non, il ne vérifie pas les doublons. express.static()envoie la première correspondance qu'il trouve. Étant donné que chaque express.static()instruction ne crée qu'un seul chemin de correspondance possible, il ne doit pas y avoir de doublons à partir d'une express.static()instruction. Si vous disposez de plusieurs express.static()instructions pouvant chacune avoir une correspondance, la première de votre code est celle dont la correspondance sera utilisée. De plus, vous ne devriez vraiment pas avoir plusieurs fichiers avec le même nom et le même chemin d'accès accessibles par les express.static()instructions. La meilleure pratique est de ne pas le faire.
jfriend00
1
@RobertOschler - Vous devez être très, très prudent sur ce à quoi vous fournissez un accès non réglementé. En général, vous ne devez pointer que express.static()vers un répertoire qui contient UNIQUEMENT des fichiers publics (y compris les sous-répertoires). Donc, je ne peux pas vraiment imaginer un projet où il y aurait beaucoup de distrépertoires publiés. Cela me semble très inhabituel. Vous souhaitez peut-être créer un itinéraire spécifique vers un fichier spécifique ou vers 2-3 fichiers. Dans ce cas, vous êtes responsable de lever l'ambiguïté de ces fichiers. De toute façon, cela ne vous fera aucun bien d'avoir quatre script.jsfichiers.
jfriend00
1
@RobertOschler - Si vous ne faites pas un chemin unique devant eux pour correspondre, il n'y a tout simplement aucun moyen pour un code de savoir lequel servir quand /script.jsest demandé par le navigateur. Ainsi, la réponse abstraite est que vous ne devez pas autoriser la situation de noms de fichiers en double, car ce n'est pas un problème résoluble, sauf si vous avez besoin d'un préfixe unique pour chaque répertoire de fichiers. Ensuite, les noms de racine en double ne sont pas un problème de toute façon. Pour une réponse plus spécifique avec une recommandation détaillée, vous devez publier votre propre question avec des détails exacts.
jfriend00
18

J'utiliserais le module path npm puis ferais quelque chose comme ceci:

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

IMPORTANT: nous utilisons path.join pour créer des chemins se joignant en utilisant le système de manière agnostique, c'est-à-dire que sur Windows et Unix, nous avons différents séparateurs de chemin (/ et)

Alexander Egorov
la source
marquez en double comme ci-dessus, path.join n'est qu'une mesure ajoutée, mais toujours la même solution en ajoutant un chemin statique au répertoire node_modules.
blamb
2
"path.join est juste une mesure supplémentaire, mais toujours la même solution" pas la même solution. Strictement parlant, il utilise une jointure spécifique au système (gardez à l'esprit les séparateurs / et \ dans Windows et Unix)
Alexander Egorov
4
OK, je vois votre point, oui c'est toujours bon d'avoir un système agnostique, je ne savais pas que c'était ça. Merci d'avoir fait remarquer cela. Il serait sage d'ajouter cela à la phrase dans votre réponse, au lieu de simplement fournir du code :-), par exemple, expliquez votre code.
blamb le
10

Comme mentionné par jfriend00, vous ne devez pas exposer la structure de votre serveur. Vous pouvez copier vos fichiers de dépendance de projet dans quelque chose comme public/scripts. Vous pouvez le faire très facilement avec dep-linker comme ceci:

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done
Marcelo Lazaroni
la source
1
Les scripts srcressembleront alors à quelque chose /scripts/[package-name]/dist/file.min.js.
Jacob Ford
7

Si vous voulez une solution rapide et facile (et que vous avez installé gulp).

Dans mon, gulpfile.jsje lance une simple tâche de copier-coller qui place tous les fichiers dont j'ai besoin dans le ./public/modules/répertoire.

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']);

L'inconvénient est qu'il n'est pas automatisé. Cependant, si vous n'avez besoin que de quelques scripts et styles copiés (et conservés dans une liste), cela devrait faire l'affaire.

Jesse
la source
On pourrait ajouter cela dans leur package.json dans les scripts 'scripts': {'install':'yarn gulp copy-modules'}, en supposant que yarn est le gestionnaire de packages et que gulp est installé dans le package.
Todd
6

Le répertoire 'node_modules' ne se trouve peut-être pas dans le répertoire actuel, vous devez donc résoudre le chemin de manière dynamique.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
Jake
la source
+1, bien que je ne pense pas que cela fonctionnerait dans tous les cas - Module lié egan npm qui n'est pas dans un sous
Alasdair McLeay
3

Je veux mettre à jour cette question avec une solution plus simple. Créez un lien symbolique vers node_modules.

Le moyen le plus simple d'accorder l'accès public à node_modules est de créer un lien symbolique pointant vers vos node_modules à partir de votre répertoire public. Le lien symbolique fera comme si les fichiers existaient où que le lien soit créé.

Par exemple, si le serveur de noeud a du code pour servir des fichiers statiques

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

et __dirname fait référence à / path / to / app afin que vos fichiers statiques soient servis depuis / path / vers / app / dist

et node_modules se trouve dans / path / to / app / node_modules, puis créez un lien symbolique comme celui-ci sur mac / linux:

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

ou comme ça sur windows:

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

Maintenant, obtenez une demande pour:

node_modules/some/path 

recevra une réponse avec le fichier à l'adresse

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

qui est vraiment le fichier

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

Si votre répertoire dans / path / to / app / dist n'est pas un emplacement sûr, peut-être en raison d'interférences d'un processus de construction avec gulp ou grunt, vous pouvez ajouter un répertoire séparé pour le lien et ajouter un nouvel appel serveStatic tel que:

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

et dans le noeud, ajoutez:

app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));
Curtwphillips
la source
1
Mais vous avez ensuite déplacé votre application vers un autre chemin et le lien symbolique n'est pas valide. Ou si vous souhaitez exécuter votre application sur une autre machine (test / prod), vous devrez la recréer. Tout contributeur à votre application doit faire de même. Il ajoute une certaine maintenance sur les solutions ci-dessus.
mati.o
@ mati.o Qu'en est-il des liens symboliques relatifs? J'aime cette solution, mais uniquement avec des liens symboliques relatifs.
Lukáš Bednařík
3

Je n'ai trouvé aucune solution propre (je ne veux pas exposer la source de tous mes modules node_modules) alors j'ai juste écrit un script Powershell pour les copier:

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

foreach ($dep in $deps) {
    Copy-Item "node_modules/$dep/dist" "static/$dep" -Recurse
}
Timmmm
la source
1

J'ai fait les changements ci-dessous pour AUTO-INCLUDE les fichiers dans l'index html. De sorte que lorsque vous ajoutez un fichier dans le dossier, il sera automatiquement récupéré dans le dossier, sans que vous ayez à inclure le fichier dans 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
la source
1

Voici ce que j'ai configuré sur mon expressserveur:

// 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>

Bonne chance...

Akash
la source