Como incluir scripts localizados dentro da pasta node_modules?

275

Tenho uma pergunta sobre as melhores práticas para inclusão node_modulesem um site HTML.

Imagine que eu tenho o Bootstrap dentro da minha node_modulespasta. Agora, para a versão de produção do site, como incluiria o script Bootstrap e os arquivos CSS localizados dentro da node_modulespasta? Faz sentido deixar o Bootstrap dentro dessa pasta e fazer algo como o seguinte?

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

Ou eu teria que adicionar regras ao meu arquivo gulp que copiasse esses arquivos na minha pasta dist? Ou seria melhor deixar o gulp de alguma forma remover completamente o bootstrap local do meu arquivo HTML e substituí-lo pela versão CDN?

Chris
fonte
2
tópico relacionado stackoverflow.com/questions/24397379/… . ASSIM, aqui está o questino @Palak Bhansali Supondo apenas uma necessidade, isso justifica a implementação do bower com esse único objetivo, por exemplo, por exemplo, um aplicativo gulp express não está instalando o bootstrap diretamente do npm, necessitando de acesso aos arquivos agora no gulp, que estão no node_modules. Isso justifica um caso de uso único para a necessidade de choupo agora para esse único propósito? Esse é o problema que estou enfrentando. Eu já uso compositor, npm, gulp, grunhido, não quero caramanchão pessoalmente e também não quero grunhido neste aplicativo.
blamb
Excelente pergunta que precisa de uma solução intuitiva!
precisa saber é o seguinte

Respostas:

297

Normalmente, você não deseja expor nenhum dos seus caminhos internos para saber como o servidor está estruturado para o mundo externo. O que você pode é criar uma /scriptsrota estática em seu servidor que busca seus arquivos em qualquer diretório em que residam. Portanto, se seus arquivos estiverem "./node_modules/bootstrap/dist/". Em seguida, a tag de script em suas páginas fica assim:

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

Se você estava usando express com nodejs, uma rota estática é tão simples quanto isto:

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

Em seguida, todas as solicitações de navegador /scripts/xxx.jsserão buscadas automaticamente no seu distdiretório em __dirname + /node_modules/bootstrap/dist/xxx.js.

Nota: As versões mais recentes do NPM colocam mais coisas no nível superior, não aninhadas tão profundamente; portanto, se você estiver usando uma versão mais recente do NPM, os nomes dos caminhos serão diferentes dos indicados na pergunta do OP e na resposta atual. Mas, o conceito ainda é o mesmo. Você descobrir onde os arquivos estão localizados fisicamente na sua unidade de servidor e você faz um app.use()com express.static()para fazer um pseudo-caminho para esses arquivos para que você não está expondo a organização do sistema de arquivos do servidor real para o cliente.


Se você não deseja criar uma rota estática como essa, provavelmente é melhor copiar os scripts públicos para um caminho que o servidor da Web trate como /scriptsou qualquer outra designação de nível superior que você deseja usar. Geralmente, você pode fazer essa cópia parte do seu processo de criação / implantação.


Se você deseja tornar público apenas um arquivo em particular em um diretório e nem tudo o que é encontrado nesse diretório, pode criar manualmente rotas individuais para cada arquivo, em vez de usar express.static()como:

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

E o código para criar uma rota para isso

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

Ou, se você ainda deseja delinear rotas para scripts /scripts, faça o seguinte:

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

E o código para criar uma rota para isso

app.get('/scripts/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});
jfriend00
fonte
2
Preciso copiá-las manualmente ou existe uma maneira do Grunt de fazer isso? Eu acho que, de alguma forma, o melhor seria copiar completamente a pasta de autoinicialização, /scriptsporque dessa maneira todas as dependências dentro do módulo seriam mantidas.
Chris
@phpheini - talvez isso ajude: stackoverflow.com/questions/18966485/… e isso npmjs.com/package/grunt-copy
jfriend00:
1
@RobertOschler - Não, ele não verifica se há dups. express.static()envia a primeira correspondência encontrada. Como cada express.static()instrução cria apenas um caminho correspondente possível, não deve haver duplicatas de uma express.static()instrução. Se você tiver várias express.static()instruções de que cada uma pode ter uma correspondência, a primeira no seu código será aquela cuja correspondência será usada. Além disso, você realmente não deve ter vários arquivos com o mesmo nome e caminho relativo alcançáveis ​​por express.static()instruções. A melhor prática é não fazer isso.
precisa saber é o seguinte
1
@RobertOschler - Você precisa ter muito, muito cuidado com o que fornece acesso não regulamentado. Em geral, você deve apontar apenas express.static()para um diretório que SOMENTE contenha arquivos públicos (incluindo subdiretórios). Portanto, não consigo imaginar um projeto em que muitos distdiretórios fossem publicados. Isso me parece muito incomum. Talvez você queira fazer uma rota específica para um arquivo específico ou para 2-3 arquivos. Nesse caso, você é responsável por desambiguar esses arquivos. De qualquer forma, não é bom ter quatro script.jsarquivos.
precisa saber é o seguinte
1
@RobertOschler - Se você não exige um caminho exclusivo na frente deles para corresponder, não há como nenhum código saber qual deles servir quando /script.jssolicitado pelo navegador. Portanto, a resposta abstrata é que você não deve permitir a existência de nomes de arquivos duplicados porque não é um problema solucionável, a menos que você precise de um prefixo exclusivo para cada diretório de arquivos. Em seguida, nomes raiz duplicados não são um problema de qualquer maneira. Para uma resposta mais específica com uma recomendação detalhada, você precisará postar sua própria pergunta com detalhes exatos.
precisa saber é o seguinte
18

Eu usaria o módulo path npm e, em seguida, faria algo assim:

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

IMPORTANTE: usamos path.join para fazer a junção de caminhos usando a maneira independente do sistema, ou seja, no Windows e no Unix, temos diferentes separadores de caminho (/ e)

Alexander Egorov
fonte
marcar duplicado como acima, path.join é apenas uma medida adicionada, mas ainda a mesma solução, adicionando um caminho estático ao diretório node_modules.
blamb
2
"path.join é apenas uma medida adicionada, mas ainda a mesma solução" não a mesma solução. Estritamente falando ele usa específico sistema de união (tenha em mente / e \ separadores em Windows e UNIX)
Alexander Egorov
4
OK, entendo o seu ponto, sim, é sempre bom ter um sistema independente, eu não sabia que era isso. Obrigado por apontar isso. Seria sensato adicionar isso à frase na sua resposta, em vez de apenas fornecer o código :-), por exemplo, explique o seu código.
blamb
10

