Jak dołączyć skrypty znajdujące się w folderze node_modules?

275

Mam pytanie dotyczące najlepszych praktyk w zakresie włączenia node_modules do witryny HTML.

Wyobraź sobie, że mam Bootstrap w moim node_modulesfolderze. W przypadku wersji produkcyjnej witryny, jak dołączyć skrypt Bootstrap i pliki CSS znajdujące się w node_modulesfolderze? Czy sensowne jest pozostawienie Bootstrap w tym folderze i zrobienie czegoś takiego?

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

Czy też muszę dodać reguły do ​​mojego pliku gulp, który następnie skopiuje te pliki do mojego folderu dist? A może lepiej byłoby pozwolić, by łyk całkowicie usunął lokalny bootstrap z mojego pliku HTML i zastąpił go wersją CDN?

Chris
źródło
2
powiązany temat stackoverflow.com/questions/24397379/... . Tak więc oto questino @Palak Bhansali Zakładając, że jest tylko jedna potrzeba, czy usprawiedliwia wdrożenie altany tylko w tym celu, powiedzmy na przykład, że aplikacja ekspresowa gulp instaluje bootstrap bezpośrednio z npm, potrzebując dostępu do plików teraz w gulp, które są w module_węzła. Czy uzasadnia to jednorazowy przypadek użycia altanki tylko w tym jedynym celu? Właśnie na to wpadam. Już używam kompozytora, npm, łyka, chrząknięcia, nie chcę osobiście altany i nie chcę chrząkać w tej aplikacji.
blamb
Doskonałe pytanie, które wymaga intuicyjnego rozwiązania!
Sydwell,

Odpowiedzi:

297

Zwykle nie chcesz ujawniać żadnej z wewnętrznych ścieżek dotyczących struktury serwera na zewnątrz. Możesz zrobić /scriptsstatyczną trasę na serwerze, która pobiera pliki z dowolnego katalogu, w którym się znajdują. Więc jeśli twoje pliki się znajdują "./node_modules/bootstrap/dist/". Następnie tag skryptu na twoich stronach wygląda następująco:

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

Jeśli korzystasz z express z nodejs, statyczna trasa jest tak prosta:

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

Następnie wszelkie żądania przeglądarki /scripts/xxx.jsbędą automatycznie pobierane z distkatalogu na stronie__dirname + /node_modules/bootstrap/dist/xxx.js .

Uwaga: Nowsze wersje NPM umieszczają więcej rzeczy na najwyższym poziomie, nie zagnieżdżone tak głęboko, więc jeśli używasz nowszej wersji NPM, nazwy ścieżek będą inne niż wskazane w pytaniu PO i aktualnej odpowiedzi. Ale koncepcja jest nadal taka sama. Aby dowiedzieć się, gdzie pliki są fizycznie znajduje się na dysku serwera, a ty zrób app.use()z express.static()zrobić pseudo-ścieżkę do tych plików, dzięki czemu nie naraża faktyczną organizację systemu plików serwera do klienta.


Jeśli nie chcesz tworzyć statycznej trasy w ten sposób, prawdopodobnie lepiej jest po prostu skopiować publiczne skrypty na ścieżkę, którą twój serwer internetowy traktuje jako /scriptsdowolne oznaczenie najwyższego poziomu, którego chcesz użyć. Zwykle kopiowanie może stać się częścią procesu kompilacji / wdrażania.


Jeśli chcesz ustawić tylko jeden konkretny plik w katalogu, a nie wszystkie znalezione w nim katalogi, możesz ręcznie utworzyć indywidualne trasy dla każdego pliku, zamiast używać express.static()takich jak:

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

I kod do utworzenia trasy do tego

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

Lub, jeśli nadal chcesz wyznaczyć trasy dla skryptów /scripts, możesz to zrobić:

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

I kod do utworzenia trasy do tego

app.get('/scripts/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});
jfriend00
źródło
2
Czy muszę je ręcznie skopiować, czy jest na to sposób Grunt? Myślę, że najlepiej byłoby całkowicie skopiować folder bootstrap, /scriptsponieważ w ten sposób utrzymane byłyby wszelkie zależności w module.
Chris
@phpheini - być może to pomoże: stackoverflow.com/questions/18966485/... i ten npmjs.com/package/grunt-copy
jfriend00
1
@RobertOschler - Nie, nie sprawdza duplikatów. express.static()wysyła pierwsze znalezione dopasowanie. Ponieważ każda express.static()instrukcja tworzy tylko jedną możliwą ścieżkę dopasowania, nie powinno być duplikatów z jednej express.static()instrukcji. Jeśli masz wiele express.static()instrukcji, z których każda może pasować, to pierwsza w twoim kodzie to ta, która zostanie użyta. Ponadto naprawdę nie powinieneś mieć wielu plików o tej samej nazwie i ścieżce względnej osiągalnej przez express.static()instrukcje. Najlepszą praktyką jest nie robienie tego.
jfriend00
1
@RobertOschler - Musisz bardzo, bardzo uważać na to, co zapewniasz nieuregulowany dostęp. Zasadniczo powinieneś wskazywać tylko express.static()katalog, który zawiera TYLKO pliki publiczne (zawierają podkatalogi). Tak więc nie mogę sobie wyobrazić projektu, w którym distopublikowano wiele katalogów. To wydaje mi się bardzo niezwykłe. Być może chcesz wyznaczyć określoną trasę do określonego pliku lub 2-3 plików. W takim przypadku jesteś odpowiedzialny za ujednoznacznienie tych plików. I tak nie przyda ci się mieć cztery script.jspliki.
jfriend00
1
@RobertOschler - Jeśli nie utworzysz przed nimi unikalnej ścieżki w celu dopasowania, żaden kod nie będzie wiedział, który z nich podać, gdy /script.jszażąda tego przeglądarka. Tak więc abstrakcyjna odpowiedź jest taka, że ​​nie powinieneś dopuszczać do istnienia sytuacji zduplikowanych nazw plików, ponieważ nie jest to problem do rozwiązania, chyba że potrzebujesz unikalnego prefiksu dla każdego katalogu plików. Zatem zduplikowane nazwy katalogu głównego i tak nie stanowią problemu. Aby uzyskać bardziej szczegółową odpowiedź ze szczegółową rekomendacją, musisz opublikować własne pytanie z dokładnymi szczegółami.
jfriend00
18

Użyłbym modułu ścieżki npm, a następnie zrobiłbym coś takiego:

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

WAŻNE: używamy path.join, aby ścieżki łączyły się w sposób agnostyczny systemowy, tj. W Windowsie i Uniksie mamy różne separatory ścieżek (/ i)

Alexander Egorov
źródło
oznacz duplikat jak powyżej, path.join jest tylko dodatkową miarą, ale nadal takim samym rozwiązaniem, dodając ścieżkę statyczną do katalogu node_modules.
blamb
2
„path.join to tylko dodatkowy środek, ale wciąż to samo rozwiązanie”, a nie to samo rozwiązanie. Ściśle mówiąc, używa łączenia specyficznego dla systemu (pamiętaj o separatorach i \ w Windowsie i Uniksie)
Alexander Egorov
4
OK, rozumiem o co ci chodzi, tak, zawsze dobrze jest mieć system niezależny, nie wiedziałem, że o to właśnie chodziło. Dzięki za zwrócenie na to uwagi. Rozsądnie byłoby dodać to do zdania w odpowiedzi, zamiast po prostu podać kod :-), np. Wyjaśnić swój kod.
blamb
10

Jak wspomniano przez jfriend00, nie powinieneś ujawniać swojej struktury serwera. Możesz skopiować pliki zależności projektu do czegoś takiego public/scripts. Możesz to zrobić bardzo łatwo za pomocą dep-linkera :

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done
Marcelo Lazaroni
źródło
1
Skrypty srcs będą wtedy podobne /scripts/[package-name]/dist/file.min.js.
Jacob Ford
7

