Wie füge ich Skripte hinzu, die sich im Ordner node_modules befinden?

275

Ich habe eine Frage zu Best Practices für die Aufnahme node_modulesin eine HTML-Website.

Stellen Sie sich vor, ich habe Bootstrap in meinem node_modulesOrdner. Wie würde ich nun für die Produktionsversion der Website das Bootstrap-Skript und die CSS-Dateien in den node_modulesOrdner aufnehmen? Ist es sinnvoll, Bootstrap in diesem Ordner zu belassen und Folgendes zu tun?

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

Oder müsste ich meiner gulp-Datei Regeln hinzufügen, die diese Dateien dann in meinen dist-Ordner kopieren? Oder ist es am besten, wenn gulp den lokalen Bootstrap vollständig aus meiner HTML-Datei entfernt und durch die CDN-Version ersetzt?

Chris
quelle
2
verwandtes Thema stackoverflow.com/questions/24397379/… . Hier ist das questino @Palak Bhansali. Wenn nur ein Bedarf angenommen wird, rechtfertigt dies die Implementierung von bower für diesen einzigen Zweck, beispielsweise wenn eine gulp express-App Bootstrap direkt von npm installiert und Zugriff auf die Dateien in gulp benötigt, die sich in node_modules befinden. Rechtfertigt dies einen einzigen Anwendungsfall für die Notwendigkeit einer Laube für diesen alleinigen Zweck? Das ist das Problem, auf das ich stoße. Ich benutze bereits Composer, npm, schlucken, grunzen, will mich nicht persönlich beugen und will auch nicht grunzen in dieser App.
Blamb
Ausgezeichnete Frage, die eine intuitive Lösung benötigt!
Sydwell

Antworten:

297

Normalerweise möchten Sie keinen Ihrer internen Pfade für die Struktur Ihres Servers nach außen offenlegen. Sie können /scriptsauf Ihrem Server eine statische Route erstellen, die die Dateien aus dem Verzeichnis abruft, in dem sie sich gerade befinden. Wenn sich Ihre Dateien also befinden "./node_modules/bootstrap/dist/". Dann sieht das Skript-Tag auf Ihren Seiten einfach so aus:

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

Wenn Sie Express mit NodeJS verwendet haben, ist eine statische Route so einfach:

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

Dann werden alle Browseranfragen von /scripts/xxx.jsautomatisch aus Ihrem distVerzeichnis unter abgerufen __dirname + /node_modules/bootstrap/dist/xxx.js.

Hinweis: Neuere Versionen von NPM stellen mehr Dinge auf die oberste Ebene, nicht so tief verschachtelt. Wenn Sie also eine neuere Version von NPM verwenden, unterscheiden sich die Pfadnamen von den Angaben in der Frage des OP und in der aktuellen Antwort. Das Konzept ist jedoch immer noch dasselbe. Sie finden heraus , wo die Dateien physisch auf dem Server - Laufwerk befinden, und Sie machen eine app.use()mit express.static()einem pseudo-Pfad zu diesen Dateien zu machen , damit Sie nicht die tatsächliche Server - Dateisystem - Organisation an den Kunden auszusetzen sind.


Wenn Sie keine statische Route wie diese erstellen möchten, ist es wahrscheinlich besser, die öffentlichen Skripte einfach in einen Pfad zu kopieren, den Ihr Webserver als /scriptsoder welche Top-Level-Bezeichnung Sie verwenden möchten. Normalerweise können Sie dieses Kopieren zu einem Teil Ihres Erstellungs- / Bereitstellungsprozesses machen.


Wenn Sie nur eine bestimmte Datei in einem Verzeichnis veröffentlichen möchten und nicht alles, was in diesem Verzeichnis enthalten ist, können Sie manuell einzelne Routen für jede Datei erstellen, anstatt Folgendes zu verwenden express.static():

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

Und der Code, um eine Route dafür zu erstellen

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

Wenn Sie dennoch Routen für Skripte mit abgrenzen möchten /scripts, können Sie Folgendes tun:

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

Und der Code, um eine Route dafür zu erstellen