Como mencionado por jfriend00, você não deve expor sua estrutura de servidor. Você pode copiar os arquivos de dependência do projeto para algo como public/scripts. Você pode fazer isso muito facilmente com o dep-linker como este:

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done
Marcelo Lazaroni
fonte
1
Os scripts srcs serão então algo como /scripts/[package-name]/dist/file.min.js.
Jacob Ford
7

Se você deseja uma solução rápida e fácil (e você tem o gulp instalado).

No meu, gulpfile.jseu executo uma tarefa simples de copiar e colar que coloca todos os arquivos que eu possa precisar no ./public/modules/diretório

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

A desvantagem disso é que não é automatizado. No entanto, se tudo o que você precisa é de alguns scripts e estilos copiados (e mantidos em uma lista), isso deve funcionar.

Jesse
fonte
Pode-se adicionar isso no pacote.json nos scripts 'scripts': {'install':'yarn gulp copy-modules'}, assumindo que o yarn é o gerenciador de pacotes e o gulp está instalado no pacote.
Todd
6

O diretório 'node_modules' pode não estar no diretório atual, portanto, você deve resolver o caminho dinamicamente.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
Jake
fonte
+1, embora eu não acho que isso iria funcionar em todos os casos - módulo Egan npm vinculada que não está em uma subpasta node_modules
Alasdair McLeay
3

Quero atualizar esta pergunta com uma solução mais fácil. Crie um link simbólico para node_modules.

A maneira mais fácil de conceder acesso público ao node_modules é criar um link simbólico apontando para o node_modules de dentro do diretório público. O link simbólico fará com que os arquivos existam onde quer que o link seja criado.

Por exemplo, se o servidor do nó tiver código para veicular arquivos estáticos

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

e __dirname refere-se a / path / to / app para que seus arquivos estáticos sejam servidos em / path / to / app / dist

e node_modules estiver em / path / to / app / node_modules, crie um link simbólico como este no mac / linux:

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

ou assim no windows:

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

Agora, um pedido de obtenção para:

node_modules/some/path 

receberá uma resposta com o arquivo em

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

que é realmente o arquivo em

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

Se seu diretório em / path / to / app / dist não for um local seguro, talvez por causa da interferência de um processo de compilação com gulp ou grunt, você poderá adicionar um diretório separado para o link e adicionar uma nova chamada serveStatic, como:

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

e no nó adicione:

app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));
curtwphillips
fonte
1
Mas você mudou seu aplicativo para um caminho diferente e o link simbólico é inválido. Ou você deseja executar seu aplicativo em uma máquina diferente (teste / produção), precisará criá-lo novamente. Qualquer colaborador do seu aplicativo deve fazer o mesmo. Ele adiciona um pouco de manutenção às soluções acima.
precisa
@ mati.o E quanto aos links simbólicos relativos? Eu gosto dessa solução, mas apenas com links simbólicos relativos.
Lukáš Bednařík 30/10
3

Não encontrei nenhuma solução limpa (não quero expor a fonte de todos os meus node_modules), então acabei de escrever um script do Powershell para copiá-las:

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

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

Fiz as alterações abaixo para incluir automaticamente os arquivos no índice html. Para que quando você adicionar um arquivo à pasta, ele será automaticamente retirado da pasta, sem que você precise incluir o arquivo em 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>
<% } %>
Super Nova
fonte
1

Isto é o que eu configurei no meu 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>

Boa sorte...

Akash
fonte