Created
February 21, 2025 06:59
-
-
Save debashisbarman/e068549f5eaacb45fff55ca7c89bcfcb to your computer and use it in GitHub Desktop.
Web technologies detection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const WebTechDetector = { | |
// Core detection logic | |
technologies: [], | |
// Utility methods | |
utils: { | |
// Convert to array if not already | |
toArray: (value) => (Array.isArray(value) ? value : [value]), | |
// Create regex pattern | |
createPattern: (pattern, caseSensitive = false) => { | |
if (typeof pattern === 'object') return pattern; | |
return { | |
regex: new RegExp(pattern, caseSensitive ? '' : 'i'), | |
confidence: 100, | |
value: pattern, | |
}; | |
}, | |
// Transform patterns for consistent processing | |
transformPatterns: function (patterns, caseSensitive = false) { | |
if (!patterns) return []; | |
// Normalize input to object format | |
if ( | |
typeof patterns === 'string' || | |
typeof patterns === 'number' || | |
Array.isArray(patterns) | |
) { | |
patterns = { main: patterns }; | |
} | |
return Object.keys(patterns).reduce((parsed, key) => { | |
const normalizedKey = caseSensitive ? key : key.toLowerCase(); | |
parsed[normalizedKey] = this.toArray(patterns[key]).map((pattern) => | |
this.createPattern(pattern, caseSensitive), | |
); | |
return parsed; | |
}, {}); | |
}, | |
}, | |
// Set up technology definitions | |
setTechnologies: function (techData) { | |
this.technologies = Object.keys(techData).map((name) => { | |
const tech = techData[name]; | |
return { | |
name, | |
confidence: tech.confidence || 100, | |
categories: tech.cats || [], | |
html: this.utils.transformPatterns(tech.html), | |
url: this.utils.transformPatterns(tech.url), | |
headers: this.utils.transformPatterns(tech.headers), | |
scripts: this.utils.transformPatterns(tech.scriptSrc), | |
meta: this.utils.transformPatterns(tech.meta), | |
env: this.utils.transformPatterns(tech.env), | |
implies: tech.implies || [], | |
}; | |
}); | |
}, | |
// Comprehensive technology detection | |
detect: function (html, url = '', headers = {}) { | |
const detectedTechs = new Set(); | |
// Helper to add detected technology and its implications | |
const addTech = (tech) => { | |
if (!detectedTechs.has(tech.name)) { | |
detectedTechs.add(tech.name); | |
// Add implied technologies | |
tech.implies.forEach((impliedTech) => { | |
const found = this.technologies.find((t) => t.name === impliedTech); | |
if (found) addTech(found); | |
}); | |
} | |
}; | |
// Detect technologies | |
this.technologies.forEach((tech) => { | |
// Check HTML patterns | |
const htmlMatch = | |
tech.html && | |
Object.values(tech.html).some((patterns) => | |
patterns.some((pattern) => pattern.regex.test(html)), | |
); | |
// Check URL patterns | |
const urlMatch = | |
tech.url && | |
Object.values(tech.url).some((patterns) => | |
patterns.some((pattern) => pattern.regex.test(url)), | |
); | |
// Check script sources | |
const scriptMatch = | |
tech.scripts && | |
Object.values(tech.scripts).some((patterns) => | |
patterns.some((pattern) => html.includes(pattern.value)), | |
); | |
// Check meta tags | |
const metaMatch = | |
tech.meta && | |
Object.values(tech.meta).some((patterns) => | |
patterns.some((pattern) => html.includes(pattern.value)), | |
); | |
// Check for environmental indicators | |
const envMatch = | |
tech.env && | |
Object.values(tech.env).some((patterns) => | |
patterns.some((pattern) => { | |
try { | |
return window[pattern.value] !== undefined; | |
} catch { | |
return false; | |
} | |
}), | |
); | |
// If any detection method succeeds, add the technology | |
if (htmlMatch || urlMatch || scriptMatch || metaMatch || envMatch) { | |
addTech(tech); | |
} | |
}); | |
// Convert Set to array of detection results | |
return Array.from(detectedTechs).map((techName) => { | |
const tech = this.technologies.find((t) => t.name === techName); | |
return { | |
name: techName, | |
categories: tech.categories, | |
confidence: tech.confidence, | |
}; | |
}); | |
}, | |
}; | |
const detectWebTechnologies = (html, url = '', headers = {}) => { | |
const techData = { | |
// Web Application Frameworks | |
React: { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['data-reactroot', '__REACT_DEVTOOLS_GLOBAL_HOOK__', 'react-root'], | |
scriptSrc: ['react.production.min.js', 'react.development.js'], | |
env: ['React'], | |
implies: ['JSX'], | |
}, | |
Angular: { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['ng-app', 'ng-controller', '\\*ngFor', '\\*ngIf'], | |
scriptSrc: ['angular.js', '@angular/core'], | |
env: ['angular'], | |
implies: ['TypeScript', 'RxJS'], | |
}, | |
'Vue.js': { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['data-v-', '\\[v-cloak\\]', 'v-for'], | |
scriptSrc: ['vue.js', 'vue.runtime.js'], | |
env: ['Vue'], | |
implies: [], | |
}, | |
Svelte: { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['__SVELTE', 'svelte-'], | |
scriptSrc: ['svelte.js', 'svelte.min.js'], | |
env: ['Svelte'], | |
implies: [], | |
}, | |
'Solid.js': { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['_$SSR', 'solid-js'], | |
scriptSrc: ['solid-js', 'solid.js'], | |
env: ['Solid'], | |
implies: ['JSX'], | |
}, | |
Qwik: { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['q:container', 'qwik-city'], | |
scriptSrc: ['@builder.io/qwik', 'qwik.js'], | |
implies: ['TypeScript'], | |
}, | |
Preact: { | |
cats: ['JavaScript Frameworks', 'Web Apps'], | |
html: ['preact-root'], | |
scriptSrc: ['preact.js', 'preact.min.js'], | |
implies: ['JSX'], | |
}, | |
// Web App Meta Frameworks | |
'Next.js': { | |
cats: ['Web Apps', 'SSR Frameworks'], | |
html: ['__NEXT_DATA__', '<div id="__next">'], | |
scriptSrc: ['/_next/static/'], | |
implies: ['React', 'Node.js'], | |
}, | |
'Nuxt.js': { | |
cats: ['Web Apps', 'SSR Frameworks'], | |
html: ['__NUXT__', 'data-nuxt'], | |
implies: ['Vue.js', 'Node.js'], | |
}, | |
Remix: { | |
cats: ['Web Apps', 'SSR Frameworks'], | |
html: ['data-remix-', '__remix-states'], | |
implies: ['React', 'Node.js'], | |
}, | |
Astro: { | |
cats: ['Web Apps', 'SSR Frameworks'], | |
html: ['astro-island', 'astro-static'], | |
scriptSrc: ['@astrojs/'], | |
implies: ['Node.js'], | |
}, | |
SvelteKit: { | |
cats: ['Web Apps', 'SSR Frameworks'], | |
html: ['__SVELTEKIT', 'sveltekit-'], | |
implies: ['Svelte', 'Node.js'], | |
}, | |
Gatsby: { | |
cats: ['Web Apps', 'SSR Frameworks'], | |
html: ['___gatsby', 'gatsby-focus-wrapper'], | |
scriptSrc: ['/gatsby-'], | |
implies: ['React', 'Node.js'], | |
}, | |
// Backend Frameworks | |
Express: { | |
cats: ['Backend Frameworks', 'Web Apps'], | |
headers: { 'X-Powered-By': 'Express' }, | |
implies: ['Node.js'], | |
}, | |
NestJS: { | |
cats: ['Backend Frameworks', 'Web Apps'], | |
headers: { 'X-Powered-By': 'NestJS' }, | |
implies: ['Node.js', 'TypeScript'], | |
}, | |
Django: { | |
cats: ['Backend Frameworks', 'Web Apps'], | |
html: ['csrfmiddlewaretoken'], | |
implies: ['Python'], | |
}, | |
Laravel: { | |
cats: ['Backend Frameworks', 'Web Apps'], | |
html: ['Laravel', 'XSRF-TOKEN'], | |
implies: ['PHP'], | |
}, | |
'Ruby on Rails': { | |
cats: ['Backend Frameworks', 'Web Apps'], | |
html: ['csrf-token'], | |
headers: { 'X-Powered-By': 'Ruby on Rails' }, | |
implies: ['Ruby'], | |
}, | |
FastAPI: { | |
cats: ['Backend Frameworks', 'Web Apps'], | |
html: ['/docs', '/redoc'], | |
implies: ['Python'], | |
}, | |
// UI Component Libraries | |
'Tailwind CSS': { | |
cats: ['CSS Frameworks'], | |
html: ['class="bg-', 'class="text-', 'class="flex'], | |
}, | |
'Material UI': { | |
cats: ['UI Components', 'Web Apps'], | |
html: ['MuiButton-', 'MuiTypography-'], | |
implies: ['React'], | |
}, | |
'Chakra UI': { | |
cats: ['UI Components', 'Web Apps'], | |
html: ['chakra-', 'data-chakra-'], | |
implies: ['React'], | |
}, | |
'Radix UI': { | |
cats: ['UI Components', 'Web Apps'], | |
html: ['radix-', 'data-radix-'], | |
implies: ['React'], | |
}, | |
'Shadcn UI': { | |
cats: ['UI Components', 'Web Apps'], | |
html: ['ui-', 'shadcn-'], | |
implies: ['React', 'Tailwind CSS', 'Radix UI'], | |
}, | |
DaisyUI: { | |
cats: ['UI Components', 'Web Apps'], | |
html: ['btn-primary', 'btn-secondary'], | |
implies: ['Tailwind CSS'], | |
}, | |
PrimeVue: { | |
cats: ['UI Components', 'Web Apps'], | |
html: ['p-button', 'p-datatable'], | |
implies: ['Vue.js'], | |
}, | |
// State Management | |
Redux: { | |
cats: ['State Management', 'Web Apps'], | |
env: ['__REDUX_DEVTOOLS_EXTENSION__'], | |
implies: ['React'], | |
}, | |
Zustand: { | |
cats: ['State Management', 'Web Apps'], | |
scriptSrc: ['zustand'], | |
implies: ['React'], | |
}, | |
Pinia: { | |
cats: ['State Management', 'Web Apps'], | |
scriptSrc: ['pinia'], | |
implies: ['Vue.js'], | |
}, | |
Jotai: { | |
cats: ['State Management', 'Web Apps'], | |
scriptSrc: ['jotai'], | |
implies: ['React'], | |
}, | |
XState: { | |
cats: ['State Management', 'Web Apps'], | |
scriptSrc: ['xstate'], | |
implies: [], | |
}, | |
MobX: { | |
cats: ['State Management', 'Web Apps'], | |
scriptSrc: ['mobx'], | |
implies: [], | |
}, | |
// Form Management | |
'React Hook Form': { | |
cats: ['Forms', 'Web Apps'], | |
scriptSrc: ['react-hook-form'], | |
implies: ['React'], | |
}, | |
Formik: { | |
cats: ['Forms', 'Web Apps'], | |
html: ['formik-', 'data-formik'], | |
implies: ['React'], | |
}, | |
VeeValidate: { | |
cats: ['Forms', 'Web Apps'], | |
html: ['v-validate'], | |
implies: ['Vue.js'], | |
}, | |
// Data Fetching & API | |
GraphQL: { | |
cats: ['API', 'Web Apps'], | |
html: ['graphql-playground', 'graphiql'], | |
scriptSrc: ['graphql'], | |
}, | |
'REST API': { | |
cats: ['API', 'Web Apps'], | |
html: ['/api/', '/rest/'], | |
headers: { 'Content-Type': 'application/json' }, | |
}, | |
'TanStack Query': { | |
cats: ['Data Fetching', 'Web Apps'], | |
scriptSrc: ['@tanstack/query', 'react-query'], | |
implies: ['React'], | |
}, | |
'Apollo Client': { | |
cats: ['Data Fetching', 'Web Apps'], | |
scriptSrc: ['apollo-client'], | |
implies: ['GraphQL'], | |
}, | |
tRPC: { | |
cats: ['API', 'Web Apps'], | |
scriptSrc: ['@trpc/client'], | |
implies: ['TypeScript'], | |
}, | |
// Testing Frameworks | |
Jest: { | |
cats: ['Testing', 'Web Apps'], | |
env: ['jest'], | |
implies: [], | |
}, | |
Vitest: { | |
cats: ['Testing', 'Web Apps'], | |
scriptSrc: ['vitest'], | |
implies: [], | |
}, | |
Cypress: { | |
cats: ['Testing', 'Web Apps'], | |
env: ['Cypress'], | |
implies: [], | |
}, | |
Playwright: { | |
cats: ['Testing', 'Web Apps'], | |
env: ['playwright'], | |
implies: [], | |
}, | |
// Build Tools | |
Vite: { | |
cats: ['Build Tools', 'Web Apps'], | |
html: ['/@vite/client', '/vite-plugin-'], | |
implies: [], | |
}, | |
Webpack: { | |
cats: ['Build Tools', 'Web Apps'], | |
html: ['webpack-', '__webpack_'], | |
implies: [], | |
}, | |
Turbopack: { | |
cats: ['Build Tools', 'Web Apps'], | |
html: ['__turbopack'], | |
implies: ['Rust'], | |
}, | |
esbuild: { | |
cats: ['Build Tools', 'Web Apps'], | |
scriptSrc: ['esbuild'], | |
implies: ['Go'], | |
}, | |
// Database & Backend Services | |
MongoDB: { | |
cats: ['Databases', 'Web Apps'], | |
html: ['mongodb-atlas', 'realm-web'], | |
}, | |
PostgreSQL: { | |
cats: ['Databases', 'Web Apps'], | |
headers: { 'X-Powered-By': 'PostgreSQL' }, | |
}, | |
Supabase: { | |
cats: ['Backend as a Service', 'Web Apps'], | |
scriptSrc: ['supabase-js', '@supabase/supabase-js'], | |
}, | |
Firebase: { | |
cats: ['Backend as a Service', 'Web Apps'], | |
scriptSrc: ['firebase-app.js', 'firebase/app'], | |
html: ['firebase-app'], | |
}, | |
Prisma: { | |
cats: ['ORM', 'Web Apps'], | |
env: ['PrismaClient'], | |
implies: ['TypeScript'], | |
}, | |
Drizzle: { | |
cats: ['ORM', 'Web Apps'], | |
scriptSrc: ['drizzle-orm'], | |
implies: ['TypeScript'], | |
}, | |
}; | |
// Initialize and detect technologies | |
WebTechDetector.setTechnologies(techData); | |
return WebTechDetector.detect(html, url); | |
}; | |
export default detectWebTechnologies; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment