@@ -26,7 +26,6 @@ import remarkMdxImages from 'remark-mdx-images';
2626import getAppRegistry from './build/appRegistry' ;
2727import getPackageRegistry from './build/packageRegistry' ;
2828import { apiCategories } from './build/resolveOpenAPI' ;
29- import { BuildTimer , logBuildInfo , OperationAggregator } from './buildTimer' ;
3029import getAllFilesRecursively from './files' ;
3130import remarkDefList from './mdx-deflist' ;
3231import rehypeOnboardingLines from './rehype-onboarding-lines' ;
@@ -66,12 +65,6 @@ if (process.env.CI) {
6665 mkdirSync ( CACHE_DIR , { recursive : true } ) ;
6766}
6867
69- // Track MDX compilation performance
70- const mdxCompilationAggregator = new OperationAggregator ( 'MDX Compilation' , {
71- progressInterval : 500 , // log every 500 compilations (less verbose)
72- slowThreshold : 3000 , // 3 seconds (only really slow pages)
73- } ) ;
74-
7568const md5 = ( data : BinaryLike ) => createHash ( 'md5' ) . update ( data ) . digest ( 'hex' ) ;
7669
7770async function readCacheFile < T > ( file : string ) : Promise < T > {
@@ -153,108 +146,7 @@ export const getVersionsFromDoc = (frontMatter: FrontMatter[], docPath: string)
153146 return [ ...new Set ( versions ) ] ;
154147} ;
155148
156- // Lock file management for cross-worker cache coordination
157- const lockFile = ( cacheFile : string ) =>
158- path . join ( root , '.next' , 'cache' , `${ cacheFile } .lock` ) ;
159-
160- /**
161- * Try to load frontmatter from disk cache created by first worker
162- * If cache is being built, wait for it to complete
163- */
164- async function loadFrontmatterFromCache (
165- cacheFile : string
166- ) : Promise < FrontMatter [ ] | null > {
167- const cachePath = path . join ( root , '.next' , 'cache' , cacheFile ) ;
168- const lockPath = lockFile ( cacheFile ) ;
169-
170- // Check if another worker is building the cache
171- let waitAttempts = 0 ;
172- while ( waitAttempts < 30 ) {
173- // Wait up to 15 seconds
174- try {
175- // If lock file exists, another worker is building cache
176- await access ( lockPath ) ;
177- await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
178- waitAttempts ++ ;
179- continue ;
180- } catch {
181- // No lock file, proceed
182- break ;
183- }
184- }
185-
186- try {
187- const cacheData = JSON . parse ( await readFile ( cachePath , 'utf-8' ) ) ;
188- const age = Date . now ( ) - cacheData . timestamp ;
189-
190- // Cache is valid for 1 hour (in case of incremental builds)
191- if ( age < 60 * 60 * 1000 ) {
192- logBuildInfo (
193- `✓ Loaded ${ cacheData . frontmatter . length } files from worker cache (${ ( age / 1000 ) . toFixed ( 0 ) } s old)`
194- ) ;
195- return cacheData . frontmatter ;
196- }
197- return null ;
198- } catch {
199- // Cache doesn't exist or is invalid
200- return null ;
201- }
202- }
203-
204- /**
205- * Save frontmatter to disk cache for other workers
206- */
207- async function saveFrontmatterToCache (
208- cacheFile : string ,
209- frontmatter : FrontMatter [ ]
210- ) : Promise < void > {
211- try {
212- const cachePath = path . join ( root , '.next' , 'cache' , cacheFile ) ;
213- const cacheData = {
214- timestamp : Date . now ( ) ,
215- frontmatter,
216- } ;
217- await mkdir ( path . dirname ( cachePath ) , { recursive : true } ) ;
218- await import ( 'fs/promises' ) . then ( fs =>
219- fs . writeFile ( cachePath , JSON . stringify ( cacheData ) )
220- ) ;
221- logBuildInfo ( `✓ Saved ${ frontmatter . length } files to worker cache for other workers` ) ;
222- } catch ( error ) {
223- // Non-fatal - other workers will just build their own
224- // eslint-disable-next-line no-console
225- console . error ( 'Failed to save frontmatter cache:' , error ) ;
226- }
227- }
228-
229149async function getDocsFrontMatterUncached ( ) : Promise < FrontMatter [ ] > {
230- // Try to load from worker cache first
231- const cached = await loadFrontmatterFromCache ( 'frontmatter.json' ) ;
232- if ( cached ) {
233- return cached ;
234- }
235-
236- // Create lock file so other workers wait
237- const lockPath = lockFile ( 'frontmatter.json' ) ;
238- let lockFd : number | null = null ;
239- try {
240- // Try to create lock file atomically
241- const { open} = await import ( 'fs/promises' ) ;
242- lockFd = await open ( lockPath , 'wx' )
243- . then ( fh => fh . fd )
244- . catch ( ( ) => null ) ;
245-
246- // If we couldn't create lock, another worker is building - wait for them
247- if ( ! lockFd ) {
248- const cached2 = await loadFrontmatterFromCache ( 'frontmatter.json' ) ;
249- if ( cached2 ) {
250- return cached2 ;
251- }
252- // Cache not ready, fall through to build ourselves
253- }
254- } catch {
255- // Lock file issues are non-fatal
256- }
257-
258150 const frontMatter = await getAllFilesFrontMatter ( ) ;
259151
260152 const categories = await apiCategories ( ) ;
@@ -287,50 +179,10 @@ async function getDocsFrontMatterUncached(): Promise<FrontMatter[]> {
287179 }
288180 } ) ;
289181
290- // Save to cache for other workers (if we got the lock)
291- if ( lockFd ) {
292- await saveFrontmatterToCache ( 'frontmatter.json' , frontMatter ) ;
293- // Remove lock file
294- try {
295- const { unlink} = await import ( 'fs/promises' ) ;
296- await unlink ( lockPath ) ;
297- } catch {
298- // Non-fatal
299- }
300- }
301-
302182 return frontMatter ;
303183}
304184
305185export async function getDevDocsFrontMatterUncached ( ) : Promise < FrontMatter [ ] > {
306- // Try to load from worker cache first
307- const cached = await loadFrontmatterFromCache ( 'frontmatter-dev.json' ) ;
308- if ( cached ) {
309- return cached ;
310- }
311-
312- // Create lock file so other workers wait
313- const lockPath = lockFile ( 'frontmatter-dev.json' ) ;
314- let lockFd : number | null = null ;
315- try {
316- // Try to create lock file atomically
317- const { open} = await import ( 'fs/promises' ) ;
318- lockFd = await open ( lockPath , 'wx' )
319- . then ( fh => fh . fd )
320- . catch ( ( ) => null ) ;
321-
322- // If we couldn't create lock, another worker is building - wait for them
323- if ( ! lockFd ) {
324- const cached2 = await loadFrontmatterFromCache ( 'frontmatter-dev.json' ) ;
325- if ( cached2 ) {
326- return cached2 ;
327- }
328- // Cache not ready, fall through to build ourselves
329- }
330- } catch {
331- // Lock file issues are non-fatal
332- }
333-
334186 const folder = 'develop-docs' ;
335187 const docsPath = path . join ( root , folder ) ;
336188 const files = await getAllFilesRecursively ( docsPath ) ;
@@ -358,18 +210,6 @@ export async function getDevDocsFrontMatterUncached(): Promise<FrontMatter[]> {
358210 )
359211 ) . filter ( isNotNil ) ;
360212
361- // Save to cache for other workers (if we got the lock)
362- if ( lockFd ) {
363- await saveFrontmatterToCache ( 'frontmatter-dev.json' , frontMatters ) ;
364- // Remove lock file
365- try {
366- const { unlink} = await import ( 'fs/promises' ) ;
367- await unlink ( lockPath ) ;
368- } catch {
369- // Non-fatal
370- }
371- }
372-
373213 return frontMatters ;
374214}
375215
@@ -383,12 +223,10 @@ export function getDevDocsFrontMatter(): Promise<FrontMatter[]> {
383223}
384224
385225async function getAllFilesFrontMatter ( ) : Promise < FrontMatter [ ] > {
386- const timer = new BuildTimer ( 'getAllFilesFrontMatter' ) ;
387226 const docsPath = path . join ( root , 'docs' ) ;
388227 const files = await getAllFilesRecursively ( docsPath ) ;
389228 const allFrontMatter : FrontMatter [ ] = [ ] ;
390229
391- const readFilesTimer = new BuildTimer ( 'Reading MDX frontmatter' ) ;
392230 await Promise . all (
393231 files . map (
394232 limitFunction (
@@ -414,10 +252,8 @@ async function getAllFilesFrontMatter(): Promise<FrontMatter[]> {
414252 )
415253 )
416254 ) ;
417- readFilesTimer . end ( true ) ; // Silent - we'll show in summary
418255
419256 // Add all `common` files in the right place.
420- const commonFilesTimer = new BuildTimer ( 'Processing common platform files' ) ;
421257 const platformsPath = path . join ( docsPath , 'platforms' ) ;
422258 for await ( const platform of await opendir ( platformsPath ) ) {
423259 if ( platform . isFile ( ) ) {
@@ -561,8 +397,7 @@ async function getAllFilesFrontMatter(): Promise<FrontMatter[]> {
561397 ) ;
562398 }
563399 }
564- commonFilesTimer . end ( true ) ; // Silent - we'll show in summary
565- timer . end ( ) ;
400+
566401 return allFrontMatter ;
567402}
568403
@@ -600,8 +435,6 @@ export const addVersionToFilePath = (filePath: string, version: string) => {
600435} ;
601436
602437export async function getFileBySlug ( slug : string ) : Promise < SlugFile > {
603- const compileStart = Date . now ( ) ;
604-
605438 // no versioning on a config file
606439 const configPath = path . join ( root , slug . split ( VERSION_INDICATOR ) [ 0 ] , 'config.yml' ) ;
607440
@@ -707,71 +540,54 @@ export async function getFileBySlug(slug: string): Promise<SlugFile> {
707540 source . includes ( '<LambdaLayerDetail' ) ;
708541
709542 if ( process . env . CI ) {
710- // Build cache key from: source content + registry data (if needed) + build ID
543+ // Build cache key from source content
711544 const sourceHash = md5 ( source ) ;
712545
713546 // For files that depend on registry, include registry version in cache key
714- let registryHash = '' ;
547+ // This prevents serving stale content when registry is updated
715548 if ( dependsOnRegistry ) {
716549 try {
717- // Get registry data (already cached in memory by each worker)
550+ // Get registry data (cached in memory per worker)
718551 const [ apps , packages ] = await Promise . all ( [
719552 getAppRegistry ( ) ,
720553 getPackageRegistry ( ) ,
721554 ] ) ;
722555 // Hash the registry data to create a stable version identifier
723- registryHash = md5 ( JSON . stringify ( { apps, packages} ) ) ;
556+ const registryHash = md5 ( JSON . stringify ( { apps, packages} ) ) ;
557+ cacheKey = `${ sourceHash } -${ registryHash } ` ;
724558 } catch ( err ) {
725- // If registry fetch fails, skip caching
559+ // If registry fetch fails, skip caching for safety
726560 // eslint-disable-next-line no-console
727- console . warn ( `Failed to fetch registry for cache key: ${ err . message } ` ) ;
728- registryHash = 'no-registry' ;
561+ console . warn (
562+ `Failed to fetch registry for cache key, skipping cache: ${ err . message } `
563+ ) ;
564+ cacheKey = null ;
729565 }
566+ } else {
567+ // Regular files without registry dependencies
568+ cacheKey = sourceHash ;
730569 }
731570
732- // Use VERCEL_GIT_COMMIT_REF (branch name) for cache key
733- // This allows cache to persist across commits on the same branch!
734- // Falls back to VERCEL_DEPLOYMENT_ID for non-git deploys, then BUILD_ID, then 'local'
735- const cacheVersion =
736- process . env . VERCEL_GIT_COMMIT_REF ||
737- process . env . VERCEL_DEPLOYMENT_ID ||
738- process . env . BUILD_ID ||
739- 'local' ;
740-
741- // Cache key: source + registry version + branch/build identifier
742- // This ensures:
743- // 1. All workers in the same build share cache (same cacheVersion)
744- // 2. Cache persists across commits on same branch (same VERCEL_GIT_COMMIT_REF)
745- // 3. Cache is invalidated when registry changes (registryHash changes)
746- // 4. Cache is invalidated when source changes (sourceHash changes)
747- cacheKey = dependsOnRegistry
748- ? `${ sourceHash } -${ registryHash } -${ cacheVersion } `
749- : `${ sourceHash } -${ cacheVersion } ` ;
750-
751- cacheFile = path . join ( CACHE_DIR , `${ cacheKey } .br` ) ;
752- assetsCacheDir = path . join ( CACHE_DIR , cacheKey ) ;
571+ if ( cacheKey ) {
572+ cacheFile = path . join ( CACHE_DIR , `${ cacheKey } .br` ) ;
573+ assetsCacheDir = path . join ( CACHE_DIR , cacheKey ) ;
753574
754- try {
755- const [ cached , _ ] = await Promise . all ( [
756- readCacheFile < SlugFile > ( cacheFile ) ,
757- cp ( assetsCacheDir , outdir , { recursive : true } ) ,
758- ] ) ;
759- if ( dependsOnRegistry ) {
760- // eslint-disable-next-line no-console
761- console . info (
762- `✓ Using cached registry-dependent file: ${ sourcePath } (branch: ${ cacheVersion } , registry: ${ registryHash . slice ( 0 , 8 ) } )`
763- ) ;
764- }
765- return cached ;
766- } catch ( err ) {
767- if (
768- err . code !== 'ENOENT' &&
769- err . code !== 'ABORT_ERR' &&
770- err . code !== 'Z_BUF_ERROR'
771- ) {
772- // If cache is corrupted, ignore and proceed
773- // eslint-disable-next-line no-console
774- console . warn ( `Failed to read MDX cache: ${ cacheFile } ` , err ) ;
575+ try {
576+ const [ cached , _ ] = await Promise . all ( [
577+ readCacheFile < SlugFile > ( cacheFile ) ,
578+ cp ( assetsCacheDir , outdir , { recursive : true } ) ,
579+ ] ) ;
580+ return cached ;
581+ } catch ( err ) {
582+ if (
583+ err . code !== 'ENOENT' &&
584+ err . code !== 'ABORT_ERR' &&
585+ err . code !== 'Z_BUF_ERROR'
586+ ) {
587+ // If cache is corrupted, ignore and proceed
588+ // eslint-disable-next-line no-console
589+ console . warn ( `Failed to read MDX cache: ${ cacheFile } ` , err ) ;
590+ }
775591 }
776592 }
777593 }
@@ -915,25 +731,9 @@ export async function getFileBySlug(slug: string): Promise<SlugFile> {
915731 } ) ;
916732 }
917733
918- // Track compilation time
919- const compileDuration = Date . now ( ) - compileStart ;
920- mdxCompilationAggregator . track ( slug , compileDuration ) ;
921-
922734 return resultObj ;
923735}
924736
925- // Log final MDX compilation stats when process exits
926- if ( typeof process !== 'undefined' ) {
927- process . on ( 'beforeExit' , ( ) => {
928- const stats = mdxCompilationAggregator . getStats ( ) ;
929- if ( stats . count > 0 ) {
930- logBuildInfo ( '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' ) ;
931- mdxCompilationAggregator . logFinalSummary ( ) ;
932- logBuildInfo ( '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━' ) ;
933- }
934- } ) ;
935- }
936-
937737const fileBySlugCache = new Map < string , Promise < SlugFile > > ( ) ;
938738
939739/**
0 commit comments