app.get('/scripts/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});
jfriend00
quelle
2
Muss ich diese manuell kopieren oder gibt es eine Grunt-Möglichkeit, dies zu tun? Ich denke, irgendwie wäre es am besten, den Bootstrap-Ordner vollständig zu kopieren, /scriptsda auf diese Weise alle Abhängigkeiten innerhalb des Moduls erhalten bleiben.
Chris
@phpheini - vielleicht hilft dies: stackoverflow.com/questions/18966485/… und diese npmjs.com/package/grunt-copy
jfriend00
1
@ RobertOschler - Nein, es wird nicht nach Dups gesucht. express.static()sendet die erste gefundene Übereinstimmung. Da jede express.static()Anweisung nur einen möglichen übereinstimmenden Pfad erstellt, sollten keine Duplikate von einer express.static()Anweisung vorhanden sein. Wenn Sie mehrere express.static()Anweisungen haben, von denen jede eine Übereinstimmung haben könnte, ist die erste in Ihrem Code diejenige, deren Übereinstimmung verwendet wird. Außerdem sollten Sie nicht mehrere Dateien mit demselben Namen und demselben relativen Pfad haben, die durch express.static()Anweisungen erreichbar sind . Best Practice ist, das nicht zu tun.
jfriend00
1
@RobertOschler - Sie müssen sehr, sehr vorsichtig sein, auf was Sie unregulierten Zugriff gewähren. Im Allgemeinen sollten Sie nur express.static()auf ein Verzeichnis verweisen , das NUR öffentliche Dateien enthält (einschließlich Unterverzeichnisse). Ich kann mir also kein Projekt vorstellen, in dem viele distVerzeichnisse veröffentlicht wurden. Das kommt mir sehr ungewöhnlich vor. Vielleicht möchten Sie eine bestimmte Route zu einer bestimmten Datei oder zu 2-3 Dateien erstellen. In diesem Fall sind Sie dafür verantwortlich, diese Dateien eindeutig zu definieren. Es wird dir script.jssowieso nichts nützen, vier Dateien zu haben.
jfriend00
1
@RobertOschler - Wenn Sie keinen eindeutigen Pfad vor sich benötigen, um eine Übereinstimmung zu erzielen, kann kein Code erkennen, welcher Code bereitgestellt werden soll, wenn er /script.jsvom Browser angefordert wird. Die abstrakte Antwort lautet also, dass Sie nicht zulassen sollten, dass doppelte Dateinamen vorhanden sind, da dies kein lösbares Problem ist, es sei denn, Sie benötigen ein eindeutiges Präfix für jedes Dateiverzeichnis. Dann sind doppelte Stammnamen sowieso kein Problem. Für eine genauere Antwort mit einer detaillierten Empfehlung müssten Sie Ihre eigene Frage mit genauen Details posten.
jfriend00
18

Ich würde das Pfad-npm-Modul verwenden und dann so etwas tun:

var path = require('path');
app.use('/scripts', express.static(path.join(__dirname, 'node_modules/bootstrap/dist')));
Alexander Egorov
quelle
Markieren Sie das Duplikat wie oben. path.join ist nur eine zusätzliche Kennzahl, aber immer noch dieselbe Lösung, indem Sie dem Verzeichnis node_modules einen statischen Pfad hinzufügen.
Blamb
2
"path.join ist nur eine zusätzliche Maßnahme, aber immer noch dieselbe Lösung", nicht dieselbe Lösung. Genau genommen verwendet es systemspezifische Verknüpfungen (beachten Sie / und \ Trennzeichen in Windows und Unix)
Alexander Egorov
4
OK, ich verstehe Ihren Standpunkt, ja, es ist immer gut, systemunabhängig zu sein. Ich wusste nicht, dass das so ist. Vielen Dank für den Hinweis. Es wäre ratsam, dies dem Satz in Ihrer Antwort hinzuzufügen, anstatt nur Code anzugeben :-), z. B. Ihren Code zu erklären.
Blamb
10

Wie von jfriend00 erwähnt, sollten Sie Ihre Serverstruktur nicht offenlegen. Sie könnten Ihre Projektabhängigkeitsdateien in so etwas wie kopieren public/scripts. Mit dep-linker geht das ganz einfach :

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done
Marcelo Lazaroni
quelle
1
Skripte srcwerden dann so etwas wie sein /scripts/[package-name]/dist/file.min.js.
Jacob Ford
7

