Enhance file watching by ensuring 'node_modules' and '.git' are always ignored (#6391)

* Enhance file watching by ensuring 'node_modules' and '.git' are always ignored

* fix: tests

* rm: dialog assignments

* fix:  duplication
This commit is contained in:
sanish chirayath
2025-12-12 19:04:52 +05:30
committed by GitHub
parent 2327b21c85
commit 2f5537c8db
4 changed files with 214 additions and 9 deletions

View File

@@ -85,7 +85,12 @@ class ApiSpecWatcher {
this.watcherWorkspaces[watchPath] = workspacePath;
}
const ignores = brunoConfig?.ignore || [];
// Always ignore node_modules and .git, regardless of user config
// This prevents infinite loops with symlinked directories (e.g., npm workspaces)
const defaultIgnores = ['node_modules', '.git'];
const userIgnores = brunoConfig?.ignore || [];
const ignores = [...new Set([...defaultIgnores, ...userIgnores])];
const self = this;
setTimeout(() => {
const watcher = chokidar.watch(watchPath, {
@@ -95,6 +100,12 @@ class ApiSpecWatcher {
const normalizedPath = filepath.replace(/\\/g, '/');
const relativePath = path.relative(watchPath, normalizedPath);
// Check if any path segment matches a default ignore pattern (handles symlinks)
const pathSegments = relativePath.split(path.sep);
if (pathSegments.some((segment) => defaultIgnores.includes(segment))) {
return true;
}
return ignores.some((ignorePattern) => {
const normalizedIgnorePattern = ignorePattern.replace(/\\/g, '/');
return relativePath === normalizedIgnorePattern || relativePath.startsWith(normalizedIgnorePattern);

View File

@@ -745,7 +745,12 @@ class CollectionWatcher {
this.startCollectionDiscovery(win, collectionUid);
const ignores = brunoConfig?.ignore || [];
// Always ignore node_modules and .git, regardless of user config
// This prevents infinite loops with symlinked directories (e.g., npm workspaces)
const defaultIgnores = ['node_modules', '.git'];
const userIgnores = brunoConfig?.ignore || [];
const ignores = [...new Set([...defaultIgnores, ...userIgnores])];
setTimeout(() => {
const watcher = chokidar.watch(watchPath, {
ignoreInitial: false,
@@ -754,6 +759,12 @@ class CollectionWatcher {
const normalizedPath = normalizeAndResolvePath(filepath);
const relativePath = path.relative(watchPath, normalizedPath);
// Check if any path segment matches a default ignore pattern (handles symlinks)
const pathSegments = relativePath.split(path.sep);
if (pathSegments.some((segment) => defaultIgnores.includes(segment))) {
return true;
}
return ignores.some((ignorePattern) => {
return relativePath === ignorePattern || relativePath.startsWith(ignorePattern);
});

View File

@@ -103,13 +103,11 @@ const openCollection = async (win, watcher, collectionPath, options = {}) => {
let brunoConfig = await getCollectionConfigFile(collectionPath);
const uid = generateUidBasedOnHash(collectionPath);
if (!brunoConfig.ignore || brunoConfig.ignore.length === 0) {
// 5 Feb 2024:
// bruno.json now supports an "ignore" field to specify which folders to ignore
// if the ignore field is not present, we default to ignoring node_modules and .git
// this is to maintain backwards compatibility with older collections
brunoConfig.ignore = ['node_modules', '.git'];
}
// Always ensure node_modules and .git are ignored, regardless of user config
// This prevents infinite loops with symlinked directories (e.g., npm workspaces)
const defaultIgnores = ['node_modules', '.git'];
const userIgnores = brunoConfig.ignore || [];
brunoConfig.ignore = [...new Set([...defaultIgnores, ...userIgnores])];
// Transform the config to add existence checks for protobuf files and import paths
brunoConfig = await transformBrunoConfigAfterRead(brunoConfig, collectionPath);

View File

@@ -0,0 +1,185 @@
import { test, expect } from '../../../playwright';
import * as path from 'path';
import * as fs from 'fs';
import { closeAllCollections, openCollectionAndAcceptSandbox } from '../../utils/page';
import { buildCommonLocators } from '../../utils/page/locators';
test.describe('Default ignores for node_modules and .git', () => {
test.afterEach(async ({ page }) => {
await closeAllCollections(page);
});
test('Should always ignore node_modules even when user has custom ignore config', async ({
page,
electronApp,
createTmpDir
}) => {
const locators = buildCommonLocators(page);
const collectionDir = await createTmpDir('node-modules-ignore-test');
// Create bruno.json with custom ignore that doesn't include node_modules
const brunoConfig = {
version: '1',
name: 'Node Modules Ignore Test',
type: 'collection',
ignore: ['custom-folder', 'another-folder'] // Explicitly NOT including node_modules
};
fs.writeFileSync(path.join(collectionDir, 'bruno.json'), JSON.stringify(brunoConfig, null, 2));
// Create node_modules directory with .bru files inside
const nodeModulesDir = path.join(collectionDir, 'node_modules');
fs.mkdirSync(nodeModulesDir);
fs.mkdirSync(path.join(nodeModulesDir, 'some-package'));
// Create a .bru file inside node_modules (should be ignored)
fs.writeFileSync(
path.join(nodeModulesDir, 'some-package', 'fake-request.bru'),
`meta {
name: Fake Request In Node Modules
type: http
seq: 1
}
get {
url: https://fake.com
body: none
auth: none
}
`
);
// Create a real request at the collection root
fs.writeFileSync(
path.join(collectionDir, 'real-request.bru'),
`meta {
name: Real Request
type: http
seq: 1
}
get {
url: https://real.com
body: none
auth: none
}
`
);
// Mock the electron dialog
await electronApp.evaluate(
({ dialog }, { collectionDir }) => {
dialog.showOpenDialog = async () => ({
canceled: false,
filePaths: [collectionDir]
});
},
{ collectionDir }
);
// Open the collection
await locators.plusMenu.button().click();
await locators.dropdown.tippyItem('Open collection').click();
// Wait for collection to load
await expect(locators.sidebar.collection('Node Modules Ignore Test')).toBeVisible({ timeout: 30000 });
// Accept the sandbox mode
await openCollectionAndAcceptSandbox(page, 'Node Modules Ignore Test', 'safe');
// Verify only the real request is visible
await expect(locators.sidebar.request('Real Request')).toBeVisible({ timeout: 10000 });
// The fake request inside node_modules should NOT be visible
await expect(locators.sidebar.request('Fake Request In Node Modules')).not.toBeVisible();
// node_modules folder should not appear in the sidebar
await expect(locators.sidebar.folder('node_modules')).not.toBeVisible();
});
test('Should always ignore .git even when user has custom ignore config', async ({
page,
electronApp,
createTmpDir
}) => {
const locators = buildCommonLocators(page);
const collectionDir = await createTmpDir('git-ignore-test');
// Create bruno.json with custom ignore that doesn't include .git
const brunoConfig = {
version: '1',
name: 'Git Ignore Test',
type: 'collection',
ignore: ['custom-folder'] // Explicitly NOT including .git
};
fs.writeFileSync(path.join(collectionDir, 'bruno.json'), JSON.stringify(brunoConfig, null, 2));
// Create .git directory with .bru files inside
const gitDir = path.join(collectionDir, '.git');
fs.mkdirSync(gitDir);
fs.mkdirSync(path.join(gitDir, 'hooks'));
// Create a .bru file inside .git (should be ignored)
fs.writeFileSync(
path.join(gitDir, 'hooks', 'fake-git-request.bru'),
`meta {
name: Fake Request In Git
type: http
seq: 1
}
get {
url: https://fake-git.com
body: none
auth: none
}
`
);
// Create a real request at the collection root
fs.writeFileSync(
path.join(collectionDir, 'real-request.bru'),
`meta {
name: Real Git Request
type: http
seq: 1
}
get {
url: https://real.com
body: none
auth: none
}
`
);
// Mock the electron dialog
await electronApp.evaluate(
({ dialog }, { collectionDir }) => {
dialog.showOpenDialog = async () => ({
canceled: false,
filePaths: [collectionDir]
});
},
{ collectionDir }
);
// Open the collection
await locators.plusMenu.button().click();
await locators.dropdown.tippyItem('Open collection').click();
// Wait for collection to load
await expect(locators.sidebar.collection('Git Ignore Test')).toBeVisible({ timeout: 30000 });
// Accept the sandbox mode
await openCollectionAndAcceptSandbox(page, 'Git Ignore Test', 'safe');
// Verify only the real request is visible
await expect(locators.sidebar.request('Real Git Request')).toBeVisible({ timeout: 10000 });
// The fake request inside .git should NOT be visible
await expect(locators.sidebar.request('Fake Request In Git')).not.toBeVisible();
// .git folder should not appear in the sidebar
await expect(locators.sidebar.folder('.git')).not.toBeVisible();
});
});