Skip to content

Instantly share code, notes, and snippets.

@nhphong
Last active July 12, 2024 00:34
Show Gist options
  • Save nhphong/97d6c64f367d395069dfa83b4da32051 to your computer and use it in GitHub Desktop.
Save nhphong/97d6c64f367d395069dfa83b4da32051 to your computer and use it in GitHub Desktop.
Handle .gitignore file
const readGitignoreFile = async (directoryHandle) => {
try {
const fileHandle = await directoryHandle.getFileHandle('.gitignore');
const file = await fileHandle.getFile();
const content = await file.text();
return content.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
} catch (error) {
return [];
}
};
const DEFAULT_IGNORE_PATTERNS = new Set([
// Version control
'.git',
// Dependencies
'node_modules',
// Build outputs
'dist', 'build',
// Test coverage and reports
'coverage', 'vitest-coverage', 'cypress-coverage', '.nyc_output', 'reports',
'cypress/videos', 'cypress/screenshots',
// Logs and temporary files
'*.log', '.DS_Store', '*.swp', '*.local',
// Environment and configuration files
'*.env', 'cypress.env.json',
// IDE and editor files
'.vscode', '.idea', '*.suo', '*.ntvs*', '*.njsproj', '*.sln', '*.sw?',
// Additional patterns
'.jwt'
]);
const shouldIgnore = (filePath, ignorePatterns) => {
if (DEFAULT_IGNORE_PATTERNS.has(filePath)) {
return true;
}
let shouldIgnore = false;
for (const pattern of ignorePatterns) {
if (pattern.startsWith('!')) {
if (matchesPattern(filePath, pattern.slice(1))) {
shouldIgnore = false;
break;
}
} else if (matchesPattern(filePath, pattern)) {
shouldIgnore = true;
break;
}
}
return shouldIgnore;
};
const matchesPattern = (filePath, pattern) => {
pattern = pattern.replace(/^\//, '');
filePath = filePath.replace(/^\//, '');
const escapeRegExp = (string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
if (filePath === pattern) return true;
if (pattern.endsWith('/') && (filePath.startsWith(pattern) || filePath + '/' === pattern)) {
return true;
}
if (pattern.includes('*')) {
const regex = new RegExp('^' + pattern.split('*').map(escapeRegExp).join('.*') + '$');
return regex.test(filePath);
}
const patternParts = pattern.split('/');
const pathParts = filePath.split('/');
for (let i = 0; i <= pathParts.length - patternParts.length; i++) {
if (patternParts.every((part, index) => part === pathParts[i + index])) {
return true;
}
}
return false;
};
const getIgnorePatternsForPath = async (rootHandle, currentPath) => {
const pathParts = currentPath.split('/');
let currentHandle = rootHandle;
let allPatterns = [...DEFAULT_IGNORE_PATTERNS];
for (let i = 0; i < pathParts.length; i++) {
if (pathParts[i] === '') continue;
const patterns = await readGitignoreFile(currentHandle);
allPatterns = [...allPatterns, ...patterns];
if (i < pathParts.length - 1) {
try {
currentHandle = await currentHandle.getDirectoryHandle(pathParts[i]);
} catch (error) {
break;
}
}
}
return allPatterns;
};
const getFilesAndFolders = async (rootHandle, dirHandle, currentPath = '') => {
const entries = [];
const ignorePatterns = await getIgnorePatternsForPath(rootHandle, currentPath);
try {
for await (const entry of dirHandle.values()) {
const newPath = currentPath ? `${currentPath}/${entry.name}` : entry.name;
if (shouldIgnore(newPath, ignorePatterns)) continue;
if (entry.kind === "file") {
entries.push({ name: entry.name, path: newPath, kind: "file", handle: entry });
} else if (entry.kind === "directory") {
const subEntries = await getFilesAndFolders(rootHandle, entry, newPath);
if (subEntries.length > 0) {
entries.push({ name: entry.name, path: newPath, kind: "directory", children: subEntries, handle: entry });
}
}
}
} catch (error) {
// Handle error silently or add minimal error logging if needed
}
return entries;
};
// Export the functions and constants
window.GitignoreHandler = {
readGitignoreFile,
DEFAULT_IGNORE_PATTERNS,
shouldIgnore,
matchesPattern,
getIgnorePatternsForPath,
getFilesAndFolders
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment