Chargez les bibliothèques Javascript "Vanilla" dans Node.js

Il existe des bibliothèques Javascript tierces qui ont des fonctionnalités que je voudrais utiliser dans un nœud.serveur js. (Plus précisément, je veux utiliser une bibliothèque JavaScript QuadTree que j'ai trouvée.) Mais ces bibliothèques ne sont que des fichiers .js simples et non "Node".les librairies js".

En tant que telles, ces bibliothèques ne suivent pas la syntaxe exports.var_name de ce nœud.js attend pour ses modules. Autant je comprends que cela signifie quand vous ne module = require('module_name'); ou module = require('./path/to/file.js');, vous vous retrouverez avec un module sans publiquement fonctions accessibles, etc.

Ma question est alors "comment charger un fichier JavaScript arbitraire dans Node.js telle que je peux utiliser sa fonctionnalité sans avoir à la réécrire pour qu'elle fasse exports?"

Je suis très nouveau à Node.js alors faites-moi savoir s'il y a un trou flagrant dans ma compréhension de la façon dont cela fonctionne.


EDIT : recherche dans les choses plus et je vois maintenant que le modèle de chargement du module ce nœud.js utilise fait partie d'un récemment développé une norme pour le chargement des bibliothèques Javascript appelées CommonJS . Il le dit directement sur la page doc du module pour le nœud.js , mais je l'ai manqué jusqu'à maintenant.

Il se peut que la réponse à ma question soit " attendez que les auteurs de votre bibliothèque écrivent une interface CommonJS ou faites-le vous-même."

101
demandé sur Chris W. 2011-03-02 20:35:24

7 réponses

Il y a une bien meilleure méthode que d'utiliser eval: le vm module.

Par exemple, voici mon execfile module, qui évalue le script à path dans context, ou le contexte mondial:

var vm = require("vm");
var fs = require("fs");
module.exports = function(path, context) {
  context = context || {};
  var data = fs.readFileSync(path);
  vm.runInNewContext(data, context, path);
  return context;
}

Et il peut être utilisé comme ceci:

> var execfile = require("execfile");
> // `someGlobal` will be a global variable while the script runs
> var context = execfile("example.js", { someGlobal: 42 });
> // And `getSomeGlobal` defined in the script is available on `context`:
> context.getSomeGlobal()
42
> context.someGlobal = 16
> context.getSomeGlobal()
16

example.js contient:

function getSomeGlobal() {
    return someGlobal;
}

Le grand avantage de cette méthode est que vous avez un contrôle complet sur les variables globales dans le script exécuté: vous pouvez passer des globaux personnalisés (via context), et tous les les globals créés par le script seront ajoutés à context. Le débogage est également plus facile car les erreurs de syntaxe et autres seront signalées avec le nom de fichier correct.

73
répondu David Wolever 2015-10-30 22:26:35

Voici ce que je pense être la réponse la plus juste pour cette situation.

Disons que vous avez un fichier de script appelé quadtree.js.

Vous devriez construire un node_module personnalisé qui a ce genre de structure de répertoire...

./node_modules/quadtree/quadtree-lib/
./node_modules/quadtree/quadtree-lib/quadtree.js
./node_modules/quadtree/quadtree-lib/README
./node_modules/quadtree/quadtree-lib/some-other-crap.js
./node_modules/quadtree/index.js

Tout dans votre répertoire ./node_modules/quadtree/quadtree-lib/ sont des fichiers de votre bibliothèque tierce.

Ensuite, votre fichier ./node_modules/quadtree/index.js chargera simplement cette bibliothèque à partir du système de fichiers et effectuera le travail d'exportation correctement.

var fs = require('fs');

// Read and eval library
filedata = fs.readFileSync('./node_modules/quadtree/quadtree-lib/quadtree.js','utf8');
eval(filedata);

/* The quadtree.js file defines a class 'QuadTree' which is all we want to export */

exports.QuadTree = QuadTree

Maintenant, vous pouvez utiliser votre module quadtree comme tout autre le nœud du module...

var qt = require('quadtree');
qt.QuadTree();

J'aime cette méthode car il n'est pas nécessaire de changer le code source de votre bibliothèque tierce-donc c'est plus facile à maintenir. Tout ce que vous devez faire lors de la mise à niveau est de regarder leur code source et de vous assurer que vous exportez toujours les objets appropriés.

77
répondu Chris W. 2011-03-07 21:53:22

Le moyen le plus simple est: eval(require('fs').readFileSync('./path/to/file.js', 'utf8')); Cela fonctionne très bien pour les tests dans le shell interactif.

29
répondu Christopher Weiss 2012-03-22 13:31:11

AFAIK, c'est bien ainsi que les modules doivent être chargés. Cependant, au lieu de pointer toutes les fonctions exportées sur l'objet exports, Vous pouvez également les pointer sur this (ce qui serait autrement l'objet global).

Donc, si vous voulez garder les autres bibliothèques compatibles, vous pouvez le faire:

this.quadTree = function () {
  // the function's code
};

Ou, lorsque la bibliothèque externe a déjà son propre espace de noms, par exemple jQuery (pas que vous pouvez utiliser that dans un environnement Côté Serveur):

this.jQuery = jQuery;

Dans un environnement non-noeud, {[3] } se résoudrait à l'objet global, ce qui en ferait une variable globale... qui c'était déjà le cas. Donc il ne devrait pas casser quoi que ce soit.

Modifier : James Herdman a un belle écriture sur le nœud.js pour les débutants, qui mentionne également cela.

5
répondu Martijn 2011-03-02 18:15:38

Je ne suis pas sûr si je vais réellement finir par l'utiliser parce que c'est une solution plutôt hacky, mais une façon de contourner cela est de construire un petit importateur de mini-module comme celui-ci...

Dans le fichier ./node_modules/vanilla.js:

var fs = require('fs');

exports.require = function(path,names_to_export) {
    filedata = fs.readFileSync(path,'utf8');
    eval(filedata);
    exported_obj = {};
    for (i in names_to_export) {
        to_eval = 'exported_obj[names_to_export[i]] = ' 
            + names_to_export[i] + ';'
        eval(to_eval); 
    }
    return exported_obj;
}

Ensuite, lorsque vous souhaitez utiliser les fonctionnalités de votre bibliothèque, vous devez choisir manuellement les noms à exporter.

Donc, pour une bibliothèque comme le fichier ./lib/mylibrary.js...

function Foo() { //Do something... }
biz = "Blah blah";
var bar = {'baz':'filler'};

Lorsque vous souhaitez utiliser ses fonctionnalités dans votre nœud.code js...

var vanilla = require('vanilla');
var mylibrary = vanilla.require('./lib/mylibrary.js',['biz','Foo'])
mylibrary.Foo // <-- this is Foo()
mylibrary.biz // <-- this is "Blah blah"
mylibrary.bar // <-- this is undefined (because we didn't export it)

Je ne sais pas comment Eh bien, tout cela fonctionnerait dans la pratique cependant.

3
répondu Chris W. 2011-03-02 18:59:10

J'ai pu simplement ajouter module.exports = au script qui était une fonction dans leur fichier.

Par exemple, où leur code , dans leur fichier que j'ai placé à './ libs / annonce.js', commence par function apprise(string, args, callback){ Je l'ai changé en:

module.exports = function(string, args, callback){

Puis mon code lit:

window.apprise = require('./libs/apprise.js');

Et j'étais prêt à partir. YMMV, c'était avec webpack .

1
répondu John Mee 2017-05-25 06:23:31

UNE FONCTION simple include(filename) avec une meilleure messagerie d'erreur (pile,nom de fichier, etc.) pour eval, en cas d'erreur:

var fs = require('fs');
// circumvent nodejs/v8 "bug":
// https://github.com/PythonJS/PythonJS/issues/111
// http://perfectionkills.com/global-eval-what-are-the-options/
// e.g. a "function test() {}" will be undefined, but "test = function() {}" will exist
var globalEval = (function() {
    var isIndirectEvalGlobal = (function(original, Object) {
        try {
            // Does `Object` resolve to a local variable, or to a global, built-in `Object`,
            // reference to which we passed as a first argument?
            return (1, eval)('Object') === original;
        } catch (err) {
            // if indirect eval errors out (as allowed per ES3), then just bail out with `false`
            return false;
        }
    })(Object, 123);
    if (isIndirectEvalGlobal) {
        // if indirect eval executes code globally, use it
        return function(expression) {
            return (1, eval)(expression);
        };
    } else if (typeof window.execScript !== 'undefined') {
        // if `window.execScript exists`, use it
        return function(expression) {
            return window.execScript(expression);
        };
    }
    // otherwise, globalEval is `undefined` since nothing is returned
})();

function include(filename) {
    file_contents = fs.readFileSync(filename, "utf8");
    try {
        //console.log(file_contents);
        globalEval(file_contents);
    } catch (e) {
        e.fileName = filename;
        keys = ["columnNumber", "fileName", "lineNumber", "message", "name", "stack"]
        for (key in keys) {
            k = keys[key];
            console.log(k, " = ", e[k])
        }
        fo = e;
        //throw new Error("include failed");
    }
}

Mais il devient même plus sale avec nodejs: vous devez spécifier ceci:

export NODE_MODULE_CONTEXTS=1
nodejs tmp.js

Sinon, vous ne pouvez pas utiliser de variables globales dans les fichiers inclus avec include(...).

0
répondu lama12345 2015-06-30 22:15:56