มีวิธีที่ดีกว่าprocess.cwd()การกำหนดไดเรกทอรีรูทของกระบวนการ node.js หรือไม่? บางสิ่งบางอย่างที่เทียบเท่ากับRails.rootแต่สำหรับ Node.js ฉันกำลังมองหาบางสิ่งที่สามารถคาดการณ์ได้และเชื่อถือได้มากที่สุด
process.env.PWD... ดูคำตอบของฉันด้านล่าง
มีวิธีที่ดีกว่าprocess.cwd()การกำหนดไดเรกทอรีรูทของกระบวนการ node.js หรือไม่? บางสิ่งบางอย่างที่เทียบเท่ากับRails.rootแต่สำหรับ Node.js ฉันกำลังมองหาบางสิ่งที่สามารถคาดการณ์ได้และเชื่อถือได้มากที่สุด
process.env.PWD... ดูคำตอบของฉันด้านล่าง
คำตอบ:
มีหลายวิธีในการเข้าถึงสิ่งนี้แต่ละคนมีข้อดีและข้อเสียของตัวเอง:
จากhttp://nodejs.org/api/modules.html :
เมื่อไฟล์ถูกเรียกใช้โดยตรงจากโหนด
require.mainจะถูกตั้งค่าเป็นmoduleมีการตั้งค่าของมันซึ่งหมายความว่าคุณสามารถตรวจสอบว่าไฟล์นั้นถูกเรียกใช้โดยตรงโดยการทดสอบrequire.main === moduleเพราะ
moduleยังมีfilenameสถานที่ให้บริการ (ปกติเทียบเท่า__filename)require.main.filenameจุดเริ่มต้นของการประยุกต์ใช้ในปัจจุบันสามารถหาได้โดยการตรวจสอบ
ดังนั้นหากคุณต้องการไดเรกทอรีฐานสำหรับแอปของคุณคุณสามารถทำได้:
var path = require('path');
var appDir = path.dirname(require.main.filename);
วิธีนี้จะใช้งานได้ดีที่สุด แต่ถ้าคุณใช้แอพของคุณด้วยตัวเรียกใช้เช่นpm2หรือเรียกใช้มอคค่าการทดสอบวิธีนี้จะล้มเหลว
โหนดมีวัตถุเนมสเปซส่วนกลางที่เรียกว่าglobal- สิ่งใดก็ตามที่คุณแนบกับวัตถุนี้จะมีอยู่ทุกที่ในแอปของคุณ ดังนั้นในindex.js(หรือapp.jsชื่อไฟล์หลักของคุณ) คุณสามารถกำหนดตัวแปรกลางได้
// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);
// lib/moduleA/component1.js
require(appRoot + '/lib/moduleB/component2.js');
ทำงานได้อย่างสม่ำเสมอ แต่คุณต้องพึ่งพาตัวแปรทั่วโลกซึ่งหมายความว่าคุณไม่สามารถนำส่วนประกอบ / etc มาใช้ใหม่ได้อย่างง่ายดาย
ส่งคืนไดเร็กทอรีการทำงานปัจจุบัน ไม่น่าเชื่อถือเลยเพราะมันขึ้นอยู่กับไดเรกทอรีที่กระบวนการเปิดตัวจาก :
$ 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
เพื่อแก้ไขปัญหานี้เราได้สร้างโมดูลโหนดเรียกว่าแอปรากเส้นทาง การใช้งานง่าย:
var appRoot = require('app-root-path');
var myModule = require(appRoot + '/lib/my-module.js');
แอปรากเส้นทางโมดูลใช้เทคนิคที่แตกต่างกันในการกำหนดเส้นทางรากของ app ที่คำนึงถึงการติดตั้งโมดูลทั่วโลก (ตัวอย่างเช่นถ้าแอปของคุณกำลังทำงานใน/var/www/แต่โมดูลที่ติดตั้งใน~/.nvm/v0.x.x/lib/node/ ) มันจะไม่ทำงาน 100% ของเวลา แต่มันจะทำงานในสถานการณ์ที่พบบ่อยที่สุด
ทำงานโดยไม่มีการกำหนดค่าในสถานการณ์ส่วนใหญ่ ยังมีวิธีอำนวยความสะดวกเพิ่มเติมที่ดีอีกด้วย ข้อเสียที่ใหญ่ที่สุดคือมันจะไม่ทำงานถ้า:
node_modulesไดเรกทอรีของแอพ(ตัวอย่างเช่นหากคุณติดตั้งไว้ทั่วโลก)คุณสามารถแก้ไขปัญหานี้ได้โดยการตั้งค่าAPP_ROOT_PATHตัวแปรสภาพแวดล้อมหรือโดยการโทรหา.setPath()โมดูล แต่ในกรณีนี้คุณน่าจะใช้งานglobalวิธีนี้ได้
หากคุณกำลังมองหาวิธีในการกำหนดรูทพา ธ ของแอปปัจจุบันหนึ่งในวิธีการแก้ปัญหาด้านบนมีแนวโน้มที่จะทำงานได้ดีที่สุดสำหรับคุณ ในทางกลับกันถ้าคุณกำลังพยายามที่จะแก้ปัญหาของการโหลดโมดูลแอปอย่างน่าเชื่อถือฉันขอแนะนำให้ดูในNODE_PATHตัวแปรสภาพแวดล้อม
ระบบ Modulesของ Node ค้นหาโมดูลในหลากหลายตำแหน่ง หนึ่งในสถานที่เหล่านี้เป็นที่ใดก็ตามที่process.env.NODE_PATHจุด หากคุณตั้งค่าตัวแปรสภาพแวดล้อมนี้คุณก็สามารถทำได้requireโมดูลด้วยตัวโหลดโมดูลมาตรฐานโดยไม่มีการเปลี่ยนแปลงอื่นใด
ตัวอย่างเช่นถ้าคุณตั้งค่าNODE_PATHเพื่อ/var/www/libที่ต่อไปนี้จะทำงานได้ดี:
require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js
วิธีที่ยอดเยี่ยมในการทำเช่นนี้คือใช้npm:
"scripts": {
"start": "NODE_PATH=. node app.js"
}
ตอนนี้คุณสามารถเริ่มแอพด้วยnpm startและคุณเป็นทองคำ ฉันรวมสิ่งนี้เข้ากับโมดูลการบังคับใช้โหนดของฉันซึ่งป้องกันการโหลดแอปโดยไม่NODE_PATHตั้งใจโดยไม่ได้ตั้งค่า สำหรับการควบคุมมากยิ่งขึ้นกว่าการบังคับใช้ตัวแปรสิ่งแวดล้อมเห็นcheckenv
หนึ่ง gotcha: NODE_PATH ต้องตั้งค่านอกแอพโหนด คุณไม่สามารถทำอะไรเช่นprocess.env.NODE_PATH = path.resolve(__dirname)เนื่องจากตัวโหลดโมดูลจะแคชรายการไดเรกทอรีที่จะค้นหาก่อนที่แอปของคุณจะทำงาน
[เพิ่ม 4/6/16]อีกโมดูลที่มีแนวโน้มมากที่ความพยายามที่จะแก้ปัญหานี้คือหยัก
require.main.filenameจะได้ผลเกือบตลอดเวลา แต่ไม่ตลอดเวลา
require.main.filenameดูเหมือนจะทำงานกับ pm2 ไม่ทราบเกี่ยวกับมอคค่า
path.parse(process.mainModule.filename).dir
__dirnameไม่ใช่ทั่วโลก เป็นแบบโลคัลกับโมดูลปัจจุบันดังนั้นแต่ละไฟล์มีค่าโลคัลของตนเองแตกต่างกัน
process.cwd()หากคุณต้องการไดเรกทอรีรากของกระบวนการทำงานคุณอาจไม่ต้องการที่จะใช้
หากคุณต้องการความสามารถในการคาดการณ์และความน่าเชื่อถือคุณอาจต้องทำให้มันเป็นความต้องการของแอปพลิเคชันของคุณที่ตั้งค่าตัวแปรสภาพแวดล้อม แอปของคุณค้นหาMY_APP_HOME(หรืออะไรก็ตาม) และหากมีและแอปพลิเคชันมีอยู่ในไดเรกทอรีนั้นแสดงว่าทั้งหมดนั้นดี หากไม่ได้กำหนดไว้หรือไดเรกทอรีไม่มีแอปพลิเคชันของคุณก็ควรออกโดยมีข้อผิดพลาดแจ้งให้ผู้ใช้สร้างตัวแปร สามารถตั้งค่าเป็นส่วนหนึ่งของกระบวนการติดตั้ง
process.env.MY_ENV_VARIABLEคุณสามารถอ่านตัวแปรสภาพแวดล้อมในโหนดกับสิ่งที่ต้องการ
bin/server.js cd bin && server.js(สมมติว่าไฟล์ js เหล่านี้ถูกทำเครื่องหมายว่าสามารถเรียกใช้งานได้)
process.cwd()ได้ผลเหมือนมีเสน่ห์สำหรับฉันแม้ว่าจะทำการทดสอบมอคค่า ขอบคุณ!
1- สร้างไฟล์ในโปรเจ็กต์รูทเรียกว่าsettings.js
2- ในไฟล์นี้เพิ่มรหัสนี้
module.exports = {
POST_MAX_SIZE : 40 , //MB
UPLOAD_MAX_FILE_SIZE: 40, //MB
PROJECT_DIR : __dirname
};
3- ภายใน node_modules สร้างชื่อโมดูลใหม่มัน "การตั้งค่า" และภายใน index.js โมดูลเขียนรหัสนี้:
module.exports = require("../../settings");
4- และทุกเวลาที่คุณต้องการไดเรกทอรีโครงการของคุณเพียงแค่ใช้
var settings = require("settings");
settings.PROJECT_DIR;
ด้วยวิธีนี้คุณจะมีไดเรกทอรีโครงการทั้งหมดที่เกี่ยวข้องกับไฟล์นี้;)
node_modulesมักจะถูกแยกออกจากการควบคุมเวอร์ชัน ดังนั้นหากคุณทำงานกับทีมหรือเคยต้องการโคลนที่เก็บของคุณคุณจะต้องหาวิธีแก้ปัญหาอื่นเพื่อให้ไฟล์การตั้งค่านั้นตรงกัน
วิธีที่ง่ายที่สุดในการรับรูทโกลบอล ( สมมติว่าคุณใช้ NPM เพื่อรันแอป node.js ของคุณ 'เริ่มต้น npm' เป็นต้น )
var appRoot = process.env.PWD;
หากคุณต้องการข้ามการตรวจสอบข้างต้น
สมมติว่าคุณต้องการตรวจสอบข้ามprocess.env.PWDกับการตั้งค่าของคุณ node.js แอป หากคุณต้องการให้การทดสอบรันไทม์บางอย่างเพื่อตรวจสอบความถูกต้องของprocess.env.PWDคุณสามารถตรวจสอบได้ด้วยรหัสนี้ (ที่ฉันเขียนซึ่งดูเหมือนว่าจะทำงานได้ดี) คุณสามารถตรวจสอบชื่อของโฟลเดอร์สุดท้ายใน appRoot ด้วย npm_package_name ในไฟล์ package.json ของคุณตัวอย่างเช่น:
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.');
}
คุณยังสามารถใช้โมดูล NPM นี้ได้require('app-root-path')ซึ่งทำงานได้ดีมากสำหรับวัตถุประสงค์นี้
PWDจะไม่ได้กำหนดและสิ่งนี้จะล้มเหลว
process.cwd()
process.cwd()เหมือนกันกับรูทโปรเจคเสมอ
ฉันพบว่ามันใช้งานได้อย่างสม่ำเสมอสำหรับฉันแม้ว่าจะมีการเรียกใช้แอปพลิเคชันจากโฟลเดอร์ย่อยเนื่องจากสามารถใช้เฟรมเวิร์กทดสอบบางอย่างเช่น Mocha:
process.mainModule.paths[0].split('node_modules')[0].slice(0, -1);
ทำไมมันถึงได้ผล
ที่รันไทม์โหนดสร้างรีจิสตรีของพา ธ เต็มของไฟล์ที่โหลดทั้งหมด โมดูลจะถูกโหลดขึ้นมาก่อนและอยู่ด้านบนสุดของรีจิสตรีนี้ โดยการเลือกองค์ประกอบแรกของรีจิสตรีและคืนค่าพา ธ ก่อนไดเรกทอรี 'node_modules' เราจะสามารถระบุรูทของแอปพลิเคชันได้
มันเป็นเพียงโค้ดหนึ่งบรรทัด แต่เพื่อความเรียบง่าย (สาเกของฉัน) ฉันได้บรรจุมันลงในโมดูล NPM:
https://www.npmjs.com/package/node-root.pddivine
สนุก!
process.mainModule deprectaed ตั้งแต่: v14.0.0 - ใช้require.main.paths[0].split('node_modules')[0].slice(0, -1);แทน
"root dirs" ทั้งหมดเหล่านี้ส่วนใหญ่จำเป็นต้องแก้ไขเส้นทางเสมือนบางส่วนไปยังเส้นทางกองจริงดังนั้นคุณควรจะดูที่path.resolve?
var path= require('path');
var filePath = path.resolve('our/virtual/path.ext');
ง่ายๆแค่เพิ่มบรรทัดนี้ลงในโมดูลของคุณในรูทปกติคือ app.js
global.__basedir = __dirname;
จากนั้น _basedir จะสามารถเข้าถึงโมดูลทั้งหมดของคุณได้
บางทีคุณอาจลองสำรวจไปข้างบน__filenameจนกว่าจะเจอ a package.jsonและตัดสินใจว่าเป็นไดเรกทอรีหลักที่ไฟล์ปัจจุบันของคุณเป็นของ
ที่จริงแล้วฉันพบว่าวิธีแก้ปัญหาเล็กน้อยอาจมีประสิทธิภาพมากที่สุด: คุณเพียงวางไฟล์ต่อไปนี้ที่ไดเรกทอรีรากของโครงการของคุณ: root-path.js ซึ่งมีรหัสต่อไปนี้:
import * as path from 'path'
const projectRootPath = path.resolve(__dirname)
export const rootPath = projectRootPath
เทคนิคที่ฉันพบว่ามีประโยชน์เมื่อใช้ express คือการเพิ่มสิ่งต่อไปนี้ใน app.js ก่อนที่เส้นทางอื่น ๆ ของคุณจะถูกตั้งค่า
// set rootPath
app.use(function(req, res, next) {
req.rootPath = __dirname;
next();
});
app.use('/myroute', myRoute);
ไม่จำเป็นต้องใช้ globals และคุณมีเส้นทางของไดเรกทอรีรากเป็นคุณสมบัติของวัตถุคำขอ
สิ่งนี้จะทำงานได้ถ้าแอพของคุณอยู่ในรูทของโปรเจคซึ่งโดยค่าเริ่มต้นแล้ว
เพิ่มที่นี้ไว้ที่จุดเริ่มต้นของไฟล์หลักของแอพ (เช่น app.js):
global.__basedir = __dirname;
ชุดนี้เป็นตัวแปรส่วนกลางที่จะเทียบเท่ากับ dir ฐานของแอปของคุณเสมอ ใช้มันเหมือนกับตัวแปรอื่น ๆ :
const yourModule = require(__basedir + '/path/to/module.js');
ง่าย ...
ฉันรู้ว่าอันนี้สายเกินไปแล้ว แต่เราสามารถดึง URL รูทได้ด้วยสองวิธี
วิธีที่ 1
var path = require('path');
path.dirname(require.main.filename);
วิธีที่ 2
var path = require('path');
path.dirname(process.mainModule.filename);
ลิงค์อ้างอิง: - https://gist.github.com/geekiam/e2e3e0325abd9023d3a3
INIT_CWD process.envนี่คือสิ่งที่ฉันกำลังทำงานกับในโครงการของฉันconst {INIT_CWD} = process.env; // process.env.INIT_CWD
const paths = require(`${INIT_CWD}/config/paths`);
โชคดี...
INIT_CWDแก้ไขdirectoryจากสิ่งที่npm-scriptexectued
หากคุณต้องการกำหนดรูทโปรเจ็กต์จากแอ็พพลิเคชันที่กำลังทำงาน node.js คุณก็สามารถทำได้เช่นกัน
process.mainModule.path
ที่ด้านบนของไฟล์หลักเพิ่ม:
mainDir = __dirname;
จากนั้นใช้ในไฟล์ใดก็ได้ที่คุณต้องการ:
console.log('mainDir ' + mainDir);
mainDirถูกกำหนดไว้ทั่วโลกหากคุณต้องการใช้ในไฟล์ปัจจุบันเท่านั้น - ใช้__dirnameแทนmain.js, ,index.jsgulpfile.jsฉันใช้สิ่งนี้
สำหรับโมดูลของฉันชื่อ mymodule
var BASE_DIR = __dirname.replace(/^(.*\/mymodule)(.*)$/, '$1')
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
npm install sexy-require --saveรวมrequire('sexy-require')หนึ่งครั้งที่ด้านบนสุดของไฟล์แอปพลิเคชันหลักของคุณ
require('sexy-require');
const routers = require('/routers');
const api = require('$api');
...ขั้นตอนเพิ่มเติม การกำหนดค่าพา ธ สามารถกำหนดไว้ใน.pathsไฟล์ในไดเรกทอรีรากของโครงการของคุณ
$db = /server/database
$api-v1 = /server/api/legacy
$api-v2 = /server/api/v2ขั้นตอนนี้จะลดทอนไดเรกทอรีไดเรกทอรีจนกว่าจะมีnode_modulesไดเรกทอรีซึ่งโดยปกติจะระบุรากโครงการของคุณ:
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))
}
นอกจากนี้ยังตรวจสอบให้แน่ใจว่าไม่มีnode_modulesในเส้นทางที่ส่งคืนเนื่องจากหมายความว่ามีอยู่ในการติดตั้งแพคเกจที่ซ้อนกัน
process.mainModuleจะเลิกตั้งแต่วี 14.0.0 เมื่อพูดถึงคำตอบโปรดใช้require.mainส่วนที่เหลือยังคงอยู่
process.mainModule.paths
.filter(p => !p.includes('node_modules'))
.shift()
รับพา ธ ทั้งหมดในโมดูลหลักและกรองออกด้วย "node_modules" จากนั้นรับพา ธ แรกของรายการพา ธ ที่เหลืออยู่ พฤติกรรมที่ไม่คาดคิดจะไม่เกิดข้อผิดพลาดเพียงundefinedอย่างเดียว
$ mochaทำงานได้ดีสำหรับฉันแม้เมื่อโทรคือ
สร้างฟังก์ชั่นใน 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'));
คุณสามารถเพิ่มเส้นทางไดเรกทอรีรากในตัวแปรแอพด่วนและรับเส้นทางนี้จากแอพ สำหรับการเพิ่มนี้app.set('rootDirectory', __dirname);ในไฟล์ index.js หรือ app.js ของคุณ และใช้req.app.get('rootDirectory')สำหรับรับเส้นทางไดเรกทอรีรากในรหัสของคุณ
คำถามเก่า ๆ ฉันรู้ แต่ก็ไม่มีคำถามที่พูดถึงการใช้ progress.argvแต่ไม่มีการกล่าวถึงคำถามกับการใช้งาน อาร์เรย์ argv มีชื่อพา ธ เต็มและชื่อไฟล์ (มีหรือไม่มีนามสกุล. js) ที่ใช้เป็นพารามิเตอร์ที่จะดำเนินการโดยโหนด เนื่องจากสิ่งนี้ยังสามารถมีธงคุณต้องกรองสิ่งนี้
นี่ไม่ใช่ตัวอย่างที่คุณสามารถใช้โดยตรง (เพราะใช้กรอบงานของตัวเอง) แต่ฉันคิดว่ามันทำให้คุณมีความคิดว่าจะทำอย่างไร ฉันยังใช้วิธีแคชเพื่อหลีกเลี่ยงการเรียกฟังก์ชั่นนี้เน้นระบบมากเกินไปโดยเฉพาะอย่างยิ่งเมื่อไม่ได้ระบุนามสกุล (และจำเป็นต้องมีการตรวจสอบไฟล์) เช่น:
node myfile
หรือ
node myfile.js
นั่นคือเหตุผลที่ฉันแคชดูรหัสด้านล่าง
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 || '';
}
};
การค้นหาเส้นทางรากของแอปอิเล็กตรอนอาจยุ่งยาก เนื่องจากเส้นทางหลักแตกต่างกันสำหรับกระบวนการหลักและโหมดแสดงภาพภายใต้เงื่อนไขต่าง ๆ เช่นการผลิตการพัฒนาและเงื่อนไขการแพคเกจ
ฉันได้เขียนแพ็กเกจอิเล็กตรอนรูทพา ธเพื่อจับภาพรูทพา ธ ของแอปอิเล็กตรอน
$ 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' });
สิ่งนี้จะทำ:
path.join(...process.argv[1].split(/\/|\\/).slice(0, -1))
มันใช้งานได้สำหรับฉัน
process.env.PWD
คำนำ
นี่เป็นคำถามที่เก่ามาก แต่ดูเหมือนว่าจะยังประสาทในปี 2020 เช่นในปี 2012 ฉันได้ตรวจสอบคำตอบอื่น ๆ ทั้งหมดและไม่สามารถหา tecnique (โปรดทราบว่านี่มีข้อ จำกัด แต่คนอื่น ๆ ทั้งหมดไม่ใช่ สามารถใช้ได้ในทุกสถานการณ์เช่นกัน)
กระบวนการลูก GIT +
หากคุณใช้ GIT เป็นระบบควบคุมเวอร์ชันของคุณปัญหาในการพิจารณารูทโปรเจ็กต์สามารถลดลงเป็น (ซึ่งฉันจะพิจารณารูทที่เหมาะสมของโปรเจค - หลังจากทั้งหมดคุณต้องการให้ VCS ของคุณมีขอบเขตการมองเห็นอย่างเต็มที่) :
เส้นทางที่เก็บข้อมูล retreive
เนื่องจากคุณต้องเรียกใช้คำสั่ง CLI เพื่อทำเช่นนั้นเราจะต้องวางกระบวนการลูก นอกจากนี้เนื่องจากรูทโปรเจ็กต์ไม่น่าจะมีการเปลี่ยนแปลงอย่างมากในช่วงกลางรันไทม์เราจึงสามารถใช้child_processAPI โมดูลโมดูลแบบซิงโครนัสเมื่อเริ่มต้น
ฉันพบว่าspawnSync()เหมาะสมที่สุดสำหรับงาน สำหรับคำสั่งจริงที่จะเรียกใช้git worktree(พร้อม--porcelainตัวเลือกเพื่อความสะดวกในการแยกวิเคราะห์) เป็นสิ่งที่เราต้องเรียกเส้นทางรากที่แน่นอน
ในตัวอย่างฉันเลือกที่จะคืนค่าอาร์เรย์ของเส้นทางเนื่องจากอาจมี worktree มากกว่าหนึ่ง (แม้ว่าพวกเขามีแนวโน้มที่จะมีเส้นทางทั่วไป) เพียงเพื่อให้แน่ใจ โปรดทราบว่าเมื่อเราใช้คำสั่ง CLI shellควรตั้งค่าตัวเลือกเป็นtrue (ความปลอดภัยไม่ควรเป็นปัญหาเนื่องจากไม่มีอินพุตที่ไม่น่าเชื่อถือ)
วิธีการเปรียบเทียบและทางเลือก
การเข้าใจว่าสถานการณ์ที่ VCS ไม่สามารถเข้าถึงได้ฉันได้รวมข้อผิดพลาดสองสามข้อหลังจากวิเคราะห์เอกสารและคำตอบอื่น ๆ เพื่อสรุปผลการแก้ปัญหาที่นำเสนอต้มลงไป (ไม่รวมโมดูลของบุคคลที่สาม & แพคเกจเฉพาะ):
| โซลูชัน | ข้อได้เปรียบ | ปัญหาหลัก
| ------------------------ | ----------------------- | -------------------------------- |
| `__filename` | | ชี้ไปที่ไฟล์โมดูล | สัมพันธ์กับโมดูล |
| `__dirname` | ชี้ไปที่โมดูล dir | เหมือนกับ "__filename" |
| `node_modules` tree walk | รับประกันเกือบราก | ต้นไม้สลับซับซ้อนหากซ้อนกัน |
| `path.resolve (". ")` | รูตถ้า CWD เป็นรูต | เหมือนกับ `process.cwd ()` |
| `process.argv [1]` | เหมือนกับ "__filename" | เหมือนกับ "__filename" |
| `process.env.INIT_CWD` | ชี้ไปที่ `npm run` dir | ต้องการ `npm '&& CLI launch |
| `process.env.PWD` | ชี้ไปที่ dir ปัจจุบัน สัมพันธ์กับ (คือ) เรียกใช้ dir |
| `process.cwd ()` | เหมือนกับ `env.PWD` | `process.chdir (เส้นทาง)` ที่ runtime |
| `require.main.filename` | | รูตถ้า `=== โมดูล '| ล้มเหลวในโมดูล `require`d |
จากตารางเปรียบเทียบด้านบนสากลมากที่สุดมีสองวิธี:
require.main.filenameเป็นวิธีง่าย ๆ ในการรูทถ้าrequire.main === moduleเจอnode_modulesTree Walk เสนอเมื่อเร็ว ๆ นี้ใช้สมมติฐานอื่น:ถ้าไดเรกทอรีของโมดูลมี
node_modulesdir อยู่ภายในก็น่าจะเป็นรูต
สำหรับแอพหลักมันจะรับรูทแอพและโมดูล - รูทโปรเจ็กต์
ทางเลือก 1. เดินต้นไม้
การนำไปใช้ของฉันใช้วิธีการที่หละหลวมมากขึ้นโดยการหยุดเมื่อพบไดเรกทอรีเป้าหมายสำหรับโมดูลที่กำหนดรูทคือโปรเจ็กต์รูท หนึ่งสามารถโยงสายหรือขยายเพื่อกำหนดความลึกของการค้นหา:
/**
* @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));
};
ทางเลือกที่ 2 โมดูลหลัก
การใช้งานครั้งที่สองนั้นไม่สำคัญ
/**
* @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] :
[];
};
การดำเนินงาน
ฉันขอแนะนำให้ใช้ tree walker เป็นทางเลือกเพราะมันมีความหลากหลายมากกว่า:
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);
};
ข้อเสีย
สิ่งที่ชัดเจนที่สุดคือการติดตั้ง GIT และเริ่มต้นซึ่งอาจไม่พึงประสงค์ / ไม่น่าเชื่อ (หมายเหตุด้าน: การติดตั้ง GIT บนเซิร์ฟเวอร์ที่ใช้งานจริงไม่ใช่เรื่องแปลกและไม่ปลอดภัยแม้ว่า) สามารถไกล่เกลี่ยได้โดยการล้มตามที่อธิบายไว้ข้างต้น
หมายเหตุ
export ฟังก์ชั่นที่จะทำให้มันเป็นโมดูลอ้างอิง
git worktree การอ้างอิงspawnSync การอ้างอิงrequire.main การอ้างอิงpath.dirname() การอ้างอิงง่าย: require('path').resolve('./')
ลอง path._makeLong('some_filename_on_root.js');
ตัวอย่าง:
cons path = require('path');
console.log(path._makeLong('some_filename_on_root.js');
สิ่งนั้นจะส่งคืนพา ธ แบบเต็มจากรูทของแอปพลิเคชันโหนดของคุณ (ตำแหน่งเดียวกันของ package.json)
เพียงใช้:
path.resolve("./") ... output is your project root directory
path.dirname(process.mainModule.filename);