Mam skrypt, którego potrzebuję ze skryptu Node.js, który chcę, aby silnik JavaScript był niezależny.
Na przykład chcę to zrobić exports.x = y;
tylko wtedy, gdy działa w Node.js. Jak mogę przeprowadzić ten test?
Pisząc to pytanie, nie wiedziałem, że funkcja modułów Node.js jest oparta na CommonJS .
W przypadku konkretnego przykładu, który podałem, dokładniejsze pytanie brzmiałoby:
W jaki sposób skrypt może stwierdzić, czy był wymagany jako moduł CommonJS?
javascript
node.js
commonjs
theosp
źródło
źródło
Odpowiedzi:
Szukając wsparcia CommonJS , robi to biblioteka Underscore.js :
Edytuj: do zaktualizowanego pytania:
Przykład tutaj zachowuje wzorzec Module.
źródło
Cóż, nie ma niezawodnego sposobu na wykrycie działania w Node.js, ponieważ każda witryna może łatwo zadeklarować te same zmienne, ale ponieważ
window
domyślnie nie ma obiektu w Node.js, możesz przejść na odwrót i sprawdzić, czy działasz w Przeglądarka.Oto, czego używam w przypadku bibliotek, które powinny działać zarówno w przeglądarce, jak i pod Node.js:
Może nadal wybuchnąć w przypadku, gdy
window
jest to zdefiniowane w Node.js, ale nie ma dobrego powodu, aby ktoś to zrobił, ponieważ jawnie musiałbyś opuścićvar
lub ustawić właściwośćglobal
obiektu.EDYTOWAĆ
Aby wykryć, czy Twój skrypt był wymagany jako moduł CommonJS, znowu nie jest to łatwe. Jedyną rzeczą, którą commonJS określa, jest to, że A: moduły zostaną dołączone poprzez wywołanie funkcji,
require
a B: moduły eksportują rzeczy poprzez właściwościexports
obiektu. Teraz, jak to jest zaimplementowane, pozostawiono systemowi bazowemu. Node.js opakowuje zawartość modułu w anonimową funkcję:Zobacz: https://github.com/ry/node/blob/master/src/node.js#L325
Ale nie próbuj tego wykrywać za pomocą jakichś szalonych
arguments.callee.toString()
rzeczy, zamiast tego użyj mojego przykładowego kodu powyżej, który sprawdza przeglądarkę. Node.js jest o wiele czystszym środowiskiem, więc jest mało prawdopodobne, żewindow
zostanie tam zadeklarowany.źródło
window
w pierwszej linii swojego modułu, nie powinieneś mieć żadnych problemów. Możesz również uruchomić anonimową funkcję i sprawdzić jej[[Class]]
zawartośćthis
(działa tylko w trybie nieścisłościtypeof self === 'object'
może być bezpieczniejsze, ponieważtypeof window === 'undefined'
nie działa w zakresie pracowników sieci Web.Obecnie natknąłem się na niewłaściwe wykrywanie węzła, który nie jest świadomy środowiska węzła w Electron z powodu wprowadzającego w błąd wykrywania funkcji. Poniższe rozwiązania jednoznacznie identyfikują środowisko procesowe.
Zidentyfikuj tylko Node.js.
Pozwoli to wykryć, czy pracujesz w procesie Node, ponieważ
process.release
zawiera „metadane związane z bieżącą wersją [Node-]”.Po pojawieniu się io.js wartość
process.release.name
może się również zmienićio.js
(zobacz dokumentację procesu ). Aby poprawnie wykryć środowisko gotowe do węzła, należy sprawdzić, co następuje:Zidentyfikuj węzeł (> = 3.0.0) lub io.js
To stwierdzenie zostało przetestowane z Node 5.5.0, Electron 0.36.9 (z Node 5.1.1) i Chrome 48.0.2564.116.
Zidentyfikuj węzeł (> = 0.10.0) lub io.js
Komentarz @ daluege zainspirował mnie do rozważenia bardziej ogólnego dowodu. To powinno działać z Node.js> = 0,10 . Nie znalazłem unikalnego identyfikatora dla poprzednich wersji.
Ps: Umieszczam tę odpowiedź tutaj, ponieważ pytanie mnie tutaj prowadzi, chociaż OP szukał odpowiedzi na inne pytanie.
źródło
process
iprocess.version
istnieje w pakiecie, więc dodałem dodatkowe sprawdzenie,process.version
gdzieprocess.release.node
jest niezdefiniowane po stronie klienta, ale ma wersję węzła jako wartość po stronie serweraprocess.version
zmiennej (w React, WebPack lub React-Webpack). Byłbym wdzięczny za każdą wskazówkę, w której zdefiniowano zmienną wersji, aby dodać ją do odpowiedzi. W zależności od ograniczeń release.node do węzła> = 3.xxfunction isNodejs() { return typeof "process" !== "undefined" && process && process.versions && process.versions.node; }
Problem z próbą ustalenia, w jakim środowisku działa twój kod, polega na tym, że każdy obiekt można zmodyfikować i zadeklarować, co sprawia, że ustalenie, które obiekty są natywne dla środowiska, a które zostały zmodyfikowane przez program, jest prawie niemożliwe.
Jest jednak kilka sztuczek, których możemy użyć, aby z całą pewnością dowiedzieć się, w jakim środowisku się znajdujesz.
Zacznijmy od ogólnie przyjętego rozwiązania używanego w bibliotece podkreślników:
typeof module !== 'undefined' && module.exports
Ta technika jest w rzeczywistości idealna dla serwera, ponieważ gdy
require
wywoływana jest funkcja, resetujethis
obiekt do pustego obiektu i ponownie definiujemodule
dla Ciebie, co oznacza, że nie musisz się martwić o jakiekolwiek ingerencje z zewnątrz. Dopóki Twój kod jest załadowanyrequire
, jesteś bezpieczny.Jednak to się rozpada w przeglądarce, ponieważ każdy może łatwo zdefiniować,
module
aby wyglądał, jakby to był obiekt, którego szukasz. Z jednej strony może to być zachowanie, które chcesz, ale określa również, jakich zmiennych użytkownik biblioteki może używać w zakresie globalnym. Może ktoś chce użyć zmiennej z nazwąmodule
, która sięexports
w niej znajduje, do innego użytku. Jest to mało prawdopodobne, ale kim jesteśmy, aby oceniać, jakich zmiennych może używać ktoś inny, tylko dlatego, że inne środowisko używa tej nazwy zmiennej?Sztuczka polega jednak na tym, że jeśli założymy, że twój skrypt jest ładowany w zasięgu globalnym (co będzie, jeśli będzie ładowany przez tag skryptu), zmienna nie może być zarezerwowana w zewnętrznym zamknięciu, ponieważ przeglądarka na to nie pozwala . Teraz pamiętaj, że w węźle
this
obiekt jest pustym obiektem, alemodule
zmienna jest nadal dostępna. To dlatego, że jest zadeklarowany w zewnętrznym zamknięciu. Więc możemy następnie naprawić kontrolę podkreślenia, dodając dodatkową kontrolę:this.module !== module
Dzięki temu, jeśli ktoś zadeklaruje
module
w zasięgu globalnym w przeglądarce, zostanie on umieszczony wthis
obiekcie, co spowoduje niepowodzenie testu, ponieważthis.module
będzie to ten sam obiekt co module. W węźlethis.module
nie istnieje imodule
istnieje w zewnętrznym zamknięciu, więc test powiedzie się, ponieważ nie są równoważne.Tak więc ostateczny test to:
typeof module !== 'undefined' && this.module !== module
Uwaga: Chociaż pozwala to teraz na
module
swobodne używanie zmiennej w zakresie globalnym, nadal można to ominąć w przeglądarce, tworząc nowe zamknięcie i deklarującmodule
w nim, a następnie ładując skrypt w tym zamknięciu. W tym momencie użytkownik w pełni replikuje środowisko węzłów i miejmy nadzieję, że wie, co robi i próbuje spełnić wymagania stylu węzła. Jeśli kod jest wywoływany w tagu skryptu, nadal będzie zabezpieczony przed nowymi zewnętrznymi zamknięciami.źródło
Cannot read property 'module' of undefined
ponieważ jest to nieokreślone na przykład w testachPoniższe działa w przeglądarce, chyba że celowo, jawnie sabotowane:
Bam.
źródło
process+''
zamiastprocess.toString()
?Object.prototype.toString.call(process)
var process = null;
spowodowałoby niepowodzenie drugiego przypadku. Zarówno w Javascript, jak i w Javie wyrażenie'' + x
daje to samo,x.toString()
z wyjątkiem sytuacji, gdyx
jest nieprzyjemne, pierwsze generuje"null"
lub"undefined"
gdy drugie zgłosi błąd.Oto całkiem fajny sposób, aby to zrobić:
To działa, ponieważ w przeglądarkach globalna zmienna „this” ma odniesienie do siebie zwane „window”. To odniesienie do siebie nie istnieje w Node.
Aby złamać powyższe sugerowane sprawdzenie przeglądarki, musisz wykonać coś podobnego do poniższego
przed wykonaniem sprawdzenia.
źródło
const isBrowser = this.window !== undefined
? Teoretycznie w węźle mogę zrobić,this.window = this
aby oszukać rozwiązanie.Jeszcze jedno wykrycie środowiska :
(Znaczenie: większość odpowiedzi jest w porządku.)
Trochę paranoicznie, prawda? Możesz uczynić to bardziej szczegółowym, sprawdzając więcej elementów globalnych .
Ale NIE !.
Wszystko to i tak można sfałszować / zasymulować.
Na przykład, aby sfałszować
global
obiekt:Nie zostanie to dołączone do oryginalnego obiektu globalnego węzła, ale zostanie dołączone do
window
obiektu w przeglądarce. Oznacza to, że jesteś w Node env w przeglądarce.Życie jest krótkie!
Czy obchodzi nas, czy nasze środowisko jest fałszywe? Stałoby się tak, gdyby jakiś głupi programista zadeklarował zmienną globalną o nazwie
global
w zasięgu globalnym. Albo jakiś zły programista w jakiś sposób wstrzykuje kod do naszego pliku env.Możemy uniemożliwić wykonanie naszego kodu, gdy to złapiemy, ale może to spowodować wiele innych zależności naszej aplikacji. W końcu kod się zepsuje. Jeśli twój kod jest wystarczająco dobry, nie powinieneś przejmować się każdym głupim błędem, który mógł popełnić ktoś inny.
Więc co?
W przypadku kierowania na 2 środowiska: przeglądarka i węzeł;
"use strict"
; i albo po prostu sprawdźwindow
lubglobal
; i wyraźnie wskaż, że w dokumentach Twój kod obsługuje tylko te środowiska. Otóż to!Jeśli to możliwe dla twojego przypadku użycia; zamiast wykrywania środowiska; wykonuje synchroniczne wykrywanie funkcji w bloku try / catch. (ich wykonanie zajmie kilka milisekund).
na przykład
źródło
Większość proponowanych rozwiązań faktycznie można sfałszować. Solidnym sposobem jest sprawdzenie wewnętrznej
Class
właściwości obiektu globalnego przy użyciu rozszerzeniaObject.prototype.toString
. Klasy wewnętrznej nie można sfałszować w JavaScript:źródło
Object.prototype.toString
co jest naprawdę złą praktyką.var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};
({}.toString.call(window))
jest równy"[object global]"
.window.toString()
produkuje"[object Window]"
Co na temat korzystania z obiektu procesu i sprawdzania execPath za
node
?źródło
window.process = {execPath: "/usr/local/bin/node"};
?Powiązane: aby sprawdzić, czy był wymagany jako moduł, czy też działa bezpośrednio w węźle, możesz sprawdzić
require.main !== module
. http://nodejs.org/docs/latest/api/modules.html#accessing_the_main_moduleźródło
Oto moja odmiana tego, co powyżej:
Aby go użyć, zmodyfikuj "House" w drugim ostatnim wierszu tak, aby była dowolną nazwą modułu w przeglądarce i opublikuj jakąkolwiek chcesz wartość modułu (zwykle konstruktor lub literał obiektu ).
W przeglądarkach globalnym obiektem jest window i ma odniesienie do samego siebie (jest window.window, które jest == window). Wydaje mi się, że jest to mało prawdopodobne, chyba że jesteś w przeglądarce lub w środowisku, które chce, abyś uwierzył, że jesteś w przeglądarce. We wszystkich innych przypadkach, jeśli istnieje zadeklarowana globalna zmienna „moduł”, używa jej, w przeciwnym razie używa obiektu globalnego.
źródło
Używam
process
do sprawdzenia node.js w ten sposóblub
Udokumentowane tutaj
źródło
process.title
można zmienićW chwili pisania tego artykułu ta odpowiedź jest raczej opcją „wkrótce”, ponieważ wykorzystuje bardzo nowe funkcje JavaScript.
runtime
Wartość będzie albonode
albonot node
.Jak wspomniano, zależy to od kilku nowych funkcji JavaScript.
globalThis
to sfinalizowana funkcja w specyfikacji ECMAScript 2020. Opcjonalne łączenie łańcuchowe / zerowe łączenie (?
częśćglobalThis.process?.release?.name
) jest obsługiwane w silniku V8, który jest dostarczany z Chrome 80. Od 08.04.2020 ten kod będzie działał w przeglądarce, ale nie będzie działał w Node, ponieważ gałąź Node 13 używa V8 7.9.xxx. Uważam, że Node 14 (ma zostać wydany 21.04.2020) ma używać V8 8.x +.Takie podejście wiąże się ze sporą dawką obecnych ograniczeń. Jednak; tempo, w jakim publikowane są przeglądarki / węzeł, ostatecznie będzie niezawodnym, jednolinijkowym.
źródło
Node.js ma
process
obiekt, więc jeśli nie masz żadnego innego skryptu, który go tworzy,process
możesz go użyć do określenia, czy kod działa w Node.źródło
Jest to całkiem bezpieczny i prosty sposób na zapewnienie kompatybilności między javascriptem po stronie serwera i klienta, który będzie również działał z zawartymi po stronie klienta browserify, RequireJS lub CommonJS:
źródło
Edycja : W odniesieniu do zaktualizowanego pytania: „W jaki sposób skrypt może stwierdzić, czy był wymagany jako moduł commonjs?” Myślę, że nie może. Możesz sprawdzić, czy
exports
jest to obiekt (if (typeof exports === "object")
), ponieważ specyfikacja wymaga, aby była dostarczana do modułów, ale jedyne, co ci mówi, to to, że ...exports
jest obiektem. :-)Oryginalna odpowiedź:
Jestem pewien, że istnieje jakiś symbol specyficzny dla NodeJS (
nie, musisz go użyć,EventEmitter
być możerequire
aby pobrać moduł zdarzeń; patrz poniżej ), który możesz sprawdzić, ale jak powiedział David, najlepiej byłoby, gdybyś wykrył tę funkcję (raczej niż środowisko), jeśli ma to jakiś sens.Aktualizacja : Może coś takiego:
Ale to tylko mówi ci, że jesteś w środowisku z
require
czymś bardzo, bardzo podobnym do NodeJSBuffer
. :-)źródło
window
Równie łatwo mogę złamać Twój czek, definiując zmienną w aplikacji NodeJS. :-)window
w module NodeJS, więc mógłby dołączyć kod, którywindow
był obiektem globalnym i nie chciał modyfikować tego kodu. Ja bym tego nie zrobił, ty nie, ale założę się, że ktoś to zrobił. :-) Albo po prostu mieliwindow
na myśli coś zupełnie innego.typeof process !== "undefined" && process.title === "node"
źródło
Ze źródła pakietu debugowania:
https://github.com/visionmedia/debug/blob/master/src/index.js#L6
źródło
Weź źródło node.js i zmień je, aby zdefiniować zmienną, taką jak
runningOnNodeJS
. Sprawdź tę zmienną w swoim kodzie.Jeśli nie możesz mieć własnej prywatnej wersji node.js, otwórz żądanie funkcji w projekcie. Poproś, aby zdefiniowali zmienną, która podaje wersję node.js, w której pracujesz. Następnie sprawdź tę zmienną.
źródło
window
globalnego, zgaduję, że złożę wniosek o funkcję w tym.var
instrukcji, ludzie, którzy po prostu przeciekają do globalnej przestrzeni nazw, cóż, nie rozumieją wtedy koncepcji samodzielnych modułówBardzo stary post, ale właśnie go rozwiązałem, opakowując wymagane instrukcje w próbkę
źródło