UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

234 lines (226 loc) 8.97 kB
import pkgJson from 'next/package.json' with { type: 'json' }; import { execSync } from 'node:child_process'; import { readFileSync } from 'node:fs'; import * as os from 'node:os'; import { dirname, join } from 'node:path'; import { pathToFileURL } from 'node:url'; // Read Next.js version to handle version-specific config const nextMajorVersion = parseInt(pkgJson.version.split('.')[0], 10); /** * See the docs of the Netlify environment variables: * https://docs.netlify.com/configure-builds/environment-variables/#build-metadata. * * A few comments: * - process.env.CONTEXT === 'production' means that the branch in Netlify was configured as production. * For example, the `master` branch of the Core team is considered a `production` build on Netlify based * on https://app.netlify.com/sites/material-ui/settings/deploys#branches. * - Each team has different site https://app.netlify.com/teams/mui/sites. * The following logic must be compatible with all of them. */ let DEPLOY_ENV = 'development'; // Same as process.env.PULL_REQUEST_ID if (process.env.CONTEXT === 'deploy-preview') { DEPLOY_ENV = 'pull-request'; } if (process.env.CONTEXT === 'production' || process.env.CONTEXT === 'branch-deploy') { DEPLOY_ENV = 'production'; } // The 'master' and 'next' branches are NEVER a production environment. We use these branches for staging. if ((process.env.CONTEXT === 'production' || process.env.CONTEXT === 'branch-deploy') && (process.env.HEAD === 'master' || process.env.HEAD === 'next')) { DEPLOY_ENV = 'staging'; } /** * ==================================================================================== */ process.env.DEPLOY_ENV = DEPLOY_ENV; const SHOW_PRIVATE_PAGES = String(process.env.DEPLOY_ENV !== 'production' && process.env.DEPLOY_ENV !== 'staging'); process.env.SHOW_PRIVATE_PAGES = SHOW_PRIVATE_PAGES; /** * URL prefix pointing at the source tree of the currently-deployed commit * (e.g. `https://github.com/owner/repo/tree/<ref>/`). Used by demo * factories to rewrite local `file://` URLs gathered at build time into * hosted Git URLs. * * Resolution order: * - Repository URL: `process.env.REPOSITORY_URL` (set by Netlify), falling * back to the `repository` field of the nearest ancestor `package.json`. * - Ref: `process.env.HEAD` (set by Netlify to the source branch name, even * on deploy-previews where `BRANCH` is the unbrowsable `pull/<id>/head`) * or `git rev-parse --abbrev-ref HEAD` locally — preferred so links keep * tracking new commits on the branch. Falls back to * `process.env.COMMIT_REF` (the deployed SHA) and finally to * `git rev-parse HEAD`. * * Resolves to an empty string when neither source yields a value. */ const repoRootDir = findRepoRootDir(); // Stored as a `file://` URL (with exactly one trailing slash) so consumers // can use it in isomorphic code without depending on Node's `url` module to // convert from a filesystem path. We normalize via the `URL` object so a repo // root that already ends in a separator (e.g. `/` or `C:\` at a drive root) // doesn't produce a double-slash suffix. const SOURCE_CODE_ROOT_DIR = repoRootDir ? toDirFileUrl(repoRootDir) : ''; const SOURCE_CODE_ROOT_URL = resolveSourceCodeRootUrl(repoRootDir); process.env.SOURCE_CODE_ROOT_DIR = SOURCE_CODE_ROOT_DIR; process.env.SOURCE_CODE_ROOT_URL = SOURCE_CODE_ROOT_URL; function toDirFileUrl(dir) { const url = pathToFileURL(dir); if (!url.pathname.endsWith('/')) { url.pathname = `${url.pathname}/`; } return url.href; } function resolveSourceCodeRootUrl(rootDir) { const repositoryUrl = process.env.REPOSITORY_URL ?? (rootDir ? readRepositoryUrlFromPackageJson(rootDir) : undefined); const headEnv = process.env.HEAD; const ref = (headEnv && isUsableBranchRef(headEnv) ? headEnv : undefined) ?? readBranchFromGit() ?? process.env.COMMIT_REF ?? readCommitShaFromGit(); if (!repositoryUrl || !ref) { return ''; } const repoBase = repositoryUrl.replace(/^git\+/, '').replace(/\.git$/, '').replace(/\/$/, ''); return `${repoBase}/tree/${ref}/`; } function findRepoRootDir() { // Prefer git: `git rev-parse --show-toplevel` returns the absolute path to // the repository root regardless of which subdirectory we run from. try { const top = execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); if (top) { return top; } } catch { // git unavailable or not a repo, fall through to filesystem walk. } // Fallback: walk up looking for the root `package.json` (one with a // `repository` field, indicating it's the project root rather than a // workspace package). let dir = process.cwd(); while (true) { try { const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')); if (pkg.repository) { return dir; } } catch { // package.json missing or unreadable at this level, keep walking up. } const parent = dirname(dir); if (parent === dir) { return undefined; } dir = parent; } } function readRepositoryUrlFromPackageJson(rootDir) { try { const pkg = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf8')); const repo = pkg.repository; return typeof repo === 'string' ? repo : repo?.url; } catch { return undefined; } } function readBranchFromGit() { try { const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); return isUsableBranchRef(branch) ? branch : undefined; } catch { return undefined; } } /** * A branch ref is usable as a `tree/<ref>` segment in a GitHub URL when it * looks like an actual branch name. We reject: * - empty strings * - `HEAD` (detached checkout, e.g. on Netlify) * - `pull/<id>/head` and `pull/<id>/merge` (Netlify deploy-preview refs that * GitHub doesn't expose as browsable trees) */ function isUsableBranchRef(branch) { if (!branch || branch === 'HEAD') { return false; } if (/^pull\/\d+\/(head|merge)$/.test(branch)) { return false; } return true; } function readCommitShaFromGit() { try { const sha = execSync('git rev-parse HEAD', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); return sha || undefined; } catch { return undefined; } } export function withDeploymentConfig(nextConfig) { return { trailingSlash: true, reactStrictMode: true, productionBrowserSourceMaps: true, ...nextConfig, env: { // production | staging | pull-request | development DEPLOY_ENV, SHOW_PRIVATE_PAGES, ...nextConfig.env, // https://docs.netlify.com/configure-builds/environment-variables/#git-metadata // reference ID (also known as "SHA" or "hash") of the commit we're building. COMMIT_REF: process.env.COMMIT_REF, // ID of the PR and the Deploy Preview it generated (for example, 1211) PULL_REQUEST_ID: process.env.REVIEW_ID, // This can be set manually in the .env to see the ads in dev mode. ENABLE_AD_IN_DEV_MODE: process.env.ENABLE_AD_IN_DEV_MODE, // URL representing the unique URL for an individual deploy, e.g. // https://5b243e66dd6a547b4fee73ae--petsof.netlify.app SITE_DEPLOY_URL: process.env.DEPLOY_URL, // Name of the site, its Netlify subdomain; for example, material-ui-docs SITE_NAME: process.env.SITE_NAME, // URL for the linked Git repository. REPOSITORY_URL: process.env.REPOSITORY_URL, // Reference to check out after fetching changes from the Git repository. // Can be useful for split testing. BRANCH: process.env.BRANCH, // URL prefix pointing at the source tree of the currently-deployed // commit (e.g. `https://github.com/owner/repo/tree/<branch>/`). Derived // from REPOSITORY_URL/BRANCH with package.json/git fallbacks. SOURCE_CODE_ROOT_URL, // `file://` URL (with trailing slash) of the repository root, used to // translate `import.meta.url` file URLs into paths relative to the repo // root before applying SOURCE_CODE_ROOT_URL. SOURCE_CODE_ROOT_DIR, // For template images TEMPLATE_IMAGE_URL: '' }, experimental: { scrollRestoration: true, workerThreads: false, ...(process.env.CI ? { cpus: process.env.NEXT_PARALLELISM ? parseInt(process.env.NEXT_PARALLELISM, 10) : os.availableParallelism() } : {}), ...nextConfig.experimental }, ...(nextMajorVersion < 16 ? { // TODO remove this once all our projects are on Next.js 16+ // https://nextjs.org/blog/next-16 eslint: { ignoreDuringBuilds: true, ...nextConfig.eslint } } : {}), typescript: { // Motivated by https://github.com/vercel/next.js/issues/7687 ignoreBuildErrors: true, ...nextConfig.typescript } }; }