diff --git a/.lintstagedrc.js b/.lintstagedrc.js index 717b7424a3c85..74436ca10182c 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -1,7 +1,7 @@ module.exports = { // Lint & Prettify TS and JS files - only the staged files "**/*.(ts|tsx|js|jsx)": (filenames) => [ - `cross-env NODE_ENV=test npx eslint --fix --max-warnings=0 ${filenames.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ")}`, + `cross-env NODE_ENV=test npx eslint --fix --max-warnings=0 --no-warn-ignored ${filenames.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ")}`, `npx prettier --write ${filenames.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ")}`, ], diff --git a/Makefile b/Makefile index bc86a2e26bc8b..8a821a3c1da79 100644 --- a/Makefile +++ b/Makefile @@ -19,27 +19,43 @@ setup: npm install --legacy-peer-deps # "make site" - The default lightweight build keeps the dev server fast by skipping heavy collections. -## Run a partial build of layer5.io on your local machine. +## Run a lightweight dev server (core profile: excludes blog, events, integrations, members, news, resources). site: - @echo "🏗️ Building lightweight site version (core profile excludes Members, Integrations, Blog, News, Events, and Resources collections)..." - @echo " Use LITE_BUILD_PROFILE=content make site to include content collections while still skipping the heaviest routes." + @echo "🏗️ Starting dev server — core profile (fastest startup)" + @echo " Excluded: blog, events, integrations, members, news, resources" + @echo " → To include blog/news/events/resources: make site-content" + @echo " → To include all collections: make site-full" + @echo " → See all profiles: make profiles" @npm run develop:lite +## Run a content dev server (content profile: includes blog, news, events, resources; skips members and integrations). +site-content: + @echo "🏗️ Starting dev server — content profile" + @echo " Excluded: integrations, members" + @echo " → For the lightest build: make site" + @echo " → To include all collections: make site-full" + @npm run develop:content + # "make site-full" forces the dev server to include every collection. -## Run a full build of layer5.io on your local machine. +## Run a full dev server (all collections; slowest startup — includes members and integrations). site-full: - @echo "🏗️ Building full site version (including Members and Integrations collections)..." + @echo "🏗️ Starting dev server — full profile (all collections)" + @echo " → For a faster dev server: make site" @npm run develop -## Run layer5.io on your local machine. Alternate method. +## Run dev server directly via gatsby CLI, bypassing env-cmd (skips .env.development; Linux/macOS only). site-fast: BUILD_FULL_SITE=false LITE_BUILD_PROFILE=core GATSBY_CPU_COUNT=4 SHARP_CONCURRENCY=4 UV_THREADPOOL_SIZE=4 NODE_OPTIONS=--max-old-space-size=8192 gatsby develop -## Build layer5.io on your local machine. +## Build layer5.io for production. build: npm run build -## Empty build cache and rebuild layer5.io on your local machine (developer use only; CI uses `npm run build` directly). +## Clear Gatsby build cache without rebuilding (use before switching profiles or after dependency changes). +cache-clean: + npm run clean + +## Clear Gatsby build cache and run a full production build (slow; use make cache-clean to skip the rebuild). clean: npm run clean && make build @@ -47,19 +63,38 @@ clean: lint: npm run lint -## Kill process running the site +## Kill process running the site. kill: lsof -ti:8000 | xargs kill -9 2>/dev/null || true ## Prepare a list of features for the pricing page. -features: +features: curl -L https://docs.google.com/spreadsheets/d/e/2PACX-1vQwzrUSKfuSRcpkp7sJTw1cSB63s4HCjYLJeGPWECsvqn222hjaaONQlN4X8auKvlaB0es3BqV5rQyz/pub\?gid\=1153419764\&single\=true\&output\=csv -o .github/build/spreadsheet.csv node .github/build/features-to-json.js .github/build/spreadsheet.csv src/sections/Pricing/feature_data.json rm .github/build/spreadsheet.csv -.PHONY: setup build site site-full clean site-fast lint features +## List available build profiles and the collections each excludes. +profiles: + @echo "Build profiles:" + @echo "" + @echo " core (make site) — excludes: blog, events, integrations, members, news, resources" + @echo " content (make site-content) — excludes: integrations, members" + @echo " full (make site-full) — excludes: nothing (all collections included)" + @echo " none (make site-custom) — excludes: only what BUILD_COLLECTIONS_EXCLUDE specifies" + @echo "" + @echo "À la carte (comma-separated collection names to exclude):" + @echo " BUILD_COLLECTIONS_EXCLUDE=members,events make site-custom" -## Analyze webpack bundle with FCP optimization +## Run dev server with only the collections you specify (set BUILD_COLLECTIONS_EXCLUDE to exclude by name). +site-custom: + @echo "🏗️ Starting dev server — custom profile (none base + explicit exclusions)" + @echo " Excluded: $${BUILD_COLLECTIONS_EXCLUDE:-none}" + @echo " Example: BUILD_COLLECTIONS_EXCLUDE=members,events make site-custom" + @npm run develop:custom + +## Analyze webpack bundle composition (opens browser with interactive bundle breakdown). site-analyze: @echo "🏗️ Building site with webpack bundle analyzer..." ANALYZE_BUNDLE=true npm run build + +.PHONY: setup build site site-content site-full site-fast site-custom cache-clean clean lint kill features profiles site-analyze diff --git a/gatsby-node.js b/gatsby-node.js index 356b23dd86e25..f5eeb362a9340 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -635,6 +635,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { heading: "Member profiles disabled in lite mode", description: "The members collection is intentionally skipped when BUILD_FULL_SITE=false to keep local builds fast.", + enabledBy: "full", }, }, { @@ -646,6 +647,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { heading: "Integrations disabled in lite mode", description: "Integrations are heavy to source, so this route shows a placeholder during lightweight builds.", + enabledBy: "full", }, }, { @@ -657,6 +659,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { heading: "Blog posts disabled in lite mode", description: "The default lightweight build skips the blog collection to keep local builds responsive.", + enabledBy: "content", }, }, { @@ -668,6 +671,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { heading: "News posts disabled in lite mode", description: "The default lightweight build skips the news collection to reduce local memory consumption.", + enabledBy: "content", }, }, { @@ -679,6 +683,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { heading: "Resources disabled in lite mode", description: "The default lightweight build skips the resources collection to reduce local memory consumption.", + enabledBy: "content", }, }, { @@ -690,6 +695,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { heading: "Events disabled in lite mode", description: "The default lightweight build skips the events collection to keep local builds responsive.", + enabledBy: "content", }, }, ]; @@ -700,7 +706,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { envCreatePage({ path: page.path, matchPath: page.matchPath, - context: page.context, + context: { ...page.context, collection: page.collection }, component: LitePlaceholderTemplate, }), ); diff --git a/package.json b/package.json index fbcf9d651896b..e148d256b555d 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,11 @@ "clean:all": "gatsby clean && rimraf node_modules", "develop": "cross-env BUILD_FULL_SITE=true GATSBY_CPU_COUNT=4 SHARP_CONCURRENCY=4 UV_THREADPOOL_SIZE=4 NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop", "develop:lite": "cross-env BUILD_FULL_SITE=false LITE_BUILD_PROFILE=core GATSBY_CPU_COUNT=4 SHARP_CONCURRENCY=4 UV_THREADPOOL_SIZE=4 NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop", + "develop:content": "cross-env BUILD_FULL_SITE=false LITE_BUILD_PROFILE=content GATSBY_CPU_COUNT=4 SHARP_CONCURRENCY=4 UV_THREADPOOL_SIZE=4 NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop", + "develop:custom": "cross-env BUILD_FULL_SITE=false LITE_BUILD_PROFILE=none GATSBY_CPU_COUNT=4 SHARP_CONCURRENCY=4 UV_THREADPOOL_SIZE=4 NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop", "dev": "npm run develop:lite", "start": "npm run develop:lite", + "start:content": "npm run develop:content", "start:full": "npm run develop", "serve": "gatsby serve", "lint": "eslint --fix .", diff --git a/src/templates/lite-placeholder.js b/src/templates/lite-placeholder.js index 8c908e42ce96d..c5f55ae24cee6 100644 --- a/src/templates/lite-placeholder.js +++ b/src/templates/lite-placeholder.js @@ -1,55 +1,351 @@ -import React from "react"; +import React, { useState, useCallback, useEffect, useRef } from "react"; import SEO from "../components/seo"; +const BRAND_COLOR = "#00b39f"; + +const ALL_COLLECTIONS = [ + "blog", + "events", + "integrations", + "members", + "news", + "resources", +]; + +// Which profile is the minimum required to enable each collection. +const COLLECTION_WEIGHT = { + blog: "content", + events: "content", + news: "content", + resources: "content", + integrations: "full", + members: "full", +}; + +// Quick-restore commands shown above the picker, keyed by enabledBy value. +const RESTORE_COMMANDS = { + content: [ + { + cmd: "make site-content", + note: "includes blog, news, events, resources — skips members and integrations", + }, + { cmd: "make site-full", note: "includes all collections" }, + ], + full: [{ cmd: "make site-full", note: "includes all collections" }], +}; + +const CONTENT_EXCLUSIONS = new Set(["integrations", "members"]); + +// Derive the right command from the set of collections the user wants to include. +function generateCommand(included) { + const excluded = ALL_COLLECTIONS.filter((c) => !included.has(c)).sort(); + + if (excluded.length === 0) return "make site-full"; + if (excluded.length === ALL_COLLECTIONS.length) return "make site"; + + const isContentProfile = + excluded.length === CONTENT_EXCLUSIONS.size && + excluded.every((c) => CONTENT_EXCLUSIONS.has(c)); + if (isContentProfile) return "make site-content"; + + return `BUILD_COLLECTIONS_EXCLUDE=${excluded.join(",")} make site-custom`; +} + +// ── Copy button ──────────────────────────────────────────────────────────────── + +function CopyButton({ text }) { + const [copied, setCopied] = useState(false); + const timerRef = useRef(null); + + useEffect(() => { + return () => { + if (timerRef.current) clearTimeout(timerRef.current); + }; + }, []); + + const handleCopy = useCallback(() => { + if ( + typeof navigator === "undefined" || + typeof window === "undefined" || + !window.isSecureContext || + !navigator.clipboard?.writeText + ) { + return; + } + + navigator.clipboard + .writeText(text) + .then(() => { + setCopied(true); + if (timerRef.current) clearTimeout(timerRef.current); + timerRef.current = setTimeout(() => setCopied(false), 1500); + }) + .catch(() => {}); + }, [text]); + + return ( + <> + + + {copied ? "Copied!" : ""} + + + ); +} + +// ── Single command row with optional note ────────────────────────────────────── + +function CommandLine({ cmd, note }) { + return ( +
+
+ + {cmd} + + +
+ {note && ( +

+ {note} +

+ )} +
+ ); +} + +// ── Main template ────────────────────────────────────────────────────────────── + const LitePlaceholder = ({ pageContext, location }) => { const { heading = "Content disabled in lite mode", description = "This route is intentionally skipped when BUILD_FULL_SITE=false.", + enabledBy = "full", + collection, } = pageContext; - const instructions = - "Run `make site-full` (or set BUILD_FULL_SITE=true) to source the full dataset, then reload this path."; + const restoreCommands = RESTORE_COMMANDS[enabledBy] ?? RESTORE_COMMANDS.full; + + // Pre-check the current route's collection so the generated command is immediately useful. + const [included, setIncluded] = useState(() => { + const initial = new Set(); + if (collection && ALL_COLLECTIONS.includes(collection)) + initial.add(collection); + return initial; + }); + + const toggle = useCallback((name) => { + setIncluded((prev) => { + const next = new Set(prev); + next.has(name) ? next.delete(name) : next.add(name); + return next; + }); + }, []); + + const customCommand = generateCommand(included); return ( - <> - -
+

