178 votes

Comment vérifier si un script s'exécute sous Node.js ?

J'ai un script que je demande à un script de Node.js, que je veux garder indépendant du moteur JavaScript.

Par exemple, je veux faire exports.x = y; seulement s'il est exécuté sous Node.js. Comment puis-je effectuer ce test ?


En postant cette question, je ne savais pas que la fonctionnalité des modules Node.js était basée sur CommonJS .

Pour l'exemple spécifique que j'ai donné, une question plus précise aurait été :

Comment un script peut-il dire s'il a été requis comme module CommonJS ?

3 votes

Je n'en ai aucune idée. por qué vous essayez de le faire, mais en règle générale, vous devriez utiliser la détection des caractéristiques plutôt que celle des moteurs. quirksmode.org/js/support.html

4 votes

Il s'agit en fait d'une demande sur la manière d'implémenter la détection des caractéristiques, mais la question se décrit mal.

0 votes

J'ai publié une bibliothèque pour mon propre usage, cela m'aidera. npmjs.com/package/detect-is-node

120voto

Ivo Wetzel Points 27802

Il n'y a pas de moyen fiable de détecter l'exécution dans Node.js puisque chaque site web peut facilement déclarer les mêmes variables. window dans Node.js par défaut, vous pouvez aller dans l'autre sens et vérifier si vous êtes à l'intérieur d'un Browser.

C'est ce que j'utilise pour les librairies qui doivent fonctionner à la fois dans un navigateur et sous Node.js :

if (typeof window === 'undefined') {
    exports.foo = {};

} else {
    window.foo = {};
}

Il pourrait encore exploser au cas où window est défini dans Node.js mais il n'y a pas de bon raison pour que quelqu'un fasse cela, puisque vous auriez explicitement besoin de laisser de côté var ou définir la propriété sur le global objet.

EDITAR

Pour détecter si votre script a été requis comme module CommonJS, là encore ce n'est pas facile. La seule chose que commonJS spécifie est que A : Les modules seront inclus via un appel à la fonction require et B : les modules exportent des choses via les propriétés de l'élément exports objet. Maintenant, la façon dont cela est mis en œuvre est laissée au système sous-jacent. Node.js enveloppe le contenu du module dans une fonction anonyme :

function (exports, require, module, __filename, __dirname) { 

Ver: https://github.com/ry/node/blob/master/src/node.js#L325

Mais Ne le fais pas. essayer de détecter cela via une sorte de arguments.callee.toString() utilisez plutôt mon exemple de code ci-dessus qui vérifie la présence du navigateur. Node.js est un environnement bien plus propre, il est donc peu probable que window y seront déclarés.

2 votes

A propos de "Node.js est un environnement beaucoup plus propre, il est donc peu probable que window y soit déclaré" : eh bien, je suis juste venu ici pour chercher un moyen de savoir si mon script s'exécutait dans un navigateur émulé par node.js + JSDOM ou dans un simple navigateur... La raison est que j'ai une boucle infinie qui utilise setTimeout pour vérifier l'emplacement de l'URL, ce qui est bien dans un navigateur, mais qui fait que le script de node.js s'exécute éternellement... Donc il pourrait y avoir une fenêtre dans un script node.js après tout :)

2 votes

@Eric Je doute fortement qu'il soit présent dans la portée globale, donc à moins que vous n'importiez quelque chose en tant que window dans la première ligne de votre module, vous ne devriez pas avoir de problèmes. Vous pouvez également exécuter une fonction anonyme et vérifier que le résultat de la fonction [[Class]] de this à l'intérieur (fonctionne uniquement en mode non strict) Voir "Classe" sous : bonsaiden.github.com/JavaScript-Garden/#typeof

1 votes

Mon problème diffère légèrement de celui de l'OP : Je n'ai pas besoin du script, il est chargé par JSDOM avec la fenêtre émulée comme contexte global.... Il est toujours exécuté par node.js + V8, juste dans un contexte différent des modules habituels.

82voto

Ross Points 5397

En recherchant le support de CommonJS c'est ainsi que le Underscore.js La bibliothèque le fait :

Edit : pour votre question mise à jour :

(function () {

    // Establish the root object, `window` in the browser, or `global` on the server.
    var root = this; 

    // Create a reference to this
    var _ = new Object();

    var isNode = false;

    // Export the Underscore object for **CommonJS**, with backwards-compatibility
    // for the old `require()` API. If we're not in CommonJS, add `_` to the
    // global object.
    if (typeof module !== 'undefined' && module.exports) {
            module.exports = _;
            root._ = _;
            isNode = true;
    } else {
            root._ = _;
    }
})();

L'exemple ici conserve le modèle du module.

48 votes

Cela détecte le support de CommonJS, que les navigateurs peuvent supporter.

8 votes

Il y a un problème ici et nailer l'a "cloué". J'essaie CommonJS dans le navigateur, et le chargeur de module que j'utilise définit module.exports, donc cette solution me dirait incorrectement que je suis dans node.

1 votes

@MarkMelville sans doute, c'est exactement ce que le PO demande donc pas un problème .

25voto

TimE Points 633

Le problème lorsqu'on essaie de déterminer dans quel environnement le code s'exécute, c'est que n'importe quel objet peut être modifié et déclaré, ce qui rend presque impossible de déterminer quels objets sont natifs de l'environnement et lesquels ont été modifiés par le programme.

Cependant, il existe quelques astuces permettant de déterminer avec certitude l'environnement dans lequel vous vous trouvez.

Commençons par la solution généralement acceptée qui est utilisée dans la bibliothèque underscore :

typeof module !== 'undefined' && module.exports

Cette technique est en fait parfaitement adaptée au côté serveur, car lorsque la fonction require est appelée, elle réinitialise le this en un objet vide, et redéfinit l'objet module pour vous, ce qui signifie que vous n'avez pas à vous soucier d'une quelconque altération extérieure. Tant que votre code est chargé avec require vous êtes en sécurité.

Cependant, cela s'écroule sur le navigateur, car n'importe qui peut facilement définir module pour faire croire que c'est l'objet que vous recherchez. D'un côté, c'est peut-être le comportement que vous souhaitez, mais il dicte également quelles variables l'utilisateur de la bibliothèque peut utiliser dans la portée globale. Peut-être que quelqu'un veut utiliser une variable portant le nom de module qui a exports à l'intérieur de celui-ci pour une autre utilisation. C'est peu probable, mais qui sommes-nous pour juger des variables que quelqu'un d'autre peut utiliser, juste parce qu'un autre environnement utilise ce nom de variable ?

L'astuce cependant, est que si nous supposons que votre script est chargé dans la portée globale (ce qui sera le cas s'il est chargé via une balise script), une variable ne peut pas être réservée dans une fermeture externe, car le navigateur ne le permet pas. Maintenant, rappelez-vous que dans node, la balise this est un objet vide, pourtant, l'objet module est toujours disponible. C'est parce qu'elle est déclarée dans une fermeture externe. Nous pouvons donc corriger la vérification d'underscore en ajoutant une vérification supplémentaire :

this.module !== module

Avec cela, si quelqu'un déclare module dans l'étendue globale du navigateur, il sera placé dans l'étendue globale de l'application. this ce qui entraînera l'échec du test car this.module sera le même objet que le module. Sur le nœud, this.module n'existe pas, et module existe à l'intérieur d'une fermeture externe, donc le test réussira, car ils ne sont pas équivalents.

Ainsi, le test final est :

typeof module !== 'undefined' && this.module !== module

Remarque : Bien que cela permette maintenant à l module pour être utilisée librement dans la portée globale, il est toujours possible de contourner cela dans le navigateur en créant une nouvelle fermeture et en déclarant module à l'intérieur de celle-ci, puis en chargeant le script à l'intérieur de cette fermeture. À ce stade, l'utilisateur reproduit entièrement l'environnement du nœud et, avec un peu de chance, sait ce qu'il fait et essaie de faire un require de style nœud. Si le code est appelé dans une balise script, il sera toujours à l'abri de toute nouvelle fermeture externe.

3 votes

Wow, merci d'avoir clairement expliqué le raisonnement derrière chaque élément de votre one-liner.

0 votes

Obtenu Cannot read property 'module' of undefined parce que c'est indéfini dans les tests mocha par exemple

23voto

user3751385 Points 26

Ce qui suit fonctionne dans le navigateur, à moins d'être intentionnellement et explicitement saboté :

if(typeof process === 'object' && process + '' === '[object process]'){
    // is node
}
else{
    // not node
}

Bam.

4 votes

Var process = { toString : function () { return '[object process]' ; } } ;

1 votes

Y a-t-il une raison pour laquelle vous utilisez process+'' au lieu de process.toString() ?

3 votes

Presque. Utilisez ceci à la place : Object.prototype.toString.call(process)

8voto

Fabian Jakobs Points 7047

La plupart des solutions proposées peuvent en fait être truquées. Une méthode robuste consiste à vérifier les Class de l'objet global en utilisant la méthode Object.prototype.toString . La classe interne ne peut pas être simulée en JavaScript :

var isNode = 
    typeof global !== "undefined" && 
    {}.toString.call(global) == '[object global]';

2 votes

Cela redeviendra vrai sous le navigateur.

1 votes

Avez-vous testé cela ? Je ne vois pas comment browserify peut changer la classe interne d'un objet. Cela nécessiterait de changer du code dans la VM JavaScript ou d'écraser Object.prototype.toString ce qui est une très mauvaise pratique.

0 votes

Je l'ai testé. Voici ce que fait browserify : var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X