-
Notifications
You must be signed in to change notification settings - Fork 2
Build Pipeline
360Ghar's npm run build is a 10-stage pipeline that ingests external entity data, generates SEO/AI-discovery artifacts, builds sitemaps and RSS feeds, optimizes images, runs the Vite production build, purges CSS, and prerenders pages with Puppeteer. Each stage is an idempotent Node.js script in scripts/; the orchestrator is the build script in package.json.
| File | Role |
|---|---|
package.json |
build script orchestrator + build:* sub-scripts |
netlify.toml |
Netlify build command, env vars, redirects, headers |
scripts/ingest-gurgaon-entities.mjs |
Stage 1a: fetch external sitemaps |
scripts/merge-entities.mjs |
Stage 1b: merge + dedupe |
scripts/build-localities-json.mjs |
Stage 1c: localities.json
|
scripts/build-localities-index.mjs |
Stage 1d: localities-index.json
|
scripts/generate-locality-sitemap.mjs |
Stage 1e + 3: locality sitemap |
scripts/write-ai-discovery.mjs |
Stage 2: AI discovery artifacts |
scripts/generate-sitemaps.mjs |
Stage 3: static + landing sitemaps |
scripts/generate-datahub-sitemap.mjs |
Stage 3: data hub sitemap |
scripts/generate-dynamic-sitemaps.mjs |
Stage 3: property + dynamic sitemaps |
scripts/generate-rss.mjs |
Stage 4: RSS feeds |
scripts/optimize-images.mjs |
Stage 5: AVIF/WebP image variants |
scripts/generate-og-image.mjs |
Stage 6: OG image |
vite.config.js |
Stage 7: Vite production build |
scripts/purge-main-css.mjs |
Stage 8: PurgeCSS on main entry |
scripts/generate-prerender-routes.mjs |
Stage 9a: prerender route manifest |
scripts/prerender-pages.mjs |
Stage 9b: Puppeteer prerendering |
scripts/purge-bootstrap.mjs |
Stage 10: PurgeCSS on Bootstrap |
"build": "npm run build:entities && npm run build:ai-discovery && npm run build:sitemaps && npm run build:rss && npm run build:images && node scripts/generate-og-image.mjs && vite build && node scripts/purge-main-css.mjs && npm run build:prerender && node scripts/purge-bootstrap.mjs"Sub-scripts:
"build:entities": "node scripts/ingest-gurgaon-entities.mjs && node scripts/merge-entities.mjs && node scripts/build-localities-json.mjs && node scripts/build-localities-index.mjs && node scripts/generate-locality-sitemap.mjs",
"build:ai-discovery": "node scripts/write-ai-discovery.mjs",
"build:sitemaps": "node scripts/generate-sitemaps.mjs && node scripts/generate-locality-sitemap.mjs && node scripts/generate-datahub-sitemap.mjs && node scripts/generate-dynamic-sitemaps.mjs",
"build:rss": "node scripts/generate-rss.mjs",
"build:images": "node scripts/optimize-images.mjs --quiet",
"build:prerender": "npm run build:prerender-routes && node scripts/prerender-pages.mjs",
"build:prerender-routes": "node scripts/generate-prerender-routes.mjs"-
ingest-gurgaon-entities.mjs - fetches sitemaps from Magicbricks, SquareYards, NoBroker, CommonFloor; extracts
<loc>URLs matchinggurgaon|gurugram; decodes locality/society/project names. Output:scripts/reports/entity-raw.json. - merge-entities.mjs - dedupes + merges raw entities.
-
build-localities-json.mjs - writes
src/data/localities.json. -
build-localities-index.mjs - writes
src/data/localities-index.json(sorted, slugged). -
generate-locality-sitemap.mjs - writes
public/sitemap-localities.xml.
isPlaceholderName() filters junk tokens; each entity carries confidence and sourceCoverage. See SEO & Programmatic.
write-ai-discovery.mjs calls buildAiDiscoveryArtifacts() from src/seo/aiDiscovery.js and enriches the feed with the top 50 localities + 20 societies from localities-index.json. Outputs:
public/.well-known/ai.txtpublic/.well-known/api-catalogpublic/llms.txtpublic/llms-full.txtpublic/data/llm-feed.json
-
generate-sitemaps.mjs -
sitemap.xml(index),sitemap-static.xml,sitemap-landing.xml. Emits hreflang alternates (en,hi,x-default) for every URL, prunes viasrc/data/pseo-prune-list.json, and supportsSITEMAP_MAX_LANDING_PER_CITY/SITEMAP_BATCHfor phased releases. -
generate-locality-sitemap.mjs -
sitemap-localities.xml(re-run; idempotent). -
generate-datahub-sitemap.mjs -
sitemap-datahub.xml. -
generate-dynamic-sitemaps.mjs -
sitemap-properties.xml+ any dynamic batches (fetches live properties from the API).
generate-rss.mjs fetches blog posts and properties via scripts/lib/paginatedApi.mjs, plus localities from localities-index.json. Outputs:
-
public/rss.xml- main feed (blog + properties) -
public/rss/localities.xml- locality feed
Each item is XML-escaped and dated with toRfc2822().
optimize-images.mjs uses sharp to generate next-gen variants for every PNG/JPG under public/assets/images (above 50KB):
-
<name>.webp(quality 80) -
<name>.avif(quality 50) - For "responsive" hero/banner art, also width-constrained variants at 320 / 640 / 768 / 1024 px in both WebP + AVIF.
Idempotent: outputs are skipped if newer than the source. Flags: --force (regenerate all), --quiet (summary only).
generate-og-image.mjs resizes public/assets/images/thumbs/banner-three.webp to 1200x630 JPG (quality 85, progressive) at public/og-image-home.jpg for Open Graph / Twitter cards.
vite build runs the standard Vite production build (React plugin, SCSS, code-splitting, compression plugin, PWA plugin, bundle visualizer). Output: dist/.
purge-main-css.mjs runs PurgeCSS on the main entry CSS chunk (dist/assets/index-[hash].css, ~179KB from main.scss + component styles). Scans built HTML/JS + source JSX for used selectors, with a safelist for dynamic classes (show, collapsing, collapse, modal, etc.). Reduces the entry chunk significantly.
-
generate-prerender-routes.mjs - builds
scripts/prerender-routes.jsonfromindexableStaticRoutes,seedLandingPrerenderRoutes,seedLocalityPrerenderRoutes, and locality data. Each route haswaitForSelector/waitForText/waitForTitle. -
prerender-pages.mjs - spawns
vite previewon127.0.0.1:4317, launches headless Puppeteer (--no-sandboxon Linux), and for each route:- Navigates with a 60s timeout.
- Waits for the configured signal.
- Sets
window.__PRERENDER_INJECTED = { isPrerendering: true }. - Serializes
page.content()todist/<route>.html. - Rewrites stylesheets to be non-blocking (strips duplicate
<noscript>copies, convertsmedia="print"preloads).
The isPrerendering flag is checked by authStore.initializeAuth, locationStore.initializeLocation, posthogService.init, and main.jsx to skip network calls.
purge-bootstrap.mjs runs PurgeCSS on public/assets/css/bootstrap.min.css (~190KB) against built HTML/JS + source JSX, reducing it to ~40-50KB. Safelist preserves dynamically-added classes (show, collapsing, collapse, modal, tooltip, popover, fade, active, disabled, open, btn-, form-, and col- responsive variants).
flowchart TD
S1[1. Entity Ingestion] --> S2[2. AI Discovery]
S2 --> S3[3. Sitemaps]
S3 --> S4[4. RSS]
S4 --> S5[5. Image Optimization]
S5 --> S6[6. OG Image]
S6 --> S7[7. Vite Build]
S7 --> S8[8. Main CSS Purge]
S8 --> S9[9. Prerender]
S9 --> S10[10. Bootstrap Purge]
S10 --> Dist[dist/ deployed to Netlify]
netlify.toml config:
[build]
publish = "dist"
command = "npx puppeteer browsers install chrome && npm run build"
[build.environment]
PUPPETEER_CACHE_DIR = "./node_modules/.cache/puppeteer"
NODE_VERSION = "20"
VITE_API_SERVER = "https://api.360ghar.com"
VITE_API_BASE_URL = "https://api.360ghar.com/api/v1"Puppeteer's Chrome is installed explicitly before the build so prerendering works in Netlify's CI. Key redirects:
-
www.360ghar.com->360ghar.com(301) - trailing-slash normalization (301)
-
/gurugram/*->/gurgaon/*(301) -
/apartments->/flats(301, all intents) - Hindi equivalents under
/hi/* - SPA catch-all
/*->/index.html(200)
Cache headers: hashed JS/CSS/fonts are immutable (1 year); images 30 days + stale-while-revalidate; HTML always revalidates; service worker never cached. The root / advertises AI-discovery Link headers (RFC 8288) for api-catalog, service-doc, service-meta, mcp-server, agent-skills, llms-txt, openid-configuration.
-
ESLint must stay on major version 9 (
@eslint/js^9.39.4).eslint-plugin-react@7.xdeclarespeerDependency: eslint ^2 || ... || ^9and does not support eslint 10+. Upgrading to eslint 10 causes Netlifynpm installto fail withERESOLVE. - Node >= 18 (Netlify pins 20).
- Puppeteer ^24.40.0 (devDependency) for prerendering.
- sharp ^0.34.5 for image optimization.
- Before upgrading any devDependency to a new major, check peer ranges with
npm info <pkg> peerDependencies, runnpm installlocally, and runnpm run build && npm run lint.
- SEO & Programmatic - what the sitemap/prerender stages produce
- Internationalization - Hindi hreflang twins in sitemaps
- Authentication - prerender skips auth init
- Analytics - prerender skips PostHog