Ctrl K
ComunidadGuías…

packages/app/next.config.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { configureRuntimeEnv } from 'next-runtime-env/build/configure.js';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Read version from package.json
const packageJson = JSON.parse(
  readFileSync(join(__dirname, 'package.json'), 'utf-8'),
);
const { version } = packageJson;

// Generate public/whats-new.json — the recent releases' headlines that the Help
// menu's "What's new" section fetches as a small static asset (instead of
// shipping the whole, ever-growing changelog). Reads the repo-root CHANGELOG.md,
// the release-level summary, not the app-only package changelog. Done here
// (rather than a package.json pre-script) because Yarn 4 does not run arbitrary
// pre/post lifecycle scripts; next.config is evaluated by both `next dev`
// (Turbopack) and `next build` (Webpack), so this runs in every build mode. The
// ClickStack static export additionally needs `whats-new.json` allow-listed in
// scripts/prepare-clickhouse-build-export.js, and the Docker builder stages must
// COPY the root CHANGELOG.md and scripts/ in as build inputs (see the
// Dockerfiles).
//
// The parser is imported dynamically, inside the try, on purpose. A static
// top-level import is resolved before this module's body runs, so it would be
// unguardable: the prod image copies next.config.mjs on its own and re-evaluates
// it under `next start`, where no build sources are present — a static import
// there crashes the container on startup instead of harmlessly falling through
// to the already-generated public/whats-new.json.
try {
  const { default: parseWhatsNew } = await import(
    './scripts/parse-whats-new.js'
  );
  const changelog = readFileSync(
    join(__dirname, '..', '..', 'CHANGELOG.md'),
    'utf-8',
  );
  const { releases } = parseWhatsNew(changelog, { maxReleases: 5 });

  writeFileSync(
    join(__dirname, 'public', 'whats-new.json'),
    JSON.stringify({ releases }),
  );
} catch (err) {
  // The invariant is "the app must not ship without the asset", so key the
  // failure on that rather than on which phase we think we are in. `next start`
  // re-evaluates this config at runtime, where the build sources are absent but
  // public/whats-new.json already exists from the build stage — that case is
  // fine. Nothing having produced the asset at all is not.
  //
  // Deliberately not keyed on NEXT_PHASE: that is an undocumented Next internal,
  // and if it were ever unset the build would fall through to a warn and ship an
  // image whose "What's new" is empty for every user.
  if (!existsSync(join(__dirname, 'public', 'whats-new.json'))) {
    throw new Error(
      `Failed to generate whats-new.json and no file exists from an earlier ` +
        `build, so the app would ship without it: ${err.message}`,
    );
  }
  console.warn(
    'Could not regenerate public/whats-new.json; using the existing file:',
    err.message,
  );
}

// The newest RELEASE version in the notes, which keys the Help-button "you
// haven't read the latest release notes" sparkle.
//
// Deliberately not NEXT_PUBLIC_APP_VERSION: any deployment that stamps a build
// id into that (a git short SHA, a CI build number) mints a new string on every
// deploy, so the nudge fired for every user on every deploy whether the notes
// had changed or not. This comes from the changelog headings instead, so it moves
// only when a release is published.
//
// Read back from disk rather than off the object written above, so the fallback
// path — regeneration failed and an earlier build's asset is being shipped — is
// keyed to the asset that actually ships.
const whatsNewVersion = JSON.parse(
  readFileSync(join(__dirname, 'public', 'whats-new.json'), 'utf-8'),
).releases[0]?.version;

// Support legacy consumers of next-runtime-env that expect this value under window.__ENV
process.env.NEXT_PUBLIC_APP_VERSION = version;

configureRuntimeEnv();

const basePath = process.env.NEXT_PUBLIC_HYPERDX_BASE_PATH;

const nextConfig = {
  // Allow overriding the build/dev output directory to avoid lock conflicts
  // when running dev and E2E simultaneously (e.g. NEXT_DIST_DIR=.next-e2e)
  ...(process.env.NEXT_DIST_DIR ? { distDir: process.env.NEXT_DIST_DIR } : {}),
  reactCompiler: true,
  // click-ui ships per-component CSS imports from node_modules, which Next
  // rejects as global CSS unless the package is transpiled.
  transpilePackages: ['@clickhouse/click-ui'],
  basePath: basePath,
  env: {
    // Ensures bundler-time replacements for client/server code that references this env var
    NEXT_PUBLIC_APP_VERSION: version,
    // Inlined at build time rather than exposed through __ENV.js: builds that
    // omit that script (static exports, embedded builds) still need the sparkle.
    NEXT_PUBLIC_WHATS_NEW_VERSION: whatsNewVersion,
  },
  // External packages to prevent bundling issues (moved from experimental in Next.js 15+)
  // https://github.com/open-telemetry/opentelemetry-js/issues/4297#issuecomment-2285070503
  serverExternalPackages: [
    '@opentelemetry/instrumentation',
    '@opentelemetry/sdk-node',
    '@opentelemetry/auto-instrumentations-node',
    '@clickhouse/clickstack-node',
    // Outside of Vercel preview deployments, the `/api/[...all]` catch-all
    // proxies to a separately-deployed API service and never imports the
    // `@hyperdx/api` package at runtime. Mark it (and its subpaths) as a
    // CommonJS external so production app builds (Docker fullstack image,
    // standalone Next output) stay byte-for-byte equivalent to today and
    // do not pull in passport-saml, mongoose, AWS SDK, etc.
    ...(process.env.HDX_PREVIEW_INLINE_API !== 'true' ? ['@hyperdx/api'] : []),
  ],
  typescript: {
    tsconfigPath: 'tsconfig.build.json',
  },
  // Dev uses Turbopack; production build uses Webpack (--webpack).
  // Reason: Turbopack has CSS module parsing issues with nested :global syntax
  // used in styles/SearchPage.module.scss and other SCSS files.
  // TODO: single bundler when Turbopack CSS is solid.
  // Ignore otel warnings (Webpack): https://github.com/open-telemetry/opentelemetry-js/issues/4173#issuecomment-1822938936
  webpack: (
    config,
    { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack },
  ) => {
    if (isServer) {
      config.ignoreWarnings = [{ module: /opentelemetry/ }];

      if (process.env.HDX_PREVIEW_INLINE_API !== 'true') {
        config.externals = [
          ...(config.externals ?? []),
          ({ request }, callback) => {
            if (
              request === '@hyperdx/api' ||
              request?.startsWith?.('@hyperdx/api/')
            ) {
              return callback(null, `commonjs ${request}`);
            }
            return callback();
          },
        ];
      }
    }
    return config;
  },
  async headers() {
    return [
      {
        source: '/(.*)?', // Matches all pages
        headers: [
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
          ...(process.env.NEXT_PUBLIC_NOINDEX === 'true'
            ? [{ key: 'X-Robots-Tag', value: 'noindex, nofollow' }]
            : []),
        ],
      },
    ];
  },
  productionBrowserSourceMaps: false,
  ...(process.env.NEXT_OUTPUT_STANDALONE === 'true'
    ? {
        output: 'standalone',
      }
    : {}),
  ...(process.env.NEXT_PUBLIC_CLICKHOUSE_BUILD
    ? {
        assetPrefix: '/clickstack',
        basePath: '/clickstack',
        images: { unoptimized: true },
        output: 'export',
      }
    : {}),
  logging: {
    incomingRequests: {
      // We also log this in the API server, so we don't want to log it twice.
      ignore: [/\/api\/.*/],
    },
  },
};

export default nextConfig;