Come includere gli script che si trovano nella cartella node_modules?


275

Ho una domanda relativa alle migliori pratiche da includere node_modulesin un sito Web HTML.

Immagina di avere Bootstrap nella mia node_modulescartella. Ora, per la versione di produzione del sito Web, come dovrei includere lo script Bootstrap e i file CSS che si trovano all'interno della node_modulescartella? Ha senso lasciare Bootstrap all'interno di quella cartella e fare qualcosa di simile al seguente?

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

O dovrei aggiungere regole al mio file gulp che poi copia quei file nella mia cartella dist? O sarebbe meglio lasciare che Gulp rimuova in qualche modo completamente il bootstrap locale dal mio file HTML e lo sostituisca con la versione CDN?


2
argomento correlato stackoverflow.com/questions/24397379/… . Quindi ecco il questino @Palak Bhansali Supponendo che sia necessario solo uno, giustifica l'implementazione del bower per questo unico scopo, ad esempio un'app gulp express non sta eseguendo il bootstrap direttamente da npm, ha bisogno di accedere ai file ora in gulp, che sono in node_modules. Ciò giustifica un caso monouso per aver bisogno del pergolato ora per questo unico scopo? Questo è il problema in cui mi imbatto. Uso già compositore, npm, gulp, grunt, non voglio perderti personalmente e non voglio nemmeno grugnito su questa app.
blamb

Ottima domanda che necessita di una soluzione intuitiva!
Sydwell,

Risposte:


297

Di solito, non vuoi esporre nessuno dei tuoi percorsi interni per come il tuo server è strutturato al mondo esterno. Quello che puoi è fare una /scriptsrotta statica nel tuo server che recupera i suoi file da qualunque directory si trovino. Quindi, se i tuoi file sono "./node_modules/bootstrap/dist/". Quindi, il tag di script nelle tue pagine assomiglia a questo:

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

Se si utilizzava express con nodejs, una route statica è semplice come questa:

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

Quindi, tutte le richieste del browser /scripts/xxx.jsverranno automaticamente recuperate dalla distdirectory in __dirname + /node_modules/bootstrap/dist/xxx.js.

Nota: le versioni più recenti di NPM mettono più cose al primo livello, non nidificate così in profondità, quindi se si utilizza una versione più recente di NPM, i nomi dei percorsi saranno diversi da quelli indicati nella domanda del PO e nella risposta corrente. Ma il concetto è sempre lo stesso. È scoprire dove i file sono situati fisicamente sul disco del server e si fa uno app.use()con express.static()per fare una pseudo-percorso per i file in modo che non sta esponendo l'organizzazione del sistema di file server reale al client.


Se non vuoi fare un percorso statico come questo, probabilmente stai meglio semplicemente copiando gli script pubblici in un percorso che il tuo server web considera come /scriptso qualunque designazione di livello superiore che desideri utilizzare. Di solito, è possibile rendere questa copia parte del processo di compilazione / distribuzione.


Se si desidera rendere pubblico solo un determinato file in una directory e non tutto ciò che si trova in quella directory con esso, è possibile creare manualmente percorsi individuali per ciascun file anziché utilizzare express.static()come:

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

E il codice per creare un percorso per quello

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

Oppure, se si desidera ancora delineare i percorsi per gli script con /scripts, è possibile effettuare questa operazione:

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

E il codice per creare un percorso per quello

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

2
Devo copiarli manualmente o esiste un modo Grunt per farlo? Penso che in qualche modo il migliore sarebbe copiare completamente la cartella bootstrap /scriptsperché in questo modo verrebbero mantenute tutte le dipendenze all'interno del modulo.
Chris,


1
@RobertOschler - No, non verifica la presenza di duplicati. express.static()manda la prima partita che trova. Poiché ogni express.static()istruzione crea solo un possibile percorso di corrispondenza, non dovrebbero esserci duplicati da express.static()un'istruzione. Se hai più express.static()affermazioni secondo cui ognuna potrebbe avere una corrispondenza, la prima nel tuo codice è quella la cui corrispondenza verrà utilizzata. Inoltre, non dovresti davvero avere più file con lo stesso nome e percorso relativo raggiungibili dalle express.static()istruzioni. La migliore pratica è di non farlo.
jfriend00

1
@RobertOschler - Devi stare molto, molto attento a ciò a cui dai accesso non regolamentato. In generale, è necessario puntare solo express.static()su una directory che contiene SOLO file pubblici (includere sottodirectory). Quindi, non riesco davvero a immaginare un progetto in cui sono state pubblicate molte distdirectory. Mi sembra molto insolito. Forse vuoi fare un percorso specifico per un file specifico o per 2-3 file. In tal caso, sei responsabile di non chiarire questi file. Non ti farà comunque bene avere quattro script.jsfile.
jfriend00,

