Bestimmen Sie das Projektstammverzeichnis aus einer laufenden Anwendung node.js.


314

Gibt es eine bessere Möglichkeit, als process.cwd()das Stammverzeichnis eines laufenden node.js-Prozesses zu ermitteln? So etwas wie das Äquivalent von Rails.root, aber für Node.js. Ich suche etwas, das so vorhersehbar und zuverlässig wie möglich ist.


1
Gibt es eine Chance, dass Sie die akzeptierte, falsche Antwort nicht akzeptieren?
Dave Newton

9
versuche process.env.PWD... siehe meine Antwort unten.
Alexander Mills

Antworten:


622

Es gibt verschiedene Möglichkeiten, dies zu erreichen, jede mit ihren eigenen Vor- und Nachteilen:

require.main.filename

Von http://nodejs.org/api/modules.html :

Wenn eine Datei direkt vom Knoten ausgeführt wird, require.mainwird sie auf ihre gesetzt module. Das heißt, Sie können durch Testen feststellen, ob eine Datei direkt ausgeführt wurderequire.main === module

Da moduleeine filenameEigenschaft bereitgestellt wird (normalerweise äquivalent zu __filename), kann der Einstiegspunkt der aktuellen Anwendung durch Überprüfen erhalten werden require.main.filename.

Wenn Sie also das Basisverzeichnis für Ihre App möchten, können Sie Folgendes tun:

var path = require('path');
var appDir = path.dirname(require.main.filename);

Für und Wider

Dies funktioniert die meiste Zeit hervorragend, aber wenn Sie Ihre App mit einem Launcher wie pm2 ausführen oder Mokka- Tests ausführen, schlägt diese Methode fehl.

global.X

Der Knoten hat ein globales Namespace-Objekt namens global- alles, was Sie an dieses Objekt anhängen, ist überall in Ihrer App verfügbar. Sie können also in Ihrer index.js(oder wie auch app.jsimmer Ihre Haupt-App-Datei benannt ist) einfach eine globale Variable definieren:

// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);

// lib/moduleA/component1.js
require(appRoot + '/lib/moduleB/component2.js');

Für und Wider

Funktioniert konsistent, aber Sie müssen sich auf eine globale Variable verlassen, was bedeutet, dass Sie Komponenten / etc. Nicht einfach wiederverwenden können.

process.cwd ()

Dies gibt das aktuelle Arbeitsverzeichnis zurück. Nicht zuverlässig überhaupt, da es völlig abhängig ist, was Verzeichnis der Prozess gestartet wurde aus :

$ cd /home/demo/
$ mkdir subdir
$ echo "console.log(process.cwd());" > subdir/demo.js
$ node subdir/demo.js
/home/demo
$ cd subdir
$ node demo.js
/home/demo/subdir

App-Root-Pfad

Um dieses Problem zu beheben, habe ich ein Knotenmodul namens app-root-path erstellt . Die Verwendung ist einfach:

var appRoot = require('app-root-path');
var myModule = require(appRoot + '/lib/my-module.js');

Das App-Root-Path- Modul verwendet verschiedene Techniken, um den Root-Pfad der App zu bestimmen, wobei global installierte Module berücksichtigt werden (z. B. wenn Ihre App ausgeführt wird /var/www/, das Modul jedoch installiert ist ~/.nvm/v0.x.x/lib/node/). Es wird nicht 100% der Zeit funktionieren, aber es wird in den meisten gängigen Szenarien funktionieren.

Für und Wider

Funktioniert in den meisten Fällen ohne Konfiguration. Bietet auch einige nette zusätzliche Komfortmethoden (siehe Projektseite). Der größte Nachteil ist, dass es nicht funktioniert, wenn:

  • Sie verwenden einen Launcher wie pm2
  • UND , das Modul ist nicht im node_modulesVerzeichnis Ihrer App installiert (z. B. wenn Sie es global installiert haben).

Sie können dies umgehen, indem Sie entweder eine APP_ROOT_PATHUmgebungsvariable festlegen oder .setPath()das Modul aufrufen. In diesem Fall ist es jedoch wahrscheinlich besser, die globalMethode zu verwenden.

Umgebungsvariable NODE_PATH

Wenn Sie nach einer Möglichkeit suchen , den Stammpfad der aktuellen App zu ermitteln , funktioniert eine der oben genannten Lösungen wahrscheinlich am besten für Sie. Wenn Sie andererseits versuchen, das Problem des zuverlässigen Ladens von App-Modulen zu lösen, empfehle ich dringend, die NODE_PATHUmgebungsvariable zu untersuchen.

Das Modulsystem von Node sucht an verschiedenen Orten nach Modulen. Einer dieser Orte ist überall dort, wo process.env.NODE_PATHPunkte liegen . Wenn Sie diese Umgebungsvariable festlegen, können Sie requireModule mit dem Standardmodullader ohne weitere Änderungen erstellen.

Zum Beispiel, wenn Sie setzen NODE_PATHauf /var/www/lib, die folgende würde gut funktionieren:

require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js

Eine gute Möglichkeit, dies zu tun, ist die Verwendung von npm:

"scripts": {
    "start": "NODE_PATH=. node app.js"
}

Jetzt können Sie Ihre App mit starten npm startund Sie sind golden. Ich kombiniere dies mit meinem Enforce-Node-Path- Modul, das verhindert, dass die App versehentlich ohne NODE_PATHSet geladen wird . Weitere Informationen zur Durchsetzung von Umgebungsvariablen finden Sie unter checkenv .

Ein Gotcha: NODE_PATH muss seinen Satz außerhalb des Knotens App. Sie können so etwas nicht tun, process.env.NODE_PATH = path.resolve(__dirname)da der Modullader die Liste der Verzeichnisse zwischenspeichert, die durchsucht werden, bevor Ihre App ausgeführt wird.

[hinzugefügt am 06.04.16] Ein weiteres wirklich vielversprechendes Modul, das versucht, dieses Problem zu lösen, ist wellig .


1
@ Kevin in diesem Fall ist Mokka der Einstiegspunkt Ihrer Anwendung. Dies ist nur ein Beispiel dafür, warum es so schwierig ist, den "Projektstamm" zu finden - es hängt so stark von der Situation ab und davon, was Sie unter "Projektstamm" verstehen.
Inxilpro

1
@ Kevin Ich verstehe völlig. Mein Punkt ist nur, dass das Konzept der "Projektwurzel" für einen Menschen viel einfacher zu verstehen ist als für einen Computer . Wenn Sie eine narrensichere Methode wünschen, müssen Sie sie konfigurieren. Die Verwendung funktioniert die meiste Zeit, aber nicht die ganze Zeit. require.main.filename
Inxilpro

2
Tangential verwandt: Dies ist eine unglaublich clevere Möglichkeit, Ihr Node-Projekt so zu organisieren, dass Sie sich nicht so viele Sorgen um dieses Problem machen müssen: allanhortle.com/2015/02/04/…
inxilpro

1
Ich weiß nicht, ob es eine Änderung in pm2 oder eine Änderung in Node.js gab, require.main.filenamescheint aber mit pm2 zu funktionieren. Ich weiß nichts über Mokka.
Justin Warkentin

8
path.parse(process.mainModule.filename).dir
Cory Robinson

53

__dirnameist kein globaler; Es ist lokal für das aktuelle Modul, sodass jede Datei ihren eigenen lokalen, unterschiedlichen Wert hat.

Wenn Sie das Stammverzeichnis des laufenden Prozesses möchten, möchten Sie wahrscheinlich verwenden process.cwd().

Wenn Sie Vorhersehbarkeit und Zuverlässigkeit wünschen, müssen Sie wahrscheinlich eine Anforderung für Ihre Anwendung festlegen, dass eine bestimmte Umgebungsvariable festgelegt wird. Ihre App sucht MY_APP_HOME(oder was auch immer) und wenn es dort ist und die Anwendung in diesem Verzeichnis existiert, ist alles in Ordnung. Wenn es undefiniert ist oder das Verzeichnis Ihre Anwendung nicht enthält, sollte es mit einem Fehler beendet werden, der den Benutzer auffordert, die Variable zu erstellen. Es kann als Teil eines Installationsprozesses festgelegt werden.

Sie können Umgebungsvariablen im Knoten mit so etwas wie lesen process.env.MY_ENV_VARIABLE.