Jeśli chcesz szybkiego i łatwego rozwiązania (i masz zainstalowany łyk).

W moim gulpfile.jsuruchamiam proste zadanie kopiowania, które umieszcza wszystkie potrzebne pliki w ./public/modules/katalogu.

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

Wadą tego jest to, że nie jest zautomatyzowane. Jeśli jednak wystarczy kilka skryptów i stylów skopiowanych (i przechowywanych na liście), to powinno wystarczyć.

Jesse
źródło
Można to dodać w 'scripts': {'install':'yarn gulp copy-modules'}pakiecie.json w skryptach , zakładając, że przędza jest menedżerem pakietów, a gulp jest zainstalowany w pakiecie.
Todd
6

Katalog „node_modules” może nie znajdować się w bieżącym katalogu, dlatego ścieżkę należy rozwiązać dynamicznie.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));
Jake
źródło
+1, choć nie sądzę, aby działało to we wszystkich przypadkach - np. Moduł połączony npm, który nie znajduje się w podfolderze node_modules
Alasdair McLeay
3

Chcę zaktualizować to pytanie za pomocą prostszego rozwiązania. Utwórz dowiązanie symboliczne do node_modules.

Najłatwiejszym sposobem na przyznanie publicznego dostępu do modułów_węzła jest utworzenie dowiązania symbolicznego do modułów_węzła z katalogu publicznego. Dowiązanie symboliczne sprawi, że pliki będą istnieć wszędzie tam, gdzie zostanie utworzone łącze.

Na przykład, jeśli serwer węzłów ma kod do obsługi plików statycznych

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

a __dirname odnosi się do / path / to / app, więc twoje pliki statyczne są obsługiwane z / path / to / app / dist

a node_modules znajduje się w / path / to / app / node_modules, a następnie utwórz takie dowiązanie symboliczne na mac / linux:

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

lub tak w systemie Windows:

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

Teraz otrzymaj prośbę o:

node_modules/some/path 

otrzyma odpowiedź z plikiem na

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

który tak naprawdę jest plikiem

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

Jeśli twój katalog w / path / to / app / dist nie jest bezpieczną lokalizacją, być może z powodu ingerencji w proces kompilacji z gulpem lub chrząknięciem, możesz dodać osobny katalog dla linku i dodać nowe wywołanie servStatic, takie jak:

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

i w węźle dodaj:

app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));
curtwphillips
źródło
1
Ale potem przeniosłeś swoją aplikację na inną ścieżkę, a dowiązanie symboliczne jest nieprawidłowe. Lub chcesz uruchomić aplikację na innym komputerze (test / prod), musisz ją ponownie utworzyć. Każdy współpracownik Twojej aplikacji musi zrobić to samo. Dodaje pewne poprawki do powyższych rozwiązań.
mati.o,
@ mati.o Co z względnymi symbolicznymi linkami? Lubię to rozwiązanie, ale tylko z względnymi dowiązaniami symbolicznymi.
Lukáš Bednařík
3

Nie znalazłem żadnych czystych rozwiązań (nie chcę ujawniać źródła wszystkich moich modułów_węzła), więc po prostu napisałem skrypt Powershell, aby je skopiować:

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

foreach ($dep in $deps) {
    Copy-Item "node_modules/$dep/dist" "static/$dep" -Recurse
}
Timmmm
źródło
1

Zrobiłem poniższe zmiany, aby AUTO-ZAWIERA pliki w indeksie HTML. Aby dodać plik do folderu, zostanie on automatycznie pobrany z folderu, bez konieczności dołączania pliku do pliku 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
źródło
1

Oto co skonfigurowałem na moim expressserwerze:

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

Powodzenia...

Akash
źródło