Skip to content

Instantly share code, notes, and snippets.

@debashisbarman
Created February 21, 2025 06:59
Show Gist options
  • Save debashisbarman/e068549f5eaacb45fff55ca7c89bcfcb to your computer and use it in GitHub Desktop.
Save debashisbarman/e068549f5eaacb45fff55ca7c89bcfcb to your computer and use it in GitHub Desktop.
Web technologies detection
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