503 Stimmen

Ermitteln des Projektstamms aus einer laufenden node.js-Anwendung

Gibt es einen anderen Weg, außer process.cwd() , um den Pfadnamen des Root-Verzeichnisses des aktuellen Projekts zu erhalten. Implementiert Node so etwas wie die Eigenschaft von Ruby, Rails.root ,. Ich bin auf der Suche nach etwas, das konstant und zuverlässig ist.

26voto

didxga Punkte 5592

Fügen Sie einfach diese Zeile zu Ihrem Modul im Root hinzu, normalerweise ist es app.js o app.ts .

global.__basedir = __dirname;

Dann wird _basedir für alle Ihre Module zugänglich sein.

Anmerkung: Für die Typescript-Implementierung folgen Sie dem obigen Schritt und können dann den Pfad zum Root-Verzeichnis verwenden, indem Sie global.__basedir

21voto

kvz Punkte 4837

Versuchen Sie, sich von __dirname bis Sie eine package.json und entscheiden Sie, dass dies das Haupt-Root-Verzeichnis der Anwendung ist, zu dem Ihre aktuelle Datei gehört.

Según Knotenpunkt-Dokumente

Die package.json-Datei befindet sich normalerweise im Root-Verzeichnis eines Node.js-Projekts.

const fs = require('fs')
const path = require('path')

function getAppRootDir () {
  let currentDir = __dirname
  while(!fs.existsSync(path.join(currentDir, 'package.json'))) {
    currentDir = path.join(currentDir, '..')
  }
  return currentDir
}

20voto

Patrick Punkte 473

Ich habe festgestellt, dass dies bei mir immer funktioniert, auch wenn die Anwendung aus einem Unterordner aufgerufen wird, wie es bei einigen Test-Frameworks wie Mocha der Fall sein kann:

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

Warum es funktioniert:

Zur Laufzeit erstellt node ein Register mit den vollständigen Pfaden aller geladenen Dateien. Die Module werden zuerst geladen und stehen daher an erster Stelle dieses Verzeichnisses. Durch Auswahl des ersten Elements des Verzeichnisses und Rückgabe des Pfads vor dem Verzeichnis "node_modules" können wir die Wurzel der Anwendung ermitteln.

Es ist nur eine Zeile Code, aber der Einfachheit halber (meinetwegen) habe ich ihn in ein NPM-Modul gepackt:

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

Viel Spaß!

EDIT :

process.mainModule es Abgelehnt ab Version 14.0.0

Utilice require.main stattdessen:

require.main.paths[0].split('node_modules')[0].slice(0, -1);

20voto

Präambel

Dies ist eine sehr alte Frage, aber sie scheint im Jahr 2020 den Nerv der Zeit genauso zu treffen wie im Jahr 2012. Ich habe alle anderen Antworten durchgesehen und konnte die folgende Technik nicht finden (sie hat ihre eigenen Grenzen, aber auch die anderen sind nicht auf jede Situation anwendbar):

Git + untergeordneter Prozess

Wenn Sie Git als Ihre Versionskontrollsystem Das Problem der Bestimmung der Projektwurzel (die ich als die eigentliche Wurzel des Projekts ansehen würde - schließlich möchten Sie, dass Ihr VCS den größtmöglichen Sichtbarkeitsumfang hat) kann reduziert werden:

Repository abrufen Wurzelpfad

Da Sie dazu einen CLI-Befehl ausführen müssen, müssen wir einen Kindprozess erzeugen. Da es außerdem sehr unwahrscheinlich ist, dass sich das Projekt Root während der Laufzeit ändert, können wir die synchrone Version des Befehls child_process Modul beim Start.

Ich fand spawnSync() die für die Stelle am besten geeignet sind. Was den auszuführenden Befehl betrifft, git worktree (mit einem --porcelain zur Vereinfachung des Parsens) ist alles, was benötigt wird, um den absoluten Pfad der Wurzel abzurufen.

In dem Beispiel am Ende der Antwort habe ich mich dafür entschieden, ein Array von Pfaden zurückzugeben, weil es möglicherweise mehrere Arbeitsbäume (obwohl sie wahrscheinlich gemeinsame Wege haben), nur um sicher zu sein. Beachten Sie, dass wir einen CLI-Befehl verwenden, shell sollte die Option true (die Sicherheit sollte kein Problem darstellen, da es keine nicht vertrauenswürdige Eingabe gibt).

Vergleich von Ansätzen und Ausweichmöglichkeiten

Da ich weiß, dass eine Situation, in der VCS unzugänglich ist, möglich ist, habe ich nach der Analyse von Dokumenten und anderen Antworten ein paar Ausweichmöglichkeiten eingebaut. Die vorgeschlagenen Lösungen belaufen sich auf (ohne Module und Pakete von Drittanbietern):

Solución

Vorteil

Hauptproblem

__filename

zeigt auf die Moduldatei

relativ zum Modul

__dirname

zeigt auf das Modulverzeichnis

gleich wie __filename

node_modules Baumspaziergang

fast garantiert Wurzel

komplexe Baumwanderung bei Verschachtelung

path.resolve(".")

Wurzel, wenn CWD Wurzel ist

gleich wie process.cwd()

process.argv\[1\]

gleich wie __filename

gleich wie __filename

process.env.INIT_CWD

zeigt auf npm run dir

erfordert npm && CLI-Start

process.env.PWD

zeigt auf das aktuelle Verzeichnis

relativ zu (ist das) Startverzeichnis

process.cwd()

gleich wie env.PWD

process.chdir(path) während der Laufzeit

require.main.filename

Wurzel wenn === module

scheitert an require d-Module

Aus der obigen Vergleichstabelle geht hervor, dass die folgenden Ansätze am universellsten sind:

  • require.main.filename als eine einfache Möglichkeit, die Wurzel zu erhalten, wenn require.main === module erfüllt ist
  • node_modules Baumweg vorgeschlagen kürzlich geht von einer anderen Annahme aus:

wenn das Verzeichnis des Moduls den Namen node_modules dir im Inneren, ist es wahrscheinlich die Root

Für die Hauptanwendung wird der Anwendungsstamm und für ein Modul der Projektstamm verwendet.

Fallback 1. Baumlauf

Meine Implementierung verwendet einen laxeren Ansatz, indem sie anhält, sobald ein Zielverzeichnis gefunden wird, da für ein bestimmtes Modul dessen Root das Projekt-Root ist. Man kann die Aufrufe verketten oder sie erweitern, um die Suchtiefe konfigurierbar zu machen:

/**
 * @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. Hauptmodul

Die zweite Implementierung ist trivial:

/**
 * @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] :
            [];
    };

Umsetzung

Ich würde vorschlagen, den Tree Walker als bevorzugte Lösung zu verwenden, da er vielseitiger ist:

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);
};

Benachteiligungen

Die offensichtlichste ist, dass Git installiert und initialisiert werden muss, was unerwünscht/unplausibel sein kann (Nebenbemerkung: Git muss auf Produktionsservern installiert ist nicht ungewöhnlich, und es ist auch nicht unsicher ). Kann wie oben beschrieben durch Fallbacks vermittelt werden.

16voto

Konstantin Isaev Punkte 602

All diese "Root-Verzeichnisse" müssen meist einen virtuellen Pfad in einen realen Stapelpfad auflösen, daher sollten Sie sich vielleicht path.resolve ?

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X