-

- {heading} + {heading} +

+

+ {description} +

+ + {/* ── Quick restore ─────────────────────────────────────────────────── */} +
+

+ Restart your dev server with one of these commands to restore this + route:

-

{description}

-

- {instructions} + {restoreCommands.map(({ cmd, note }) => ( + + ))} +

+ + {/* ── À la carte picker ─────────────────────────────────────────────── */} +
+

+ Build collections à la carte

- {location?.pathname && ( -

- Requested path: {location.pathname} -

- )} -
- +

+ Check the collections you want included, then copy the generated + command. +

+
+ {ALL_COLLECTIONS.map((name) => ( + + ))} +
+ +
+ + {customCommand.startsWith("BUILD_COLLECTIONS_EXCLUDE") && ( +

+ Uses make site-custom which starts with no preset + exclusions, then applies only what you specify. +

+ )} +
+ + + {/* ── Contributing guide link ───────────────────────────────────────── */} +

+ All build profiles and environment variables are documented in the{" "} + + contributing guide + + . +

+ + {location?.pathname && ( +

+ Requested path: {location.pathname} +

+ )} + ); }; export default LitePlaceholder; export const Head = ({ pageContext }) => { - const { heading = "Content disabled in lite mode", description = "" } = - pageContext; - const instructions = - "Run make site-full or set BUILD_FULL_SITE=true to include heavy collections in development."; - + const { + heading = "Content disabled in lite mode", + description = "", + enabledBy = "full", + } = pageContext; + const commands = RESTORE_COMMANDS[enabledBy] ?? RESTORE_COMMANDS.full; return ( ); }; diff --git a/src/utils/build-collections.js b/src/utils/build-collections.js index 54bf6b780cbda..1dccee66480bf 100644 --- a/src/utils/build-collections.js +++ b/src/utils/build-collections.js @@ -1,6 +1,7 @@ const DEFAULT_LITE_BUILD_PROFILE = "core"; const LITE_BUILD_PROFILES = Object.freeze({ + none: [], content: ["members", "integrations"], core: ["members", "integrations", "blog", "news", "events", "resources"], }); @@ -16,7 +17,8 @@ const parseCsv = (value = "") => const getExcludedCollections = ({ isFullSiteBuild: shouldBuildFullSite = isFullSiteBuild(), - liteBuildProfile = process.env.LITE_BUILD_PROFILE || DEFAULT_LITE_BUILD_PROFILE, + liteBuildProfile = process.env.LITE_BUILD_PROFILE || + DEFAULT_LITE_BUILD_PROFILE, buildCollectionsExclude = process.env.BUILD_COLLECTIONS_EXCLUDE, } = {}) => { if (shouldBuildFullSite) { @@ -37,4 +39,4 @@ module.exports = { LITE_BUILD_PROFILES, getExcludedCollections, isFullSiteBuild, -}; \ No newline at end of file +};