Jak sprawić, aby node.js wymagał wartości bezwzględnej? (zamiast względnego)

234

Chciałbym wymagać moich plików zawsze od katalogu głównego mojego projektu, a nie od bieżącego modułu.

Na przykład, jeśli spojrzysz na https://github.com/visionmedia/express/blob/2820f2227de0229c5d7f28009aa432f9f3a7b5f9/examples/downloads/app.js wiersz 6 zobaczysz

express = require('../../')

To naprawdę złe IMO. Wyobraź sobie, że chciałbym umieścić wszystkie moje przykłady bliżej źródła tylko o jeden poziom. Byłoby to niemożliwe, ponieważ musiałbym aktualizować ponad 30 przykładów i wiele razy w każdym z nich. Do tego:

express = require('../')

Moim rozwiązaniem byłoby mieć specjalny przypadek oparty na katalogu głównym: jeśli ciąg zaczyna się od $, to jest względny w stosunku do folderu głównego projektu.

Jakakolwiek pomoc się przyda, dzięki

Aktualizacja 2

Teraz używam wymaganego.js, który pozwala pisać w jeden sposób i działa zarówno na kliencie, jak i na serwerze. Require.js pozwala także tworzyć niestandardowe ścieżki.

Aktualizacja 3

Teraz przeniosłem się na webpack + gulp i korzystam z rozszerzonego wymagania do obsługi modułów po stronie serwera. Zobacz tutaj uzasadnienie: http://hackhat.com/p/110/module-loader-webpack-vs-requirejs-vs-browserify/

Totty.js
źródło
Jeśli kiedykolwiek zdecydujesz się na użycie stałej / zmiennej ścieżki głównej, ta odpowiedź na to działa . Rozwiązanie wykorzystuje mały moduł github do określenia ścieżki rootowania.
steampowered

Odpowiedzi:

162

A co z:

var myModule = require.main.require('./path/to/module');

Wymaga pliku tak, jakby był wymagany z głównego pliku js, więc działa całkiem dobrze, dopóki główny plik js znajduje się w katalogu głównym projektu ... i to doceniam.

