Skip to content

Commit 71b8a64

Browse files
msukkariclaude
andcommitted
perf(web): optimize build performance with layer caching and package imports
- Separate Docker build stages to copy package.json before source, enabling yarn install layer cache reuse on source-only changes - Consolidate 4 separate yarn install calls into single install with --mode=skip-build, avoiding redundant installs in shared-libs-builder - Add BuildKit cache mount for .next/cache to persist Next.js compilation cache across Docker builds - Enable experimental.optimizePackageImports for 18 barrel-export packages (lucide-react, radix-ui, ai-sdk providers, etc.) to reduce compilation time - Make Sentry widenClientFileUpload and reactComponentAnnotation conditional on Sentry credentials being present, removing overhead from OSS builds - Add --parallel to root build and build:deps scripts for concurrent builds of independent packages (db, schemas, queryLanguage) - Add new build:web script for building only web package and dependencies Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 7d2d4bb commit 71b8a64

File tree

3 files changed

+85
-23
lines changed

3 files changed

+85
-23
lines changed

Dockerfile

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,33 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /cmd/ ./cmd/...
3333
FROM node-alpine AS shared-libs-builder
3434
WORKDIR /app
3535

36+
# Step 1: Copy only package manifests for dependency installation.
37+
# This layer is cached as long as no package.json or lock file changes.
3638
COPY package.json yarn.lock* .yarnrc.yml ./
3739
COPY .yarn ./.yarn
40+
COPY ./packages/db/package.json ./packages/db/package.json
41+
COPY ./packages/db/prisma ./packages/db/prisma
42+
COPY ./packages/schemas/package.json ./packages/schemas/package.json
43+
COPY ./packages/shared/package.json ./packages/shared/package.json
44+
COPY ./packages/queryLanguage/package.json ./packages/queryLanguage/package.json
45+
# All workspace package.json files are needed for Yarn resolution
46+
COPY ./packages/web/package.json ./packages/web/package.json
47+
COPY ./packages/backend/package.json ./packages/backend/package.json
48+
COPY ./packages/mcp/package.json ./packages/mcp/package.json
49+
50+
RUN yarn install --mode=skip-build
51+
52+
# Step 2: Copy source files and build explicitly in topological order.
3853
COPY ./packages/db ./packages/db
54+
COPY ./schemas ./schemas
3955
COPY ./packages/schemas ./packages/schemas
40-
COPY ./packages/shared ./packages/shared
4156
COPY ./packages/queryLanguage ./packages/queryLanguage
57+
COPY ./packages/shared ./packages/shared
4258

43-
RUN yarn workspace @sourcebot/db install
44-
RUN yarn workspace @sourcebot/schemas install
45-
RUN yarn workspace @sourcebot/shared install
46-
RUN yarn workspace @sourcebot/query-language install
59+
RUN yarn workspace @sourcebot/db build && \
60+
yarn workspace @sourcebot/schemas build && \
61+
yarn workspace @sourcebot/query-language build && \
62+
yarn workspace @sourcebot/shared build
4763
# ------------------------------------
4864

4965
# ------ Build Web ------
@@ -79,20 +95,32 @@ ENV SENTRY_SMUAT=$SENTRY_SMUAT
7995
RUN apk add --no-cache libc6-compat
8096
WORKDIR /app
8197

98+
# Step 1: Install dependencies (cached unless package.json/lock changes).
8299
COPY package.json yarn.lock* .yarnrc.yml ./
83100
COPY .yarn ./.yarn
84-
COPY ./packages/web ./packages/web
85-
COPY --from=shared-libs-builder /app/node_modules ./node_modules
101+
COPY ./packages/web/package.json ./packages/web/package.json
102+
COPY ./packages/db/package.json ./packages/db/package.json
103+
COPY ./packages/db/prisma ./packages/db/prisma
104+
COPY ./packages/schemas/package.json ./packages/schemas/package.json
105+
COPY ./packages/shared/package.json ./packages/shared/package.json
106+
COPY ./packages/queryLanguage/package.json ./packages/queryLanguage/package.json
107+
COPY ./packages/backend/package.json ./packages/backend/package.json
108+
COPY ./packages/mcp/package.json ./packages/mcp/package.json
109+
110+
RUN yarn install --mode=skip-build
111+
112+
# Step 2: Copy pre-built shared libraries.
86113
COPY --from=shared-libs-builder /app/packages/db ./packages/db
87114
COPY --from=shared-libs-builder /app/packages/schemas ./packages/schemas
88115
COPY --from=shared-libs-builder /app/packages/shared ./packages/shared
89116
COPY --from=shared-libs-builder /app/packages/queryLanguage ./packages/queryLanguage
90117

91-
# Fixes arm64 timeouts
92-
RUN yarn workspace @sourcebot/web install
118+
# Step 3: Copy web source and build.
119+
COPY ./packages/web ./packages/web
93120

94121
ENV NEXT_TELEMETRY_DISABLED=1
95-
RUN yarn workspace @sourcebot/web build
122+
RUN --mount=type=cache,target=/app/packages/web/.next/cache \
123+
yarn workspace @sourcebot/web build
96124
ENV SKIP_ENV_VALIDATION=0
97125
# ------------------------------
98126

@@ -117,16 +145,28 @@ ENV SENTRY_RELEASE=$SENTRY_RELEASE
117145

118146
WORKDIR /app
119147