2
Bei vorsichtiger Anwendung könnte dies recht gut funktionieren. Aber es würde unterschiedliche Ergebnisse geben, wenn man bin/server.jsvs macht cd bin && server.js. (vorausgesetzt, diese js-Dateien sind als ausführbar markiert)
Myrne Stol

1
Das Verwenden process.cwd()hat für mich wie ein Zauber gewirkt, selbst wenn ich Mokka-Tests durchgeführt habe. Vielen Dank!
Diogo Eichert

48

1- Erstellen Sie eine Datei im Projektstamm. Nennen Sie sie settings.js

2- Fügen Sie in dieser Datei diesen Code hinzu

module.exports = {
    POST_MAX_SIZE : 40 , //MB
    UPLOAD_MAX_FILE_SIZE: 40, //MB
    PROJECT_DIR : __dirname
};

3- Erstellen Sie innerhalb von node_modules einen neuen Modulnamen mit dem Namen "settings" und schreiben Sie innerhalb des Moduls index.js diesen Code:

module.exports = require("../../settings");

4- und wann immer Sie möchten, verwenden Sie einfach Ihr Projektverzeichnis

var settings = require("settings");
settings.PROJECT_DIR; 

auf diese Weise haben Sie alle Projektverzeichnisse relativ zu dieser Datei;)


33
-1: Um die Einstellungsdatei zu laden, benötigen Sie einen Pfad, um dann den Referenzpfad zu dieser Datei zu erhalten? Nichts lösen ...
Goliatone

2
Upvoted für die Zeit zum Überprüfen und Bearbeiten. Es fühlt sich immer noch spröde an, aber das könnte nur daran liegen, dass es keinen besseren Weg gibt, dies zu erreichen
Goliatone

8
Benutzer sollten diesen Ansatz berücksichtigen, der node_moduleshäufig von der Versionskontrolle ausgeschlossen wird. Wenn Sie also mit einem Team zusammenarbeiten oder jemals Ihr Repository klonen müssen, müssen Sie eine andere Lösung finden, um diese Einstellungsdatei synchron zu halten.
Travesty3

@ Travesty3 das Einstellungsmodul ist eigentlich ein leeres Modul, das den Inhalt einer Datei im Projektstamm exportiert: P
Fareed Alnamrouti

@goliatone Mit seiner Lösung können Sie die Datei von überall abrufen, ohne den Pfad zu kennen. Sie müssen lediglich "Einstellungen" kennen. Ohne sie müssten Sie explizit wissen, aus wie vielen Ordnern Sie zurückkehren müssen, bis Sie das Projektverzeichnis erreichen. Dies funktioniert, weil node automatisch node_modules durchsucht und immer weiß, wo sich das befindet.

26

Der einfachste Weg, um das globale Stammverzeichnis zu erhalten ( vorausgesetzt, Sie verwenden NPM, um Ihre node.js-App 'npm start' usw. auszuführen ).

var appRoot = process.env.PWD;

Wenn Sie das oben Gesagte überprüfen möchten

Angenommen, Sie möchten process.env.PWDdie Einstellungen Ihrer Anwendung node.js überprüfen . Wenn Sie möchten, dass einige Laufzeit-Tests die Gültigkeit von überprüfen process.env.PWD, können Sie dies mit diesem Code (den ich geschrieben habe, der anscheinend gut funktioniert) überprüfen. Sie können den Namen des letzten Ordners in appRoot mit dem Namen npm_package_name in Ihrer Datei package.json abgleichen, zum Beispiel:

    var path = require('path');

    var globalRoot = __dirname; //(you may have to do some substring processing if the first script you run is not in the project root, since __dirname refers to the directory that the file is in for which __dirname is called in.)

    //compare the last directory in the globalRoot path to the name of the project in your package.json file
    var folders = globalRoot.split(path.sep);
    var packageName = folders[folders.length-1];
    var pwd = process.env.PWD;
    var npmPackageName = process.env.npm_package_name;
    if(packageName !== npmPackageName){
        throw new Error('Failed check for runtime string equality between globalRoot-bottommost directory and npm_package_name.');
    }
    if(globalRoot !== pwd){
        throw new Error('Failed check for runtime string equality between globalRoot and process.env.PWD.');
    }

Sie können auch dieses NPM-Modul verwenden, require('app-root-path')das für diesen Zweck sehr gut funktioniert


5
Dies funktioniert hervorragend auf (den meisten) Unix-Systemen. Sobald Sie möchten, dass Ihr npm-Modul / Ihre npm-App unter Windows funktioniert, PWDist dies nicht definiert und schlägt fehl.
Jeremy Wiebe

1
process.cwd()
Muhammad Umer

@ MuhammadUmer warum sollte process.cwd()immer das gleiche sein wie Projektstamm?
Alexander Mills

Wenn Sie es in der Root-Datei aufrufen, dann wäre es
Muhammad Umer

14

Ich habe festgestellt, dass dies für mich konsistent funktioniert, auch wenn die Anwendung aus einem Unterordner aufgerufen wird, wie dies bei einigen Testframeworks wie Mocha der Fall sein kann:

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

Warum es funktioniert:

Zur Laufzeit erstellt der Knoten eine Registrierung der vollständigen Pfade aller geladenen Dateien. Die Module werden zuerst geladen und befinden sich somit oben in dieser Registrierung. Durch Auswahl des ersten Elements der Registrierung und Rückgabe des Pfads vor dem Verzeichnis 'node_modules' können wir das Stammverzeichnis der Anwendung ermitteln.

Es ist nur eine Codezeile, aber der Einfachheit halber (meinetwegen) habe ich sie in ein NPM-Modul eingepackt:

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

Genießen!


1
process.mainModule deprectaed seit: v14.0.0 - require.main.paths[0].split('node_modules')[0].slice(0, -1);stattdessen verwenden.
RobC

10

All diese "Root-Verzeichnisse" müssen meistens einen virtuellen Pfad in einen realen Stapelpfad auflösen. Vielleicht sollten Sie sich das ansehen path.resolve?

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

9

So einfach wie das Hinzufügen dieser Zeile zu Ihrem Modul in root, normalerweise ist es app.js.

global.__basedir = __dirname;

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


8

Vielleicht können Sie versuchen, von oben nach oben zu gehen, __filenamebis Sie ein finden package.json, und entscheiden, dass dies das Hauptverzeichnis ist, zu dem Ihre aktuelle Datei gehört.


7

Eigentlich finde ich die vielleicht triviale Lösung auch am robustesten: Sie legen einfach die folgende Datei im Stammverzeichnis Ihres Projekts ab: root-path.js mit folgendem Code:

import * as path from 'path'
const projectRootPath = path.resolve(__dirname)
export const rootPath = projectRootPath

4

Eine Technik, die ich bei der Verwendung von Express als nützlich empfunden habe, besteht darin, app.js Folgendes hinzuzufügen, bevor eine Ihrer anderen Routen festgelegt wird

// set rootPath
app.use(function(req, res, next) {
  req.rootPath = __dirname;
  next();
});

app.use('/myroute', myRoute);

Sie müssen keine Globals verwenden und haben den Pfad des Stammverzeichnisses als Eigenschaft des Anforderungsobjekts.

Dies funktioniert, wenn sich Ihre app.js im Stammverzeichnis Ihres Projekts befindet, was standardmäßig der Fall ist.


4

Fügen Sie dies irgendwo am Anfang Ihrer Haupt-App-Datei hinzu (z. B. app.js):

global.__basedir = __dirname;

Dadurch wird eine globale Variable festgelegt, die immer dem Basisverzeichnis Ihrer App entspricht. Verwenden Sie es wie jede andere Variable:

const yourModule = require(__basedir + '/path/to/module.js');

Einfach...


3

Ich weiß, dass dieser schon zu spät ist. Wir können die Stamm-URL jedoch mit zwei Methoden abrufen

1. Methode

var path = require('path');
path.dirname(require.main.filename);

2. Methode

var path = require('path');
path.dirname(process.mainModule.filename);

Referenzlink: - https://gist.github.com/geekiam/e2e3e0325abd9023d3a3


3

Es gibt eine INIT_CWDEigenschaft auf process.env. Damit arbeite ich gerade in meinem Projekt.

const {INIT_CWD} = process.env; // process.env.INIT_CWD 
const paths = require(`${INIT_CWD}/config/paths`);

Viel Glück...


1
Arbeitete wie ein Zauber für ein Paket, das das Projekt, von dem aus es aufgerufen wird, als Schritt nach der Installation manipuliert. Ich habe es jedoch noch nicht in einer anderen Abhängigkeitsebene getestet, in der ein Projekt eine Abhängigkeit verwendet, die mein Paket verwendet.
JamesDev

1
@JamesDev, wird INIT_CWDin das aufgelöst, directoryvon dem das ausgeführt npm-scriptwurde.
Akash

2

Wenn Sie das Projektstammverzeichnis aus einer laufenden Anwendung node.js ermitteln möchten, können Sie dies einfach auch tun.

process.mainModule.path

1

Fügen Sie oben in der Hauptdatei Folgendes hinzu:

mainDir = __dirname;

Verwenden Sie es dann in einer beliebigen Datei:

console.log('mainDir ' + mainDir);
  • mainDir wird global definiert, wenn Sie es nur in der aktuellen Datei benötigen - verwenden __dirname stattdessen.
  • Hauptdatei ist in der Regel in Stammordner des Projekts und wird wie genannt main.js, index.js, gulpfile.js.

1

Ich benutze das.

Für mein Modul namens mymodule

var BASE_DIR = __dirname.replace(/^(.*\/mymodule)(.*)$/, '$1')


1

Mach es sexy 💃🏻.

const users = require('../../../database/users'); // 👎 what you have
// OR
const users = require('$db/users'); // 👍 no matter how deep you are
const products = require('/database/products'); // 👍 alias or pathing from root directory


Drei einfache Schritte, um das Problem des hässlichen Pfades zu lösen.

  1. Installieren Sie das Paket: npm install sexy-require --save
  2. Fügen Sie require('sexy-require')einmal oben in Ihre Hauptanwendungsdatei ein.

    require('sexy-require');
    const routers = require('/routers');
    const api = require('$api');
    ...
  3. Optionaler Schritt. Die Pfadkonfiguration kann in der .pathsDatei im Stammverzeichnis Ihres Projekts definiert werden.

    $db = /server/database
    $api-v1 = /server/api/legacy
    $api-v2 = /server/api/v2

Scheint anständig, schade, dass es so einen lächerlichen Namen hatte.
JHH

@JHH gut ... Ich musste einen besseren Namen finden
Sultan

1

Dadurch wird der Verzeichnisbaum heruntergefahren, bis er ein node_modulesVerzeichnis enthält , das normalerweise Ihren Projektstamm angibt:

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

function getProjectRoot(currentDir = __dirname.split(path.sep)) {
  if (!currentDir.length) {
    throw Error('Could not find project root.')
  }
  const nodeModulesPath = currentDir.concat(['node_modules']).join(path.sep)
  if (fs.existsSync(nodeModulesPath) && !currentDir.includes('node_modules')) {
    return currentDir.join(path.sep)
  }
  return this.getProjectRoot(currentDir.slice(0, -1))
}

Außerdem wird sichergestellt, dass node_modulesder zurückgegebene Pfad keine enthält , da dies bedeutet, dass er in einer verschachtelten Paketinstallation enthalten ist.


1

process.mainModuleist seit v 14.0.0 veraltet . Wenn auf die Antwort Bezug genommen wird , bitte Gebrauch require.main hält der Rest immer noch.

process.mainModule.paths
  .filter(p => !p.includes('node_modules'))
  .shift()

Holen Sie sich alle Pfade in Hauptmodulen und filtern Sie diese mit "node_modules" heraus. Rufen Sie dann den ersten der verbleibenden Pfadlisten ab. Unerwartetes Verhalten löst keinen Fehler aus, nur einen undefined.

Funktioniert gut für mich, auch wenn ich zB anrufe $ mocha.


0

Erstellen Sie eine Funktion in app.js.

/*Function to get the app root folder*/

var appRootFolder = function(dir,level){
    var arr = dir.split('\\');
    arr.splice(arr.length - level,level);
    var rootFolder = arr.join('\\');
    return rootFolder;
}

// view engine setup
app.set('views', path.join(appRootFolder(__dirname,1),'views'));

0

Sie können einfach den Stammverzeichnispfad in die Express-App-Variable einfügen und diesen Pfad von der App abrufen. Für dieses Add app.set('rootDirectory', __dirname);in Ihrem index.js oder app.js Datei. Und verwenden Sie req.app.get('rootDirectory'), um den Stammverzeichnispfad in Ihrem Code abzurufen.


0

Alte Frage, ich weiß, jedoch keine Frage zu erwähnen progress.argv. Das argv-Array enthält einen vollständigen Pfadnamen und einen Dateinamen (mit oder ohne Erweiterung .js), die als vom Knoten auszuführender Parameter verwendet wurden. Da dies auch Flags enthalten kann, müssen Sie dies filtern.

Dies ist kein Beispiel, das Sie direkt verwenden können (aufgrund der Verwendung meines eigenen Frameworks), aber ich denke, es gibt Ihnen eine Vorstellung davon, wie es geht. Ich verwende auch eine Cache-Methode, um zu vermeiden, dass das Aufrufen dieser Funktion das System zu stark belastet, insbesondere wenn keine Erweiterung angegeben ist (und eine Überprüfung des Vorhandenseins einer Datei erforderlich ist), zum Beispiel:

node myfile

oder

node myfile.js

Das ist der Grund, warum ich es zwischenspeichere, siehe auch Code unten.


function getRootFilePath()
{
        if( !isDefined( oData.SU_ROOT_FILE_PATH ) )
        {
            var sExt = false;

            each( process.argv, function( i, v )
            {
                 // Skip invalid and provided command line options
                if( !!v && isValidString( v ) && v[0] !== '-' )
                {
                    sExt = getFileExt( v );

                    if( ( sExt === 'js' ) || ( sExt === '' && fileExists( v+'.js' )) )
                    {

                        var a = uniformPath( v ).split("/"); 

                         // Chop off last string, filename
                        a[a.length-1]='';

                         // Cache it so we don't have to do it again.
                        oData.SU_ROOT_FILE_PATH=a.join("/"); 

                         // Found, skip loop
                        return true;
                    }
                }
            }, true ); // <-- true is: each in reverse order
        }

        return oData.SU_ROOT_FILE_PATH || '';
    }
}; 

0

Das Finden des Wurzelpfads einer Elektronen-App kann schwierig werden. Weil der Root-Pfad für den Hauptprozess und den Renderer unter verschiedenen Bedingungen wie Produktion, Entwicklung und Paketbedingungen unterschiedlich ist.

Ich habe ein npm-Paket für den Elektronenwurzelpfad geschrieben , um den Wurzelpfad einer Elektronen-App zu erfassen.

$ npm install electron-root-path

or 

$ yarn add electron-root-path


// Import ES6 way
import { rootPath } from 'electron-root-path';

// Import ES2015 way
const rootPath = require('electron-root-path').rootPath;

// e.g:
// read a file in the root
const location = path.join(rootPath, 'package.json');
const pkgInfo = fs.readFileSync(location, { encoding: 'utf8' });

0

Das wird es tun:

path.join(...process.argv[1].split(/\/|\\/).slice(0, -1))


0

Präambel

Dies ist eine sehr alte Frage, aber sie scheint 2020 wie 2012 immer noch die Nerven zu treffen. Ich habe alle anderen Antworten überprüft und konnte keine Technik finden (beachten Sie, dass dies seine Grenzen hat, alle anderen jedoch nicht auch in jeder Situation anwendbar).

GIT + untergeordneter Prozess

Wenn Sie GIT als Versionskontrollsystem verwenden, kann das Problem der Ermittlung des Projektstamms auf reduziert werden (was ich als den richtigen Stamm des Projekts betrachten würde - schließlich möchten Sie, dass Ihr VCS den größtmöglichen Sichtbarkeitsbereich hat). ::

Repository-Stammpfad abrufen

Da Sie dazu einen CLI-Befehl ausführen müssen, müssen wir einen untergeordneten Prozess erzeugen. Da es sehr unwahrscheinlich ist, dass sich das Projektstammverzeichnis zur Laufzeit ändert, können wir child_processbeim Start die synchrone Version der Modul-APIs verwenden.

Ich fand spawnSync()es am besten für den Job geeignet. Der eigentliche Befehl, der ausgeführt werden soll git worktree(mit einer --porcelainOption zur Erleichterung des Parsens), ist alles, was wir benötigen, um den absoluten Root-Pfad abzurufen.

Im Beispiel habe ich mich dafür entschieden, ein Array von Pfaden zurückzugeben, da es möglicherweise mehr als einen Arbeitsbaum gibt (obwohl sie wahrscheinlich gemeinsame Pfade haben), nur um sicherzugehen. Beachten Sie, dass bei Verwendung eines CLI-Befehls die shellOption auf gesetzt werden sollte true(Sicherheit sollte kein Problem darstellen, da keine nicht vertrauenswürdigen Eingaben vorliegen).

Ansatzvergleich und Fallbacks

Nachdem ich verstanden habe, dass auf VCS nicht zugegriffen werden kann, habe ich nach der Analyse von Dokumenten und anderen Antworten einige Fallbacks hinzugefügt. Zusammenfassend lässt sich sagen, dass sich die vorgeschlagenen Lösungen auf Folgendes beschränken (ausgenommen Module von Drittanbietern und paketspezifisch):

| Lösung | Vorteil | Hauptproblem |
| ------------------------ | ----------------------- | -------------------------------- |
| `__Dateiname` | zeigt auf Moduldatei | relativ zum Modul |
| `__dirname` | zeigt auf Modul dir | wie `__filename` |
| `node_modules` tree walk | fast garantierte Wurzel | komplexer Baum, der verschachtelt geht |
| `path.resolve (". ")` | root, wenn CWD root ist | wie `process.cwd ()` |
| `process.argv [1]` | wie `__filename` | 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 ()` | wie `env.PWD` | `process.chdir (path)` zur Laufzeit |
| `require.main.filename` | root if `=== module` | schlägt bei `require`d modules | fehl

Aus der obigen Vergleichstabelle sind zwei Ansätze am universellsten:

  • require.main.filenameals einfache Möglichkeit, root zu bekommen, wenn require.main === moduleerfüllt ist
  • node_modulesDer kürzlich vorgeschlagene Baumspaziergang verwendet eine andere Annahme:

Wenn das Verzeichnis des Moduls node_modulesdir enthält, ist es wahrscheinlich das Stammverzeichnis

Für die Haupt-App erhält es das App-Stammverzeichnis und für das Modul das Projektstammverzeichnis.

Fallback 1. Baumspaziergang

Meine Implementierung verwendet einen lockeren Ansatz, indem sie angehalten wird, sobald ein Zielverzeichnis gefunden wurde, da für ein bestimmtes Modul sein Stamm sein Projektstamm ist. Man kann die Anrufe verketten oder 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] :
            [];
    };

Implementierung

Ich würde vorschlagen, den Tree Walker als Fallback 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);
};

Nachteile

Am offensichtlichsten ist die Installation und Initialisierung von GIT, was unerwünscht / unplausibel sein kann (Randnotiz: Die Installation von GIT auf Produktionsservern ist weder ungewöhnlich noch unsicher ). Kann wie oben beschrieben durch Fallbacks vermittelt werden.

Anmerkungen

  1. Einige Ideen zur weiteren Erweiterung von Ansatz 1:
    • Führen Sie config als Funktionsparameter ein
    • export die Funktion, es zu einem Modul zu machen
    • Überprüfen Sie, ob GIT installiert und / oder initialisiert ist

Verweise

  1. git worktree Referenz
  2. spawnSync Referenz
  3. require.main Referenz
  4. path.dirname() Referenz


-1

Versuchen path._makeLong('some_filename_on_root.js');

Beispiel:

cons path = require('path');
console.log(path._makeLong('some_filename_on_root.js');

Dadurch wird der vollständige Pfad vom Stammverzeichnis Ihrer Knotenanwendung zurückgegeben (gleiche Position von package.json).


-1

Benutz einfach:

 path.resolve("./") ... output is your project root directory

das funktioniert super! path.resolve (".") funktioniert auch
Noel Schenk

Das gibt nur das aktuelle Verzeichnis an, das möglicherweise nicht das Stammverzeichnis ist.
Orad

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.