¿Cómo incluir scripts ubicados dentro de la carpeta node_modules?

275

Tengo una pregunta sobre las mejores prácticas para incluir node_modulesen un sitio web HTML.

Imagina que tengo Bootstrap dentro de mi node_modulescarpeta. Ahora, para la versión de producción del sitio web, ¿cómo incluiría el script Bootstrap y los archivos CSS ubicados dentro de la node_modulescarpeta? ¿Tiene sentido dejar Bootstrap dentro de esa carpeta y hacer algo como lo siguiente?

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

¿O tendría que agregar reglas a mi archivo Gulp que luego copiar esos archivos en mi carpeta dist? ¿O sería mejor dejar que Gulp elimine por completo el bootstrap local de mi archivo HTML y reemplazarlo con la versión CDN?

Chris
fuente
2
tema relacionado stackoverflow.com/questions/24397379/… . Así que aquí está el questino @Palak Bhansali Suponiendo que solo se necesita, justifica la implementación de Bower para este único propósito, por ejemplo, una aplicación Gulp Express que instala bootstrap directamente desde npm, necesitando acceso a los archivos ahora en Gulp, que están en node_modules. ¿Esto justifica un caso de uso único para necesitar una glorieta ahora para este único propósito? Ese es el problema con el que me encuentro. Ya uso compositor, npm, trago, gruñido, no quiero Bower personalmente, y tampoco quiero gruñir en esta aplicación.
blamb
¡Excelente pregunta que necesita una solución intuitiva!
Sydwell

Respuestas:

297

Por lo general, no desea exponer ninguna de sus rutas internas de cómo está estructurado su servidor al mundo exterior. Lo que puede hacer es hacer una /scriptsruta estática en su servidor que recupere sus archivos de cualquier directorio en el que residan. Entonces, si sus archivos están en "./node_modules/bootstrap/dist/". Luego, la etiqueta del script en sus páginas se ve así:

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

Si estaba utilizando express con nodejs, una ruta estática es tan simple como esta:

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

Luego, cualquier solicitud del navegador se /scripts/xxx.jsobtendrá automáticamente de su distdirectorio en __dirname + /node_modules/bootstrap/dist/xxx.js.

Nota: Las versiones más nuevas de NPM ponen más cosas en el nivel superior, no anidadas tan profundamente, por lo que si está utilizando una versión más nueva de NPM, los nombres de ruta serán diferentes a los indicados en la pregunta del OP y en la respuesta actual. Pero, el concepto sigue siendo el mismo. A averiguar donde los archivos están ubicados físicamente en la unidad de servidor y de hacer una app.use()con express.static()para hacer un pseudo-camino a esos archivos para que no se está exponiendo a la organización del sistema de archivos del servidor real para el cliente.


Si no desea hacer una ruta estática como esta, entonces probablemente sea mejor que simplemente copie los scripts públicos en una ruta que su servidor web trate como /scriptso la designación de nivel superior que desee usar. Por lo general, puede hacer que esta copia sea parte de su proceso de compilación / implementación.


Si desea hacer público solo un archivo en particular en un directorio y no todo lo que se encuentra en ese directorio, puede crear manualmente rutas individuales para cada archivo en lugar de utilizarlo express.static()como:

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

Y el código para crear una ruta para eso

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

O, si aún desea delinear rutas para scripts /scripts, puede hacer esto:

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

Y el código para crear una ruta para eso

app.get('/scripts/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});
jfriend00
fuente
2
¿Necesito copiarlos manualmente o hay una forma Grunt de hacerlo? Creo que de alguna manera lo mejor sería copiar completamente la carpeta bootstrap /scriptsporque de esa manera se mantendrían las dependencias dentro del módulo.
Chris
@phpheini, quizás esto ayude: stackoverflow.com/questions/18966485/… y esto npmjs.com/package/grunt-copy
jfriend00
1
@RobertOschler: No, no comprueba dups. express.static()envía la primera coincidencia que encuentra. Como cada express.static()declaración solo crea una ruta de coincidencia posible, no debe haber duplicados de una express.static()declaración. Si tiene varias express.static()declaraciones de que cada una podría tener una coincidencia, entonces la primera en su código es aquella cuya coincidencia se utilizará. Además, realmente no debería tener varios archivos con el mismo nombre y ruta relativa a los que puedan acceder las express.static()declaraciones. La mejor práctica es no hacer eso.
jfriend00
1
@RobertOschler: debe tener mucho cuidado con lo que proporciona acceso no regulado. En general, solo debe apuntar express.static()a un directorio que SOLO contenga archivos públicos (incluya subdirectorios). Por lo tanto, no puedo imaginar un proyecto en el que se distpublicaran muchos directorios. Eso me parece muy inusual. Quizás desee hacer una ruta específica a un archivo específico o a 2-3 archivos. En ese caso, usted es responsable de desambiguar esos archivos. De script.jstodos modos, no te hará ningún bien tener cuatro archivos.
jfriend00
1
@RobertOschler: si no se requiere una ruta única delante de ellos para que coincida, simplemente no hay forma de que ningún código sepa cuál servir cuando /script.jsel navegador lo solicita. Entonces, la respuesta abstracta es que no debe permitir que exista la situación de nombres de archivo duplicados porque no es un problema solucionable a menos que requiera un prefijo único para cada directorio de archivos. Entonces, los nombres raíz duplicados no son un problema de todos modos. Para obtener una respuesta más específica con una recomendación detallada, deberá publicar su propia pregunta con detalles exactos.
jfriend00
18

Usaría el módulo path npm y luego haría algo como esto:

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

IMPORTANTE: utilizamos path.join para hacer que las rutas se unan de manera independiente del sistema, es decir, en Windows y Unix tenemos diferentes separadores de ruta (/ y)

Alexander Egorov
fuente
marque duplicado como arriba, path.join es solo una medida agregada, pero sigue siendo la misma solución al agregar una ruta estática al directorio node_modules.
blamb
2
"path.join es solo una medida adicional, pero sigue siendo la misma solución", no la misma solución. Estrictamente hablando, utiliza la unión específica del sistema (tenga en cuenta / y \ separadores en Windows y Unix)
Alexander Egorov
44
Bien, veo su punto, sí, siempre es bueno tener un sistema independiente, no sabía que eso era. Gracias por señalar eso. Sería aconsejable agregar eso a la oración en su respuesta, en lugar de simplemente proporcionar el código :-), por ejemplo, explique su código.
blamb
10

