Skip to content

Commit f1f5276

Browse files
committed
counting files js
1 parent 4590f7c commit f1f5276

File tree

3 files changed

+344
-6
lines changed

3 files changed

+344
-6
lines changed

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"Bash(mkdir:*)",
77
"Bash(mv:*)",
88
"Bash(rmdir:*)",
9-
"Bash(curl:*)"
9+
"Bash(curl:*)",
10+
"Bash(node:*)"
1011
],
1112
"deny": []
1213
}

count-files.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
6+
// Folders to exclude from counting
7+
const EXCLUDED_FOLDERS = new Set([
8+
'.github',
9+
'.git',
10+
'node_modules',
11+
'.vscode',
12+
'.idea',
13+
'assets'
14+
]);
15+
16+
// Root files to exclude
17+
const EXCLUDED_ROOT_FILES = new Set([
18+
'README.md',
19+
'CONTRIBUTING.md',
20+
'CLAUDE.md',
21+
'PAGES.md',
22+
'LICENSE',
23+
'.gitignore',
24+
'package.json',
25+
'package-lock.json',
26+
'_config.yml',
27+
'sitemap.xml',
28+
'index.html',
29+
'core-apis.html',
30+
'server-side-components.html',
31+
'client-side-components.html',
32+
'modern-development.html',
33+
'integration.html',
34+
'specialized-areas.html',
35+
'count-files.js'
36+
]);
37+
38+
// File extensions to count
39+
const COUNTED_EXTENSIONS = new Set([
40+
'.js', '.ts', '.json', '.html', '.css', '.py', '.java', '.c', '.cpp',
41+
'.cs', '.php', '.rb', '.go', '.rs', '.swift', '.kt', '.md', '.txt',
42+
'.xml', '.sql', '.sh', '.bat', '.ps1', '.yml', '.yaml'
43+
]);
44+
45+
function shouldCountFile(fileName, isRoot = false) {
46+
// Exclude root-level config files
47+
if (isRoot && EXCLUDED_ROOT_FILES.has(fileName)) {
48+
return false;
49+
}
50+
51+
// Check if file has a counted extension
52+
const ext = path.extname(fileName).toLowerCase();
53+
return COUNTED_EXTENSIONS.has(ext);
54+
}
55+
56+
function countFilesRecursively(dirPath, isRoot = true) {
57+
let count = 0;
58+
let folderCounts = {};
59+
60+
try {
61+
const items = fs.readdirSync(dirPath);
62+
63+
for (const item of items) {
64+
const itemPath = path.join(dirPath, item);
65+
const stat = fs.statSync(itemPath);
66+
67+
if (stat.isDirectory()) {
68+
// Skip excluded folders
69+
if (EXCLUDED_FOLDERS.has(item)) {
70+
console.log(`Excluded folder: ${item}`);
71+
continue;
72+
}
73+
74+
// Recursively count files in subdirectory
75+
const subCount = countFilesRecursively(itemPath, false);
76+
count += subCount;
77+
folderCounts[item] = subCount;
78+
79+
if (isRoot) {
80+
console.log(`${item}: ${subCount} files`);
81+
}
82+
} else if (stat.isFile()) {
83+
// Count relevant files
84+
if (shouldCountFile(item, isRoot)) {
85+
count++;
86+
if (isRoot) {
87+
console.log(`Root file counted: ${item}`);
88+
}
89+
}
90+
}
91+
}
92+
} catch (error) {
93+
console.error(`Error reading directory ${dirPath}:`, error.message);
94+
}
95+
96+
return count;
97+
}
98+
99+
// Count files starting from current directory
100+
console.log('Counting files in repository...');
101+
console.log('='.repeat(50));
102+
103+
const totalFiles = countFilesRecursively('.');
104+
105+
// Round to nearest 100 and add + for marketing display
106+
const roundedFiles = Math.floor(totalFiles / 100) * 100;
107+
const displayCount = `${roundedFiles}+`;
108+
109+
console.log('='.repeat(50));
110+
console.log(`Total files counted: ${totalFiles}`);
111+
console.log(`Rounded display count: ${displayCount}`);
112+
113+
// Update the index.html file with the rounded count
114+
const indexPath = './index.html';
115+
if (fs.existsSync(indexPath)) {
116+
try {
117+
let indexContent = fs.readFileSync(indexPath, 'utf8');
118+
119+
// Replace the estimated number with the rounded count
120+
const oldPattern = /totalFiles = \d+;.*\/\/ Actual count from local files/g;
121+
const newLine = `totalFiles = "${displayCount}"; // Rounded count from ${totalFiles} local files`;
122+
123+
// Also handle the old pattern if it exists
124+
const oldPattern2 = /totalFiles = \d+;.*\/\/ Realistic estimate/g;
125+
126+
indexContent = indexContent.replace(oldPattern, newLine);
127+
indexContent = indexContent.replace(oldPattern2, newLine);
128+
129+
// Update the textContent assignment to handle string instead of number
130+
indexContent = indexContent.replace(
131+
/snippetsElement\.textContent = totalFiles\.toLocaleString\(\);/g,
132+
'snippetsElement.textContent = totalFiles;'
133+
);
134+
135+
fs.writeFileSync(indexPath, indexContent);
136+
console.log(`Updated index.html with rounded count: ${displayCount} (from actual ${totalFiles})`);
137+
} catch (error) {
138+
console.error('Error updating index.html:', error.message);
139+
}
140+
} else {
141+
console.log('index.html not found - could not update automatically');
142+
}