Wenn Sie eine schnelle und einfache Lösung wünschen (und Sie haben gulp installiert).

In meinem gulpfile.jsProgramm führe ich eine einfache Aufgabe zum Kopieren und Einfügen aus, mit der alle benötigten Dateien in ein ./public/modules/Verzeichnis gestellt werden.

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

Der Nachteil dabei ist, dass es nicht automatisiert ist. Wenn Sie jedoch nur ein paar Skripte und Stile benötigen, die kopiert (und in einer Liste gespeichert) wurden, sollte dies die Aufgabe erfüllen.

Jesse
quelle
Man könnte dies in ihrer package.json in Skripten hinzufügen 'scripts': {'install':'yarn gulp copy-modules'}, vorausgesetzt, Garn ist der Paketmanager und gulp ist im Paket installiert.
Todd
6

Das Verzeichnis 'node_modules' befindet sich möglicherweise nicht im aktuellen Verzeichnis, daher sollten Sie den Pfad dynamisch auflösen.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
Jake
quelle
+1, obwohl ich nicht denke, dass dies in allen Fällen funktionieren würde - egan npm verknüpftes Modul, das sich nicht in einem node_modules-Unterordner befindet
Alasdair McLeay
3

Ich möchte diese Frage mit einer einfacheren Lösung aktualisieren. Erstellen Sie eine symbolische Verknüpfung zu node_modules.

Der einfachste Weg, öffentlichen Zugriff auf node_modules zu gewähren, besteht darin, einen symbolischen Link zu erstellen, der aus Ihrem öffentlichen Verzeichnis auf Ihre node_modules verweist. Der Symlink macht es so, als ob die Dateien überall dort existieren, wo der Link erstellt wird.

Zum Beispiel, wenn der Knotenserver über Code zum Bereitstellen statischer Dateien verfügt

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

und __dirname bezieht sich auf / path / to / app, sodass Ihre statischen Dateien von / path / to / app / dist bereitgestellt werden

und node_modules befindet sich unter / path / to / app / node_modules. Erstellen Sie dann einen solchen Symlink unter mac / linux:

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

oder so unter Windows:

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

Jetzt eine Anfrage bekommen für:

node_modules/some/path 

erhält eine Antwort mit der Datei unter

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

Das ist wirklich die Datei bei

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

Wenn Ihr Verzeichnis unter / path / to / app / dist kein sicherer Speicherort ist, möglicherweise aufgrund einer Störung durch einen Erstellungsprozess mit gulp oder grunt, können Sie ein separates Verzeichnis für den Link hinzufügen und einen neuen ServeStatic-Aufruf hinzufügen, z.

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

und im Knoten hinzufügen:

app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));
Curtwphillips
quelle
1
Aber dann haben Sie Ihre App auf einen anderen Pfad verschoben und der Symlink ist ungültig. Wenn Sie Ihre App auf einem anderen Computer (Test / Produkt) ausführen möchten, müssen Sie sie erneut erstellen. Jeder Mitwirkende an Ihrer App muss dasselbe tun. Es fügt einige Wartungsarbeiten zu den oben genannten Lösungen hinzu.
Mati.o
@ mati.o Was ist mit relativen symbolischen Links? Ich mag diese Lösung, aber nur mit relativen Symlinks.
Lukáš Bednařík
3

Ich habe keine sauberen Lösungen gefunden (ich möchte nicht die Quelle aller meiner node_modules offenlegen), also habe ich einfach ein Powershell-Skript geschrieben, um sie zu kopieren:

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

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

Ich habe die folgenden Änderungen an AUTO-INCLUDE der Dateien im Index-HTML vorgenommen. Wenn Sie eine Datei in den Ordner einfügen, wird diese automatisch aus dem Ordner übernommen, ohne dass Sie die Datei in index.html aufnehmen müssen

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

Folgendes habe ich auf meinem expressServer eingerichtet:

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

Viel Glück...

Akash
quelle