cronvel
źródło
Niezły pomysł (: możesz następnie zdefiniować inne metody, aby w jakiś sposób odwzorować aplikację w module Requ.main. Myślę, że mógłbyś wtedy zrobić request.main.req ('client / someMod'). Fajny pomysł, ale to by bądź bardziej gadatliwy niż moje obecne wymagania. Nie sądzę też, żeby było warto, ponieważ nie lubię też przeglądania, ponieważ zmiany nie są natychmiastowe i pomijam zmiany (ponieważ mój kod powinien działać zarówno w przeglądarce, jak i node.js)
Totty.js
4
Jeśli uznasz to za zbyt szczegółowe, po prostu użyj .bind (): var rootReq = requ.bind (requ.main); rootReq ('./path/to/module');
cronvel
tak, może to być przydatne dla kogoś, kto nadal chce używać Browserify po stronie klienta. Dla mnie nie ma już potrzeby, ale i tak dziękuję za odpowiedź (:
Totty.js
6
JEŻELI GŁÓWNA JEST NA ROOTIE PROJEKTU :)
Alexander Mills
12
To rozwiązanie nie będzie działać, jeśli kod objęty jest testami jednostkowymi, takimi jak test Mocha
Alx Lark,
129

W Podręczniku Browserify znajduje się naprawdę interesująca sekcja :

unikanie ../../../../../../ ..

Nie wszystko w aplikacji poprawnie należy do publicznego npm, a narzut związany z konfiguracją prywatnego npm lub git repo jest wciąż dość duży w wielu przypadkach. Oto kilka podejść do unikania ../../../../../../../ problemu ścieżek względnych.

node_modules

Ludzie czasami sprzeciwiają się umieszczeniu modułów specyficznych dla aplikacji w module_węzła, ponieważ nie jest oczywiste, jak sprawdzić moduły wewnętrzne bez sprawdzania modułów innych firm z npm.

Odpowiedź jest dość prosta! Jeśli masz .gitignoreplik, który ignoruje node_modules:

node_modules

Możesz po prostu dodać wyjątek !dla każdego z wewnętrznych modułów aplikacji:

node_modules/*
!node_modules/foo
!node_modules/bar

Pamiętaj, że nie możesz anulować ignorowania podkatalogu, jeśli rodzic jest już ignorowany. Więc zamiast ignorując node_modules, trzeba ignorować każdym katalogu wnętrze node_modules z node_modules/* trik, a następnie można dodać wyjątki.

Teraz w dowolnym miejscu aplikacji będziesz mógł require('foo') lubrequire('bar') nie posiadające bardzo duży i kruchy ścieżkę względną.

Jeśli masz dużo modułów i chcesz je bardziej oddzielić od modułów innych firm zainstalowanych przez npm, możesz po prostu umieścić je wszystkie w katalogu, na node_modulesprzykład node_modules/app:

node_modules/app/foo
node_modules/app/bar

Teraz będziesz mógł require('app/foo')lubrequire('app/bar') z dowolnego miejsca w aplikacji.

W swoim .gitignoredodaj po prostu wyjątek dla node_modules/app:

node_modules/*
!node_modules/app

Jeśli aplikacja nie przekształca skonfigurowany w package.json, musisz utworzyć osobne package.json z własnym przekształcenia w swojej dziedzinie node_modules/foolubnode_modules/app/foo katalogu katalogu komponentów, ponieważ transformacje nie mają zastosowania poza granicami modułów. Dzięki temu Twoje moduły będą bardziej odporne na zmiany konfiguracji w aplikacji i łatwiej będzie samodzielnie używać pakietów poza aplikacją.

dowiązanie symboliczne

Inną przydatną sztuczką, jeśli pracujesz nad aplikacją, w której możesz tworzyć dowiązania symboliczne i nie potrzebujesz obsługiwać okien, jest dowiązanie symboliczne lib/ lub app/folder do node_modules. Z katalogu głównego projektu wykonaj:

ln -s ../lib node_modules/app

a teraz z dowolnego miejsca w swoim projekcie będziesz mógł wymagać plików lib/, wykonując require('app/foo.js')polecenie uzyskania lib/foo.js.

ścieżki niestandardowe

Możesz zobaczyć, że niektóre miejsca mówią o używaniu $NODE_PATH zmiennej środowiskowej lubopts.paths o dodaniu katalogów dla węzła i przeglądaniu w celu znalezienia modułów.

W przeciwieństwie do większości innych platform, korzystanie z tablicy katalogów ścieżek w stylu powłoki $NODE_PATHnie jest tak korzystne w węźle w porównaniu do efektywnego wykorzystania node_moduleskatalogu.

Wynika to z faktu, że aplikacja jest ściślej sprzężona z konfiguracją środowiska wykonawczego, więc jest więcej ruchomych części, a aplikacja będzie działać tylko wtedy, gdy środowisko zostanie poprawnie skonfigurowane.

węzeł i przeglądarka obsługują oba, ale odradzają korzystanie z nich $NODE_PATH.

Paolo Moretti
źródło
17
Jedyną wadą umieszczenia go w node_modulesfolderze jest to, że utrudnia to nuke ( rm -rf node_modules) folder
Michael
13
@Michael Nie tak trudniej: git clean -dx node_modules
Peter Wilkinson
3
Lub, jeśli zapomniałeś git cleanskładni, zawsze możesz rm -rf node_modules && git checkout node_modules- upewnij się, że git stashw przypadku jakichkolwiek zmian w node_modulespodkatalogach.
derenio
1
Podoba mi się pomysł użycia node_modules, ale nie do przechowywania kodu źródłowego, biorąc pod uwagę jego zmienność. Czy nie byłoby sensowniej opublikować oddzielny moduł i zapisać go jako zależność w oryginalnym projekcie? Zapewnia jasne rozwiązanie dla zmienności katalogu node_modules i polega wyłącznie na npm, a nie na git, dowiązaniach symbolicznych lub rozwiązaniu $ NODE_PATH.
Kevin Koshiol
1
NODE_PATH wygląda jak droga. „Twoja aplikacja będzie działać tylko wtedy, gdy środowisko zostanie poprawnie skonfigurowane”, to zawsze prawda! Czy nie jest łatwiej uzyskać konfigurację środowiska (zwykle w jednym pliku) niż zmieniać każdy import w każdym pliku?
CpILL
73

Lubię utworzyć nowy node_modulesfolder dla wspólnego kodu, a następnie pozwolić, aby węzeł i wymagał robienia tego, co robi najlepiej.

na przykład:

- node_modules // => these are loaded from your package.json
- app
  - node_modules // => add node-style modules
    - helper.js
  - models
    - user
    - car
- package.json
- .gitignore

Na przykład, jeśli jesteś w środku car/index.js, możesz require('helper')i węzeł go znajdzie!

Jak działają moduły_węzła

Węzeł ma sprytny algorytm rozwiązywania modułów, który jest unikalny wśród konkurencyjnych platform.

Jeśli require('./foo.js')z /beep/boop/bar.js, węzeł będzie szukał ./foo.jsw /beep/boop/foo.js. Ścieżki zaczynające się od ./lub ../zawsze są lokalne dla pliku, który wywołuje require().

Jeśli jednak potrzebujesz nie względnej nazwy, takiej jak require('xyz')from /beep/boop/foo.js, węzeł przeszukuje te ścieżki w kolejności, zatrzymując się przy pierwszym dopasowaniu i zgłaszając błąd, jeśli nic nie zostanie znalezione:

/beep/boop/node_modules/xyz
/beep/node_modules/xyz
/node_modules/xyz

Dla każdego xyzistniejącego katalogu węzeł najpierw szuka znaku, xyz/package.jsonaby sprawdzić, czy "main"pole istnieje. Te "main"określa, który plik pola powinno przejąć Jeśli require()ścieżka katalogu.

Na przykład jeśli /beep/node_modules/xyzjest pierwszym dopasowaniem i /beep/node_modules/xyz/package.jsonma:

{
  "name": "xyz",
  "version": "1.2.3",
  "main": "lib/abc.js"
}

wówczas eksport z /beep/node_modules/xyz/lib/abc.jszostanie zwrócony przez require('xyz').

Jeśli nie package.jsonma "main"pola lub nie ma go , index.jsprzyjmuje się:

/beep/node_modules/xyz/index.js
Blair Anderson
źródło
2
świetne wyjaśnienie, jak to działa podczas ładowania modułu
go
2
To bardzo eleganckie rozwiązanie, pozwala uniknąć wszystkich problemów z powyższymi odpowiedziami. Należy rozważyć odpowiedź, imho.
rodurico
38

Wielkie zdjęcie

Wydaje się „naprawdę zły”, ale daj mu czas. To jest naprawdę dobre. Te wyraźne require()zapewniają całkowitą przejrzystość i łatwość zrozumienia, która jest jak powiew świeżego powietrza podczas cyklu życia projektu.

Pomyśl o tym w ten sposób: czytasz przykład, zanurzając palce w Node.js i zdecydowałeś, że to „naprawdę zły IMO”. Jesteś drugim przywódcą społeczności Node.js, który zapisał więcej godzin na pisanie i utrzymywanie aplikacji Node.js niż ktokolwiek inny. Jaka jest szansa, że ​​autor popełnił taki debiutant? (I zgadzam się, z mojego środowiska Ruby i Python, na początku wydaje się to katastrofą.)

Wokół Node.js. jest dużo szumu i przeciwdziałania. Ale kiedy opadnie kurz, uznamy, że wyraźne moduły i pakiety „najpierw lokalne” były głównym motorem adopcji.

Wspólny przypadek

Oczywiście node_modulesz bieżącego katalogu przeszukiwany jest rodzic, a następnie dziadek, pradziadek itp. Więc pakietów zainstalowano już działa w ten sposób. Zwykle możesz to zrobić require("express")z dowolnego miejsca w projekcie i działa dobrze.

Jeśli zauważysz, że ładujesz wspólne pliki z katalogu głównego projektu (być może dlatego, że są to wspólne funkcje narzędziowe), to jest duża wskazówka, że ​​nadszedł czas, aby stworzyć pakiet. Pakiety są bardzo proste: przenieś swoje pliki node_modules/i umieść package.json tam. Voila! Wszystko w tej przestrzeni nazw jest dostępne z całego projektu. Pakiety to właściwy sposób na przeniesienie kodu do globalnej przestrzeni nazw.

Inne obejścia

Ja osobiście nie używam tych technik, ale one odpowiadają na twoje pytanie i oczywiście znasz swoją sytuację lepiej niż ja.

Możesz ustawić $NODE_PATHna katalog główny projektu. Ten katalog zostanie przeszukany, gdy Ty require().

Następnie możesz pójść na kompromis i wymagać wspólnego, lokalnego pliku ze wszystkich swoich przykładów. Ten wspólny plik po prostu ponownie eksportuje prawdziwy plik z katalogu dziadków.

przykłady / pliki do pobrania / app.js (i wiele innych podobnych)

var express = require('./express')

przykłady / pliki do pobrania / express.js

module.exports = require('../../')

Teraz, gdy przenosisz te pliki, najgorszym przypadkiem jest naprawienie modułu z jedną podkładką dystansową .

JasonSmith
źródło
14
Zgadzam się, że chłopaki z Node.js musieli wybrać względne wymaganie z jakiegoś powodu. Po prostu nie widzę jego zalet, ani z twojej odpowiedzi. Wciąż wydaje mi się to „złe”;)
Adam Schmideg
21
„Jesteście zgadywaniem liderów społeczności Node.js” - ci sami przywódcy zdecydowali się użyć callbacków zamiast kontraktów futures / obietnic. Większość moich konsultacji z nodejs polega na przeklinaniu „przywódców” i przekonaniu ludzi, aby przenieśli się do JVM. Co jest znacznie łatwiejsze po kilku miesiącach używania nodejów :)
David Sergey
8
@nirth, przejść do JVM? Na miłość boską, dlaczego?
Ivancho
31
„Jesteście liderami społeczności Node.js”, unikajcie tego zniechęcającego do myślenia tonu.
atlex2
15
Cholera, jest drugim zgadującym przywódcą węzła. Tak rozwija się branża. Gdyby faceci z węzła nie odgadli liderów, którzy wsparli modele współbieżności oparte na wątkach, nie mielibyśmy węzła.
d512,
20

Spójrz na node-rfr .

To takie proste:

var rfr = require('rfr');
var myModule = rfr('projectSubDir/myModule');
Warmsea
źródło
myślę, że drugą linią powinien być var ​​myModule = rfr ('/ projectSubDir / myModule');
Sikorski
1
Z dokumentacji: var module2 = rfr ('lib / module2'); // Wiodący ukośnik można pominąć.
igelineau
Próbowałem, a rfr działa OK, aby wykonać z węzłem, ale psuje nawigację kodu za pomocą VS Code ... Nie udało mi się znaleźć obejścia, aby móc korzystać z autouzupełniania w VS ...
Alex Mantaut
13

Jeśli używasz przędzy zamiast npm , możesz użyć obszarów roboczych .

Powiedzmy, że mam folder, servicesktórego chciałbym łatwiej wymagać:

.
├── app.js
├── node_modules
├── test
├── services
   ├── foo
   └── bar
└── package.json

Aby utworzyć obszar roboczy Przędza, utwórz package.jsonplik w services folder:

{
  "name": "myservices",
  "version": "1.0.0"
}

W głównym pakiecie.json dodaj:

"private": true,
"workspaces": ["myservices"]

Uruchom yarn installz katalogu głównego projektu.

Następnie w dowolnym miejscu kodu możesz:

const { myFunc } = require('myservices/foo')

zamiast czegoś takiego:

const { myFunc } = require('../../../../../../services/foo')
cyberwombat
źródło
6
Być może warto wyjaśnić, że działa to tylko w przypadku przędzy , a nie npm? Pomyślałem, że to prawdopodobnie zadziała również dla npm, więc spędziłem trochę czasu zastanawiając się, co zrobiłem źle, dopóki nie spróbowałem użyć przędzy. Być może było to głupie założenie, ale być może nie jestem jedynym.
ArneHugo
2
Zredagowałem trochę, aby to wyjaśnić. Przepraszam za zamieszanie.
cyberwombat
12

IMHO, najprostszym sposobem jest zdefiniowanie własnej funkcji jako części GLOBALobiektu. Utwórz projRequire.jsw katalogu głównym swojego projektu z następującą zawartością:

var projectDir = __dirname;

module.exports = GLOBAL.projRequire = function(module) {
  return require(projectDir + module);
}

W głównym pliku przed wprowadzeniem requiredowolnego modułu specyficznego dla projektu:

// init projRequire
require('./projRequire');

Potem działa dla mnie:

// main file
projRequire('/lib/lol');

// index.js at projectDir/lib/lol/index.js
console.log('Ok');


@Totty, wymyśliłem inne rozwiązanie, które może działać w przypadku opisanym w komentarzach. Opis będzie tl;dr, więc lepiej pokażę zdjęcie ze strukturą mojego projektu testowego .

Aleksei Zabrodskii
źródło
cóż, do tej pory wydaje się to najlepszym sposobem. Robię: GLOBAL.requires = wymagają ('r'). R; w moim pliku index.js. Ale mam problem w moich testach ślubowania, nie uruchamiają one index.js, więc moje testy kończą się niepowodzeniem, ponieważ wymaga to, że jest niezdefiniowane. W każdym razie na razie mogę dodać GLOBAL.requires = wymagany ('r'). R; na szczycie każdego testu. jakiś lepszy pomysł? github.com/totty90/production01_server/commit/…
Totty.js
problem występuje, gdy jestem w „teście-testów / węzłów_modules / other.js” i potrzebuję „teście testów-pathes / modułów_węzłów / some.js”. Powinienem wymagać („./ some”) zamiast wymagać („prj / some”). I w ten sposób cała moja aplikacja będzie w katalogu node_modules?
Totty.js
@Totty, żaden problem nie wymaga prj/someod prj/other(właśnie przetestowane require('prj/some'). Wszystkie wspólne moduły aplikacji mogą się tam znaleźć (np. Warstwa bazy danych). Nie zrobi różnicy, gdzie jest, powiedzmy, twój lib. Spróbuj sprawdzić, czy pasuje.
Aleksei Zabrodskii
tak, zaktualizowałem go: github.com/totty90/production01_server/tree/master/node_modules/…, który działał świetnie. Ale mogę umieścić wszystkie moje pliki o jeden poziom wyżej bez korzystania z modułów node_modules?
Totty.js
12

Używam process.cwd()w swoich projektach. Na przykład:

var Foo = require(process.cwd() + '/common/foo.js');

Warto zauważyć, że doprowadzi to do requirepowstania absolutnej ścieżki, chociaż nie mam jeszcze z tym problemów.

Walter Roman
źródło
1
To zły pomysł, ponieważ CWD nie musi być tym samym katalogiem, w którym zapisana jest aplikacja.
jiwopene
11

Jest to dobre omówienie tego problemu tutaj .

Zetknąłem się z tym samym problemem architektonicznym: chciałem zapewnić mojej aplikacji więcej organizacji i wewnętrznych przestrzeni nazw, bez:

  • mieszanie modułów aplikacji z zewnętrznymi zależnościami lub kłopotanie się prywatnymi repozytoriami npm dla kodu specyficznego dla aplikacji
  • stosowanie względnych wymagań, które utrudniają refaktoryzację i rozumienie
  • używając dowiązań symbolicznych lub zmieniając ścieżkę węzła, co może zaciemniać lokalizacje źródeł i nie działa dobrze z kontrolą źródła

Ostatecznie zdecydowałem się uporządkować kod przy użyciu konwencji nazewnictwa plików, a nie katalogów. Struktura wyglądałaby mniej więcej tak:

  • npm-shrinkwrap.json
  • pakiet.json
  • node_modules
    • ...
  • src
    • app.js
    • app.config.js
    • app.models.bar.js
    • app.models.foo.js
    • app.web.js
    • app.web.routes.js
    • ...

Następnie w kodzie:

var app_config = require('./app.config');
var app_models_foo = require('./app.models.foo');

Lub tylko

var config = require('./app.config');
var foo = require('./app.models.foo');

a zależności zewnętrzne są dostępne jak zwykle z node_modules:

var express = require('express');

W ten sposób cały kod aplikacji jest hierarchicznie zorganizowany w moduły i dostępny dla wszystkich innych kodów względem katalogu głównego aplikacji.

Główną wadą jest oczywiście to, że w przeglądarce plików nie można rozwinąć / zwinąć drzewa, tak jakby było ono faktycznie zorganizowane w katalogach. Ale podoba mi się to, że jest bardzo jasne, skąd pochodzi cały kod i nie używa żadnej „magii”.

pośrednio oświetlony
źródło
Z treści, którą połączyłeś, rozwiązanie nr 7, „The Wrapper”, jest dość proste i wygodne.
Pier-Luc Gendreau,
Widzę jeszcze jedną małą wygodę - „przeniesienie” pliku do innego „folderu” zmienia się w nazwę - co jest łatwiejsze niż przeniesienie pliku. Dodatkowo zauważam, że po pół godzinie pracy nad projektem prawie całe moje drzewo aplikacji jest i tak rozszerzone. Dodanie 1 poziomu przestrzeni folderów może sprawić, że duża baza kodów będzie zarządzalna i nie wprowadzi zbyt wiele, ../x/xco jest już czytelne.
Ski
Tworzysz nowe foldery, używając kropek zamiast ukośników, aby przezwyciężyć wyraźny brak nodejów.
Simone Gianni
9

Zakładając, że katalog główny projektu jest bieżącym katalogiem roboczym, powinno to działać:

// require built-in path module
path = require('path');

// require file relative to current working directory
config = require( path.resolve('.','config.js') );
protometa
źródło
config = require('./config.js');jest również ważny.
cespon
7
@cespon nie dotyczy tylko wymaganego pliku.
protometa
8

Wypróbowałem wiele z tych rozwiązań. Skończyło się na dodaniu tego na górze mojego głównego pliku (np. Index.js):

process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();

Dodaje to katalog główny projektu do NODE_PATH, gdy skrypt jest ładowany. Pozwala mi to wymagać dowolnego pliku w moim projekcie, odwołując się do jego ścieżki względnej z katalogu głównego projektu, np var User = require('models/user'). To rozwiązanie powinno działać tak długo, jak długo uruchamiasz główny skrypt w katalogu głównym projektu, zanim uruchomisz cokolwiek innego w swoim projekcie.

senornestor
źródło
8

Niektóre odpowiedzi mówią, że najlepszym sposobem jest dodanie kodu do modułu node_module jako pakietu, zgadzam się i jest to prawdopodobnie najlepszy sposób na utratę ../../../wymaganego, ale żadna z nich tak naprawdę nie pozwala.

od wersji 2.0.0możesz zainstalować pakiet z plików lokalnych, co oznacza, że ​​możesz utworzyć folder w swoim katalogu głównym ze wszystkimi żądanymi pakietami,

-modules
 --foo
 --bar 
-app.js
-package.json

więc w package.json możesz dodać modules(lub fooi bar) jako pakiet bez publikowania lub korzystania z zewnętrznego serwera w następujący sposób:

{
  "name": "baz",
  "dependencies": {
    "bar": "file: ./modules/bar",
    "foo": "file: ./modules/foo"
  }
}

Następnie npm installmożesz uzyskać dostęp do kodu za pomocą var foo = require("foo"), podobnie jak w przypadku wszystkich innych pakietów.

więcej informacji można znaleźć tutaj:

https://docs.npmjs.com/files/package.json#local-paths

a tutaj jak stworzyć pakiet:

https://docs.npmjs.com/getting-started/creating-node-modules

Yan Mayatskiy
źródło
1
„Ta funkcja jest przydatna do tworzenia lokalnych programów offline i tworzenia testów, które wymagają instalacji npm tam, gdzie nie chcesz trafić na serwer zewnętrzny, ale nie należy jej używać podczas publikowania pakietów w rejestrze publicznym”.
Ryan Smith
7

Możesz użyć modułu, który stworzyłem, Undot . To nic zaawansowanego, tylko pomocnik, dzięki czemu możesz z łatwością uniknąć tych piekielnych kropek.

Przykład:

var undot = require('undot');
var User = undot('models/user');
var config = undot('config');
var test = undot('test/api/user/auth');
Rolka
źródło
6

Możesz zdefiniować coś takiego w pliku app.js:

requireFromRoot = (function(root) {
    return function(resource) {
        return require(root+"/"+resource);
    }
})(__dirname);

a następnie za każdym razem, gdy chcesz wymagać czegoś od katalogu głównego, bez względu na to, gdzie jesteś, po prostu użyj wymagaFragRoot zamiast wymagania waniliowego. Jak dotąd działa dla mnie całkiem dobrze.

użytkownik1417684
źródło
Dzięki! Myślę, że jest to całkiem sprytne i proste.
Ryan,
Wybacz mi ojcze, bo zgrzeszyłem. I przeniesione to ES6 i uzyskałem następujący: requireFromRoot = ((root) => (resource) => require(`${root}/${resource}`))(__dirname);. Podoba Ci się rozwiązanie, ale czy naprawdę musisz tak powiązać __dirname?
Nuck,
1
Moja pamięć jest trochę mglista, ale wydaje mi się, że __dirname zmienia wartość w zależności od tego, który plik jest używany. Być może teraz, ponieważ funkcja jest zdefiniowana w jednym miejscu, ale używana w wielu miejscach, wartość pozostanie stała nawet bez tego wiązania, ale właśnie to zrobiłem, aby upewnić się, że tak jest w rzeczywistości.
user1417684,
zrobiłem to dawno temu, powoduje bóle w testowaniu środowisk env i tym podobnych. nie warte narzutów. losowy nowy globalny sprawia, że ​​nowi ludzie są niepewni bla bla
The Dembinski
Jak requiredziała ta funkcja?
Darko Maksimovic
5

Oto faktyczny sposób, w jaki działam przez ponad 6 miesięcy. Używam folderu o nazwie node_modules jako mojego folderu głównego w projekcie, w ten sposób zawsze będzie szukał tego folderu z dowolnego miejsca, które nazywam bezwzględnym wymaganiem:

  • node_modules
    • mój projekt
      • index.js Mogę wymagać („myProject / someFolder / hey.js”) zamiast wymagać („./ someFolder / hey.js”)
      • someFolder, który zawiera hey.js

Jest to bardziej przydatne, gdy jesteś zagnieżdżony w folderach, a zmiana lokalizacji pliku jest znacznie mniejsza, jeśli jest ustawiona w sposób bezwzględny. Używam tylko 2 względne wymagania w całej mojej aplikacji .

Totty.js
źródło
4
Używam podobnego podejścia, chyba że dodam lokalny (projektu) node_modulesw /srci pozostawić /node_modulesdo sprzedawców, aby przechowywać rzeczy rozdzielić. Mam więc /src/node_moduleskod lokalny i /node_modulesdostawców.
Marius Balčytis
33
IMHO folder node_modules jest tylko dla modułów node_modules. Umieszczenie całego projektu w tym folderze nie jest dobrą praktyką.
McSas,
2
@McSas, co byś zaproponował jako alternatywę, aby uzyskać taki sam efekt jak powyżej?
spieglio
3
@cspiegl Możesz użyć NODE_PATHzmiennej środowiskowej
Christopher Tarquini
5

Imho najłatwiejszym sposobem na osiągnięcie tego jest utworzenie symbolicznego linku przy uruchamianiu aplikacji w node_modules/app(lub jakkolwiek to nazwiesz), który wskazuje ../app. Następnie możesz po prostu zadzwonić require("app/my/module"). Łącza symboliczne są dostępne na wszystkich głównych platformach.

Jednak nadal powinieneś podzielić swoje rzeczy na mniejsze, łatwe do utrzymania moduły, które są instalowane przez npm. Możesz również zainstalować swoje prywatne moduły za pomocą git-url, więc nie ma powodu, aby mieć jeden, monolityczny katalog aplikacji.

Johannes Ewald
źródło
Obsługa systemu Windows wymaga bardziej dogłębnej wiedzy na temat węzła i systemu operacyjnego. Może to ograniczyć powszechne wykorzystanie projektu typu open source.
Steven Vachon
Zasadniczo nie używałbym tego wzorca dla biblioteki (którą jest większość projektów typu open source). Możliwe jest jednak utworzenie tych dowiązań symbolicznych w haku kompilacji npm, więc użytkownik nie musi mieć dogłębnej wiedzy.
Johannes Ewald
Jasne, ale Node.js w systemie Windows domyślnie nie obsługuje dowiązań symbolicznych.
Steven Vachon
4

W swoim projekcie możesz zmodyfikować dowolny plik .js używany w katalogu głównym i dodać jego ścieżkę do właściwości process.envzmiennej. Na przykład:

// in index.js
process.env.root = __dirname;

Następnie możesz uzyskać dostęp do nieruchomości wszędzie:

// in app.js
express = require(process.env.root);
AtraCaelus
źródło
4

Inna odpowiedź:

Wyobraź sobie tę strukturę folderów:

  • node_modules
    • lodash
  • src
    • subdir
      • foo.js
      • bar.js
    • main.js
  • testy

    • test.js

Następnie w pliku test.js musisz wymagać plików takich jak ten:

const foo = require("../src/subdir/foo");
const bar = require("../src/subdir/bar");
const main = require("../src/main");
const _ = require("lodash");

i w main.js :

const foo = require("./subdir/foo");
const bar = require("./subdir/bar");
const _ = require("lodash");

Teraz możesz w tym celu używać babel i babel-plugin-module-resolver . plik babelrc do skonfigurowania 2 folderów głównych:

{
    "plugins": [
        ["module-resolver", {
            "root": ["./src", "./src/subdir"]
        }]
    ]
}

Teraz możesz wymagać plików w ten sam sposób w testach i src :

const foo = require("foo");
const bar = require("bar");
const main = require("main");
const _ = require("lodash");

a jeśli chcesz użyć składni modułu es6 :

{
    "plugins": [
        ["module-resolver", {
            "root": ["./src", "./src/subdir"]
        }],
        "transform-es2015-modules-commonjs"
    ]
}

następnie importujesz pliki w testach i src w następujący sposób:

import foo from "foo"
import bar from "bar"
import _ from "lodash"
Żołnierze
źródło
3

Czy exampleskatalog nie może zawierać node_modulesdowiązania symbolicznego do katalogu głównego projektu, project -> ../../umożliwiając w ten sposób wykorzystanie przykładów require('project'), chociaż nie usuwa to mapowania, ale pozwala na użycie źródła require('project')zamiastrequire('../../') .

Przetestowałem to i działa z v0.6.18.

Lista projectkatalogu:

$ ls -lR project
project:
drwxr-xr-x 3 user user 4096 2012-06-02 03:51 examples
-rw-r--r-- 1 user user   49 2012-06-02 03:51 index.js

project/examples:
drwxr-xr-x 2 user user 4096 2012-06-02 03:50 node_modules
-rw-r--r-- 1 user user   20 2012-06-02 03:51 test.js

project/examples/node_modules:
lrwxrwxrwx 1 user user 6 2012-06-02 03:50 project -> ../../

Zawartość index.jsatrybutu przypisuje wartość do właściwości exportsobiektu i wywołuje console.logz komunikatem, że jest ona wymagana. Zawartość test.jsjest require('project').

Dan D.
źródło
czy możesz podać kod źródłowy swojego testu? dobrze, a działałoby, gdybym musiał w ten sposób wymagać („projektu.a”)?
Totty.js,
Co masz na myśli require('project.a')? Myślę, że to może znaczyć require('project/a'), chociaż require('project').ajest również możliwe?
Dan D.
ale na twoim przykładzie musiałbym utworzyć te foldery w każdym folderze, w którym znajduje się moduł, który wymaga wymaganej metody. W każdym razie musisz zadbać o czasy „../” w zależności od folderu.
Totty.js
W rzeczywistości link musiałby znajdować się tylko w node_moduleskatalogu w najbliższym rodzicu obu plików, a link byłby taki sam dla obu. Zobacz nodejs.org/api/…
Dan D.
I byłby krewny z tej lokalizacji. Na przykład: project/node_modules/project -> ../.
Dan D.
2

Jeśli ktoś szuka innego sposobu na obejście tego problemu, oto mój wkład w wysiłek:

https://www.npmjs.com/package/use-import

Podstawowy pomysł: tworzysz plik JSON w katalogu głównym projektu, który mapuje ścieżki plików na nazwy skrócone (lub użyj use-automapper, aby to zrobić za Ciebie). Następnie możesz poprosić o swoje pliki / moduły, używając tych nazw. Tak jak:

var use = require('use-import');
var MyClass = use('MyClass');

Więc to jest to.

Jon Stout
źródło
2

W tym celu lubię wykorzystywać sposób, w jaki węzeł ładuje się z katalogu node_module.

Jeśli ktoś spróbuje załadować moduł „rzecz”, zrobiłby coś takiego

require('thing');

Węzeł następnie wyszuka katalog „thing” w katalogu „node_module”.

Ponieważ moduł node_modele zwykle znajduje się u podstaw projektu, możemy wykorzystać tę spójność. (Jeśli moduł_węzła nie jest u nasady, masz inne samozapalne bóle głowy, z którymi musisz sobie poradzić.)

Jeśli wejdziemy do katalogu, a następnie wycofamy się z niego, możemy uzyskać spójną ścieżkę do katalogu głównego projektu węzła.

require('thing/../../');

Następnie, jeśli chcemy uzyskać dostęp do katalogu / happy, zrobilibyśmy to.

require('thing/../../happy');

Chociaż jest to dość zuchwałe, czuję jednak, że jeśli zmieni się funkcjonalność sposobu wczytywania modułów node_moduł, będą większe problemy do rozwiązania. To zachowanie powinno pozostać spójne.

Aby to wyjaśnić, robię to, ponieważ nazwa modułu nie ma znaczenia.

require('root/../../happy');

Ostatnio użyłem go do angular2. Chcę załadować usługę z katalogu głównego.

import {MyService} from 'root/../../app/services/http/my.service';
justonpoints
źródło
Informacje o referencji Angular w standardowej aplikacji CLI można po prostu importować src/app/my.service, można również skonfigurować VSC do korzystania z importu nie względnego dla plików maszynopisu.
Ploppy,
2

Napisałem ten mały pakiet, który pozwala wymagać pakietów według ścieżki względnej z katalogu głównego projektu, bez wprowadzania żadnych zmiennych globalnych lub nadpisywania domyślnych wartości węzłów

https://github.com/Gaafar/pkg-require

Działa to w ten sposób

// create an instance that will find the nearest parent dir containing package.json from your __dirname
const pkgRequire = require('pkg-require')(__dirname);

// require a file relative to the your package.json directory 
const foo = pkgRequire('foo/foo')

// get the absolute path for a file
const absolutePathToFoo = pkgRequire.resolve('foo/foo')

// get the absolute path to your root directory
const packageRootPath = pkgRequire.root()
gafi
źródło
Czasami mam prywatne pakiety w głównym projekcie, ten skrypt się z tym zepsuje. Poza tym nie jestem pewien, czy będzie działał dobrze z webpackiem (na wypadek, gdybyś używał webpacka z node.js tak jak ja)
Totty.js
Jeśli zagnieżdżono katalogi z plikami pakietów, każdy katalog będzie mógł wymagać tylko plików w pakiecie. Czy nie chcesz takiego zachowania? Nie testowałem z webpackiem.
gafi
Działa to doskonale w przypadku prostego projektu i jest znacznie łatwiejsze niż w przypadku innych odpowiedzi.
byxor
2

Po prostu chcą śledzić na wielkim odpowiedź od Paolo Moretti i Browserify. Jeśli używasz transpilatora (np. Babel, maszynopis) i masz osobne foldery na kod źródłowy i transpilowany, takie jak src/idist/ , możesz użyć różnych rozwiązań, takich jak

node_modules

Dzięki następującej strukturze katalogów:

app
  node_modules
    ... // normal npm dependencies for app
  src
    node_modules
      app
        ... // source code
  dist
    node_modules
      app
        ... // transpiled code

możesz następnie pozwolić babel itp. na przeniesienie srckatalogu dodist katalogu.

dowiązanie symboliczne

Za pomocą dowiązania symbolicznego możemy pozbyć się niektórych poziomów zagnieżdżania:

app
  node_modules
    ... // normal npm dependencies for app
  src
    node_modules
      app // symlinks to '..'
    ... // source code
  dist
    node_modules
      app // symlinks to '..'
    ... // transpiled code

Zastrzeżenie z babel --copy-Files--copy-files Flaga babelnie zajmuje się dowiązania dobrze. Może nawigować do ..dowiązania symbolicznego i odruchowo widzieć nieskończone pliki. Obejściem tego problemu jest użycie następującej struktury katalogów:

app
  node_modules
    app // symlink to '../src'
    ... // normal npm dependencies for app
  src
    ... // source code
  dist
    node_modules
      app // symlinks to '..'
    ... // transpiled code

W ten sposób kod pod srcnadal będzie approzstrzygany src, podczas gdy babel nie będzie już widział dowiązań symbolicznych.

użytkownik716468
źródło
Dzięki, ale nie poleciłbym robienia tej magii. Najpierw stracisz cały import, nie będą one obliczane przez twoje IDE. Jeśli użyjesz innych narzędzi, takich jak typ przepływu, to również nie będzie działać poprawnie.
Totty.js
Właściwie wydaje się, że przepływ działa w moim przypadku, co nie jest zaskakujące, ponieważ rozwiązania zależą od standardowego modelu rozdzielczości modułu węzła i dowiązań symbolicznych. Tak więc zrozumienie narzędzi takich jak przepływ nie jest magiczne. Ale IDE są różne.
user716468
2

Szukałem dokładnie takiej samej prostoty wymagającej plików z dowolnego poziomu i znalazłem alias modułu .

Wystarczy zainstalować:

npm i --save module-alias

Otwórz plik package.json, tutaj możesz dodać aliasy do swoich ścieżek, np

"_moduleAliases": {
 "@root"      : ".", // Application's root
 "@deep"      : "src/some/very/deep/directory/or/file",
 "@my_module" : "lib/some-file.js",
 "something"  : "src/foo", // Or without @. Actually, it could be any string
}

I użyj swoich aliasów, po prostu:

require('module-alias/register')
const deep = require('@deep')
const module = require('something')
Talha Imam
źródło
1

Za chwilę wypróbujemy nowy sposób rozwiązania tego problemu.

Biorąc przykłady z innych znanych projektów, takich jak spring i guice, zdefiniujemy obiekt „kontekstowy”, który będzie zawierał całą instrukcję „wymaganą”.

Ten obiekt zostanie następnie przekazany do wszystkich innych modułów do użycia.

Na przykład

var context = {}

context.module1 = require("./module1")( { "context" : context } )
context.module2 = require("./module2")( { "context" : context } )

Wymaga to od nas napisania każdego modułu jako funkcji, która odbiera opcje, co i tak jest dla nas najlepszą praktyką.

module.exports = function(context){ ... }

a wtedy odniesiesz się do kontekstu zamiast wymagać czegoś.

var module1Ref = context.moduel1;

Jeśli chcesz, możesz łatwo napisać pętlę, aby wykonać wymagane instrukcje

var context = {};
var beans = {"module1" : "./module1","module2" : "./module2" }; 
for ( var i in beans ){
    if ( beans.hasOwnProperty(i)){
         context[i] = require(beans[i])(context);
    }
};

Powinno to ułatwić życie, gdy chcesz wyśmiewać (testy), a także rozwiązać swój problem po drodze, jednocześnie czyniąc kod wielokrotnego użytku jako pakiet.

Możesz także ponownie użyć kodu inicjalizacji kontekstu, oddzielając od niego deklarację bean. na przykład twój main.jsplik może tak wyglądać

var beans = { ... }; // like before
var context = require("context")(beans); // this example assumes context is a node_module since it is reused.. 

Ta metoda ma również zastosowanie do bibliotek zewnętrznych, nie trzeba sztywno kodować ich nazw za każdym razem, gdy ich potrzebujemy - będzie to jednak wymagać specjalnego traktowania, ponieważ ich eksport nie jest funkcjami, które oczekują kontekstu.

Później możemy również zdefiniować komponenty bean jako funkcje - co pozwoli nam na requireróżne moduły w zależności od środowiska - ale poza zakresem tego wątku.

facet mograbi
źródło
1

Miałem problem z tym samym problemem, więc napisałem pakiet o nazwie include .

Uwzględnij uchwyty określające folder główny projektu poprzez zlokalizowanie pliku package.json, a następnie przekazuje podany argument ścieżki do rodzimej metody wymagania () bez całego bałaganu ścieżki względnej. Wyobrażam sobie, że nie jest to zamiennik funkcji requ (), ale narzędzie do wymagania obsługi plików lub bibliotek spakowanych / innych firm. Coś jak

var async = require('async'),
    foo   = include('lib/path/to/foo')

Mam nadzieję, że to może być przydatne.

Anthony Nichols
źródło
1

Jeśli plik js punktu wejścia aplikacji (tj. Ten, na którym faktycznie uruchamiasz „węzeł”) znajduje się w katalogu głównym projektu, możesz to zrobić naprawdę łatwo za pomocą modułu npm rootpath . Po prostu zainstaluj go przez

npm install --save rootpath

... następnie na samej górze pliku js punktu wejścia dodaj:

require('rootpath')();

Od tego momentu wszystkie wymagane wywołania są teraz względne względem katalogu głównego projektu - np. require('../../../config/debugging/log'); Staje się require('config/debugging/log');(gdzie folder konfiguracji znajduje się w katalogu głównym projektu).

Andrew Faulkner
źródło
1

W prostych wierszach możesz wywołać własny folder jako moduł:

Do tego potrzebujemy: moduł globalny i moduł ścieżki aplikacji

tutaj „App-module-path” jest modułem, umożliwia dodanie dodatkowych katalogów do ścieżki wyszukiwania modułu Node.js. A „global” oznacza, że ​​wszystko, co dołączysz do tego obiektu, będzie dostępne wszędzie w Twojej aplikacji.

Teraz spójrz na ten fragment:

global.appBasePath = __dirname;

require('app-module-path').addPath(appBasePath);

__nazwa_katalogu jest bieżącym katalogiem uruchomionym węzła. Możesz tutaj podać własną ścieżkę, aby wyszukać ścieżkę modułu.

trojański
źródło