index.html

Lines changed: 200 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,21 @@
371371
animation: spin 1s ease-in-out infinite;
372372
}
373373

374+
.loading-text {
375+
font-size: 1.5rem;
376+
opacity: 0.8;
377+
animation: pulse 2s ease-in-out infinite;
378+
}
379+
374380
@keyframes spin {
375381
to { transform: rotate(360deg); }
376382
}
377383

384+
@keyframes pulse {
385+
0%, 100% { opacity: 0.8; }
386+
50% { opacity: 0.4; }
387+
}
388+
378389
/* Utilities */
379390
.sr-only {
380391
position: absolute;
@@ -691,11 +702,195 @@ <h3>Resources</h3>
691702
});
692703
});
693704

694-
// Dynamic stats loading (placeholder)
695-
function updateStats() {
696-
// This could fetch real data from GitHub API
697-
document.getElementById('total-snippets').textContent = '950+';
698-
document.getElementById('total-contributors').textContent = 'Open Source';
705+
// GitHub API configuration
706+
const GITHUB_API_BASE = 'https://api.github.com';
707+
const REPO = 'ServiceNowDevProgram/code-snippets';
708+
const BRANCH = 'main';
709+
710+
// Folders to exclude from counting
711+
const EXCLUDED_FOLDERS = [
712+
'.github',
713+
'.git',
714+
'node_modules',
715+
'.vscode',
716+
'.idea',
717+
'assets'
718+
];
719+
720+
// Root files to exclude (these are typically config/documentation files)
721+
const EXCLUDED_ROOT_FILES = [
722+
'README.md',
723+
'CONTRIBUTING.md',
724+
'CLAUDE.md',
725+
'PAGES.md',
726+
'LICENSE',
727+
'.gitignore',
728+
'package.json',
729+
'package-lock.json',
730+
'_config.yml',
731+
'sitemap.xml',
732+
'index.html',
733+
'core-apis.html',
734+
'server-side-components.html',
735+
'client-side-components.html',
736+
'modern-development.html',
737+
'integration.html',
738+
'specialized-areas.html'
739+
];
740+
741+
// Function to fetch directory contents from GitHub API
742+
async function fetchGitHubDirectory(path) {
743+
try {
744+
const encodedPath = path ? path.split('/').map(encodeURIComponent).join('/') : '';
745+
const url = path ?
746+
`${GITHUB_API_BASE}/repos/${REPO}/contents/${encodedPath}?ref=${BRANCH}` :
747+
`${GITHUB_API_BASE}/repos/${REPO}/contents?ref=${BRANCH}`;
748+
749+
const response = await fetch(url);
750+
if (!response.ok) {
751+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
752+
}
753+
return await response.json();
754+
} catch (error) {
755+
console.error('Error fetching directory:', path, error);
756+
return [];
757+
}
758+
}
759+
760+
// Check if a folder should be excluded
761+
function shouldExcludeFolder(folderName, isRoot = false) {
762+
return EXCLUDED_FOLDERS.includes(folderName);
763+
}
764+
765+
// Check if a file should be counted
766+
function shouldCountFile(fileName, isRoot = false) {
767+
// Exclude root-level config files
768+
if (isRoot && EXCLUDED_ROOT_FILES.includes(fileName)) {
769+
return false;
770+
}
771+
772+
// Count all code and content files
773+
return fileName.match(/\.(js|ts|json|html|css|py|java|c|cpp|cs|php|rb|go|rs|swift|kt|md|txt|xml|sql|sh|bat|ps1|yml|yaml)$/i);
774+
}
775+
776+
// Recursively count all relevant files
777+
async function countAllFiles(path = '', isRoot = true) {
778+
const contents = await fetchGitHubDirectory(path);
779+
let count = 0;
780+
781+
for (const item of contents) {
782+
if (item.type === 'file') {
783+
if (shouldCountFile(item.name, isRoot)) {
784+
count++;
785+
if (isRoot) {
786+
console.log(`Root file counted: ${item.name}`);
787+
}
788+
}
789+
} else if (item.type === 'dir') {
790+
if (!shouldExcludeFolder(item.name, isRoot)) {
791+
const subPath = path ? `${path}/${item.name}` : item.name;
792+
const subCount = await countAllFiles(subPath, false);
793+
count += subCount;
794+
console.log(`${item.name}: ${subCount} files`);
795+
} else {
796+
console.log(`Excluded folder: ${item.name}`);
797+
}
798+
}
799+
}
800+
801+
return count;
802+
}
803+
804+
// Fetch contributor count from GitHub API
805+
async function getContributorCount() {
806+
try {
807+
const response = await fetch(`${GITHUB_API_BASE}/repos/${REPO}/contributors`);
808+
809+
if (!response.ok) {
810+
throw new Error(`Contributors API failed: ${response.status}`);
811+
}
812+
813+
const contributors = await response.json();
814+
return contributors.length;
815+
816+
} catch (error) {
817+
console.error('Error fetching contributors:', error);
818+
return null;
819+
}
820+
}
821+
822+
// Use GitHub's search API to count files more efficiently
823+
async function countFilesWithSearch() {
824+
try {
825+
// Search for files in the repo excluding common config/system files
826+
const searchQuery = `repo:${REPO} -path:.github -path:assets -filename:README.md -filename:CONTRIBUTING.md -filename:CLAUDE.md -filename:PAGES.md -filename:LICENSE -filename:.gitignore -filename:package.json -filename:_config.yml -filename:sitemap.xml -filename:index.html -filename:core-apis.html -filename:server-side-components.html -filename:client-side-components.html -filename:modern-development.html -filename:integration.html -filename:specialized-areas.html`;
827+
828+
const response = await fetch(`https://api.github.com/search/code?q=${encodeURIComponent(searchQuery)}&per_page=1`);
829+
830+
if (!response.ok) {
831+
throw new Error(`Search API failed: ${response.status}`);
832+
}
833+
834+
const data = await response.json();
835+
return data.total_count;
836+
837+
} catch (error) {
838+
console.error('Search API failed, falling back to manual count:', error);
839+
return await countAllFiles();
840+
}
841+
}
842+
843+
// Dynamic stats loading with realistic numbers
844+
async function updateStats() {
845+
const snippetsElement = document.getElementById('total-snippets');
846+
const contributorsElement = document.getElementById('total-contributors');
847+
848+
// Show loading state briefly for visual effect
849+
snippetsElement.innerHTML = '<span class="loading-text">Counting...</span>';
850+
contributorsElement.innerHTML = '<span class="loading-text">Loading...</span>';
851+
852+
// Simulate loading time
853+
setTimeout(async () => {
854+
try {
855+
console.log('Fetching repository statistics...');
856+
857+
// Fetch both stats in parallel
858+
const [fileCount, contributorCount] = await Promise.all([
859+
countFilesWithSearch(),
860+
getContributorCount()
861+
]);
862+
863+
console.log(`Files found via search: ${fileCount}`);
864+
console.log(`Contributors found: ${contributorCount}`);
865+
866+
// Handle file count
867+
let totalFiles = fileCount;
868+
if (totalFiles === 0 || totalFiles < 100) {
869+
console.log('Search API unavailable or returned low count, using estimate');
870+
totalFiles = "1900+"; // Rounded count from 1984 local files
871+
}
872+
873+
// Handle contributor count
874+
let contributorDisplay = '240+';
875+
if (contributorCount !== null && contributorCount > 0) {
876+
contributorDisplay = contributorCount.toString();
877+
} else {
878+
console.log('Contributors API unavailable, using fallback');
879+
}
880+
881+
// Update the display with counts
882+
snippetsElement.textContent = totalFiles;
883+
contributorsElement.textContent = contributorDisplay;
884+
885+
console.log(`Stats displayed - Files: ${totalFiles}, Contributors: ${contributorDisplay}`);
886+
887+
} catch (error) {
888+
console.error('Error fetching stats:', error);
889+
// Fallback to estimated numbers
890+
snippetsElement.textContent = '1900+';
891+
contributorsElement.textContent = '240+';
892+
}
893+
}, 800); // Small delay for better UX
699894
}
700895

701896
// Initialize

0 commit comments

Comments
 (0)