148+
# Step 1: Install dependencies (cached unless package.json/lock changes).
120149
COPY package.json yarn.lock* .yarnrc.yml ./
121150
COPY .yarn ./.yarn
122-
COPY ./schemas ./schemas
123-
COPY ./packages/backend ./packages/backend
124-
COPY --from=shared-libs-builder /app/node_modules ./node_modules
151+
COPY ./packages/backend/package.json ./packages/backend/package.json
152+
COPY ./packages/db/package.json ./packages/db/package.json
153+
COPY ./packages/db/prisma ./packages/db/prisma
154+
COPY ./packages/schemas/package.json ./packages/schemas/package.json
155+
COPY ./packages/shared/package.json ./packages/shared/package.json
156+
COPY ./packages/queryLanguage/package.json ./packages/queryLanguage/package.json
157+
COPY ./packages/web/package.json ./packages/web/package.json
158+
COPY ./packages/mcp/package.json ./packages/mcp/package.json
159+
160+
RUN yarn install --mode=skip-build
161+
162+
# Step 2: Copy pre-built shared libraries and backend source.
125163
COPY --from=shared-libs-builder /app/packages/db ./packages/db
126164
COPY --from=shared-libs-builder /app/packages/schemas ./packages/schemas
127165
COPY --from=shared-libs-builder /app/packages/shared ./packages/shared
128166
COPY --from=shared-libs-builder /app/packages/queryLanguage ./packages/queryLanguage
129-
RUN yarn workspace @sourcebot/backend install
167+
COPY ./schemas ./schemas
168+
COPY ./packages/backend ./packages/backend
169+
130170
RUN yarn workspace @sourcebot/backend build
131171

132172
# Upload source maps to Sentry if we have the necessary build-time args.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"packages/*"
55
],
66
"scripts": {
7-
"build": "cross-env SKIP_ENV_VALIDATION=1 yarn workspaces foreach --all --topological run build",
7+
"build": "cross-env SKIP_ENV_VALIDATION=1 yarn workspaces foreach --all --topological --parallel run build",
88
"test": "yarn workspaces foreach --all --topological run test",
99
"dev": "concurrently --kill-others --names \"zoekt,worker,web,mcp,schemas\" 'yarn dev:zoekt' 'yarn dev:backend' 'yarn dev:web' 'yarn watch:mcp' 'yarn watch:schemas'",
1010
"with-env": "cross-env PATH=\"$PWD/bin:$PATH\" dotenv -e .env.development -c --",
@@ -18,7 +18,8 @@
1818
"dev:prisma:studio": "yarn with-env yarn workspace @sourcebot/db prisma:studio",
1919
"dev:prisma:migrate:reset": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:reset",
2020
"dev:prisma:db:push": "yarn with-env yarn workspace @sourcebot/db prisma:db:push",
21-
"build:deps": "yarn workspaces foreach --recursive --topological --from '{@sourcebot/schemas,@sourcebot/db,@sourcebot/shared,@sourcebot/query-language}' run build",
21+
"build:web": "cross-env SKIP_ENV_VALIDATION=1 yarn workspaces foreach --recursive --topological --parallel --from @sourcebot/web run build",
22+
"build:deps": "yarn workspaces foreach --recursive --topological --parallel --from '{@sourcebot/schemas,@sourcebot/db,@sourcebot/shared,@sourcebot/query-language}' run build",
2223
"tool:decrypt-jwe": "yarn with-env yarn workspace @sourcebot/web tool:decrypt-jwe"
2324
},
2425
"devDependencies": {

packages/web/next.config.mjs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { withSentryConfig } from "@sentry/nextjs";
22

3+
const hasSentryConfig = !!(process.env.SENTRY_ORG && process.env.SENTRY_SMUAT);
34

45
/** @type {import('next').NextConfig} */
56
const nextConfig = {
@@ -40,16 +41,36 @@ const nextConfig = {
4041

4142
turbopack: {},
4243

43-
// @see: https://github.com/vercel/next.js/issues/58019#issuecomment-1910531929
44-
...(process.env.NODE_ENV === 'development' ? {
45-
experimental: {
44+
experimental: {
45+
optimizePackageImports: [
46+
"lucide-react",
47+
"@radix-ui/react-icons",
48+
"react-icons",
49+
"recharts",
50+
"date-fns",
51+
"@ai-sdk/react",
52+
"@ai-sdk/openai",
53+
"@ai-sdk/anthropic",
54+
"@ai-sdk/google",
55+
"@ai-sdk/amazon-bedrock",
56+
"@ai-sdk/azure",
57+
"@ai-sdk/deepseek",
58+
"@ai-sdk/google-vertex",
59+
"@ai-sdk/mistral",
60+
"@ai-sdk/xai",
61+
"@ai-sdk/openai-compatible",
62+
"@codemirror/language-data",
63+
"posthog-js",
64+
],
65+
// @see: https://github.com/vercel/next.js/issues/58019#issuecomment-1910531929
66+
...(process.env.NODE_ENV === 'development' ? {
4667
serverActions: {
4768
allowedOrigins: [
4869
'localhost:3000'
4970
]
5071
}
51-
}
52-
} : {}),
72+
} : {}),
73+
},
5374
};
5475

5576
export default withSentryConfig(nextConfig, {
@@ -68,11 +89,11 @@ export default withSentryConfig(nextConfig, {
6889
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
6990

7091
// Upload a larger set of source maps for prettier stack traces (increases build time)
71-
widenClientFileUpload: true,
92+
widenClientFileUpload: hasSentryConfig,
7293

7394
// Automatically annotate React components to show their full name in breadcrumbs and session replay
7495
reactComponentAnnotation: {
75-
enabled: true,
96+
enabled: hasSentryConfig,
7697
},
7798

7899
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.

0 commit comments

Comments
 (0)