Como mencionó jfriend00, no debe exponer la estructura de su servidor. Puede copiar los archivos de dependencia de su proyecto a algo así public/scripts. Puede hacer esto muy fácilmente con dep-linker como este:

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done
Marcelo Lazaroni
fuente
1
Los scripts srcs serán algo así como /scripts/[package-name]/dist/file.min.js.
Jacob Ford
7

Si desea una solución rápida y fácil (y tiene un trago instalado).

En mi gulpfile.js, ejecuto una tarea simple de copiar y pegar que coloca todos los archivos que pueda necesitar en el ./public/modules/directorio.

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

La desventaja de esto es que no está automatizado. Sin embargo, si todo lo que necesita son algunos scripts y estilos copiados (y guardados en una lista), esto debería hacer el trabajo.

Jesse
fuente
Uno podría agregar esto en su package.json en scripts 'scripts': {'install':'yarn gulp copy-modules'}, suponiendo que yarn es el administrador de paquetes y gulp está instalado en el paquete.
Todd
6

El directorio 'node_modules' puede no estar en el directorio actual, por lo que debe resolver la ruta dinámicamente.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
Jake
fuente
+1, aunque no creo que esto funcione en todos los casos - módulo vinculado egan npm que no está en una subcarpeta node_modules
Alasdair McLeay
3

Quiero actualizar esta pregunta con una solución más fácil. Cree un enlace simbólico a node_modules.

La forma más fácil de otorgar acceso público a node_modules es crear un enlace simbólico que apunte a sus node_modules desde su directorio público. El enlace simbólico hará que los archivos existan donde se cree el enlace.

Por ejemplo, si el servidor de nodo tiene código para servir archivos estáticos

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

y __dirname se refiere a / path / to / app para que sus archivos estáticos se sirvan desde / path / to / app / dist

y node_modules está en / path / to / app / node_modules, luego cree un enlace simbólico como este en mac / linux:

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

o así en windows:

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

Ahora una solicitud de obtención de:

node_modules/some/path 

recibirá una respuesta con el archivo en

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

que es realmente el archivo en

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

Si su directorio en / path / to / app / dist no es una ubicación segura, quizás debido a la interferencia de un proceso de compilación con gulp o gruñido, entonces puede agregar un directorio separado para el enlace y agregar una nueva llamada serveStatic como:

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

y en el nodo agregar:

app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));
Curtwphillips
fuente
1
Pero luego movió su aplicación a una ruta diferente y el enlace simbólico no es válido. O si desea ejecutar su aplicación en una máquina diferente (prueba / producto), deberá crearla nuevamente. Cualquier contribuyente a su aplicación debe hacer lo mismo. Agrega algo de mantenimiento sobre las soluciones anteriores.
mati.o
@ mati.o ¿Qué pasa con los enlaces simbólicos relativos? Me gusta esta solución, pero solo con enlaces simbólicos relativos.
Lukáš Bednařík
3

No encontré ninguna solución limpia (no quiero exponer la fuente de todos mis node_modules), así que simplemente escribí un script de Powershell para copiarlos:

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

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

Hice los siguientes cambios para INCLUIR AUTOMÁTICAMENTE los archivos en el índice html. De modo que cuando agrega un archivo en la carpeta, se recogerá automáticamente de la carpeta, sin tener que incluir el archivo en 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
fuente
1

Esto es lo que configuré en mi expressservidor:

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

Buena suerte...

Akash
fuente