IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

NodeJS Discussion :

Installer des dépendances nom dynamiquement


Sujet :

NodeJS

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti Avatar de regseb
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2013
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2013
    Messages : 16
    Par défaut Installer des dépendances nom dynamiquement
    Salut,

    Est-ce vous connaissez une méthode pour installer des dépendances npm dynamiquement ?

    Pour être plus concret, je développe un module npm pour vérifier les fichiers d'un projet grâce à des linters (JSHint, CSSLint, ...). Le module pèse 4,4 Mo ; en installant les linters, il passe à 105 Mo. Selon la configuration, tous les linters ne sont pas utiles : certains sont redondants (HTMLHint et htmllint) ou peut-être que le projet ne contient aucun document Markdown.
    Je cherche donc un moyen pour installer seulement les linters dont l'utilisateur a besoin. Ou peut-être qu'il existe une convention pour créer un système de plugins dans npm ?

    PS : le code source du module.

    Merci d'avance.

  2. #2
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2016
    Messages
    225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2016
    Messages : 225
    Par défaut
    salut,

    Quid d'un

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    require('child_process').spawn('npm', ['i', 'module', '--save'], {stdio :'inherits'});
    ?

    https://nodejs.org/dist/latest-v5.x/...d_args_options

  3. #3
    Membre averti Avatar de regseb
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2013
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2013
    Messages : 16
    Par défaut
    Merci pour la commande. Ça télécharge bien la dépendance, mais elle n'est pas directement accessible.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    "use strict";
     
    const lib = process.argv[2];
    try {
        console.log("first");
        const first = require(lib);
        console.log("/first");
    } catch (exc) {
        console.log("catch");
        require("child_process").spawnSync("npm", ["install", lib],
                                           { "stdio": "inherit" });
        console.log("second");
        const second = require(lib);
        console.log("/second");
    }
    Le script précédent, lancé avec la commande rm -rf node_modules && node index.js jshint, affiche l'erreur suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    module.js:341
        throw err;
        ^
     
    Error: Cannot find module 'jshint'
        at Function.Module._resolveFilename (module.js:339:15)
        at Function.Module._load (module.js:290:25)
        at Module.require (module.js:367:17)
        at require (internal/module.js:16:19)
        at Object.<anonymous> (/home/regseb/dev/test/index.js:13:20)
        at Module._compile (module.js:413:34)
        at Object.Module._extensions..js (module.js:422:10)
        at Module.load (module.js:357:32)
        at Function.Module._load (module.js:314:12)
        at Function.Module.runMain (module.js:447:10)

  4. #4
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2016
    Messages
    225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2016
    Messages : 225
    Par défaut
    Si on suppose que le script était invoqué depuis un répertoire différent du sien, oui c'est normal.

    Les modules ont étaient installé dans le répertoire d'invocation du programme (IE : `process.cwd`), mais l'instruction `require` résout __toujours__ ses dépendances par rapport au chemin d'accès du fichier exécutant l'appel à `require`.

    Donc non, fait comme cela, ça ne fonctionne pas.

    A toi de voir comment modifier ton programme pour te conformer à ce comportement __normal__.

    Au passage, une réponse plus longue ici https://github.com/mh-cbon/problems-...grammatic-npmi

    Au passage², pourquoi faire un spawnSync ?


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var m = process.argv[2] || 'jshint'
     
    require('child_process')
    .spawn('npm', ['i', m, '--save'], {stdio :'inherit'})
    .on('error', console.error.bind(console))
    .on('close', function () {
      var jshint = require("./tomate/node_modules/" +m).JSHINT;
      console.log("jshint %s", typeof(jshint));
      console.log('all done it this message has displayed');
    })

  5. #5
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2016
    Messages
    225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2016
    Messages : 225
    Par défaut
    Par ailleurs, et avant que je n'oublie, on ne fait jamais de setup global pour du lint par projet.

    Cela casse les dépendances __par projet__ mises en place via `semver`.

    Mais c'est une autre histoire qui n'est pas directement liée à ta question technique.

  6. #6
    Membre averti Avatar de regseb
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2013
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2013
    Messages : 16
    Par défaut
    Le script posté est un exemple minimaliste pour montrer le problème rencontré (voici la version intégrée dans mon projet). Quand je l’exécute, je me place dans le répertoire que le script.

    J'ai testé mon script avec le version 5.4.1 de Node.js et il fonctionne. Avec la version 5.8.0, il remonte une erreur au deuxième require(). J'ai regardé le code source de la fonction require() et il y a un cache pour les appels à la fonction stat() (qui indique si le fichier existe). Je pense que c'est à cause du cache que le deuxième require() ne trouve pas la dépendance (le cache a été ajouté dans la version 5.5.0).

    J'utilise spawnSync() car je souhaite que ma fonction de chargement / installation de dépendances soit synchrone. La fonction doit être quasi-similaire à require() pour pouvoir utiliser l'une ou l'autre facilement.

    En utilisant la fonction asynchrone spawn() ou même avec un simple setTimeout, le script fonctionne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    "use strict";
     
    const lib = process.argv[2];
    try {
        const first = require(lib);
        console.log(first);
    } catch (exc) {
        require("child_process").spawnSync("npm", ["install", lib],
                                           { "stdio": "inherit" });
        setTimeout(function () {
            const second = require(lib);
            console.log(second);
        }, 0);
    }
    La seule différence avec setTimeout() : le moteur JavaScript reprend la main et exécute directement le deuxième require(). Mais je ne vois pas comment cela peut impacter le cache.

    PS : pour l'autre histoire, je n'ai pas tout compris.

  7. #7
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2016
    Messages
    225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2016
    Messages : 225
    Par défaut
    Comme je te le disais, il vaut mieux utiliser des fonctions async. Sync n'est pas de confiance avec node.
    A savoir pourquoi require est un appel synchrone, je ne sais pas, je ne m'étais jamais intéressé à la question auparavant. Je dirais que c'est une mauvaise idée, à postériori.
    A tenter de savoir pourquoi dans tel version ton programme fonctionne, et dans telle version ton programme ne fonctionne pas, je ne sais pas trop quoi te dire, si ce n'est que tu t'appuies sur une capacité non standard.
    Je dirais que cela ressemble un peu au problème "zalgo" http://blog.izs.me/post/59142742143/...for-asynchrony

    Pour en revenir à ton problème initial qui était de réduire la taille du module en réduisant le nombres de dépendances incluses par défaut, je voudrais te proposer d'ajouter une action à ton binaire pour ajouter une dépendance manuellement. Un genre de dupliqué de npm i.

    Maintenant, pour en revenir à mon histoire de semver.

    Si tu fais un projet A, en décidant d'utiliser un linter B, en version 1.
    Le temps passe, tu fais d’autres trucs, tu essaies un autre linter, tu mets à jour ton linter B, il passe en version 2 ect
    Tu reviens sur ton projet A avec son beau fichier de configuration pour le linter B en version 1, manque de bol, depuis que tu as mis à jour ce linter, il ne comprend plus ton fichier de config.
    Tu te retrouves donc à devoir mettre à jour un composant connexe de ton projet, ce n'était pas prévu, et n'aurait pas dû l'être, n'étant pas le sujet de ton travail du jour.

    Pour résoudre cela on installe le linter en dépendance de développement du projet (devdep), et on invoque la version localement installé, dit autrement, directement et explicitement lié à ton projet par une relation de semver.

    Pour aller pus loin,

    http://semver.org/
    https://www.smashingmagazine.com/201...-npm-packages/

    Celui ci explique mal le probleme, voir le présente comme une bonne solution, ce qu'il n'est pas, mais c'est pleins d'informations bien présentées, et puis c'est en français
    https://blog.hadrien.eu/2015/10/13/global-is-evil/

Discussions similaires

  1. Créer des variables avec un nom dynamique
    Par midoparis dans le forum C#
    Réponses: 7
    Dernier message: 22/01/2009, 14h17
  2. [MySQL] Quel nom choisir pour des type radio dynamique ?
    Par bullrot dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 31/10/2008, 14h06
  3. nom dynamique des cases a cocher
    Par midou84 dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 24/05/2008, 22h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo