From 7a60622b4bc74e48f8e43e5a2e124d928d6d27f6 Mon Sep 17 00:00:00 2001 From: selvi Date: Fri, 6 Mar 2026 15:44:50 +0530 Subject: [PATCH] all seo - tags updated --- package-lock.json | 260 ++++++++++++++++- package.json | 11 +- public/robots.txt | 7 + public/sitemap.xml | 1 + scripts/generate-sitemap.cjs | 151 ++++++++++ scripts/image_alt_issues.csv | 43 +++ scripts/seo-test-selenium.cjs | 266 ++++++++++++++++++ src/app/about-us/page.tsx | 72 ++--- src/app/blog/[slug]/page.tsx | 13 + src/app/blog/page.tsx | 37 +-- src/app/careers/page.tsx | 52 ++-- src/app/contact/page.tsx | 27 +- src/app/faq/page.tsx | 29 +- src/app/layout.tsx | 211 +++++++++++--- .../mobile-app-development-service/page.tsx | 9 + src/app/page.tsx | 48 +--- src/app/portfolio/page.tsx | 32 +-- .../[slug]/page.tsx | 20 ++ src/app/services-digital-solutions/page.tsx | 55 +--- src/app/website-development-service/page.tsx | 14 +- src/components/MetatronInitializer.tsx | 18 ++ src/components/home/home-1/BlogSection.tsx | 2 + src/types/index.ts | 2 + src/utils/data.ts | 24 ++ 24 files changed, 1105 insertions(+), 299 deletions(-) create mode 100644 public/robots.txt create mode 100644 public/sitemap.xml create mode 100644 scripts/generate-sitemap.cjs create mode 100644 scripts/image_alt_issues.csv create mode 100644 scripts/seo-test-selenium.cjs create mode 100644 src/components/MetatronInitializer.tsx diff --git a/package-lock.json b/package-lock.json index c263a86..286465c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@fortawesome/fontawesome-free": "^7.2.0", "@types/react-google-recaptcha": "2.1.9", "aos": "^2.3.4", - "axios": "1.13.5", + "axios": "^1.13.5", "bootstrap": "^5.3.8", "gsap": "^3.14.2", "isotope-layout": "^3.0.6", @@ -22,7 +22,10 @@ "react-google-recaptcha": "3.1.0", "react-slick": "^0.31.0", "sass": "^1.97.3", - "slick-carousel": "^1.8.1" + "selenium-webdriver": "^4.41.0", + "sitemap": "^9.0.1", + "slick-carousel": "^1.8.1", + "xml2js": "^0.6.2" }, "devDependencies": { "@types/aos": "^3.0.7", @@ -36,6 +39,12 @@ "typescript": "^5" } }, + "node_modules/@bazel/runfiles": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz", + "integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==", + "license": "Apache-2.0" + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", @@ -1154,7 +1163,6 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -1242,7 +1250,6 @@ "version": "20.19.33", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1253,7 +1260,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1287,6 +1293,15 @@ "@types/react": "*" } }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", @@ -1332,7 +1347,6 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -1858,7 +1872,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1920,6 +1933,12 @@ "lodash.throttle": "^4.0.1" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2421,6 +2440,12 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/countup.js": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.9.0.tgz", @@ -2821,7 +2846,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2995,7 +3019,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -3715,6 +3738,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immutable": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", @@ -3748,6 +3777,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4302,6 +4337,18 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4346,6 +4393,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4810,6 +4866,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4917,6 +4979,12 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4970,7 +5038,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5005,7 +5072,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5048,6 +5114,27 @@ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -5207,6 +5294,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -5247,7 +5340,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -5263,12 +5355,46 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/sax": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", + "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, + "node_modules/selenium-webdriver": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.41.0.tgz", + "integrity": "sha512-1XxuKVhr9az24xwixPBEDGSZP+P0z3ZOnCmr9Oiep0MlJN2Mk+flIjD3iBS9BgyjS4g14dikMqnrYUPIjhQBhA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/SeleniumHQ" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/selenium" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@bazel/runfiles": "^6.5.0", + "jszip": "^3.10.1", + "tmp": "^0.2.5", + "ws": "^8.19.0" + }, + "engines": { + "node": ">= 20.0.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5328,6 +5454,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", @@ -5490,6 +5622,40 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/sitemap/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/slick-carousel": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz", @@ -5537,6 +5703,15 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-convert": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", @@ -5769,7 +5944,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5777,6 +5951,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5932,7 +6115,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5964,7 +6146,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { @@ -6012,6 +6193,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6127,6 +6314,49 @@ "node": ">=0.10.0" } }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 7e12e5f..05d56fe 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,15 @@ "build": "next build", "start": "next start", "export": "next export", - "lint": "eslint" + "lint": "eslint", + "sitemap": "node scripts/generate-sitemap.cjs", + "seo-audit": "node scripts/seo-test-selenium.cjs" }, "dependencies": { "@fortawesome/fontawesome-free": "^7.2.0", "@types/react-google-recaptcha": "2.1.9", "aos": "^2.3.4", - "axios": "1.13.5", + "axios": "^1.13.5", "bootstrap": "^5.3.8", "gsap": "^3.14.2", "isotope-layout": "^3.0.6", @@ -24,7 +26,10 @@ "react-google-recaptcha": "3.1.0", "react-slick": "^0.31.0", "sass": "^1.97.3", - "slick-carousel": "^1.8.1" + "selenium-webdriver": "^4.41.0", + "sitemap": "^9.0.1", + "slick-carousel": "^1.8.1", + "xml2js": "^0.6.2" }, "devDependencies": { "@types/aos": "^3.0.7", diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..8a2eaf6 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,7 @@ +# Allow all search engines to crawl all pages except these +User-agent: * +Disallow: /website-development-service/ +Disallow: /mobile-app-development-service/ + +# Sitemap URL +Sitemap: https://metatroncubesolutions.com/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..fc8823e --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1 @@ +http://localhost:3000/daily1.0http://localhost:3000/services-digital-solutions/daily0.7http://localhost:3000/about/weekly0.7http://localhost:3000/careers/weekly0.7http://localhost:3000/portfolio/weekly0.7http://localhost:3000/blog/weekly0.7http://localhost:3000/contact/monthly0.5http://localhost:3000/faq/monthly0.5http://localhost:3000/service/website-development-company/weekly0.6http://localhost:3000/service/mobile-application-development/weekly0.6http://localhost:3000/service/graphic-designing-company/weekly0.6http://localhost:3000/service/ui-ux-designing/weekly0.6http://localhost:3000/service/search-engine-optimization-seo-content-writing/weekly0.6http://localhost:3000/service/digital-marketing-agency-in-canada/weekly0.6http://localhost:3000/service/app-development-waterloo/weekly0.6http://localhost:3000/service/kitchener-waterloo-website-design-services/weekly0.6http://localhost:3000/service/professional-website-designers-in-waterloo/weekly0.6http://localhost:3000/service/waterloo-seo-services/weekly0.6http://localhost:3000/service/web-design-toronto-custom-website-creation-by-metatroncube-software-solutions/weekly0.6http://localhost:3000/service/web-page-design-in-waterloo/weekly0.6http://localhost:3000/5-tips-to-create-viral-content-that-drives-engagement/weekly0.6http://localhost:3000/instagram-vs-facebook-choosing-the-right-platform-for-your-business/weekly0.6http://localhost:3000/how-local-seo-can-drive-more-foot-traffic-to-your-business/weekly0.6http://localhost:3000/on-page-vs-off-page-seo-what-every-business-owner-needs-to-know/weekly0.6http://localhost:3000/how-to-create-a-winning-digital-marketing-strategy-for-your-business/weekly0.6http://localhost:3000/white-hat-vs-black-hat-seo-an-in-depth-link-building-guide/weekly0.6http://localhost:3000/how-to-boost-your-small-business-with-effective-digital-marketing-strategies/weekly0.6http://localhost:3000/the-importance-of-local-seo-for-real-estate-agents/weekly0.6http://localhost:3000/how-to-optimize-your-website-for-voice-search/weekly0.6http://localhost:3000/how-ai-is-revolutionizing-web-development-and-seo/weekly0.6http://localhost:3000/top-digital-marketing-agency-in-canada-metatroncube-software-solutions/weekly0.6http://localhost:3000/best-digital-marketing-company-in-canada-metatroncube-solutions/weekly0.6http://localhost:3000/web-designers-for-small-business/weekly0.6http://localhost:3000/mobile-commerce-2024-web-app-development-evolution/weekly0.6http://localhost:3000/how-to-personalize-cold-emails-without-spending-hours-on-research/weekly0.6http://localhost:3000/how-to-overcome-the-biggest-challenges-in-cold-emailing-b2b-success/weekly0.6http://localhost:3000/9-powerful-free-google-tools-to-grow-your-business/weekly0.6http://localhost:3000/5-digital-marketing-mistakes-every-small-business-must-avoid/weekly0.6http://localhost:3000/how-digital-solutions-drove-success-for-a-local-business/weekly0.6http://localhost:3000/ai-driven-seo-how-to-future-proof-your-search-strategy/weekly0.6http://localhost:3000/mastering-omnichannel-marketing-strategies-for-2026/weekly0.6http://localhost:3000/how-metatroncube-solutions-improve-local-seo-waterloo/weekly0.6http://localhost:3000/why-metatroncube-solutions-top-choice-social-media-marketing-waterloo/weekly0.6http://localhost:3000/how-does-metatroncube-solutions-help-businesses-get-more-local-customers-through-search-engines/weekly0.6http://localhost:3000/can-metatroncube-solutions-optimize-my-google-business-profile/weekly0.6http://localhost:3000/seo-services-waterloo-small-businesses-metatroncube/weekly0.6http://localhost:3000/boost-google-maps-ranking-waterloo-metatroncube/weekly0.6http://localhost:3000/how-metatroncube-solutions-uses-ai-tools-to-boost-your-marketing-in-toronto/weekly0.6http://localhost:3000/what-types-of-social-media-ads-does-metatroncube-solutions-recommend-for-small-businesses-in-toronto/weekly0.6http://localhost:3000/should-i-run-facebook-or-google-ads-for-my-business-and-can-metatroncube-solutions-manage-them/weekly0.6http://localhost:3000/is-metatroncube-solutions-the-best-ppc-agency-in-waterloo/weekly0.6http://localhost:3000/how-can-metatroncube-solutions-help-me-get-more-followers-for-my-local-business/weekly0.6http://localhost:3000/how-to-track-roi-from-your-website-and-marketing-campaigns-with-metatroncube-solutions/weekly0.6http://localhost:3000/does-metatroncube-solutions-provide-ai-powered-marketing-solutions-for-local-businesses-in-waterloo/weekly0.6http://localhost:3000/how-can-i-track-roi-from-my-website-and-marketing-campaigns-with-metatroncube-solutions-in-waterloo/weekly0.6http://localhost:3000/can-metatroncube-solutions-manage-my-social-media-campaigns-from-start-to-finish/weekly0.6http://localhost:3000/does-metatroncube-solutions-provide-website-audits-for-small-businesses/weekly0.6http://localhost:3000/can-metatroncube-solutions-handle-both-seo-and-ppc-campaigns-for-my-business-in-waterloo/weekly0.6http://localhost:3000/what-types-of-social-media-ads-does-metatroncube-solutions-recommend-for-small-businesses-in-waterloo/weekly0.6http://localhost:3000/driving-digital-growth-with-metatroncubes-performance-focused-marketing-strategies/weekly0.6http://localhost:3000/how-metatroncube-helps-businesses-scale-faster-with-smart-digital-solutions/weekly0.6http://localhost:3000/building-powerful-digital-brands-with-metatroncube-creative-technical-expertise/weekly0.6http://localhost:3000/professional-digital-marketing-services-in-canada-for-sustainable-business-growth/weekly0.6http://localhost:3000/custom-website-development-solutions-in-canada-for-modern-businesses/weekly0.6http://localhost:3000/is-metatroncube-solutions-the-top-choice-for-social-media-marketing-in-waterloo/weekly0.6http://localhost:3000/how-can-metatroncube-solutions-improve-mobile-ux-for-my-business-website/weekly0.6http://localhost:3000/is-metatroncube-solutions-a-good-agency-for-website-redesign-in-waterloo/weekly0.6http://localhost:3000/does-metatroncube-solutions-provide-website-audits-for-small-businesses/weekly0.6http://localhost:3000/seo-for-small-businesses-waterloo/weekly0.6http://localhost:3000/modern-business-website-features-waterloo/weekly0.6http://localhost:3000/why-social-media-management-important-business/weekly0.6http://localhost:3000/why-you-need-digital-marketing-agency-business-growth/weekly0.6 \ No newline at end of file diff --git a/scripts/generate-sitemap.cjs b/scripts/generate-sitemap.cjs new file mode 100644 index 0000000..e33efef --- /dev/null +++ b/scripts/generate-sitemap.cjs @@ -0,0 +1,151 @@ +const fs = require('fs'); +const { SitemapStream, streamToPromise } = require('sitemap'); +const path = require('path'); + +const hostname = 'http://localhost:3000'; +const addTrailingSlash = true; // ✅ Set this true if your Next.js uses trailingSlash: true + +// // 🔧 Utility to format URLs based on config +// const formatUrl = (url) => { +// if (addTrailingSlash && !url.endsWith('/')) return url + '/'; +// if (!addTrailingSlash && url.endsWith('/') && url !== '/') return url.slice(0, -1); +// return url; +// }; +// Add a trailing slash only for “directory-like” URLs +const shouldAddSlash = (url) => { + // keep "/" as is + if (url === '/') return false; + // don't touch file-like URLs (has extension) + if (/\.[a-z0-9]{2,6}(\?.*)?$/i.test(url)) return false; + return true; +}; + +const formatUrl = (url) => { + // normalize to leading slash + if (!url.startsWith('/')) url = '/' + url; + + if (addTrailingSlash && shouldAddSlash(url) && !url.endsWith('/')) { + return url + '/'; + } + if (!addTrailingSlash && url.endsWith('/') && url !== '/') { + return url.slice(0, -1); + } + return url; +}; + +// ✅ Static pages +const staticLinks = [ + { url: '/', changefreq: 'daily', priority: 1.0 }, + { url: '/services-digital-solutions/', changefreq: 'daily', priority: 0.7 }, + { url: '/about/', changefreq: 'weekly', priority: 0.7 }, + { url: '/careers/', changefreq: 'weekly', priority: 0.7 }, + { url: '/portfolio/', changefreq: 'weekly', priority: 0.7 }, + { url: '/blog/', changefreq: 'weekly', priority: 0.7 }, + { url: '/contact/', changefreq: 'monthly', priority: 0.5 }, + { url: '/faq/', changefreq: 'monthly', priority: 0.5 }, + { url: '/service/website-development-company/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/mobile-application-development/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/graphic-designing-company/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/ui-ux-designing/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/search-engine-optimization-seo-content-writing/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/digital-marketing-agency-in-canada/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/app-development-waterloo/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/kitchener-waterloo-website-design-services/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/professional-website-designers-in-waterloo/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/waterloo-seo-services/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/web-design-toronto-custom-website-creation-by-metatroncube-software-solutions/', changefreq: 'weekly', priority: 0.6 }, + { url: '/service/web-page-design-in-waterloo/', changefreq: 'weekly', priority: 0.6 }, +]; + +// ✅ Dynamic blog posts +const blogPosts = [ + { slug: '5-tips-to-create-viral-content-that-drives-engagement' }, + { slug: 'instagram-vs-facebook-choosing-the-right-platform-for-your-business' }, + { slug: 'how-local-seo-can-drive-more-foot-traffic-to-your-business' }, + { slug: 'on-page-vs-off-page-seo-what-every-business-owner-needs-to-know' }, + { slug: 'how-to-create-a-winning-digital-marketing-strategy-for-your-business' }, + { slug: 'white-hat-vs-black-hat-seo-an-in-depth-link-building-guide' }, + { slug: 'how-to-boost-your-small-business-with-effective-digital-marketing-strategies' }, + { slug: 'the-importance-of-local-seo-for-real-estate-agents' }, + { slug: 'how-to-optimize-your-website-for-voice-search' }, + { slug: 'how-ai-is-revolutionizing-web-development-and-seo' }, + { slug: 'top-digital-marketing-agency-in-canada-metatroncube-software-solutions' }, + { slug: 'best-digital-marketing-company-in-canada-metatroncube-solutions' }, + { slug: 'web-designers-for-small-business' }, + { slug: 'mobile-commerce-2024-web-app-development-evolution' }, + { slug: 'how-to-personalize-cold-emails-without-spending-hours-on-research' }, + { slug: 'how-to-overcome-the-biggest-challenges-in-cold-emailing-b2b-success' }, + { slug: '9-powerful-free-google-tools-to-grow-your-business' }, + { slug: '5-digital-marketing-mistakes-every-small-business-must-avoid' }, + { slug: 'how-digital-solutions-drove-success-for-a-local-business' }, + { slug: 'ai-driven-seo-how-to-future-proof-your-search-strategy' }, + { slug: 'mastering-omnichannel-marketing-strategies-for-2026' }, + { slug: 'how-metatroncube-solutions-improve-local-seo-waterloo' }, + { slug: 'why-metatroncube-solutions-top-choice-social-media-marketing-waterloo' }, + { slug: 'how-does-metatroncube-solutions-help-businesses-get-more-local-customers-through-search-engines' }, + { slug: 'can-metatroncube-solutions-optimize-my-google-business-profile' }, + { slug: 'seo-services-waterloo-small-businesses-metatroncube' }, + { slug: 'boost-google-maps-ranking-waterloo-metatroncube' }, + { slug: 'how-metatroncube-solutions-uses-ai-tools-to-boost-your-marketing-in-toronto' }, + { slug: 'what-types-of-social-media-ads-does-metatroncube-solutions-recommend-for-small-businesses-in-toronto' }, + { slug: 'should-i-run-facebook-or-google-ads-for-my-business-and-can-metatroncube-solutions-manage-them' }, + { slug: 'is-metatroncube-solutions-the-best-ppc-agency-in-waterloo' }, + { slug: 'how-can-metatroncube-solutions-help-me-get-more-followers-for-my-local-business' }, + { slug: 'how-to-track-roi-from-your-website-and-marketing-campaigns-with-metatroncube-solutions' }, + { slug: 'does-metatroncube-solutions-provide-ai-powered-marketing-solutions-for-local-businesses-in-waterloo' }, + { slug: 'how-can-i-track-roi-from-my-website-and-marketing-campaigns-with-metatroncube-solutions-in-waterloo' }, + { slug: 'can-metatroncube-solutions-manage-my-social-media-campaigns-from-start-to-finish' }, + { slug: 'does-metatroncube-solutions-provide-website-audits-for-small-businesses' }, + { slug: 'can-metatroncube-solutions-handle-both-seo-and-ppc-campaigns-for-my-business-in-waterloo' }, + { slug: 'what-types-of-social-media-ads-does-metatroncube-solutions-recommend-for-small-businesses-in-waterloo' }, + { slug: 'driving-digital-growth-with-metatroncubes-performance-focused-marketing-strategies' }, + { slug: 'how-metatroncube-helps-businesses-scale-faster-with-smart-digital-solutions' }, + { slug: 'building-powerful-digital-brands-with-metatroncube-creative-technical-expertise' }, + { slug: 'professional-digital-marketing-services-in-canada-for-sustainable-business-growth' }, + { slug: 'custom-website-development-solutions-in-canada-for-modern-businesses' }, + { slug: 'is-metatroncube-solutions-the-top-choice-for-social-media-marketing-in-waterloo' }, + { slug: 'how-can-metatroncube-solutions-improve-mobile-ux-for-my-business-website' }, + { slug: 'is-metatroncube-solutions-a-good-agency-for-website-redesign-in-waterloo' }, + { slug: 'does-metatroncube-solutions-provide-website-audits-for-small-businesses' }, + { slug: 'seo-for-small-businesses-waterloo' }, + { slug: 'modern-business-website-features-waterloo' }, + { slug: 'why-social-media-management-important-business' }, + { slug: 'why-you-need-digital-marketing-agency-business-growth' }, + +]; + +// Convert blog slugs to sitemap entries +const blogLinks = blogPosts.map(post => ({ + url: `/${post.slug}`, + changefreq: 'weekly', + priority: 0.6 +})); + +const allLinks = [...staticLinks, ...blogLinks].map(link => ({ + ...link, + url: formatUrl(link.url), +})); + +async function generateSitemap() { + try { + const sitemap = new SitemapStream({ hostname: hostname }); + const writeStream = fs.createWriteStream(path.resolve(__dirname, '../public/sitemap.xml')); + + sitemap.pipe(writeStream); + + console.log('📦 Writing URLs to sitemap:'); + allLinks.forEach(link => { + console.log(' -', hostname + link.url); + sitemap.write(link); + }); + + sitemap.end(); + await streamToPromise(sitemap); + + console.log('✅ sitemap.xml created successfully!'); + } catch (error) { + console.error('❌ Error creating sitemap.xml:', error); + } +} + +generateSitemap(); diff --git a/scripts/image_alt_issues.csv b/scripts/image_alt_issues.csv new file mode 100644 index 0000000..ef22484 --- /dev/null +++ b/scripts/image_alt_issues.csv @@ -0,0 +1,43 @@ +Page URL,Image Src,Alt Text,Issue Type +"http://localhost:3000/","http://localhost:3000/assets/images/home/2/left-img.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/images/home/3/right-img.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-1.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-2.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-3.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-4.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-1.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-2.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-3.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-4.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-1.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-2.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-3.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-4.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-1.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-2.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-3.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/add/icon-4.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/images/home/4/lets-discuss.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/images/about/6/left.webp","(empty)","Empty Alt" +"http://localhost:3000/","http://localhost:3000/assets/img/logo.webp","(empty)","Empty Alt" +"http://localhost:3000/","","img","Duplicate Alt (3 times)" +"http://localhost:3000/","","pelocis","Duplicate Alt (3 times)" +"http://localhost:3000/","","shape","Duplicate Alt (6 times)" +"http://localhost:3000/","","icon","Duplicate Alt (18 times)" +"http://localhost:3000/","","Digital Marketing","Duplicate Alt (2 times)" +"http://localhost:3000/","","Website Development","Duplicate Alt (2 times)" +"http://localhost:3000/","","sureshkumar natarajan","Duplicate Alt (2 times)" +"http://localhost:3000/","","VINOD G","Duplicate Alt (2 times)" +"http://localhost:3000/","","Dine360 Ads","Duplicate Alt (2 times)" +"http://localhost:3000/","","Arun kumar","Duplicate Alt (2 times)" +"http://localhost:3000/services-digital-solutions/","http://localhost:3000/assets/images/services/choose/grid.webp","(empty)","Empty Alt" +"http://localhost:3000/services-digital-solutions/","http://localhost:3000/assets/images/services/service/element-small.webp","(empty)","Empty Alt" +"http://localhost:3000/services-digital-solutions/","http://localhost:3000/assets/images/services/deliver/element.webp","(empty)","Empty Alt" +"http://localhost:3000/services-digital-solutions/","http://localhost:3000/assets/images/services/journey/element-2.webp","(empty)","Empty Alt" +"http://localhost:3000/services-digital-solutions/","http://localhost:3000/assets/img/logo.webp","(empty)","Empty Alt" +"http://localhost:3000/services-digital-solutions/","","Comprehensive digital development","Duplicate Alt (3 times)" +"http://localhost:3000/services-digital-solutions/","","Strategic online growth solutions","Duplicate Alt (3 times)" +"http://localhost:3000/services-digital-solutions/","","Creative design and branding excellence","Duplicate Alt (3 times)" +"http://localhost:3000/services-digital-solutions/","","icon","Duplicate Alt (9 times)" +"http://localhost:3000/services-digital-solutions/","","img","Duplicate Alt (3 times)" +"http://localhost:3000/services-digital-solutions/","","shape","Duplicate Alt (3 times)" diff --git a/scripts/seo-test-selenium.cjs b/scripts/seo-test-selenium.cjs new file mode 100644 index 0000000..a521892 --- /dev/null +++ b/scripts/seo-test-selenium.cjs @@ -0,0 +1,266 @@ +// 🚀 Full SEO + Broken Link + 404 + Accessibility + Image Alt CSV Export +// Run with: node seo_full_audit.js + +const { Builder, By } = require("selenium-webdriver"); +const chrome = require("selenium-webdriver/chrome"); +const axios = require("axios"); +const xml2js = require("xml2js"); +const fs = require("fs"); +const path = require("path"); + +// CSV file for Image Alt issues +const csvPath = path.join(__dirname, "image_alt_issues.csv"); +fs.writeFileSync(csvPath, "Page URL,Image Src,Alt Text,Issue Type\n", "utf8"); + +// ========================== +// 1️⃣ Fetch URLs from sitemap.xml +// ========================== +async function getUrlsFromSitemap(sitemapUrl) { + try { + const res = await axios.get(sitemapUrl); + const parsed = await xml2js.parseStringPromise(res.data); + return parsed.urlset.url.map((u) => u.loc[0]); + } catch (err) { + console.error("❌ Failed to load sitemap:", err.message); + return []; + } +} + +// ========================== +// 2️⃣ Check HTTP Status +// ========================== +async function checkLinkStatus(url) { + try { + const res = await axios.get(url, { + timeout: 10000, + validateStatus: () => true, + }); + + if ( + res.status === 200 && + ( + /404/i.test(res.data.match(/]*>(.*?)<\/title>/)?.[1] ?? "") + ) + ) { + return "Soft 404"; + } + + return res.status; + } catch (err) { + return err.response ? err.response.status : "❌ No Response"; + } +} + +// ========================== +// 3️⃣ Main SEO + Accessibility + Image Alt Audit +// ========================== +async function checkSEO(url, siteDomain) { + const options = new chrome.Options(); + options.addArguments("--headless", "--no-sandbox", "--disable-gpu"); + + const driver = await new Builder() + .forBrowser("chrome") + .setChromeOptions(options) + .build(); + + try { + const pageStatus = await checkLinkStatus(url); + if (pageStatus === 404 || pageStatus === "Soft 404") { + console.log(`\n🚫 ${url} → ❌ Page not found (${pageStatus})`); + return; + } + + await driver.get(url); + const pageSource = await driver.getPageSource(); + + // Basic SEO Elements + const title = await driver.getTitle(); + const descElem = await driver.findElements(By.css('meta[name="description"]')); + const canonicalElem = await driver.findElements(By.css('link[rel="canonical"]')); + const robotsElem = await driver.findElements(By.css('meta[name="robots"]')); + const viewportElem = await driver.findElements(By.css('meta[name="viewport"]')); + const charset = await driver.findElements(By.css('meta[charset]')); + const htmlTag = await driver.findElement(By.css("html")); + const langAttr = await htmlTag.getAttribute("lang").catch(() => ""); + const h1Tags = await driver.findElements(By.css("h1")); + const h2Tags = await driver.findElements(By.css("h2")); + + // Meta Description + let descContent = descElem.length > 0 ? await descElem[0].getAttribute("content") : ""; + const descLength = descContent.length; + const descStatus = + descLength === 0 + ? "❌ Missing" + : descLength < 50 + ? `⚠️ Too short (${descLength})` + : descLength > 160 + ? `⚠️ Too long (${descLength})` + : "✅ Perfect"; + + // Title length check + const titleLength = title.length; + const titleStatus = + titleLength === 0 + ? "❌ Missing" + : titleLength < 30 + ? `⚠️ Too short (${titleLength})` + : titleLength > 65 + ? `⚠️ Too long (${titleLength})` + : "✅ Perfect"; + + // Canonical + const canonicalURL = + canonicalElem.length > 0 ? await canonicalElem[0].getAttribute("href") : "❌ Missing"; + + // 🖼️ Image Accessibility Audit + const imgs = await driver.findElements(By.css("img")); + let missingAlt = 0; + let emptyAlt = 0; + let duplicateAlt = []; + const altTextMap = new Map(); + + for (const img of imgs) { + const src = await img.getAttribute("src"); + const alt = (await img.getAttribute("alt"))?.trim() ?? null; + + if (alt === null) { + missingAlt++; + fs.appendFileSync(csvPath, `"${url}","${src}","","Missing Alt"\n`, "utf8"); + continue; + } + + if (alt === "") { + emptyAlt++; + fs.appendFileSync(csvPath, `"${url}","${src}","(empty)","Empty Alt"\n`, "utf8"); + } + + if (altTextMap.has(alt)) { + altTextMap.set(alt, altTextMap.get(alt) + 1); + } else { + altTextMap.set(alt, 1); + } + } + + for (const [altText, count] of altTextMap.entries()) { + if (altText && count > 1) { + duplicateAlt.push({ altText, count }); + fs.appendFileSync( + csvPath, + `"${url}","","${altText}","Duplicate Alt (${count} times)"\n`, + "utf8" + ); + } + } + + // Detect tracking & schema tags + const hasGTM = pageSource.includes("googletagmanager.com/gtm.js"); + const hasClarity = pageSource.includes("clarity.ms/tag"); + const hasFBPixel = pageSource.includes("fbevents.js") || pageSource.includes("fbq("); + const hasAnalytics = pageSource.includes("www.googletagmanager.com/gtag/js"); + + const ogTags = await driver.findElements(By.css("meta[property^='og:']")); + const twitterTags = await driver.findElements(By.css("meta[name^='twitter:']")); + const schemaScripts = await driver.findElements(By.css('script[type="application/ld+json"]')); + + // Links check + const anchorTags = await driver.findElements(By.css("a[href]")); + const brokenLinks = []; + for (const a of anchorTags) { + const href = await a.getAttribute("href"); + if (!href || href.startsWith("#") || href.startsWith("mailto:")) continue; + + const fullUrl = href.startsWith("http") + ? href + : `${siteDomain}${href.startsWith("/") ? href : `/${href}`}`; + + if (fullUrl.includes(siteDomain)) { + const status = await checkLinkStatus(fullUrl); + if (status === 404 || status === "Soft 404" || status === "❌ No Response") { + brokenLinks.push({ link: fullUrl, status }); + } + } + } + + // Lazy loading check + const images = await driver.findElements(By.css("img, video, iframe")); + const lazyLoadCount = await Promise.all( + images.map(async (img) => { + const loading = await img.getAttribute("loading"); + return loading === "lazy"; + }) + ); + const lazyLoaded = lazyLoadCount.filter((v) => v).length; + + // Console Summary + console.log(`\n🔍 Checking: ${url}`); + console.log("-------------------------------------------"); + console.log("Title:", titleStatus); + console.log("Meta Description:", descStatus); + console.log("Canonical URL:", canonicalURL); + console.log("Meta Robots:", robotsElem.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Viewport:", viewportElem.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Charset:", charset.length > 0 ? "✅ Found" : "❌ Missing"); + console.log("HTML lang:", langAttr ? `✅ ${langAttr}` : "⚠️ Missing"); + console.log("H1 Tags:", h1Tags.length > 0 ? `✅ ${h1Tags.length}` : "❌ Missing"); + console.log("H2 Tags:", h2Tags.length > 0 ? `ℹ️ ${h2Tags.length}` : "⚠️ None"); + console.log("Images:", imgs.length); + console.log( + "Missing Alt:", + missingAlt > 0 ? `❌ ${missingAlt}` : "✅ None" + ); + console.log( + "Empty Alt:", + emptyAlt > 0 ? `⚠️ ${emptyAlt}` : "✅ None" + ); + console.log( + "Duplicate Alt:", + duplicateAlt.length > 0 ? `⚠️ ${duplicateAlt.length}` : "✅ None" + ); + console.log("Lazy Loaded Images:", lazyLoaded > 0 ? `✅ ${lazyLoaded}` : "⚠️ None"); + console.log("Open Graph Tags:", ogTags.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Twitter Tags:", twitterTags.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Schema Markup:", schemaScripts.length > 0 ? "✅ Found" : "⚠️ Missing"); + console.log("Google Analytics:", hasAnalytics ? "✅ Found" : "⚠️ Missing"); + console.log("GTM:", hasGTM ? "✅ Found" : "⚠️ Missing"); + console.log("Clarity:", hasClarity ? "✅ Found" : "⚠️ Missing"); + console.log("Facebook Pixel:", hasFBPixel ? "✅ Found" : "⚠️ Missing"); + + if (brokenLinks.length > 0) { + console.log("\n❌ Broken Links:"); + brokenLinks.forEach((b) => console.log(` → ${b.link} [${b.status}]`)); + } else { + console.log("✅ No broken links found."); + } + + } catch (err) { + console.error(`❌ Error on ${url}:`, err.message); + } finally { + await driver.quit(); + } +} + +// ========================== +// 4️⃣ Run Full Site Audit +// ========================== +(async () => { + const sitemapUrl = "http://localhost:3000/sitemap.xml"; + const siteDomain = "http://localhost:3000"; + + console.log("📄 Fetching URLs from sitemap..."); + const urls = await getUrlsFromSitemap(sitemapUrl); + + if (urls.length === 0) { + console.error("❌ No URLs found in sitemap."); + return; + } + + console.log(`✅ Found ${urls.length} URLs in sitemap.`); + console.log("🚀 Starting Full SEO + Accessibility + Broken Link Audit..."); + + for (const url of urls) { + await checkSEO(url, siteDomain); + } + + console.log("\n✅ Full SEO Audit Completed!"); + console.log(` CSV Report: ${csvPath}`); +})(); diff --git a/src/app/about-us/page.tsx b/src/app/about-us/page.tsx index d37427f..6773d4e 100644 --- a/src/app/about-us/page.tsx +++ b/src/app/about-us/page.tsx @@ -1,48 +1,30 @@ -"use client"; - -import React, { useEffect } from "react"; -import Header2 from "@/components/layout/Header2"; -import Footer2 from "@/components/layout/Footer2"; - -import InnerBanner from "@/components/common/InnerBanner"; -import FeaturesSection from "@/components/home/home-2/FeaturesSection"; -import About2Section from "@/components/home/home-2/About2Section"; -import WhyChooseUs from "@/components/about/WhyChooseUs"; -import ServiceTabSection from "@/components/home/home-2/ServiceTabSection"; -import WorkProcessSection from "@/components/home/home-2/WorkProcessSection"; -import TextSliderSection from "@/components/home/home-2/TextSliderSection"; -import ProjectSlider2Section from "@/components/home/home-2/ProjectSlider2Section"; -import TestimonialsSection from "@/components/home/home-1/TestimonialsSection"; -import IconCounterSection from "@/components/home/home-2/IconCounterSection"; -import HelpFormSection from "@/components/home/home-2/HelpFormSection"; -import Blog2Section from "@/components/home/home-2/Blog2Section"; -import BrandSection from "@/components/home/home-1/BrandSection"; -import ChooseSection from "@/components/home/home-1/ChooseSection"; -import FaqSection from "@/components/home/home-3/FaqSection"; +import React from "react"; +import type { Metadata } from "next"; import Header1 from "@/components/layout/Header1"; import Footer1 from "@/components/layout/Footer1"; -import OfferSection from "@/components/home/OfferSection"; -import WhyChooseSection from "@/components/careers/WhyChooseSection"; + import PageHeader from "@/components/common/PageHeader"; -import HistoryTwo from "@/components/home/HistoryTwo"; +import About2Section from "@/components/home/home-2/About2Section"; import WhyChooseTwo from "@/components/home/WhyChooseTwo"; +import HistoryTwo from "@/components/home/HistoryTwo"; +import BrandSection from "@/components/home/home-1/BrandSection"; +import TestimonialsSection from "@/components/home/home-1/TestimonialsSection"; import ContactSection from "@/components/careers/ContactSection"; import CouterAreaThree from "@/components/home/CounterAreaThree"; +import MetatronInitializer from "@/components/MetatronInitializer"; + +export const metadata: Metadata = { + title: "About - Metatroncube Software Solutions | Innovative & User-Centric Tech Services in Waterloo", + description: "Metatroncube Software Solutions: Pioneering custom web & mobile apps since 2019. Based in Waterloo, we deliver innovative, user-centric digital products that power your growth.", + alternates: { + canonical: "/about-us", + }, +}; export default function Home2() { - useEffect(() => { - const init = () => { - if (typeof window !== "undefined" && (window as any).initMetatron && (window as any).jQuery) { - (window as any).initMetatron(); - } else { - setTimeout(init, 100); - } - }; - init(); - }, []); - return ( <> +
- {/* */} - {/* */} - {/* */} - - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - - - {/* */} - - {/* */} - {/* */} - {/* */} - {/* */}
diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 8fb11f1..634aac0 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -1,9 +1,22 @@ import React from "react"; import { BlogData } from "@/utils/constant.utils"; import BlogDetailsClient from "./BlogDetailsClient"; +import { Metadata } from "next"; const blogs = BlogData; +export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { + const { slug } = await params; + const blog = blogs.find((b) => b.slug === slug); + + return { + title: blog?.metatitle || blog?.title || "Blog Details", + description: blog?.metaDisc || blog?.description || "Read more on Metatroncube blog.", + alternates: { + canonical: `/blog/${slug}`, + }, + }; +} export async function generateStaticParams() { return blogs.map((blog) => ({ diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 5ce81e9..c48f2f2 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -1,21 +1,23 @@ -"use client"; - -import React, { useEffect } from "react"; +import React from "react"; +import type { Metadata } from "next"; import Header1 from "@/components/layout/Header1"; import Footer1 from "@/components/layout/Footer1"; import BlogSection from "@/components/home/home-1/BlogSection"; import PageHeader from "@/components/common/PageHeader"; -import SectionTitle from "@/components/common/SectionTitle"; +import MetatronInitializer from "@/components/MetatronInitializer"; + +export const metadata: Metadata = { + title: "Top 5 SEO Trends for 2025 | Metatroncube", + description: "Explore the latest SEO strategies for 2025. Stay ahead with insights from Metatroncube Software Solutions.", + alternates: { + canonical: "/blog", + }, +}; export default function BlogPage() { - useEffect(() => { - if (typeof window !== "undefined" && (window as any).initMetatron) { - (window as any).initMetatron(); - } - }, []); - return ( <> +
- {/*
- -
-
-

- Stay updated with our latest industry insights, success stories, and technical guides. - We share our expertise to help your business grow and stay ahead in the digital landscape. -

-
-
-
*/}
diff --git a/src/app/careers/page.tsx b/src/app/careers/page.tsx index d25731d..081c6e7 100644 --- a/src/app/careers/page.tsx +++ b/src/app/careers/page.tsx @@ -1,49 +1,35 @@ -"use client"; - -import React, { useEffect } from "react"; +import React from "react"; +import type { Metadata } from "next"; import Header1 from "@/components/layout/Header1"; import Footer1 from "@/components/layout/Footer1"; -import InnerBanner from "@/components/common/InnerBanner"; -import AboutOneSection from "@/components/careers/AboutOneSection"; -import WhyChooseUs from "@/components/about/WhyChooseUs"; -import FaqVideoSection from "@/components/careers/FaqVideoSection"; -import ContactSection from "@/components/careers/ContactSection"; -import ServiceSection from "@/components/careers/ServiceSection"; -import WorkProcessSection from "@/components/careers/WorkProcessSection"; -import FeaturesSection from "@/components/home/home-1/FeaturesSection"; import PageHeader from "@/components/common/PageHeader"; -import AboutService from "@/components/services-digital-solutions/AboutService"; -import AboutThree from "@/components/home/AboutThree"; -import FaqFour from "@/components/home/FaqFour"; -import ProjectsSection from "@/components/home/ProjectsSection"; +import WhyChooseSection from "@/components/careers/WhyChooseSection"; +import ContactSection from "@/components/careers/ContactSection"; +import FaqVideoSection from "@/components/careers/FaqVideoSection"; +import MetatronInitializer from "@/components/MetatronInitializer"; + +export const metadata: Metadata = { + title: "Careers - Metatroncube Software Solutions | Innovative & User-Centric Tech Services in Waterloo", + description: "Join MetatronCube — where innovation in web & mobile development, SEO, and digital marketing meets your growth. Explore careers and build the future with us.", + alternates: { + canonical: "/careers", + }, +}; export default function CareersPage() { - useEffect(() => { - if (typeof window !== "undefined" && (window as any).initMetatron) { - (window as any).initMetatron(); - } - }, []); - return ( <> +
- - {/* */} - - - - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} + + +
diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx index d63ae2b..eb92b47 100644 --- a/src/app/contact/page.tsx +++ b/src/app/contact/page.tsx @@ -1,27 +1,24 @@ -"use client"; - -import React, { useEffect } from "react"; +import React from "react"; +import type { Metadata } from "next"; import Header1 from "@/components/layout/Header1"; import Footer1 from "@/components/layout/Footer1"; -import InnerBanner from "@/components/common/InnerBanner"; import ContactSection from "@/components/contact/ContactSection"; import MapSection from "@/components/contact/MapSection"; import PageHeader from "@/components/common/PageHeader"; +import MetatronInitializer from "@/components/MetatronInitializer"; + +export const metadata: Metadata = { + title: "Contact - Metatroncube Software Solutions | Innovative & User-Centric Tech Services in Waterloo", + description: "Let's Connect. Drop us a Line. Whether you have a question, a project idea, or just want to chat, we're here for you.", + alternates: { + canonical: "/contact", + }, +}; export default function ContactPage() { - useEffect(() => { - const init = () => { - if (typeof window !== "undefined" && (window as any).initMetatron && (window as any).jQuery) { - (window as any).initMetatron(); - } else { - setTimeout(init, 100); - } - }; - init(); - }, []); - return ( <> +
{ - const init = () => { - if (typeof window !== "undefined" && (window as any).initMetatron && (window as any).jQuery) { - (window as any).initMetatron(); - } else { - setTimeout(init, 100); - } - }; - init(); - }, []); - return ( <> +
- {/* */}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2b5a7c5..8265bd7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -9,47 +9,182 @@ import SearchOverlay from "@/components/common/SearchOverlay"; import Offcanvas from "@/components/common/Offcanvas"; export const metadata: Metadata = { - title: "Metatroncube - IT Solutions & Technology", - description: "IT Solutions & Technology", + metadataBase: new URL("https://metatroncubesolutions.com"), + title: "Metatroncube: Leaders in Web & Mobile Dev, SEO, Digital Marketing", + description: "Metatroncube Software Solutions: Your go-to agency for cutting-edge web & app development, SEO, digital marketing, and graphic design services", + robots: { + index: true, + follow: true, + }, }; export default function RootLayout({ - children, + children, }: { - children: React.ReactNode; + children: React.ReactNode; }) { - return ( - - - - - - - - - - - - - - - - - - - {children} + return ( + + - + + + + + + + {/* ✅ GTM noscript */} + + + + + + + {children} + + {/* JS */} +