492 votes

Déterminer la racine du projet à partir d'une application node.js en cours d'exécution

Y a-t-il un autre moyen, autre que process.cwd() pour obtenir le nom du chemin du répertoire racine du projet en cours. Est-ce que Node implémente quelque chose comme la propriété de Ruby, Rails.root ,. Je cherche quelque chose qui soit constant et fiable.

25voto

didxga Points 2019

Il suffit d'ajouter cette ligne à votre module dans la racine, généralement c'est app.js ou app.ts .

global.__basedir = __dirname;

Alors _basedir sera accessible à tous vos modules.

Note : Pour l'implémentation de typescript, suivez l'étape ci-dessus et vous serez alors en mesure d'utiliser le chemin du répertoire racine en utilisant global.__basedir

20voto

kvz Points 745

Peut-être que vous pouvez essayer de traverser vers le haut à partir de __filename jusqu'à ce que vous trouviez un package.json et décide que c'est le répertoire principal auquel appartient votre fichier courant.

19voto

Patrick Points 355

J'ai constaté que cela fonctionne systématiquement pour moi, même lorsque l'application est invoquée à partir d'un sous-dossier, comme cela peut être le cas avec certains cadres de test, comme Mocha :

process.mainModule.paths[0].split('node_modules')[0].slice(0, -1);

Pourquoi ça marche :

Au moment de l'exécution, le nœud crée un registre des chemins complets de tous les fichiers chargés. Les modules sont chargés en premier, et donc en haut de ce registre. En sélectionnant le premier élément du registre et en retournant le chemin avant le répertoire 'node_modules', nous sommes en mesure de déterminer la racine de l'application.

Il ne s'agit que d'une ligne de code, mais pour des raisons de simplicité (et pour moi), je l'ai intégrée dans un module NPM :

https://www.npmjs.com/package/node-Root.pddivine

Profitez-en !

19voto

Oleg Valter Points 6078

Préambule

Cette question est très ancienne, mais elle semble toucher la corde sensible en 2020 autant qu'en 2012. J'ai vérifié toutes les autres réponses et je n'ai pas trouvé la technique suivante mentionnée (elle a ses propres limites, mais les autres ne sont pas non plus applicables à toutes les situations) :

Git + processus enfant

Si vous utilisez Git comme système de contrôle des versions Dans ce cas, le problème de la détermination de la racine du projet peut être réduit à (ce que je considérerais comme la racine correcte du projet - après tout, vous voudriez que votre VCS ait la plus grande visibilité possible) :

récupérer le référentiel Chemin racine

Puisque vous devez exécuter une commande CLI pour le faire, nous devons créer un processus enfant. De plus, comme il est très peu probable que le projet Root change en cours d'exécution, nous pouvons utiliser la version synchrone de la commande child_process au démarrage.

J'ai trouvé spawnSync() pour être le plus approprié pour le travail. Quant à la commande à exécuter, git worktree (avec un --porcelain pour faciliter l'analyse syntaxique) est tout ce qui est nécessaire pour récupérer le chemin absolu de la racine.

Dans l'exemple à la fin de la réponse, j'ai choisi de retourner un tableau de chemins parce qu'il pourrait y avoir arbres de travail multiples (bien qu'ils soient susceptibles d'avoir des chemins communs) juste pour être sûr. Notez que lorsque nous utilisons une commande CLI, shell doit être définie comme suit true (la sécurité ne devrait pas être un problème puisqu'il n'y a pas d'entrée non fiable).

Comparaison des approches et solutions de repli

Comprenant qu'une situation où VCS peut être inaccessible est possible, j'ai inclus quelques solutions de repli après avoir analysé la documentation et d'autres réponses. Les solutions proposées se résument à (à l'exclusion des modules et paquets tiers) :

Solution

Avantage

Problème principal

__filename

pointe vers le fichier du module

par rapport au module

__dirname

pointe vers le répertoire du module

même que __filename

node_modules promenade en forêt

Racine presque garantie

marche arborescente complexe si imbriquée

path.resolve(".")

Racine si le CWD est racine

même que process.cwd()

process.argv\[1\]

même que __filename

même que __filename

process.env.INIT_CWD

Les points suivants npm run dir

nécessite npm && Lancement de CLI

process.env.PWD

pointe vers le répertoire actuel

relatif à (est le) répertoire de lancement

process.cwd()

même que env.PWD

process.chdir(path) au moment de l'exécution

require.main.filename

Racine si === module

échoue sur require d modules

D'après le tableau comparatif ci-dessus, les approches suivantes sont les plus universelles :

  • require.main.filename comme un moyen facile d'obtenir la Racine si require.main === module est respecté
  • node_modules promenade dans les arbres proposée récemment utilise une autre hypothèse :

si le répertoire du module a node_modules dir à l'intérieur, il est probable que ce soit la racine

Pour l'application principale, il obtiendra la racine de l'application et pour un module - la racine de son projet.

Fallback 1. Marche dans l'arbre

Mon implémentation utilise une approche plus laxiste en s'arrêtant dès qu'un répertoire cible est trouvé car pour un module donné, sa racine est la racine du projet. On peut enchaîner les appels ou les étendre pour rendre la profondeur de recherche configurable :

/**
 * @summary gets root by walking up node_modules
 * @param {import("fs")} fs
 * @param {import("path")} pt
 */
const getRootFromNodeModules = (fs, pt) =>

    /**
     * @param {string} [startPath]
     * @returns {string[]}
     */
    (startPath = __dirname) => {

        //avoid loop if reached root path
        if (startPath === pt.parse(startPath).root) {
            return [startPath];
        }

        const isRoot = fs.existsSync(pt.join(startPath, "node_modules"));

        if (isRoot) {
            return [startPath];
        }

        return getRootFromNodeModules(fs, pt)(pt.dirname(startPath));
    };

Fallback 2. Module principal

La deuxième mise en œuvre est triviale :

/**
 * @summary gets app entry point if run directly
 * @param {import("path")} pt
 */
const getAppEntryPoint = (pt) =>

    /**
     * @returns {string[]}
     */
    () => {

        const { main } = require;

        const { filename } = main;

        return main === module ?
            [pt.parse(filename).dir] :
            [];
    };

Mise en œuvre

Je suggère d'utiliser le marcheur d'arbre comme solution de rechange préférée car il est plus polyvalent :

const { spawnSync } = require("child_process");
const pt = require('path');
const fs = require("fs");

/**
 * @summary returns worktree root path(s)
 * @param {function : string[] } [fallback]
 * @returns {string[]}
 */
const getProjectRoot = (fallback) => {

    const { error, stdout } = spawnSync(
        `git worktree list --porcelain`,
        {
            encoding: "utf8",
            shell: true
        }
    );

    if (!stdout) {
        console.warn(`Could not use GIT to find root:\n\n${error}`);
        return fallback ? fallback() : [];
    }

    return stdout
        .split("\n")
        .map(line => {
            const [key, value] = line.split(/\s+/) || [];
            return key === "worktree" ? value : "";
        })
        .filter(Boolean);
};

Inconvénients

Le plus évident est d'avoir Git installé et initialisé, ce qui peut être indésirable/implausible (remarque : avoir Git installé sur les serveurs de production n'est pas rare, pas plus qu'il n'est non sécurisé ). Peut être médiatisé par des solutions de repli comme décrit ci-dessus.

16voto

Konstantin Isaev Points 167

Tous ces "Root dirs" ont principalement besoin de résoudre un chemin virtuel vers un chemin réel de la pile, donc peut-être devriez-vous examiner les points suivants path.resolve ?

var path= require('path');
var filePath = path.resolve('our/virtual/path.ext');

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