mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-02 08:58:32 +00:00
219 lines
5.7 KiB
JavaScript
219 lines
5.7 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const chokidar = require('chokidar');
|
|
const { parseDotEnv } = require('@usebruno/filestore');
|
|
const { setDotEnvVars, clearDotEnvVars, setWorkspaceDotEnvVars, clearWorkspaceDotEnvVars } = require('../store/process-env');
|
|
|
|
const isDotEnvFile = (filename) => {
|
|
return filename === '.env' || filename.startsWith('.env.');
|
|
};
|
|
|
|
const parseVariablesToArray = (envObject) => {
|
|
return Object.entries(envObject).map(([name, value]) => ({
|
|
name,
|
|
value,
|
|
enabled: true,
|
|
secret: false
|
|
}));
|
|
};
|
|
|
|
const DEFAULT_WATCHER_OPTIONS = {
|
|
ignoreInitial: false,
|
|
persistent: true,
|
|
ignorePermissionErrors: true,
|
|
depth: 0
|
|
};
|
|
|
|
const createFileHandler = (win, options) => (pathname) => {
|
|
const { type, uid, uidKey, pathKey, basePath, setEnvVars } = options;
|
|
const filename = path.basename(pathname);
|
|
|
|
if (!isDotEnvFile(filename)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const content = fs.readFileSync(pathname, 'utf8');
|
|
const jsonData = parseDotEnv(content);
|
|
|
|
if (filename === '.env') {
|
|
setEnvVars(jsonData);
|
|
}
|
|
|
|
const variables = parseVariablesToArray(jsonData);
|
|
|
|
if (!win.isDestroyed()) {
|
|
const payload = {
|
|
type,
|
|
[uidKey]: uid,
|
|
filename,
|
|
variables,
|
|
exists: true,
|
|
processEnvVariables: { ...jsonData }
|
|
};
|
|
if (pathKey) {
|
|
payload[pathKey] = basePath;
|
|
}
|
|
win.webContents.send('main:dotenv-file-update', payload);
|
|
}
|
|
} catch (err) {
|
|
console.error(`Error processing dotenv file ${pathname}:`, err);
|
|
}
|
|
};
|
|
|
|
const createUnlinkHandler = (win, options) => (pathname) => {
|
|
const { type, uid, uidKey, pathKey, basePath, clearEnvVars } = options;
|
|
const filename = path.basename(pathname);
|
|
|
|
if (!isDotEnvFile(filename)) {
|
|
return;
|
|
}
|
|
|
|
if (filename === '.env') {
|
|
clearEnvVars();
|
|
}
|
|
|
|
if (!win.isDestroyed()) {
|
|
const payload = {
|
|
type,
|
|
[uidKey]: uid,
|
|
filename,
|
|
variables: [],
|
|
exists: false,
|
|
processEnvVariables: {}
|
|
};
|
|
if (pathKey) {
|
|
payload[pathKey] = basePath;
|
|
}
|
|
win.webContents.send('main:dotenv-file-update', payload);
|
|
}
|
|
};
|
|
|
|
class DotEnvWatcher {
|
|
constructor() {
|
|
this.collectionWatchers = new Map();
|
|
this.workspaceWatchers = new Map();
|
|
}
|
|
|
|
addCollectionWatcher(win, collectionPath, collectionUid) {
|
|
if (this.collectionWatchers.has(collectionPath)) {
|
|
this.collectionWatchers.get(collectionPath).close();
|
|
}
|
|
|
|
const watcher = chokidar.watch(collectionPath, {
|
|
...DEFAULT_WATCHER_OPTIONS,
|
|
disableGlobbing: true,
|
|
awaitWriteFinish: {
|
|
stabilityThreshold: 80,
|
|
pollInterval: 100
|
|
}
|
|
});
|
|
|
|
const handlerOptions = {
|
|
type: 'collection',
|
|
uid: collectionUid,
|
|
uidKey: 'collectionUid',
|
|
basePath: collectionPath,
|
|
setEnvVars: (data) => setDotEnvVars(collectionUid, data),
|
|
clearEnvVars: () => clearDotEnvVars(collectionUid)
|
|
};
|
|
|
|
const handleFile = createFileHandler(win, handlerOptions);
|
|
const handleUnlink = createUnlinkHandler(win, handlerOptions);
|
|
|
|
watcher.on('add', handleFile);
|
|
watcher.on('change', handleFile);
|
|
watcher.on('unlink', handleUnlink);
|
|
watcher.on('error', (err) => {
|
|
console.error(`Collection watcher error for ${collectionPath}:`, err);
|
|
});
|
|
|
|
this.collectionWatchers.set(collectionPath, watcher);
|
|
}
|
|
|
|
removeCollectionWatcher(collectionPath, collectionUid) {
|
|
if (this.collectionWatchers.has(collectionPath)) {
|
|
this.collectionWatchers.get(collectionPath).close();
|
|
this.collectionWatchers.delete(collectionPath);
|
|
}
|
|
if (collectionUid) {
|
|
clearDotEnvVars(collectionUid);
|
|
}
|
|
}
|
|
|
|
hasCollectionWatcher(collectionPath) {
|
|
return this.collectionWatchers.has(collectionPath);
|
|
}
|
|
|
|
addWorkspaceWatcher(win, workspacePath, workspaceUid) {
|
|
if (this.workspaceWatchers.has(workspacePath)) {
|
|
this.workspaceWatchers.get(workspacePath).close();
|
|
}
|
|
|
|
const watcher = chokidar.watch(workspacePath, {
|
|
...DEFAULT_WATCHER_OPTIONS,
|
|
disableGlobbing: true,
|
|
awaitWriteFinish: {
|
|
stabilityThreshold: 80,
|
|
pollInterval: 250
|
|
}
|
|
});
|
|
|
|
const handlerOptions = {
|
|
type: 'workspace',
|
|
uid: workspaceUid,
|
|
uidKey: 'workspaceUid',
|
|
pathKey: 'workspacePath',
|
|
basePath: workspacePath,
|
|
setEnvVars: (data) => setWorkspaceDotEnvVars(workspacePath, data),
|
|
clearEnvVars: () => clearWorkspaceDotEnvVars(workspacePath)
|
|
};
|
|
|
|
const handleFile = createFileHandler(win, handlerOptions);
|
|
const handleUnlink = createUnlinkHandler(win, handlerOptions);
|
|
|
|
watcher.on('add', handleFile);
|
|
watcher.on('change', handleFile);
|
|
watcher.on('unlink', handleUnlink);
|
|
watcher.on('error', (err) => {
|
|
console.error(`Workspace watcher error for ${workspacePath}:`, err);
|
|
});
|
|
|
|
this.workspaceWatchers.set(workspacePath, watcher);
|
|
}
|
|
|
|
removeWorkspaceWatcher(workspacePath) {
|
|
if (this.workspaceWatchers.has(workspacePath)) {
|
|
this.workspaceWatchers.get(workspacePath).close();
|
|
this.workspaceWatchers.delete(workspacePath);
|
|
}
|
|
clearWorkspaceDotEnvVars(workspacePath);
|
|
}
|
|
|
|
hasWorkspaceWatcher(workspacePath) {
|
|
return this.workspaceWatchers.has(workspacePath);
|
|
}
|
|
|
|
closeAll() {
|
|
const pending = [];
|
|
const collect = (watcher) => {
|
|
try {
|
|
const result = watcher?.close();
|
|
if (result && typeof result.then === 'function') pending.push(result);
|
|
} catch (err) {}
|
|
};
|
|
|
|
for (const [path, watcher] of this.collectionWatchers) collect(watcher);
|
|
this.collectionWatchers.clear();
|
|
|
|
for (const [path, watcher] of this.workspaceWatchers) collect(watcher);
|
|
this.workspaceWatchers.clear();
|
|
|
|
return Promise.allSettled(pending);
|
|
}
|
|
}
|
|
|
|
const dotEnvWatcher = new DotEnvWatcher();
|
|
|
|
module.exports = dotEnvWatcher;
|