1
@RobertOschler - Se non si rende necessario un percorso univoco di fronte a loro per abbinare, non c'è modo per nessun codice di sapere quale servire quando /script.jsrichiesto dal browser. Quindi, la risposta astratta è che non dovresti permettere alla situazione di nomi di file duplicati perché non è un problema risolvibile a meno che tu non abbia bisogno di un prefisso univoco per ogni directory di file. Quindi, i nomi di root duplicati non sono comunque un problema. Per una risposta più specifica con una raccomandazione dettagliata, è necessario pubblicare la propria domanda con dettagli esatti.
jfriend00

18

Vorrei utilizzare il modulo path npm e quindi fare qualcosa del genere:

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

IMPORTANTE: usiamo path.join per creare unioni di percorsi usando il sistema in modo agnostico, cioè su Windows e Unix abbiamo diversi separatori di percorso (/ e)


segna duplicato come sopra, path.join è solo una misura aggiunta, ma comunque la stessa soluzione aggiungendo un percorso statico alla directory node_modules.
blamb

2
"path.join è solo una misura aggiunta, ma comunque la stessa soluzione" non la stessa soluzione. A rigor di termini utilizza un join specifico del sistema (tenere presente / e \ separatori in windows e unix)
Alexander Egorov

4
OK, vedo il tuo punto, sì, è sempre bello avere il sistema agnostico, non ero consapevole che fosse quello. Grazie per la segnalazione. Sarebbe saggio aggiungerlo alla frase nella tua risposta, invece di fornire semplicemente il codice :-), ad es. Spiega il tuo codice.
blamb

10

Come menzionato da jfriend00 non dovresti esporre la struttura del tuo server. È possibile copiare i file di dipendenza del progetto in qualcosa del genere public/scripts. Puoi farlo molto facilmente con dep-linker in questo modo:

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

1
Gli script srcsaranno quindi qualcosa di simile /scripts/[package-name]/dist/file.min.js.
Jacob Ford,

7

Se vuoi una soluzione semplice e veloce (e hai installato gulp).

Nel mio, gulpfile.jseseguo una semplice operazione di copia e incolla che inserisce tutti i file di cui potrei aver bisogno nella ./public/modules/directory.

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'aspetto negativo di questo è che non è automatizzato. Tuttavia, se tutto ciò di cui hai bisogno sono alcuni script e stili copiati (e mantenuti in un elenco), questo dovrebbe fare il lavoro.


Si potrebbe aggiungere questo nel loro package.json negli script 'scripts': {'install':'yarn gulp copy-modules'}, supponendo che thread sia il gestore dei pacchetti e gulp sia installato nel pacchetto.
Todd,

6

La directory 'node_modules' potrebbe non essere nella directory corrente, quindi è necessario risolvere il percorso in modo dinamico.

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

+1, anche se non credo che funzionerebbe in tutti i casi - modulo collegato egan npm che non si trova in una sottocartella node_modules
Alasdair McLeay

3

Voglio aggiornare questa domanda con una soluzione più semplice. Creare un collegamento simbolico a node_modules.

Il modo più semplice per concedere l'accesso pubblico a node_modules è quello di creare un collegamento simbolico che punta a node_modules dall'interno della directory pubblica. Il collegamento simbolico lo farà come se i file esistessero ovunque venga creato il collegamento.

Ad esempio, se il server del nodo ha un codice per servire i file statici

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

e __dirname si riferisce a / path / to / app in modo che i file statici vengano offerti da / path / to / app / dist

e node_modules è in / path / to / app / node_modules, quindi crea un link simbolico come questo su mac / linux:

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

o così su windows:

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

Ora richiedi una richiesta per:

node_modules/some/path 

riceverà una risposta con il file all'indirizzo

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

che è davvero il file su

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

Se la tua directory in / path / to / app / dist non è un luogo sicuro, forse a causa di interferenze da un processo di compilazione con gulp o grunt, puoi aggiungere una directory separata per il link e aggiungere una nuova chiamata serveStatic come:

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

e nel nodo aggiungere:

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

1
Ma poi hai spostato la tua app in un percorso diverso e il collegamento simbolico non è valido. Oppure vuoi eseguire la tua app su un altro computer (test / prod), dovrai crearla di nuovo. Qualsiasi collaboratore della tua app deve fare lo stesso. Aggiunge un po 'di manutenzione rispetto alle soluzioni di cui sopra.
mati.o,

@ mati.o Che dire dei relativi collegamenti simbolici? Mi piace questa soluzione, ma solo con relativi link simbolici.
Lukáš Bednařík,

3

Non ho trovato soluzioni pulite (non voglio esporre l'origine di tutti i miei node_modules), quindi ho appena scritto uno script Powershell per copiarli:

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

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

1

Ho apportato le modifiche seguenti per INCLUDERE AUTOMATICAMENTE i file nell'indice html. In modo che quando aggiungi un file nella cartella, questo verrà automaticamente prelevato dalla cartella, senza che tu debba includere il file in 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>
<% } %>

1

Questo è ciò che ho installato sul mio expressserver:

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

In bocca al lupo...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.